From 5294351de52d921e3b4d9a9379161d2bc0ad5793 Mon Sep 17 00:00:00 2001 From: Gowtham Gopalakrishnan Date: Sat, 31 Jul 2021 14:16:02 +0530 Subject: [PATCH] adds support for DKIM flags. fixes #44 --- dkim/query_test.go | 8 ++++ dkim/sign.go | 2 +- dkim/verify.go | 14 +++++++ dkim/verify_test.go | 90 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) diff --git a/dkim/query_test.go b/dkim/query_test.go index d49ae2f..ec05071 100644 --- a/dkim/query_test.go +++ b/dkim/query_test.go @@ -16,6 +16,12 @@ const dnsPublicKey = "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ" + "/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhi" + "tdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB" +const dnsPublicKeySFlag = "v=DKIM1; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ" + + "KBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYt" + + "IxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v" + + "/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhi" + + "tdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB" + const dnsEd25519PublicKey = "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=" func init() { @@ -31,6 +37,8 @@ func queryTest(domain, selector string, txtLookup txtLookupFunc) (*queryResult, return parsePublicKey(dnsRawRSAPublicKey) case "brisbane._domainkey.football.example.com": return parsePublicKey(dnsEd25519PublicKey) + case "helsinki._domainkey.example.com": + return parsePublicKey(dnsPublicKeySFlag) } return nil, fmt.Errorf("unknown test DNS record %v", record) } diff --git a/dkim/sign.go b/dkim/sign.go index effb7d4..c4ba8c3 100644 --- a/dkim/sign.go +++ b/dkim/sign.go @@ -74,7 +74,7 @@ type SignOptions struct { // // The whole message header and body must be written to the Signer. Close should // always be called (either after the whole message has been written, or after -// an error occured and the signer won't be used anymore). Close may return an +// an error occurred and the signer won't be used anymore). Close may return an // error in case signing fails. // // After a successful Close, Signature can be called to retrieve the diff --git a/dkim/verify.go b/dkim/verify.go index a228ce6..0737320 100644 --- a/dkim/verify.go +++ b/dkim/verify.go @@ -292,6 +292,20 @@ func verify(h header, r io.Reader, sigField, sigValue string, options *VerifyOpt return verif, permFailError("unsupported public key query method") } + // Parse flags + for _, flag := range res.Flags { + switch flag { + case "y": + // domain is testing DKIM. + return verif, nil + case "s": + // i domain and d domain should be equal + if !strings.HasSuffix(verif.Identifier, "@"+verif.Domain) { + return verif, permFailError("identifier and domain mismatch") + } + } + } + // Parse algos algos := strings.SplitN(stripWhitespace(params["a"]), "-", 2) if len(algos) != 2 { diff --git a/dkim/verify_test.go b/dkim/verify_test.go index dfa7092..0597aea 100644 --- a/dkim/verify_test.go +++ b/dkim/verify_test.go @@ -294,3 +294,93 @@ func TestVerify_tooManySignatures(t *testing.T) { t.Fatalf("Expected %v verifications, got %v", options.MaxVerifications, len(verifs)) } } + +const validSignatureWithSFlag = `DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; + c=simple/simple; d=example.com; + h=Received:From:To:Subject:Date:Message-ID; s=helsinki; t=1627654546; v=1; + b=IKIukw2EoRWxb86Ke2ingZFWLoAeNnKYAwGghFe1y0bbn5QLSoyk78o36JXT8z1DzmPquMHt + C//jnBna6DtqBneiKAUo3OyYmv1+EbTWWEvHGGtUkrKge2VdIXM3ttaOpuTHJRG+6irPKq0Ul4T + Ofa99PVg4o6nfY6Ctakv3aYc= +Received: from helsinki.example.com [192.0.2.1] + by submitserver.example.com with SUBMISSION; + Fri, 11 Jul 2003 21:01:54 -0700 (PDT) +From: Joe Helsinki +To: Suzie Q +Subject: Is dinner ready? +Date: Fri, 11 Jul 2021 21:00:37 -0700 (PDT) +Message-ID: <20030712040037.46341.5F8J@helsinki.example.com> + +Hi. + +We lost the game. Are you hungry yet? + +Joe.` + +var testSFlagVerification = &Verification{ + Domain: "example.com", + Identifier: "@example.com", + HeaderKeys: []string{"Received", "From", "To", "Subject", "Date", "Message-ID"}, + Time: time.Unix(1627654546, 0), +} + +func TestVerify_SFlag(t *testing.T) { + r := newMailStringReader(validSignatureWithSFlag) + + verifications, err := Verify(r) + if err != nil { + t.Fatalf("Expected no error while verifying signature with flags, got: %v", err) + } else if len(verifications) != 1 { + t.Fatalf("Expected exactly one verification with flags, got %v", len(verifications)) + } + + v := verifications[0] + if !reflect.DeepEqual(testSFlagVerification, v) { + t.Errorf("Expected verification with flags to be \n%+v\n but got \n%+v", testSFlagVerification, v) + } +} + +const invalidSignatureWithSFlag = `DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; + c=simple/simple; d=example.com; + h=Received:From:To:Subject:Date:Message-ID; i=anton@id.example.com; + s=helsinki; t=1627654996; v=1; + b=k07JuWvk3PZkanimQ0QtIXRwXC5W4EhG/0sPH+r7zWKQ731fNZE//9+ofz+vcA3K5YQ0cOR2 + EXY9qjriMPHFlaYsKZ4gj5YebNhRHyjJ9xlsTPNtFLqs9wnnaKCZu3VxJpualfTaY+Vs3RMjYLq + 7IvOY4tqxnifJg2uFC/5kRbY= +Received: from helsinki.example.com [192.0.2.1] + by submitserver.example.com with SUBMISSION; + Fri, 11 Jul 2003 21:01:54 -0700 (PDT) +From: Joe Helsinki +To: Suzie Q +Subject: Is dinner ready? +Date: Fri, 11 Jul 2021 21:00:37 -0700 (PDT) +Message-ID: <20030712040037.46341.5F8J@helsinki.example.com> + +Hi. + +We lost the game. Are you hungry yet? + +Joe.` + +var testSFlagVerificationFail = &Verification{ + Domain: "example.com", + Identifier: "anton@id.example.com", + HeaderKeys: []string{"Received", "From", "To", "Subject", "Date", "Message-ID"}, + Time: time.Unix(1627654996, 0), + Err: permFailError("identifier and domain mismatch"), +} + +func TestVerify_InvalidSFlag(t *testing.T) { + r := newMailStringReader(invalidSignatureWithSFlag) + + verifications, err := Verify(r) + if err != nil { + t.Fatalf("Expected no error while verifying signature with flags, got: %v", err) + } else if len(verifications) != 1 { + t.Fatalf("Expected exactly one verification with flags, got %v", len(verifications)) + } + + v := verifications[0] + if !reflect.DeepEqual(testSFlagVerificationFail, v) { + t.Errorf("Expected verification with flags to be \n%+v\n but got \n%+v", testSFlagVerificationFail, v) + } +}