Skip to content

Commit

Permalink
dkim: accept RSA keys in both raw and SPKI form
Browse files Browse the repository at this point in the history
RFC 6376 is inconsistent about whether RSA public keys should be
formatted as RSAPublicKey or SubjectPublicKeyInfo.  Erratum 3017
(https://www.rfc-editor.org/errata/eid3017) proposes allowing both.

This commit changes the verifier to accept both formats, and changes
dkim-keygen to generate keys in SubjectPublicKeyInfo format for
consistency with other implementations including opendkim, Gmail,
and Fastmail.

Closes: #43
  • Loading branch information
AGWA authored and emersion committed Mar 15, 2021
1 parent 26ea2d1 commit 4ecd9a7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 2 deletions.
11 changes: 10 additions & 1 deletion cmd/dkim-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,16 @@ func main() {
var pubBytes []byte
switch pubKey := privKey.Public().(type) {
case *rsa.PublicKey:
pubBytes = x509.MarshalPKCS1PublicKey(pubKey)
// RFC 6376 is inconsistent about whether RSA public keys should
// be formatted as RSAPublicKey or SubjectPublicKeyInfo.
// Erratum 3017 (https://www.rfc-editor.org/errata/eid3017)
// proposes allowing both. We use SubjectPublicKeyInfo for
// consistency with other implementations including opendkim,
// Gmail, and Fastmail.
pubBytes, err = x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
log.Fatalf("Failed to marshal public key: %v", err)
}
case ed25519.PublicKey:
pubBytes = pubKey
default:
Expand Down
9 changes: 8 additions & 1 deletion dkim/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,14 @@ func parsePublicKey(s string) (*queryResult, error) {
case "rsa", "":
pub, err := x509.ParsePKIXPublicKey(b)
if err != nil {
return nil, permFailError("key syntax error: " + err.Error())
// RFC 6376 is inconsistent about whether RSA public keys should
// be formatted as RSAPublicKey or SubjectPublicKeyInfo.
// Erratum 3017 (https://www.rfc-editor.org/errata/eid3017) proposes
// allowing both.
pub, err = x509.ParsePKCS1PublicKey(b)
if err != nil {
return nil, permFailError("key syntax error: " + err.Error())
}
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
Expand Down
8 changes: 8 additions & 0 deletions dkim/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import (
"fmt"
)

const dnsRawRSAPublicKey = "v=DKIM1; p=MIGJAoGBALVI635dLK4cJJAH3Lx6upo3X/L" +
"m1tQz3mezcWTA3BUBnyIsdnRf57aD5BtNmhPrYYDlWlzw3" +
"UgnKisIxktkk5+iMQMlFtAS10JB8L3YadXNJY+JBcbeSi5" +
"TgJe4WFzNgW95FWDAuSTRXSWZfA/8xjflbTLDx0euFZOM7" +
"C4T0GwLAgMBAAE="

const dnsPublicKey = "v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ" +
"KBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYt" +
"IxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v" +
Expand All @@ -21,6 +27,8 @@ func queryTest(domain, selector string, txtLookup txtLookupFunc) (*queryResult,
switch record {
case "brisbane._domainkey.example.com", "brisbane._domainkey.example.org", "test._domainkey.football.example.com":
return parsePublicKey(dnsPublicKey)
case "newengland._domainkey.example.com":
return parsePublicKey(dnsRawRSAPublicKey)
case "brisbane._domainkey.football.example.com":
return parsePublicKey(dnsEd25519PublicKey)
}
Expand Down
46 changes: 46 additions & 0 deletions dkim/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,52 @@ func TestVerifyWithOption(t *testing.T) {
}
}

const verifiedRawRSAMailString = `DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
c=simple/simple; d=example.com;
h=Received:From:To:Subject:Date:Message-ID; [email protected];
s=newengland; t=1615825284; v=1;
b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
s4wwFRRKz/1bksZGSjD8uuSU=
Received: from client1.football.example.com [192.0.2.1]
by submitserver.example.com with SUBMISSION;
Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
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 testRawRSAVerification = &Verification{
Domain: "example.com",
Identifier: "[email protected]",
HeaderKeys: []string{"Received", "From", "To", "Subject", "Date", "Message-ID"},
Time: time.Unix(1615825284, 0),
}

func TestVerify_rawRSA(t *testing.T) {
r := newMailStringReader(verifiedRawRSAMailString)

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

v := verifications[0]
if !reflect.DeepEqual(testRawRSAVerification, v) {
t.Errorf("Expected verification to be \n%+v\n but got \n%+v", testRawRSAVerification, 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 :
Expand Down

0 comments on commit 4ecd9a7

Please sign in to comment.