Git Product home page Git Product logo

go-msgauth's Introduction

go-msgauth

Go Reference builds.sr.ht status

A Go library and tools to authenticate e-mails.

Libraries

Tools

A few tools are included in go-msgauth:

  • dkim-keygen: generate a DKIM key
  • dkim-milter: a mail filter to sign and verify DKIM signatures
  • dkim-verify: verify a DKIM-signed email
  • dmarc-lookup: lookup the DMARC policy of a domain

License

MIT

go-msgauth's People

Contributors

56kbs avatar agwa avatar dimmaq avatar emersion avatar foxcpp avatar huicao avatar ludusrusso avatar mback2k avatar pierrre avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-msgauth's Issues

DKIM t=s key flag not supported

When s is in a key's flags list, then:

Any DKIM-Signature header fields using the "i=" tag MUST have the same domain value on the right-hand side of the "@" in the "i=" tag and the value of the "d=" tag. That is, the "i=" domain MUST NOT be a subdomain of "d=". Use of this flag is RECOMMENDED unless subdomaining is required.

(RFC 6376 Section 3.6.1)

Currently, go-msgauth's DKIM verifier unconditionally allows the i= domain to be a subdomain of d=.

How to use your library?

Hi, How to use your library? Can you give us a concrete example with the domain gmail.com?

NB: It is not issue

dkim.Sign blocks indefinitely on early errors

Issue description

When the goroutine used in dkim.NewSigner returns early due to some error, the done channel never gets closed and thus the defer s.Close() in dkim.Sign blocks undefinitely.

Steps to reproduce the issue

This is a failing test:

package dkim_test

import (
	"bytes"
	"crypto/x509"
	"encoding/pem"
	"testing"

	"github.com/emersion/go-msgauth/dkim"
)

func TestSignInvalidMessage(t *testing.T) {

	rawMessage := bytes.NewBuffer([]byte("this is an invalid message"))

	key := []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA2lasFKCWoAn6nBZ1J4WZP/5T2dfqHPW2iye5vzXyAFLTNr7v
nFYYj8XcUldneSXWzbvh47oZlyA3MEFn0ICrtg2nRXSeh6yjvuicoslNOtB1Z2lp
sjBJODIrhxRs/qSgv3h0AtlBXw8nFBUglaJrdxRo3AeWNIoSo0Ai9V2lTdi6Um4H
FL0eI2E5jg1G8ALt+g8JSp+hZh7y8z4xrW/8pikK71BTVhfoVqTwItS65uNRtzmw
27ymGNTJ62wfXEYilezDsOXamgvAMbGkPCAYMXvO0ChmKQtUdx+IAxba7pfFIobE
Gvt0eABOhlCIuplQwBbEhK1D30goV4KcNy6q3QIDAQABAoIBAQCRezbl96rlsECA
SKZ/UxGuBjSw7qFb8o1TY4Ds23EIrid2TvsxXFy5T8liREL6AjCCnTICnzn17M1Z
JfuafmHryGUwbmhDVtE0n6HfBeqjycqwwRhgVrQy8Zr3QrDta5yAeC40x7Y7NMmB
JCK2EacxjTPhiFyZXXbVuCKTA3blystm76JkXSov6i5wIpF6o5cvDvAkVRZSz0zY
XvkdGgGTjQlDLfiq7EGNipd8V7ikjS+o4egugc8LQuJ3cC/gEuMibU+ULuvkIhTy
6+Ylo122NmQnKau5WVNecXyMj3ltQqNNj2WPYFuiWtNW/reB5jX1exVZ/RzDwYiy
rj3W1kyBAoGBAP2BEBIg6CfJgQjw4ymMTqAhmG89RexKZS/9nipLDjL+MjGx2mJs
msfxboY9aXvGs6nS0f//dd1B83iMWrQNhrFbGdje8dvQsSIW+geFWyNHsKC95pOV
417TrtTEgPSwaljbpyeE5+Zd9L2XQAbAy6ZUTHUEfWx2azWY0kNQuRH1AoGBANx8
+jG152b5NsyWZ7UmDILThsNl76EJNt4XuNRzgWHV1wXLDeVSm7O+FgAM9bAIxc2E
TJOpxxeNurWm5vqzZqqgeb/4BUz5KAHCcp3yoKMQjixrtPeJiRt2z31YW0l6QU4S
kJ1qBtPlPt00dqPJXqfj6rON3fZnrVe9x9E80txJAoGAV9VG7zENnvN3TNTBsFyX
xW2+dhRhzLv+EUGrcnXs5ogidgtsYhvFCS/CnqpaiPNQvq936V3mxZGbPRJMPwRM
vdiVvQmJ/SJyrSAO41o2OKQXM6p4YHxXejyX38px79XMExuP7+ZhvvSg3quwGGbm
aKvejdDPcCwbe0eG2qH2bZ0CgYBstVHF4KHOq2DRTfaj4baZaiEvhbq38wsSRS/j
z28jBYOWX57iSfBqlnXSYJFh0XF0+p2m0DZQ7pf3p+qKAJnF1okwlOBIKzAGbhCE
v3Nj8m2miRQYV785w0JZ0o5vk89O5uhWNEhZgNWVyqAT8NyyejTlgjTFoChe8jrq
dsqfwQKBgQCVptrO3fMd+SaY3KBhw0ebEfJT7Q1mTv0LufIYpKxJ7Hb3+hRwJNoO
UdVdRNFimFJDULU76KuurvPEopZfI94uQB8zgzn/ENYWgWiIo4f1H/BqVzO4vEdN
01OSrsZ2QwyaBffRNc2QxX7ZGALmoaW7sJ+yXTf73+yS0tGQvyjfgQ==
-----END RSA PRIVATE KEY-----
	   `)

	block, _ := pem.Decode([]byte(key))
	if block == nil {
		panic("no key")
	}

	dkimKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		panic("parse err")
	}

	opts := &dkim.SignOptions{
		Domain:                 "example.com",
		Selector:               "selector",
		Signer:                 dkimKey,
		HeaderCanonicalization: dkim.CanonicalizationRelaxed,
		BodyCanonicalization:   dkim.CanonicalizationSimple,
		HeaderKeys:             []string{"From", "To"},
	}

	signedMessage := &bytes.Buffer{}
	if err := dkim.Sign(signedMessage, rawMessage, opts); err != nil {
		panic(err)
	}
}

What's the expected result?

It should return an error.

What's the actual result?

It blocks indefinitely.

Additional details

I would propose moving the close(done) to the beginning of the goroutine as defer close(done) in https://github.com/emersion/go-msgauth/blob/master/dkim/sign.go#L270

dkim: Provide a low-level Verify function for more control

Use-case

I'm thinking of ways to make maddys DKIM support work well and it needs quite a lot of control over verification process for that. Here is my experience report so far:

  • maddy uses replaceable DNS resolver implementations and also uses context.Context for cancellation of checks code running in parallel.

Non-cancelable network I/O done by dkim.Verify makes latter less useful. Also, it is done not using
custom DNS resolver implementation provided by maddy.

  • maddy operates on already parsed header.

Currently I have to serialize header to bytes.Buffer and then pass it to dkim.Verify.
Allowing to pass already parsed header would be nice.

It is not that important, as I can keep non-parsed header blob around and pass it to dkim.Verify, thus eliminating one part of the problem and hoping other is not that significant.

Proposed solution(s)

  1. Expose low-level functionality
    Expose internal structures and separate operations such as: Parse DKIM key record, parse DKIM signature header, verify the signature using provided header subset and body reader.

With that I would simply pass DKIM-Signature fields to dkim.ParseField, then fetch keys using replaceable DNS resolver with context.Context support, let dkim.ParseKey parse them and then finally pass everything to the dkim.VerifySig to do the actual verification.

Even more: I can take the subset of fields required for signature and pass it to dkim.VerifySig instead of resorting to serialize-then-parse hack. That could be complicated though, so I guess being able to simply pass go-message/textproto.Header is better..

This way I even will be able to distinguish different failure types and act accordingly (e.g. increase the "quarantine score" for messages with broken signatures, immediately quarantine messages with a signature without a corresponding key in DNS, but only if this is not caused by a temporary resolution error).

... or ...

  1. Add support for custom resolver (#4), context.Context, possibly address "already parsed header" case or leave it as is.

Tell me what you think. I am willing to help with the implementation of whatever solution you think is better.

RSA Public Key Format Inconsistency

RFC 6376 Section 3.6.1 states that RSA public keys are encoded in the TXT record as "an ASN.1 DER-encoded [ITU-X660-1997] RSAPublicKey". This is the encoding produced by the dkim-keygen command.

Unfortunately, RFC 6376 contradicts itself in Appendix C, where it shows a public key in SubjectPublicKeyInfo format instead. Although Appendix C is only informative, it seems that other implementations have adopted it. opendkim's key generation tool uses SubjectPublicKeyInfo, as do the DKIM records for Gmail and Fastmail. Erratum 3017 has been filed against RFC 6376 proposing that both RSAPublicKey and SubjectPublicKeyInfo be allowed in the TXT record.

Currently, go-msgauth's DKIM verifier expects RSA public keys to use SubjectPublicKeyInfo format. If you try to verify a signature from a domain which uses RSAPublicKey format instead, you get this error:

dkim: key syntax error: x509: failed to parse public key (use ParsePKCS1PublicKey instead for this key format)

I propose the following fix:

  1. go-msgauth's verifier should accept both formats (i.e. it should try parsing the key with both x509.ParsePKIXPublicKey and x509.ParsePKCS1PublicKey) in accordance with Erratum 3017.
  2. dkim-keygen should produce SubjectPublicKeyInfo format. Given the use of SubjectPublicKeyInfo with providers like Gmail and Fastmail, there are probably more DKIM implementations that only accept SubjectPublicKeyInfo than implementations that only accept RSAPublicKey.

I'm happy to submit a PR for this.

Remove Return-Path from header sign

According to rfc6376:

5.4. Determine the Header Fields to Sign

The From header field MUST be signed (that is, included in the "h="
tag of the resulting DKIM-Signature header field). Signers SHOULD
NOT sign an existing header field likely to be legitimately modified
or removed in transit. In particular, [RFC5321] explicitly permits
modification or removal of the Return-Path header field in transit.

This makes the dkim invalid

Word wrap on headers

Hi, I started playing with this lib today, so far so good...

But in some cases I got DKIM-Result: fail (bad signature) when signing. I think it is because of the word wrapping on the headers.
I'm still researching how this should work, but so far it seems like a bug.

For example: this header is ok...

"DKIM-Signature: a=rsa-sha256; bh=1IgjdN5a9wQkm07TioMOwKf0/FIi4DDu2oUc6NEp6E\r\n
 c=; c=relaxed/relaxed; d=XXXXXXXXXX.com.br; h=From:To:Subject:Message-ID; s\r\n
 =2019; t=1573137354; v=1; b=z0oq308f6gtrdpGQp8RnYlvuIhCP/MTAP6x7jZd11hyBDrI\r\n
 zXrFKBeg4Mg4/2KhxM7/yJ5frJ6qmApdDRTiRFRyOwj/QRvS6lZT1bLyblQLnZJEmJjCO4KK1L4\r\n
 A4QZZorA2IuEA9h6k7XfA5FGydKEUwzXZOQCojCI950HSIaQM=;\r\n
..."

If I add another header on the mail message just to change the signature a little bit.

"DKIM-Signature: a=rsa-sha256; bh=1IgjdN5a9wQkm07TioMOwKf0/FIi4DDu2oUc6NEp6E\r\n
 c=; c=relaxed/relaxed; d=XXXXXXXXXX.com.br; h=From:To:Subject:Message-ID:AB\r\n
 CDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQ; s=2019;\r\n
  t=1573138398; v=1; b=Ccb4SQE56OROMpx8Q8bFgsSz3lTypUcP/p/vSBPL2CXQJhT9evQsF\r\n
 ROgsvTld1gXNbhFWp9wyjDXIsUBQS1L3LiWMeZpXfTcJx6MkpLG2h0IzjPBQ/jKnPT56CtKOdj2\r\n
 SUQZ/GITsvQhurPgWfTq6G5jrMeMu6ZdXH3Vw/1GkmU=;\r\n
...

The DKIM starts to failing, probably because of that '\r\n' that was inserted on the middle of the header text.

Also, if the header had the right size the '\r\n' will be on the selector.
Like this: s=2\r\n 019 and will result in a DNS failure.

"DKIM-Signature: a=rsa-sha256; bh=1IgjdN5a9wQkm07TioMOwKf0/FIi4DDu2oUc6NEp6E\r\n
 c=; c=relaxed/relaxed; d=XXXXXXXXXX.com.br; h=From:To:Subject:Message-ID:AB\r\n
 CDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABC; s=20\r\n
 19; t=1573138139; v=1; b=QyQd6bbOy8OI1+r6t7g16CWU2kjS6tCK2ZOBsxXhEMnACZKVY3\r\n
 ffO/5+RRgTOG748DO0zjQnuqOKPLF8O+DYQs/1fnkKbt04knC+1wNT5wK2ILgPERDlr3PG7rsNp\r\n
 juE0VCkkVMgbVmamhb02qdevU/IX2qRpVOws0vr1MnqiG8=;\r\n
...

I'm not sure how others handle this... I'll keep looking.
Let me know if you have any clue.


AFAIK we can't have FWS (folded white spaces on that header value).
https://tools.ietf.org/html/rfc6376

This is the result from the same header being handled by nodemailer:

"DKIM-Signature: a=rsa-sha256;\r\n
 bh=1IgjdN5a9wQkm07TioMOwKf0/FIi4DDu2oUc6NEp6Ec=; c=relaxed/relaxed;\r\n
 d=XXXXXXXXXX.com.br;\r\n
 h=From:To:Subject:Message-ID:ABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQ;\r\n
 s=2019; t=1573138398; v=1;\r\n
 b=Ccb4SQE56OROMpx8Q8bFgsSz3lTypUcP/p/vSBPL2CXQJhT9evQsFROgsvTld1gXNbhFWp9wyjDXIsUBQS1L3LiWMeZpXfTcJx6MkpLG2h0IzjPBQ/jKnPT56CtKOdj2SUQZ/GITsvQhurPgWfTq6G5jrMeMu6ZdXH3Vw/1GkmU=;\r\n

DKIM failing for few content

Hi, I am using go-msgauth for DKIM signing, great work congratulations !!!
I have problems with DKIM Signature in Gmail for a few email contents, for these content DKIM signature keep getting failed.

for an example :

ARC-Authentication-Results: i=1; mx.google.com;
       dkim=neutral (body hash did not verify) [email protected] header.s=mail header.b=T6i602+P;
       spf=softfail (google.com: domain of transitioning [email protected] does not designate 35.205.54.170 as permitted sender) smtp.mailfrom="[email protected]";
       dmarc=fail (p=QUARANTINE sp=REJECT dis=QUARANTINE) header.from=sendinblue.com
Return-Path: <[email protected]>
Received: from af.d.mailin.fr (170.54.205.35.bc.googleusercontent.com. [35.205.54.170])
        by mx.google.com with ESMTPS id s9si2177413wrw.217.2020.11.04.06.51.40
        for <[email protected]>
        (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
        Wed, 04 Nov 2020 06:51:40 -0800 (PST)
Received-SPF: softfail (google.com: domain of transitioning [email protected] does not designate 35.205.54.170 as permitted sender) client-ip=35.205.54.170;
Authentication-Results: mx.google.com;
       dkim=neutral (body hash did not verify) [email protected] header.s=mail header.b=T6i602+P;
       spf=softfail (google.com: domain of transitioning [email protected] does not designate 35.205.54.170 as permitted sender) smtp.mailfrom="[email protected]";
       dmarc=fail (p=QUARANTINE sp=REJECT dis=QUARANTINE) header.from=sendinblue.com
DKIM-Signature: a=rsa-sha256; bh=tsJuZ3BBjTNiYd1Pko8h9O45UrrLuZSBfAKIytHhm2o=; c=relaxed/relaxed; d=sendinblue.com; h=to:cc:from:reply-to:subject:date:mime-version:content-type:list-id:list-unsubscribe:x-csa-complaints:list-unsubscribe-post:message-id:sender:x-sib-id:x-mailin-client:x-mailin-campaign:feedback-id; q=dns/txt; s=mail; t=1604501499; v=1; b=T6i602+P7mdrFC4aPd9dKM/58FXn60O9mj6x+7LdlvBQrUQIrPUOL4yjhtkn7fUAqxvs30Vt DihM3qpitLU+zh8aMOQQT/WNoThsxwJC/QRzWxdilJxVKj6Sni6ekbrbWhzsPTD02sSZgaLq9Cg xydC4YDTgmYjGTxh43Qu1Na8=

complete headers are:

DKIM-Signature: a=rsa-sha256; bh=tsJuZ3BBjTNiYd1Pko8h9O45UrrLuZSBfAKIytHhm2o=; c=relaxed/relaxed; d=sendinblue.com; h=to:cc:from:reply-to:subject:date:mime-version:content-type:list-id:list-unsubscribe:x-csa-complaints:list-unsubscribe-post:message-id:sender:x-sib-id:x-mailin-client:x-mailin-campaign:feedback-id; q=dns/txt; s=mail; t=1604501499; v=1; b=T6i602+P7mdrFC4aPd9dKM/58FXn60O9mj6x+7LdlvBQrUQIrPUOL4yjhtkn7fUAqxvs30Vt DihM3qpitLU+zh8aMOQQT/WNoThsxwJC/QRzWxdilJxVKj6Sni6ekbrbWhzsPTD02sSZgaLq9Cg xydC4YDTgmYjGTxh43Qu1Na8=
To: <[email protected]>
Subject: TEST - TOKEN
Content-Type: multipart/related; boundary="-------?=_15190-7781879174214"
Date: Wed, 04 Nov 2020 14:51:39 +0000
Feedback-ID: 185.41.28.6:2039507_20:2039507:Sendinblue
From: SendInBlue <[email protected]>
List-Id: MjAzOTUwNy05LTA= <MjAzOTUwNy05LTA=.list-id.mailin.fr>
List-Unsubscribe: <mailto:[email protected]?subject=unsub-q8f58wqygj&body=q8f58wqygj>,<https://r-auto-staging.51b.tech:4443/mk/un/li/OzndvdDOLEokf1e4v6_EcvlIfdLOPM7UEIkT8y2Qz1Is0lAlpXwDqIP4tUB8nJm_TLLR2QXxcNP05U-aEMZIHTue_kleNlwVhy9c8oKzw4WJUqThT71cCUtifsq_iXF-fnktEkUy1Jnj4si5kohK6zOUzxyJTTgZ>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
MIME-Version: 1.0
Message-Id: <[email protected]>
Precedence: bulk
Reply-To: [email protected]
X-Mailer: Sendinblue
X-Mailin-Campaign: 20
X-Mailin-Client: 2039507
X-sib-id: _YOHDLQjhQ2BrGXt57vL3A4UFIAMU8R9CSPxtOoJn865p0B2RADz3R3oIYC5c-YWGG_JPVf9N917KoSGd0PbI1Ol45-lALTkOTh5YmjUucT_Gtn8cUX4s4agq_KPIXaJ4EJFvkFYcGSeVYfdVuoxxrtVwP6VotoAoN0XhflPUB2FDQ

PS: DKIM is not failing for every email and for every domain. for few contents it keeps failing
Any Idea? Thanks

dkim: ed25519 signing seems to be broken

ed25519: cannot sign hashed message

Setting hash to 0 for ed25519 and adding corresponding tests seems to reveal other problems (such as being unable to verify generated signatures). Need to look into it.

Extract only signature instead of complete body

Hi, this is not exactly an issue but an improvement.
There should be a flag where if someone only needs the DKIM signature instead of complete data (header + body).

r := strings.NewReader(mailString)

options := &dkim.SignOptions{
	Domain: "example.org",
	Selector: "brisbane",
	Signer: privateKey,
}

var b bytes.Buffer
if err := dkim.Sign(&b, r, options); err != nil {
	log.Fatal(err)
}

This provides complete data.

options := &dkim.SignOptions{
		Domain:                 domain,
		Selector:               defaultKeySelector,
		Signer:                 privateKey,
	}

Here we can add another field like dkimSign : true which only return the signature.

compare with EqualFold instead of ToLower

I did a profiling of my application, and noticed that msgauth/dkim is comparing strings with ToLower.

go-msgauth/dkim/header.go

Lines 152 to 158 in e98a2ee

key = strings.ToLower(key)
at := p.picked[key]
for i := len(p.h) - 1; i >= 0; i-- {
kv := p.h[i]
k, _ := parseHeaderField(kv)
if strings.ToLower(k) != key {

if strings.ToLower(k) == "from" {

Each time this function is called, it allocates a new string, which creates more work for the garbage collector.
I think there is an "allocation free" alternative: EqualFold.

WDYT ?
I can submit a benchmark if needed.

dkim: generated header not properly formatted

Hi, I'm playing with this library and I'm incurring in a strange issue.

In some case, when I try to generate a DKIM signature, the generated header get an extra "\r\n" that makes the mail invalid.

For istance, if I try to sign this email:

From: [email protected]
To: [email protected]
Subject: Send Test
Message-ID: <xxxxx@xxxxx>
Return-Path: <test@test>
Date: Mon, 09 Mar 2020 17:04:25 +0100

Message Body

I got this signed message

DKIM-Signature: a=rsa-sha256; bh=opmXtA9gMR670DN8CmqNEL8Q4RBdtB/ULgaN/SkjFn
 0=; c=simple/simple; d=mail.ludusrusso.space; h=From:To:Subject:Message-ID:
 Return-Path:Date; s=brisbane; t=1583769865; v=1; b=JHLkTc5vtTRA4udN9sAMcPWx
 TLLfqQ6ry++3GLbcWjhR769N4hK7kaL2P1iseXaoVbQpxVGzZYz9x5P8q9brfYZEuuQciWPvk1n
 k99E685amWpMsSqUnsHWifpeFtZpNHwPSVWJp03Kmeq2wE85+Fauq/P+7bVfRa/N1hUIFUpk=;

From: [email protected]
To: [email protected]
Subject: Send Test
Message-ID: <xxxxx@xxxxx>
Return-Path: <test@test>
Date: Mon, 09 Mar 2020 17:04:25 +0100

Message Body

notice that the extra black line added before From header makes the message to be worngly interpreded by a receiving SMTP.

The issues seems related to the length of the header, in fact if I try to add (or remove) headers from the message it works properly:

DKIM-Signature: a=rsa-sha256; bh=bvW0aiEWdP0ie2rawBb+IiTxlHI9KgEIYjEYTGzMRa
 0=; c=simple/simple; d=mail.ludusrusso.space; h=X-Extra-Header:From:To:Subj
 ect:Message-ID:Return-Path:Date; s=brisbane; t=1583770035; v=1; b=Rer+wvDGH
 tVS7SwYC5kdouGz6Su0B0iEvegWcwQe4GcMERi5QfJWV8hDnjappa8KX1fst2YmhFjUST+Ai3zM
 dyH1iKJiIm78Yt0n3c1f/95bP9Ey6xc1t1zYcJm3t/zQ8Sho/XjPRqdilOJYXPZB18y5JILnV67
 69eKFcsnzmic=;
X-Extra-Header: extra header
From: [email protected]
To: [email protected]
Subject: Send Test
Message-ID: <xxxxx@xxxxx>
Return-Path: <test@test>
Date: Mon, 09 Mar 2020 17:07:15 +0100

Longer message body

Note the extra header here.

If you give me some tips, I could try to solve the issue and submit a PR!
It seems to me that the issue is due to the 76 char limitation of the message line.

dkim: export isFail

Hi,

It would be nice to have the private function dkim.isFail() public available. In case of a "bad signature (body hash mismatched; bh=)" dkim.IsPermfail() and dkim.IsTempfail() returns false.

Thanks
Matthias

reduce relaxedBodyCanonicalizer allocations

In my application, nearly 50% of my memory allocations (in count) are caused by relaxedBodyCanonicalizer.

type relaxedBodyCanonicalizer struct {
w io.Writer
crlfBuf []byte
wspBuf []byte
written bool
crlfFixer crlfFixer
}
func (c *relaxedBodyCanonicalizer) Write(b []byte) (int, error) {
written := len(b)
b = c.crlfFixer.Fix(b)
canonical := make([]byte, 0, len(b))
for _, ch := range b {
if ch == ' ' || ch == '\t' {
c.wspBuf = append(c.wspBuf, ch)
} else if ch == '\r' || ch == '\n' {
c.wspBuf = nil
c.crlfBuf = append(c.crlfBuf, ch)
} else {
if len(c.crlfBuf) > 0 {
canonical = append(canonical, c.crlfBuf...)
c.crlfBuf = nil
}
if len(c.wspBuf) > 0 {
canonical = append(canonical, ' ')
c.wspBuf = nil
}

Especially lines 146 and 149.

I think this code could be optimized.
Currently each call to append causes a new memory allocation and requires to copy the memory.
Because the slices are reset to nil.
It doesn't reuse the memory previously allocated.
The solution: do c.wspBuf = c.wspBuf[:0] instead of c.wspBuf = nil.
Same for crlfBuf.

WDYT ?

dmarc.Lookup() merges multiple TXT records into a single string

net.LookupTXT() returns a slice of strings, with one string for each TXT resource record returned. If there are multiple strings inside a TXT record it concatenates those before returning it.

dmarc.LookupWithOptions then concatenates all of those TXT resource records into a single string before parsing it.

If there are multiple TXT records for _dmarc.example.com it'll likely give the wrong response. That's not going to happen in
a healthy DNS zone, but it happens occasionally.

I think that correct behaviour would be for dmarc.LookupWithOptions to remove any TXT records that don't start with "v=", then return an error if there are more than one, then parse just the first remaining TXT RR, if any.

FR: dkim-milter config file

Hello,
I would like dkim-milter to read config from a file. If you accept this feature, I could contribute it.

Improve line folding algorithm

With #29 we no longer generate broken signatures with FWS where they shouldn't be. However we could still improve our folding algorithm. Given these constraints:

  • We want to be RFC-conformant (lines < 1000 chars)
  • We don't want something too complicated (see #27)
  • Ensuring that lines are <75 chars is not necessary

We may want to fix these:

  • We still don't properly fold h= params if they are very long
  • We fold b= params at 75 chars, but we don't need to

authres: parse() function can't get correct quoted-string value

Arcoding to https://tools.ietf.org/html/rfc8601:

     reasonspec = "reason" [CFWS] "=" [CFWS] value
                ; a free-form comment on the reason the given result
                ; was returned
   The "value" is as defined in Section 5.1 of [MIME], with
   "quoted-string" updated as specified in [RFC6532].

For example:

var parseTests = []msgauthTest{
...
	{
		value: "example.com;" +
			"dkim=pass reason=\"good signature\" [email protected];",
		identifier: "example.com",
		results: []Result{
			&DKIMResult{Value: ResultPass, Reason: "good signature", Identifier: "@mail-router.example.net"},
		},
	},
}

Instead of good signature, value of Reason is "good.

Getting error on importing this project

Error :
module github.com/emersion/go-msgauth@latest found (v0.4.0), but does not contain package github.com/emersion/go-msgauth

As the code in master is not tagged

Allow caching DNS lookups

This would probably require to add a dkim.Verifier type to allow library users to specify more options.

queryDNSTXT may produce wrong DKIM record using the default resolver

I am reading the code and found that queryDNSTXT may produce the wrong txt record using the default resolver net.LookupTXT

dkim/query.go line 88 wrote that

// Long keys are split in multiple parts
txt := strings.Join(txts, "")

but the default resolve has already joined the txt record with multiple line.

// Multiple strings in one TXT record need to be
// concatenated without separator to be consistent
// with previous Go resolver.
n := 0
for _, s := range txt.TXT {
	n += len(s)
}
txtJoin := make([]byte, 0, n)
for _, s := range txt.TXT {
	txtJoin = append(txtJoin, s...)
}
if len(txts) == 0 {
	txts = make([]string, 0, 1)
}
txts = append(txts, string(txtJoin))

So, if there are multiple records along with DKIM record. It will produce a wrong DKIM record, and the following parsePublicKey will fail

DKIM check not working for CNAME key

First off, thanks for the great work on this library!

Second, this issue is not directly related to go-msgauth, but I was wondering if you could suggest a workaround. Also, I'm using this library through foxcpp/maddy, and not directly.

So here goes:
I use namecheap for my DNS needs, but their emails fail to be delivered with the following line in my logs: dkim: key unavailable: lookup s1._domainkey.namecheap.com. Upon closer examination, it looks like s1._domainkey.namecheap.com is not a TXT record, but a CNAME that resolves to s1.domainkey.u1828068.wl069.sendgrid.net.

What's more is that while nslookup (provided by dnsutils) on my (debian bookworm) server does not resolve CNAME records when looking up TXT records, the nslookup (provided by bind) on my (arch) desktop does so without any complaints.

Here's the output of both:

โฏ nslookup -type=txt s1._domainkey.namecheap.com
;; Truncated, retrying in TCP mode.
Server:		1.1.1.1
Address:	1.1.1.1#53

Non-authoritative answer:
s1._domainkey.namecheap.com	canonical name = s1.domainkey.u1828068.wl069.sendgrid.net.
s1.domainkey.u1828068.wl069.sendgrid.net	text = "k=rsa; t=s; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4EJ2WbK3G12fhP8hlHBTABlvdbKePJXwux+sjGXRnnoVdGAaw9q9D96qeW3uWqAbBSyPB06w4zTeK1qi7Ar+rBC91zKEiuoi6Rbd8xkDBG1Emo8RMhZjOHer5xl0TobynvYy6J4F/ge4OgA17nNDfc7n2Xg+OOKHVY4dVZfdgNR29eGraxD8X0E2pMBdNgtqKvt6S" "4irlnEuhvko+Ls3XqBicTnM30QO4ffyIJWlUqHEwVjBUHKXV+/sTif8UecWw2m9uLYlPbeNBAjMcRtmKYC+tKT39laA2mtPuQub9LHtgzkmAXqE9D7uvgc8gEoUgdvQyefKClRR/rKomB9CeQIDAQAB"

vs.

maddy@frodo:~$ nslookup -type=txt s1._domainkey.namecheap.com
;; Truncated, retrying in TCP mode.
;; Connection to xxx#53(xxx) for s1._domainkey.namecheap.com failed: timed out.
;; Connection to xxx#53(xxx) for s1._domainkey.namecheap.com failed: timed out.
;; Connection to xxx#53(xxx) for s1._domainkey.namecheap.com failed: timed out.
;; Connection to xxx#53(xxx) for s1._domainkey.namecheap.com failed: timed out.
;; Connection to xxx#53(xxx) for s1._domainkey.namecheap.com failed: timed out.

It also looks like the SendGrid key doesn't begin with v=DKIM1, which the code explicitly ignores, but that's an issue for another day. I'll try reaching out to them to see if they will update their keys to conform to the RFC, but I don't have high hopes for that.

DKIM Verify issue in relaxed mode

I have a verify issue with mails coming from gmail with attachments, i have cheked the mail with opendkim and other dkim libararys they Passed, but with go-msgauth i get body hash missmatch.

I have debugged it. Sometimes it happens that in the relaxedBodyCanonicalizer produces a \r\r\n which is written to the writer:

00000000: 6b48 4f53 636b 6748 3037 5662 4b4d 6557  kHOSckgH07VbKMeW
00000010: 424a 4841 2b59 6a41 4234 7750 3839 616e  BJHA+YjAB4wP89an
00000020: 4344 4751 7135 4743 5431 344a 347a 3655  CDGQq5GCT14J4z6U
00000030: 7853 356c 6370 7648 7642 3373 6f5a 6944  xS5lcpvHvB3soZiD
00000040: 792f 4848 6334 2b76 5370 5869 0d0d 0a    y/HHc4+vSpXi...
                                        ^--- \r\r \n

When i do a hotfix at the end of the Write() function:

func (c *relaxedBodyCanonicalizer) Write(b []byte) (int, error) {
...
	if bytes.ContainsAny(canonical, "\r\r\n") {
		canonical = bytes.ReplaceAll(canonical, []byte("\r\r\n"), []byte("\r\n"))
	}
	_, err := c.w.Write(canonical)
	return written, err
}

the checksum is Valid! i think the fixCRLF() should be different in relaxed Mode I have attached a PR to fix this issue

Make v tag validation optional for DKIM text records

Hi,

Some of the providers doesn't include v tag on DKIM record as it wasn't specified as required in older versions of DKIM standard. It would be great if it was optional or fallback to DKIM1 if no v tag was specified on TXT record.

Here are some of the examples of DKIM records without v tag:

Sendgrid:

dig s1._domainkey.sendgrid.com TXT

;; ANSWER SECTION:
s1._domainkey.sendgrid.com. 1800 IN	CNAME	s1.domainkey.u298828.wl079.sendgrid.net.
s1.domainkey.u298828.wl079.sendgrid.net. 1800 IN TXT "k=rsa; t=s; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2LaY8C3pdtdkSF6C+TTYw0bIKAHt70oFeM5IAHb0aLVY7mSWmjWmPHtV6MGbr4n35ABRKpobn40DJGLU7NmjaT54TDTLLsU2DjEFi+p6VQtHdqprrbFNXt5YmQmnYdsHQ0gSe/nyb6PwoX5q92HexVxfT4/etJ+WKnOf4rk2vZplVoeiaxT5Cxench8SzFLa9u1Ur" "JTdFHkWuVl3aR64Up2bOfR3u9uXjkvMXwX0NdjsZeF0GXi1fqUQXg8s7VriECC2TiioWcB0AWPJoGeNfgGO7O6Oj1mdLPQQJrxqzManJrKp43yS9cO+GVXCf92hPTb93nrAWQjehoRbS/rCrQIDAQAB"

GitLab:

dig mailo._domainkey.mg.gitlab.com TXT

;; ANSWER SECTION:
mailo._domainkey.mg.gitlab.com.	300 IN	TXT	"k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqxee1GPSB+mZgCWVbzP8T+YbV+rxnw+3vPIkylf/mvD3jHBH4Ac66B2wSFAz/mFv+ormOZUKzoHaFZdjIcXhpT91h+Lpl21TJKbKTZ/oiX4y5YXOatENLuZM/W+HEyqi9fbV51IOUG9er4kKw29tEQG+DEBtr882ygr7T2WcAFwIDAQAB"

Note:

// RFC 7489 section 6.6.3 says records not starting with "v=" should be

This requirement is actually stated in RFC 6376 6.1.1 not in RFC 7489 6.6.3 as this RFC is about DMARC records not DKIM.
https://datatracker.ietf.org/doc/html/rfc6376#section-6.1.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.