allow mocking

This commit is contained in:
Andres Erbsen
2015-08-18 12:09:42 -07:00
parent 00fde132a7
commit 47c4e9fcb7
3 changed files with 29 additions and 29 deletions

32
dkim.go
View File

@@ -13,6 +13,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/pem" "encoding/pem"
"hash" "hash"
"net"
"regexp" "regexp"
"strings" "strings"
"time" "time"
@@ -25,8 +26,6 @@ const (
MaxHeaderLineLength = 70 MaxHeaderLineLength = 70
) )
type verifyOutput int
// sigOptions represents signing options // sigOptions represents signing options
type SigOptions struct { type SigOptions struct {
@@ -181,26 +180,36 @@ func Sign(email []byte, options SigOptions) ([]byte, error) {
return append([]byte(dHeader), email...), nil return append([]byte(dHeader), email...), nil
} }
func Verify(email []byte) (dkimHeader *DKIMHeader, err error) { func Verify(email []byte, LookupTXT func(string) ([]string, error), now func() time.Time) (dkimHeader *DKIMHeader, err error) {
// parse email if LookupTXT == nil {
LookupTXT = net.LookupTXT
}
if now == nil {
now = time.Now
}
dkimHeader, err = newDkimHeaderFromEmail(email) dkimHeader, err = newDkimHeaderFromEmail(email)
if err != nil { if err != nil {
return return
} }
// we do not set query method because if it's others, validation failed earlier txt, err := LookupTXT(dkimHeader.Selector + "._domainkey." + dkimHeader.Domain)
pubKey, err := newPubKeyFromDnsTxt(dkimHeader.Selector, dkimHeader.Domain) if err != nil {
return nil, err
}
pubKey, err := newPubKeyFromDnsTxt(txt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Normalize
headers, body, err := canonicalize(email, dkimHeader.MessageCanonicalization, dkimHeader.Headers) headers, body, err := canonicalize(email, dkimHeader.MessageCanonicalization, dkimHeader.Headers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sigHash := strings.Split(dkimHeader.Algorithm, "-") sigHash := strings.Split(dkimHeader.Algorithm, "-")
// check if hash algo are compatible if len(sigHash) < 2 {
return nil, ErrVerifyInappropriateHashAlgo
}
compatible := false compatible := false
for _, algo := range pubKey.HashAlgo { for _, algo := range pubKey.HashAlgo {
if sigHash[1] == algo { if sigHash[1] == algo {
@@ -212,9 +221,10 @@ func Verify(email []byte) (dkimHeader *DKIMHeader, err error) {
return nil, ErrVerifyInappropriateHashAlgo return nil, ErrVerifyInappropriateHashAlgo
} }
// expired ? if !dkimHeader.SignatureExpiration.IsZero() {
if !dkimHeader.SignatureExpiration.IsZero() && dkimHeader.SignatureExpiration.Second() < time.Now().Second() { if dkimHeader.SignatureExpiration.Before(now()) {
return nil, ErrVerifySignatureHasExpired return nil, ErrVerifySignatureHasExpired
}
} }
bodyHash, err := getBodyHash(body, sigHash[1], dkimHeader.BodyLength) bodyHash, err := getBodyHash(body, sigHash[1], dkimHeader.BodyLength)

View File

@@ -333,38 +333,38 @@ func Test_Sign(t *testing.T) {
func Test_Verify(t *testing.T) { func Test_Verify(t *testing.T) {
// no DKIM header // no DKIM header
email := []byte(emailBase) email := []byte(emailBase)
_, err := Verify(email) _, err := Verify(email, nil, nil)
assert.Equal(t, ErrDkimHeaderNotFound, err) assert.Equal(t, ErrDkimHeaderNotFound, err)
// No From // No From
email = []byte(signedNoFrom) email = []byte(signedNoFrom)
_, err = Verify(email) _, err = Verify(email, nil, nil)
assert.Equal(t, ErrVerifyBodyHash, err) assert.Equal(t, ErrVerifyBodyHash, err)
// missing mandatory 'a' flag // missing mandatory 'a' flag
email = []byte(signedMissingFlag) email = []byte(signedMissingFlag)
_, err = Verify(email) _, err = Verify(email, nil, nil)
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, ErrDkimHeaderMissingRequiredTag, err) assert.Equal(t, ErrDkimHeaderMissingRequiredTag, err)
// missing bad algo // missing bad algo
email = []byte(signedBadAlgo) email = []byte(signedBadAlgo)
_, err = Verify(email) _, err = Verify(email, nil, nil)
assert.Equal(t, ErrSignBadAlgo, err) assert.Equal(t, ErrSignBadAlgo, err)
// relaxed // relaxed
email = []byte(signedRelaxedRelaxedLength) email = []byte(signedRelaxedRelaxedLength)
_, err = Verify(email) _, err = Verify(email, nil, nil)
assert.Equal(t, ErrTesting, err) assert.Equal(t, ErrTesting, err)
// simple // simple
email = []byte(signedSimpleSimpleLength) email = []byte(signedSimpleSimpleLength)
_, err = Verify(email) _, err = Verify(email, nil, nil)
assert.Equal(t, ErrTesting, err) assert.Equal(t, ErrTesting, err)
// gmail // gmail
email = []byte(fromGmail) email = []byte(fromGmail)
_, err = Verify(email) _, err = Verify(email, nil, nil)
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@@ -4,7 +4,6 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"net"
"strings" "strings"
) )
@@ -20,16 +19,7 @@ type pubKeyRep struct {
FlagIMustBeD bool // flag i FlagIMustBeD bool // flag i
} }
func newPubKeyFromDnsTxt(selector, domain string) (*pubKeyRep, error) { func newPubKeyFromDnsTxt(txt []string) (*pubKeyRep, error) {
txt, err := net.LookupTXT(selector + "._domainkey." + domain)
if err != nil {
if strings.HasSuffix(err.Error(), "no such host") {
return nil, ErrVerifyNoKeyForSignature
} else {
return nil, ErrVerifyKeyUnavailable
}
}
// empty record // empty record
if len(txt) == 0 { if len(txt) == 0 {
return nil, ErrVerifyNoKeyForSignature return nil, ErrVerifyNoKeyForSignature