Skip to content
This repository was archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Add Verify tests for Ed25519
Browse files Browse the repository at this point in the history
  • Loading branch information
emersion committed Mar 12, 2019
1 parent 4f594b5 commit a5db695
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 16 deletions.
15 changes: 13 additions & 2 deletions dkim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package dkim
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"time"

"golang.org/x/crypto/ed25519"
)

const testPrivateKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
Expand All @@ -24,17 +27,25 @@ GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
-----END RSA PRIVATE KEY-----
`

const testEd25519PrivateKeyBase64 = "nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A="

var (
testPrivateKey *rsa.PrivateKey
testEd25519PrivateKey ed25519.PrivateKey
)

func init() {
block, _ := pem.Decode([]byte(testPrivateKeyPEM))
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
var err error
testPrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}

testEd25519PrivateKey, err = base64.StdEncoding.DecodeString(testEd25519PrivateKeyBase64)
if err != nil {
panic(err)
}
testPrivateKey = key

now = func() time.Time {
return time.Unix(424242, 0)
Expand Down
15 changes: 14 additions & 1 deletion query_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package dkim

import (
"fmt"
)

const dnsPublicKey = "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ" +
"KBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYt" +
"IxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v" +
"/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhi" +
"tdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB"

const dnsEd25519PublicKey = "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="

func init() {
queryMethods["dns/txt"] = queryTest
}

func queryTest(domain, selector string) (*queryResult, error) {
return parsePublicKey(dnsPublicKey)
record := selector + "._domainkey." + domain
switch record {
case "brisbane._domainkey.example.com", "brisbane._domainkey.example.org", "test._domainkey.football.example.com":
return parsePublicKey(dnsPublicKey)
case "brisbane._domainkey.football.example.com":
return parsePublicKey(dnsEd25519PublicKey)
}
return nil, fmt.Errorf("unknown test DNS record %v", record)
}
27 changes: 15 additions & 12 deletions verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func verify(h header, r io.Reader, sigField, sigValue string) (*Verification, er
return verif, permFailError("incompatible signature version")
}

verif.Domain = params["d"]
verif.Domain = stripWhitespace(params["d"])

for _, tag := range requiredTags {
if _, ok := params[tag]; !ok {
Expand All @@ -162,13 +162,13 @@ func verify(h header, r io.Reader, sigField, sigValue string) (*Verification, er
}

if i, ok := params["i"]; ok {
if !strings.HasSuffix(i, "@"+params["d"]) && !strings.HasSuffix(i, "."+params["d"]) {
verif.Identifier = stripWhitespace(i)
if !strings.HasSuffix(verif.Identifier, "@"+verif.Domain) && !strings.HasSuffix(verif.Identifier, "."+verif.Domain) {
return verif, permFailError("domain mismatch")
}
} else {
params["i"] = "@" + params["d"]
verif.Identifier = "@" + verif.Domain
}
verif.Identifier = params["i"]

headerKeys := parseTagList(params["h"])
ok := false
Expand Down Expand Up @@ -210,7 +210,7 @@ func verify(h header, r io.Reader, sigField, sigValue string) (*Verification, er
var res *queryResult
for _, method := range methods {
if query, ok := queryMethods[QueryMethod(method)]; ok {
res, err = query(params["d"], params["s"])
res, err = query(verif.Domain, stripWhitespace(params["s"]))
break
}
}
Expand All @@ -221,7 +221,7 @@ func verify(h header, r io.Reader, sigField, sigValue string) (*Verification, er
}

// Parse algos
algos := strings.SplitN(params["a"], "-", 2)
algos := strings.SplitN(stripWhitespace(params["a"]), "-", 2)
if len(algos) != 2 {
return verif, permFailError("malformed algorithm name")
}
Expand Down Expand Up @@ -281,7 +281,7 @@ func verify(h header, r io.Reader, sigField, sigValue string) (*Verification, er

var bodyLen int64 = -1
if lenStr, ok := params["l"]; ok {
l, err := strconv.ParseInt(lenStr, 10, 64)
l, err := strconv.ParseInt(stripWhitespace(lenStr), 10, 64)
if err != nil {
return verif, permFailError("malformed body length: " + err.Error())
} else if l < 0 {
Expand Down Expand Up @@ -351,7 +351,7 @@ func verify(h header, r io.Reader, sigField, sigValue string) (*Verification, er
func parseTagList(s string) []string {
tags := strings.Split(s, ":")
for i, t := range tags {
tags[i] = strings.TrimSpace(t)
tags[i] = stripWhitespace(t)
}
return tags
}
Expand All @@ -360,7 +360,7 @@ func parseCanonicalization(s string) (headerCan, bodyCan Canonicalization) {
headerCan = CanonicalizationSimple
bodyCan = CanonicalizationSimple

cans := strings.SplitN(s, "/", 2)
cans := strings.SplitN(stripWhitespace(s), "/", 2)
if cans[0] != "" {
headerCan = Canonicalization(cans[0])
}
Expand All @@ -371,21 +371,24 @@ func parseCanonicalization(s string) (headerCan, bodyCan Canonicalization) {
}

func parseTime(s string) (time.Time, error) {
sec, err := strconv.ParseInt(s, 10, 64)
sec, err := strconv.ParseInt(stripWhitespace(s), 10, 64)
if err != nil {
return time.Time{}, err
}
return time.Unix(sec, 0), nil
}

func decodeBase64String(s string) ([]byte, error) {
s = strings.Map(func(r rune) rune {
return base64.StdEncoding.DecodeString(stripWhitespace(s))
}

func stripWhitespace(s string) string {
return strings.Map(func(r rune) rune {
if unicode.IsSpace(r) {
return -1
}
return r
}, s)
return base64.StdEncoding.DecodeString(s)
}

func removeSignature(s string) string {
Expand Down
59 changes: 58 additions & 1 deletion verify_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package dkim

import (
"io"
"reflect"
"strings"
"testing"
"time"
)

const verifiedMailString = `DKIM-Signature: v=1; a=rsa-sha256; s=brisbane; d=example.com;
Expand Down Expand Up @@ -37,8 +39,12 @@ var testVerification = &Verification{
BodyLength: -1,
}

func newMailStringReader(s string) io.Reader {
return strings.NewReader(strings.Replace(s, "\n", "\r\n", -1))
}

func TestVerify(t *testing.T) {
r := strings.NewReader(strings.Replace(verifiedMailString, "\n", "\r\n", -1))
r := newMailStringReader(verifiedMailString)

verifications, err := Verify(r)
if err != nil {
Expand All @@ -52,3 +58,54 @@ func TestVerify(t *testing.T) {
t.Errorf("Expected verification to be \n%+v\n but got \n%+v", testVerification, v)
}
}

const verifiedEd25519MailString = `DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed;
d=football.example.com; [email protected];
q=dns/txt; s=brisbane; t=1528637909; h=from : to :
subject : date : message-id : from : subject : date;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=/gCrinpcQOoIfuHNQIbq4pgh9kyIK3AQUdt9OdqQehSwhEIug4D11Bus
Fa3bT3FY5OsU7ZbnKELq+eXdp1Q1Dw==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=football.example.com; [email protected];
q=dns/txt; s=test; t=1528637909; h=from : to : subject :
date : message-id : from : subject : date;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=F45dVWDfMbQDGHJFlXUNB2HKfbCeLRyhDXgFpEL8GwpsRe0IeIixNTe3
DhCVlUrSjV4BwcVcOF6+FF3Zo9Rpo1tFOeS9mPYQTnGdaSGsgeefOsk2Jz
dA+L10TeYt9BgDfQNZtKdN1WO//KgIqXP7OdEFE4LjFYNcUxZQ4FADY+8=
From: Joe SixPack <[email protected]>
To: Suzie Q <[email protected]>
Subject: Is dinner ready?
Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
Message-ID: <[email protected]>
Hi.
We lost the game. Are you hungry yet?
Joe.`

var testEd25519Verification = &Verification{
Domain: "football.example.com",
Identifier: "@football.example.com",
HeaderKeys: []string{"from", "to", "subject", "date", "message-id", "from", "subject", "date"},
BodyLength: -1,
Time: time.Unix(1528637909, 0),
}

func TestVerify_ed25519(t *testing.T) {
r := newMailStringReader(verifiedEd25519MailString)

verifications, err := Verify(r)
if err != nil {
t.Fatalf("Expected no error while verifying signature, got: %v", err)
} else if len(verifications) != 2 {
t.Fatalf("Expected exactly two verifications, got %v", len(verifications))
}

v := verifications[0]
if !reflect.DeepEqual(testEd25519Verification, v) {
t.Errorf("Expected verification to be \n%+v\n but got \n%+v", testEd25519Verification, v)
}
}

0 comments on commit a5db695

Please sign in to comment.