diff --git a/dkim.go b/dkim.go index ec6fa85..0159519 100644 --- a/dkim.go +++ b/dkim.go @@ -2,6 +2,7 @@ package dkim import ( "bytes" + "io/ioutil" "strings" "time" ) @@ -88,6 +89,9 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) { if len(p) > 2 { return nil, ErrSignBadCanonicalization } + if len(p) == 1 { + options.Canonicalization = options.Canonicalization + "/simple" + } for _, c := range p { if c != "simple" && c != "relaxed" { return nil, ErrSignBadCanonicalization @@ -104,7 +108,8 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) { // normalize -> strtlower hasFrom := false for i, h := range options.Headers { - options.Headers[i] = strings.ToLower(h) + h = strings.ToLower(h) + options.Headers[i] = h if h == "from" { hasFrom = true } @@ -113,7 +118,39 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) { return nil, ErrSignHeaderShouldContainsFrom } - // + // Normalize + //normalizedHeaders, NormalizedBody, err := normalize(email, options) + + canonicalize(email, options) return nil, nil } + +func canonicalize(emailReader *bytes.Reader, options sigOptions) (headers, body []byte, err error) { + var email []byte + email, err = ioutil.ReadAll(emailReader) + emailReader.Seek(0, 0) + if err != nil { + return + } + + parts := bytes.SplitN(email, []byte{13, 10, 13, 10}, 2) + + canonicalizations := strings.Split(options.Canonicalization, "/") + // canonicalyze body + if canonicalizations[1] == "simple" { + body = bytes.TrimRight(parts[1], "\r\n") + body = append(body, []byte{13, 10}...) + } else { + for _, line := range bytes.Split(parts[1], []byte{10}) { + println(line) + } + } + + println(string(parts[0])) + println("\r\n") + println(string(parts[1])) + println(string(body)) + + return +} diff --git a/dkim_test.go b/dkim_test.go index 7b0196e..4df6fc2 100644 --- a/dkim_test.go +++ b/dkim_test.go @@ -6,20 +6,42 @@ import ( "testing" ) +const ( + privKey = `MIICXQIBAAKBgQDNUXO+Qsl1tw+GjrqFajz0ERSEUs1FHSL/+udZRWn1Atw8gz0+ +tcGqhWChBDeU9gY5sKLEAZnX3FjC/T/IbqeiSM68kS5vLkzRI84eiJrm3+IieUqI +IicsO+WYxQs+JgVx5XhpPjX4SQjHtwEC2xKkWnEv+VPgO1JWdooURcSC6QIDAQAB +AoGAM9exRgVPIS4L+Ynohu+AXJBDgfX2ZtEomUIdUGk6i+cg/RaWTFNQh2IOOBn8 +ftxwTfjP4HYXBm5Y60NO66klIlzm6ci303IePmjaj8tXQiriaVA0j4hmW+xgnqQX +PubFzfnR2eWLSOGChrNFbd3YABC+qttqT6vT0KpFyLdn49ECQQD3zYCpgelb0EBo +gc5BVGkbArcknhPwO39coPqKM4csu6cgI489XpF7iMh77nBTIiy6dsDdRYXZM3bq +ELTv6K4/AkEA1BwsIZG51W5DRWaKeobykQIB6FqHLW+Zhedw7BnxS8OflYAcSWi4 +uGhq0DPojmhsmUC8jUeLe79CllZNP3LU1wJBAIZcoCnI7g5Bcdr4nyxfJ4pkw4cQ +S4FT0XAZPR/YZrADo8/SWCWPdFTGSuaf17nL6vLD1zljK/skY5LwshrvUCMCQQDM +MY7ehj6DVFHYlt2LFSyhInCZscTencgK24KfGF5t1JZlwt34YaMqjAMACmi/55Fc +e7DIxW5nI/nDZrOY+EAjAkA3BHUx3PeXkXJnXjlh7nGZmk/v8tB5fiofAwfXNfL7 +bz0ZrT2Caz995Dpjommh5aMpCJvUGsrYCG6/Pbha9NXl` + + pubKey = `MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDNUXO+Qsl1tw+GjrqFajz0ERSE +Us1FHSL/+udZRWn1Atw8gz0+tcGqhWChBDeU9gY5sKLEAZnX3FjC/T/IbqeiSM68 +kS5vLkzRI84eiJrm3+IieUqIIicsO+WYxQs+JgVx5XhpPjX4SQjHtwEC2xKkWnEv ++VPgO1JWdooURcSC6QIDAQAB` + + domain = "tmail.io" + + selector = "test" +) + var email = `Received: (qmail 28277 invoked from network); 1 May 2015 09:43:37 -0000 Received: (qmail 21323 invoked from network); 1 May 2015 09:48:39 -0000 MIME-Version: 1.0 Date: Fri, 1 May 2015 11:48:37 +0200 Message-ID: Subject: Test DKIM -From: =?UTF-8?Q?St=C3=A9phane_Depierrepont?= +From: =?UTF-8?Q?St=C3=A9phane_Depierrepont?= To: =?UTF-8?Q?St=C3=A9phane_Depierrepont?= -Content-Type: text/plain; charset=UTF-8 - - -Hello world +Content-Type: text/plain; charset=UTF-8` + "\r\n\r\n" + `Hello world -- -Toorop` +Toorop` + "\r\n\r\n\r\n\r\n\r\n\r\n\r\n" func Test_NewSigOptions(t *testing.T) { options := NewSigOptions() @@ -66,8 +88,22 @@ func Test_SignConfig(t *testing.T) { assert.NoError(t, err) // header - /*options.Headers = []string{"toto"} + options.Headers = []string{"toto"} _, err = Sign(emailReader, options) - assert.EqualError(t, err, ErrSignHeaderShouldContainsFrom.Error())*/ + assert.EqualError(t, err, ErrSignHeaderShouldContainsFrom.Error()) + options.Headers = []string{"To", "From"} + _, err = Sign(emailReader, options) + assert.NoError(t, err) +} + +func Test_Sign(t *testing.T) { + emailReader := bytes.NewReader([]byte(email)) + options := NewSigOptions() + options.PrivateKey = privKey + options.Canonicalization = "simple/relaxed" + options.Domain = domain + options.Selector = selector + emailReader, err := Sign(emailReader, options) + assert.NoError(t, err) } diff --git a/errors.go b/errors.go index 11f4130..640df8e 100644 --- a/errors.go +++ b/errors.go @@ -6,13 +6,13 @@ import ( var ( // ErrConfigPrivateKeyRequired when there not private key in config - ErrSignPrivateKeyRequired = errors.New("PrivateKey is required in config") + ErrSignPrivateKeyRequired = errors.New("PrivateKey is required") // ErrSignDomainRequired when there is no domain defined in config - ErrSignDomainRequired = errors.New("Domain is required in config") + ErrSignDomainRequired = errors.New("Domain is required") // ErrSignSelectorRequired when there is no Selcteir defined in config - ErrSignSelectorRequired = errors.New("Selector is required in config") + ErrSignSelectorRequired = errors.New("Selector is required") // If Headers is specified it should at least contain 'from' ErrSignHeaderShouldContainsFrom = errors.New("Header must contains 'from' field")