canonicalize 2
This commit is contained in:
94
dkim.go
94
dkim.go
@@ -2,7 +2,7 @@ package dkim
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
//"fmt"
|
"container/list"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -155,6 +155,98 @@ func canonicalize(emailReader *bytes.Reader, options sigOptions) (headers, body
|
|||||||
}
|
}
|
||||||
|
|
||||||
canonicalizations := strings.Split(options.Canonicalization, "/")
|
canonicalizations := strings.Split(options.Canonicalization, "/")
|
||||||
|
|
||||||
|
// canonicalyze header
|
||||||
|
//var headersMap [][]byte
|
||||||
|
//headersMap := [][]byte{}
|
||||||
|
headersList := list.New()
|
||||||
|
currentHeader := []byte{}
|
||||||
|
for _, line := range bytes.SplitAfter(parts[0], []byte{10}) {
|
||||||
|
if line[0] == 32 || line[0] == 9 {
|
||||||
|
if len(currentHeader) == 0 {
|
||||||
|
return headers, body, ErrBadMailFormatHeaders
|
||||||
|
}
|
||||||
|
currentHeader = append(currentHeader, line...)
|
||||||
|
} else {
|
||||||
|
// New header, save current if exists
|
||||||
|
if len(currentHeader) != 0 {
|
||||||
|
//headersMap = append(headersMap, currentHeader)
|
||||||
|
headersList.PushBack(string(currentHeader))
|
||||||
|
currentHeader = []byte{}
|
||||||
|
|
||||||
|
}
|
||||||
|
currentHeader = append(currentHeader, line...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pour chaque header a conserver on traverse tous les headers dispo
|
||||||
|
// If multi instance of a field we must keep it from the bottom to the top
|
||||||
|
var match *list.Element
|
||||||
|
headersToKeepList := list.New()
|
||||||
|
|
||||||
|
for _, headerToKeep := range options.Headers {
|
||||||
|
match = nil
|
||||||
|
headerToKeepToLower := strings.ToLower(headerToKeep)
|
||||||
|
for e := headersList.Front(); e != nil; e = e.Next() {
|
||||||
|
t := strings.Split(e.Value.(string), ":")
|
||||||
|
if strings.ToLower(t[0]) == headerToKeepToLower {
|
||||||
|
match = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match != nil {
|
||||||
|
headersToKeepList.PushBack(match.Value.(string))
|
||||||
|
headersList.Remove(match)
|
||||||
|
} else {
|
||||||
|
headersToKeepList.PushBack(headerToKeep + ":\r\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if canonicalizations[0] == "simple" {
|
||||||
|
// The "simple" header canonicalization algorithm does not change header
|
||||||
|
// fields in any way. Header fields MUST be presented to the signing or
|
||||||
|
// verification algorithm exactly as they are in the message being
|
||||||
|
// signed or verified. In particular, header field names MUST NOT be
|
||||||
|
// case folded and whitespace MUST NOT be changed.
|
||||||
|
for e := headersToKeepList.Front(); e != nil; e = e.Next() {
|
||||||
|
headers = append(headers, []byte(e.Value.(string))...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The "relaxed" header canonicalization algorithm MUST apply the
|
||||||
|
// following steps in order:
|
||||||
|
|
||||||
|
// Convert all header field names (not the header field values) to
|
||||||
|
// lowercase. For example, convert "SUBJect: AbC" to "subject: AbC".
|
||||||
|
|
||||||
|
// Unfold all header field continuation lines as described in
|
||||||
|
// [RFC5322]; in particular, lines with terminators embedded in
|
||||||
|
// continued header field values (that is, CRLF sequences followed by
|
||||||
|
// WSP) MUST be interpreted without the CRLF. Implementations MUST
|
||||||
|
// NOT remove the CRLF at the end of the header field value.
|
||||||
|
|
||||||
|
// Convert all sequences of one or more WSP characters to a single SP
|
||||||
|
// character. WSP characters here include those before and after a
|
||||||
|
// line folding boundary.
|
||||||
|
|
||||||
|
// Delete all WSP characters at the end of each unfolded header field
|
||||||
|
// value.
|
||||||
|
|
||||||
|
// Delete any WSP characters remaining before and after the colon
|
||||||
|
// separating the header field name from the header field value. The
|
||||||
|
// colon separator MUST be retained.
|
||||||
|
for e := headersToKeepList.Front(); e != nil; e = e.Next() {
|
||||||
|
kv := strings.SplitN(e.Value.(string), ":", 2)
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return []byte{}, []byte{}, ErrBadMailFormatHeaders
|
||||||
|
}
|
||||||
|
k := strings.ToLower(kv[0])
|
||||||
|
k = strings.TrimSpace(k)
|
||||||
|
v := strings.Replace(kv[1], "\n", "", -1)
|
||||||
|
v = strings.Replace(v, "\r", "", -1)
|
||||||
|
v = rxReduceWS.ReplaceAllString(v, " ")
|
||||||
|
v = strings.TrimSpace(v)
|
||||||
|
headers = append(headers, []byte(k+":"+v+CRLF)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
// canonicalyze body
|
// canonicalyze body
|
||||||
if canonicalizations[1] == "simple" {
|
if canonicalizations[1] == "simple" {
|
||||||
// simple
|
// simple
|
||||||
|
|||||||
56
dkim_test.go
56
dkim_test.go
@@ -33,6 +33,9 @@ kS5vLkzRI84eiJrm3+IieUqIIicsO+WYxQs+JgVx5XhpPjX4SQjHtwEC2xKkWnEv
|
|||||||
|
|
||||||
var email = "Received: (qmail 28277 invoked from network); 1 May 2015 09:43:37 -0000" + CRLF +
|
var email = "Received: (qmail 28277 invoked from network); 1 May 2015 09:43:37 -0000" + CRLF +
|
||||||
"Received: (qmail 21323 invoked from network); 1 May 2015 09:48:39 -0000" + CRLF +
|
"Received: (qmail 21323 invoked from network); 1 May 2015 09:48:39 -0000" + CRLF +
|
||||||
|
"Received: from mail483.ha.ovh.net (b6.ovh.net [213.186.33.56])" + CRLF +
|
||||||
|
" by mo51.mail-out.ovh.net (Postfix) with SMTP id A6E22FF8934" + CRLF +
|
||||||
|
" for <toorop@toorop.fr>; Mon, 4 May 2015 14:00:47 +0200 (CEST)" + CRLF +
|
||||||
"MIME-Version: 1.0" + CRLF +
|
"MIME-Version: 1.0" + CRLF +
|
||||||
"Date: Fri, 1 May 2015 11:48:37 +0200" + CRLF +
|
"Date: Fri, 1 May 2015 11:48:37 +0200" + CRLF +
|
||||||
"Message-ID: <CADu37kTXBeNkJdXc4bSF8DbJnXmNjkLbnswK6GzG_2yn7U7P6w@tmail.io>" + CRLF +
|
"Message-ID: <CADu37kTXBeNkJdXc4bSF8DbJnXmNjkLbnswK6GzG_2yn7U7P6w@tmail.io>" + CRLF +
|
||||||
@@ -46,6 +49,22 @@ var email = "Received: (qmail 28277 invoked from network); 1 May 2015 09:43:37 -
|
|||||||
"-- " + CRLF +
|
"-- " + CRLF +
|
||||||
"Toorop " + CRLF + CRLF + CRLF + CRLF + CRLF + CRLF
|
"Toorop " + CRLF + CRLF + CRLF + CRLF + CRLF + CRLF
|
||||||
|
|
||||||
|
var headerSimple = "From: =?UTF-8?Q?St=C3=A9phane_Depierrepont?= <toorop@tmail.io>" + CRLF +
|
||||||
|
"Date: Fri, 1 May 2015 11:48:37 +0200" + CRLF +
|
||||||
|
"MIME-Version: 1.0" + CRLF +
|
||||||
|
"Received: from mail483.ha.ovh.net (b6.ovh.net [213.186.33.56])" + CRLF +
|
||||||
|
" by mo51.mail-out.ovh.net (Postfix) with SMTP id A6E22FF8934" + CRLF +
|
||||||
|
" for <toorop@toorop.fr>; Mon, 4 May 2015 14:00:47 +0200 (CEST)" + CRLF +
|
||||||
|
"Received: (qmail 21323 invoked from network); 1 May 2015 09:48:39 -0000" + CRLF +
|
||||||
|
"In-Reply-To:" + CRLF
|
||||||
|
|
||||||
|
var headerRelaxed = "from:=?UTF-8?Q?St=C3=A9phane_Depierrepont?= <toorop@tmail.io>" + CRLF +
|
||||||
|
"date:Fri, 1 May 2015 11:48:37 +0200" + CRLF +
|
||||||
|
"mime-version:1.0" + CRLF +
|
||||||
|
"received:from mail483.ha.ovh.net (b6.ovh.net [213.186.33.56]) by mo51.mail-out.ovh.net (Postfix) with SMTP id A6E22FF8934 for <toorop@toorop.fr>; Mon, 4 May 2015 14:00:47 +0200 (CEST)" + CRLF +
|
||||||
|
"received:(qmail 21323 invoked from network); 1 May 2015 09:48:39 -0000" + CRLF +
|
||||||
|
"in-reply-to:" + CRLF
|
||||||
|
|
||||||
var bodySimple = "Hello world" + CRLF +
|
var bodySimple = "Hello world" + CRLF +
|
||||||
"line with trailing space " + CRLF +
|
"line with trailing space " + CRLF +
|
||||||
"line with space " + CRLF +
|
"line with space " + CRLF +
|
||||||
@@ -112,6 +131,26 @@ func Test_SignConfig(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_canonicalize(t *testing.T) {
|
||||||
|
emailReader := bytes.NewReader([]byte(email))
|
||||||
|
options := NewSigOptions()
|
||||||
|
options.Headers = []string{"from", "date", "mime-version", "received", "received", "In-Reply-To"}
|
||||||
|
// simple/simple
|
||||||
|
options.Canonicalization = "simple/simple"
|
||||||
|
header, body, err := canonicalize(emailReader, options)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte(headerSimple), header)
|
||||||
|
assert.Equal(t, []byte(bodySimple), body)
|
||||||
|
|
||||||
|
// relaxed/relaxed
|
||||||
|
options.Canonicalization = "relaxed/relaxed"
|
||||||
|
header, body, err = canonicalize(emailReader, options)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte(headerRelaxed), header)
|
||||||
|
assert.Equal(t, []byte(bodyRelaxed), body)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func Test_Sign(t *testing.T) {
|
func Test_Sign(t *testing.T) {
|
||||||
emailReader := bytes.NewReader([]byte(email))
|
emailReader := bytes.NewReader([]byte(email))
|
||||||
options := NewSigOptions()
|
options := NewSigOptions()
|
||||||
@@ -122,20 +161,3 @@ func Test_Sign(t *testing.T) {
|
|||||||
emailReader, err := Sign(emailReader, options)
|
emailReader, err := Sign(emailReader, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_canonicalize(t *testing.T) {
|
|
||||||
emailReader := bytes.NewReader([]byte(email))
|
|
||||||
options := NewSigOptions()
|
|
||||||
// simple/simple
|
|
||||||
options.Canonicalization = "simple/simple"
|
|
||||||
_, body, err := canonicalize(emailReader, options)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte(bodySimple), body)
|
|
||||||
|
|
||||||
// relaxed/relaxed
|
|
||||||
options.Canonicalization = "relaxed/relaxed"
|
|
||||||
_, body, err = canonicalize(emailReader, options)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte(bodyRelaxed), body)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,5 +24,8 @@ var (
|
|||||||
ErrSignBadAlgo = errors.New("bad algorithm. Only rsa-sha1 or rsa-sha256 are permitted")
|
ErrSignBadAlgo = errors.New("bad algorithm. Only rsa-sha1 or rsa-sha256 are permitted")
|
||||||
|
|
||||||
// ErrBadMailFormat
|
// ErrBadMailFormat
|
||||||
ErrBadMailFormat = errors.New("bad mail formart")
|
ErrBadMailFormat = errors.New("bad mail format")
|
||||||
|
|
||||||
|
// ErrBadMailFormatHeaders
|
||||||
|
ErrBadMailFormatHeaders = errors.New("bad mail format found in headers")
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user