From 174579d04d8e067e4cf2d2e88ab6514b56bdc529 Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Fri, 22 May 2020 21:30:25 +0200 Subject: [PATCH 1/5] up folding alg --- dkim/header.go | 55 +++++++++++++++++++++++++++------------ dkim/header_test.go | 4 +-- dkim/sign.go | 4 +-- dkim/sign_ed25519_test.go | 10 ++++--- dkim/sign_test.go | 12 +++++---- 5 files changed, 56 insertions(+), 29 deletions(-) diff --git a/dkim/header.go b/dkim/header.go index f940e12..a93d98e 100644 --- a/dkim/header.go +++ b/dkim/header.go @@ -92,37 +92,60 @@ func parseHeaderParams(s string) (map[string]string, error) { return params, nil } -func formatHeaderParams(params map[string]string) string { - keys := make([]string, 0, len(params)) - found := false - for k := range params { - if k == "b" { - found = true - } else { - keys = append(keys, k) - } - } - sort.Strings(keys) - if found { - keys = append(keys, "b") - } +func formatHeaderParams(headerFieldName string, params map[string]string) string { + keys, bvalue := sortParams(params) var s string + var line string first := true + for _, k := range keys { v := params[k] if first { first = false + line += fmt.Sprintf("%v: %v=%v;", headerFieldName, k, v) + continue } else { - s += " " + nextLength := 3 + len(line) + len(v) + len(k) + if nextLength > 75 { + s += line + crlf + line = "" + } + line = fmt.Sprintf("%v %v=%v;", line, k, v) } + } - s += k + "=" + v + ";" + if line != "" { + s += line } + + if bvalue != "" { + bfiled := foldHeaderField(" b=" + bvalue) + s += crlf + bfiled + } + return s } +func sortParams(params map[string]string) ([]string, string) { + keys := make([]string, 0, len(params)) + found := false + var bvalue string + for k := range params { + if k == "b" { + bvalue = params["b"] + } else { + keys = append(keys, k) + } + } + sort.Strings(keys) + if found { + keys = append(keys, "b") + } + return keys, bvalue +} + type headerPicker struct { h header picked map[string]int diff --git a/dkim/header_test.go b/dkim/header_test.go index 2315d57..4722710 100644 --- a/dkim/header_test.go +++ b/dkim/header_test.go @@ -55,9 +55,9 @@ func TestFormatHeaderParams(t *testing.T) { "d": "example.org", } - expected := "a=rsa-sha256; d=example.org; v=1;" + expected := "DKIM-Signature: a=rsa-sha256; d=example.org; v=1;" - s := formatHeaderParams(params) + s := formatHeaderParams("DKIM-Signature", params) if s != expected { t.Errorf("Expected formatted params to be %q, but got %q", expected, s) } diff --git a/dkim/sign.go b/dkim/sign.go index 843ce18..effb7d4 100644 --- a/dkim/sign.go +++ b/dkim/sign.go @@ -333,8 +333,8 @@ func Sign(w io.Writer, r io.Reader, options *SignOptions) error { } func formatSignature(params map[string]string) string { - sig := headerFieldName + ": " + formatHeaderParams(params) - return foldHeaderField(sig) + sig := formatHeaderParams(headerFieldName, params) + return sig } func formatTagList(l []string) string { diff --git a/dkim/sign_ed25519_test.go b/dkim/sign_ed25519_test.go index 207c66d..0d892df 100644 --- a/dkim/sign_ed25519_test.go +++ b/dkim/sign_ed25519_test.go @@ -7,10 +7,12 @@ import ( "testing" ) -const signedEd25519MailString = "DKIM-Signature: a=ed25519-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJ" + "\r\n" + - " " + "VOzv8=; c=simple/simple; d=football.example.com; h=From:To:Subject:Date:Mes" + "\r\n" + - " " + "sage-ID; s=brisbane; t=424242; v=1; b=ZduPZq83AOTqjhScIfHll6W90tMG1nf34a34Q" + "\r\n" + - " " + "XKat3iFtP7NQE/3AwnHOrcsR2r5nVNoW+LeZURpT2obCthPCw==;" + "\r\n" + +const signedEd25519MailString = "DKIM-Signature: a=ed25519-sha256;" + "\r\n" + + " " + "bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; c=simple/simple;" + "\r\n" + + " " + "d=football.example.com; h=From:To:Subject:Date:Message-ID; s=brisbane;" + "\r\n" + + " " + "t=424242; v=1;" + "\r\n" + + " " + "b=O86ToNEaSJCCHtMW6YGNqUjQoWtpe5CAubhkPqVXjeTvqUa75Qsm05JD4FKETTGDX4us7tuj" + "\r\n" + + " " + "ZtiELMNQehiFDQ==" + "\r\n" + mailHeaderString + "\r\n" + mailBodyString diff --git a/dkim/sign_test.go b/dkim/sign_test.go index 68a3ab7..83f15c7 100644 --- a/dkim/sign_test.go +++ b/dkim/sign_test.go @@ -22,11 +22,13 @@ const mailBodyString = "Hi.\r\n" + const mailString = mailHeaderString + "\r\n" + mailBodyString -const signedMailString = "DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv" + "\r\n" + - " " + "8=; c=simple/simple; d=example.org; h=From:To:Subject:Date:Message-ID; s=br" + "\r\n" + - " " + "isbane; t=424242; v=1; b=bXtqB8uOEvtd1Xv/DHatdjb9onP0+vnzdYBbPMZm1qrRmhSuFH" + "\r\n" + - " " + "WsbkETafswNvJ4VqNX0gMoaYvzcmoMkUhW9m4pgZqR5y+62yA+B7WJCd6mz82UVkS1qEJeGjMxX" + "\r\n" + - " " + "mmPDkmLDA5HHL5LLTc3DLrxkwWMLzwrhQL48WhNFD1d6L4=;" + "\r\n" + +const signedMailString = "DKIM-Signature: a=rsa-sha256;" + "\r\n" + + " " + "bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; c=simple/simple;" + "\r\n" + + " " + "d=example.org; h=From:To:Subject:Date:Message-ID; s=brisbane; t=424242;" + "\r\n" + + " " + "v=1;" + "\r\n" + + " " + "b=KXuOIdcHesiuc+CNVJC0ooTO9LvodnM01dstXNTA7elICw7r9LKD5zrPnGwtP5Ye+sOndhJO" + "\r\n" + + " " + "23pJIY3LP5oXzCnV3nkZiEwIb0rKIgYxgxx8JfCWbZgurhN35rQjnwCtmcGvN1aJ7ba1x1x63Vf" + "\r\n" + + " " + "xp0zwssLR2w8R+PkR4r1ROlM=" + "\r\n" + mailHeaderString + "\r\n" + mailBodyString From 98c017d8434cf2045be6f5f5c9895753d451a59d Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Sun, 24 May 2020 16:02:58 +0200 Subject: [PATCH 2/5] fix empty b value --- dkim/header.go | 12 +++++------- dkim/sign_ed25519_test.go | 4 ++-- dkim/sign_test.go | 6 +++--- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/dkim/header.go b/dkim/header.go index a93d98e..5ad6ee2 100644 --- a/dkim/header.go +++ b/dkim/header.go @@ -93,7 +93,7 @@ func parseHeaderParams(s string) (map[string]string, error) { } func formatHeaderParams(headerFieldName string, params map[string]string) string { - keys, bvalue := sortParams(params) + keys, bvalue, found := sortParams(params) var s string var line string @@ -120,7 +120,7 @@ func formatHeaderParams(headerFieldName string, params map[string]string) string s += line } - if bvalue != "" { + if found { bfiled := foldHeaderField(" b=" + bvalue) s += crlf + bfiled } @@ -128,22 +128,20 @@ func formatHeaderParams(headerFieldName string, params map[string]string) string return s } -func sortParams(params map[string]string) ([]string, string) { +func sortParams(params map[string]string) ([]string, string, bool) { keys := make([]string, 0, len(params)) found := false var bvalue string for k := range params { if k == "b" { bvalue = params["b"] + found = true } else { keys = append(keys, k) } } sort.Strings(keys) - if found { - keys = append(keys, "b") - } - return keys, bvalue + return keys, bvalue, found } type headerPicker struct { diff --git a/dkim/sign_ed25519_test.go b/dkim/sign_ed25519_test.go index 0d892df..facd8d4 100644 --- a/dkim/sign_ed25519_test.go +++ b/dkim/sign_ed25519_test.go @@ -11,8 +11,8 @@ const signedEd25519MailString = "DKIM-Signature: a=ed25519-sha256;" + "\r\n" + " " + "bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; c=simple/simple;" + "\r\n" + " " + "d=football.example.com; h=From:To:Subject:Date:Message-ID; s=brisbane;" + "\r\n" + " " + "t=424242; v=1;" + "\r\n" + - " " + "b=O86ToNEaSJCCHtMW6YGNqUjQoWtpe5CAubhkPqVXjeTvqUa75Qsm05JD4FKETTGDX4us7tuj" + "\r\n" + - " " + "ZtiELMNQehiFDQ==" + "\r\n" + + " " + "b=iv+zd7iDETgqtf1BSyL++WgzTLNmG2msLIb5HupDqb9ynsNKWld1ApDJ0lsIxTBIyq/mKmSw" + "\r\n" + + " " + "+EnfA3YRP/CHBw==" + "\r\n" + mailHeaderString + "\r\n" + mailBodyString diff --git a/dkim/sign_test.go b/dkim/sign_test.go index 83f15c7..89e11d5 100644 --- a/dkim/sign_test.go +++ b/dkim/sign_test.go @@ -26,9 +26,9 @@ const signedMailString = "DKIM-Signature: a=rsa-sha256;" + "\r\n" + " " + "bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; c=simple/simple;" + "\r\n" + " " + "d=example.org; h=From:To:Subject:Date:Message-ID; s=brisbane; t=424242;" + "\r\n" + " " + "v=1;" + "\r\n" + - " " + "b=KXuOIdcHesiuc+CNVJC0ooTO9LvodnM01dstXNTA7elICw7r9LKD5zrPnGwtP5Ye+sOndhJO" + "\r\n" + - " " + "23pJIY3LP5oXzCnV3nkZiEwIb0rKIgYxgxx8JfCWbZgurhN35rQjnwCtmcGvN1aJ7ba1x1x63Vf" + "\r\n" + - " " + "xp0zwssLR2w8R+PkR4r1ROlM=" + "\r\n" + + " " + "b=0SgMief3lYbcE4Ke+LxAOWwGD+wTYWvTMqHUY7QQtC05EcTIyHn3vwFUxESzq6l/zx5GY68C" + "\r\n" + + " " + "nkVuMmZ2JkxMtHffdpIAY0w7bkj7EsrJJVmpjk9GANXWPThJCbnVVBZXMViQoQ6hXfzNOunV4+C" + "\r\n" + + " " + "n2QWkBhpjPEJeDBSZ279qFnA=" + "\r\n" + mailHeaderString + "\r\n" + mailBodyString From 5d1639f30421d6b630286068787f28a0e0f15bae Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Sun, 24 May 2020 16:11:26 +0200 Subject: [PATCH 3/5] add test case for long header folding --- dkim/header_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dkim/header_test.go b/dkim/header_test.go index 4722710..5ad8ee5 100644 --- a/dkim/header_test.go +++ b/dkim/header_test.go @@ -63,6 +63,24 @@ func TestFormatHeaderParams(t *testing.T) { } } +func TestLongHeaderFolding(t *testing.T) { + // see #29 and #27 + + params := map[string]string{ + "v": "1", + "a": "rsa-sha256", + "d": "example.org", + "h": "From:To:Subject:Date:Message-ID:Long-Header-Name", + } + + expected := "DKIM-Signature: a=rsa-sha256; d=example.org;\r\n h=From:To:Subject:Date:Message-ID:Long-Header-Name; v=1;" + + s := formatHeaderParams("DKIM-Signature", params) + if s != expected { + t.Errorf("Expected formatted params to be\n\n %q\n\n, but got\n\n %q", expected, s) + } +} + func TestParseHeaderParams_malformed(t *testing.T) { _, err := parseHeaderParams("abc; def") if err == nil { From e9efb5d83fa49d7b9f4cd5318572213f5870d0b0 Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Sun, 24 May 2020 16:21:31 +0200 Subject: [PATCH 4/5] add test case for long header folding --- dkim/header_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dkim/header_test.go b/dkim/header_test.go index 5ad8ee5..16379b5 100644 --- a/dkim/header_test.go +++ b/dkim/header_test.go @@ -81,6 +81,22 @@ func TestLongHeaderFolding(t *testing.T) { } } +func TestSignedHeaderFolding(t *testing.T) { + hValue := "From:To:Subject:Date:Message-ID:Long-Header-Name:Another-Long-Header-Name" + + params := map[string]string{ + "v": "1", + "a": "rsa-sha256", + "d": "example.org", + "h": hValue, + } + + s := formatHeaderParams("DKIM-Signature", params) + if !strings.Contains(s, hValue) { + t.Errorf("Signed Headers names (%v) are not well folded in the signed header %q", hValue, s) + } +} + func TestParseHeaderParams_malformed(t *testing.T) { _, err := parseHeaderParams("abc; def") if err == nil { From a231a2f8855e1bdb55963eff493b3b73e3c52f2b Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Mon, 25 May 2020 11:05:48 +0200 Subject: [PATCH 5/5] improve code style --- dkim/header.go | 30 +++++++++++------------------- dkim/sign_ed25519_test.go | 11 +++++------ dkim/sign_test.go | 13 ++++++------- 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/dkim/header.go b/dkim/header.go index 5ad6ee2..45f3f6b 100644 --- a/dkim/header.go +++ b/dkim/header.go @@ -93,34 +93,26 @@ func parseHeaderParams(s string) (map[string]string, error) { } func formatHeaderParams(headerFieldName string, params map[string]string) string { - keys, bvalue, found := sortParams(params) + keys, bvalue, bfound := sortParams(params) - var s string + s := headerFieldName + ":" var line string - first := true for _, k := range keys { v := params[k] - - if first { - first = false - line += fmt.Sprintf("%v: %v=%v;", headerFieldName, k, v) - continue - } else { - nextLength := 3 + len(line) + len(v) + len(k) - if nextLength > 75 { - s += line + crlf - line = "" - } - line = fmt.Sprintf("%v %v=%v;", line, k, v) + nextLength := 3 + len(line) + len(v) + len(k) + if nextLength > 75 { + s += line + crlf + line = "" } + line = fmt.Sprintf("%v %v=%v;", line, k, v) } if line != "" { s += line } - if found { + if bfound { bfiled := foldHeaderField(" b=" + bvalue) s += crlf + bfiled } @@ -130,18 +122,18 @@ func formatHeaderParams(headerFieldName string, params map[string]string) string func sortParams(params map[string]string) ([]string, string, bool) { keys := make([]string, 0, len(params)) - found := false + bfound := false var bvalue string for k := range params { if k == "b" { bvalue = params["b"] - found = true + bfound = true } else { keys = append(keys, k) } } sort.Strings(keys) - return keys, bvalue, found + return keys, bvalue, bfound } type headerPicker struct { diff --git a/dkim/sign_ed25519_test.go b/dkim/sign_ed25519_test.go index facd8d4..b8fd33b 100644 --- a/dkim/sign_ed25519_test.go +++ b/dkim/sign_ed25519_test.go @@ -7,12 +7,11 @@ import ( "testing" ) -const signedEd25519MailString = "DKIM-Signature: a=ed25519-sha256;" + "\r\n" + - " " + "bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; c=simple/simple;" + "\r\n" + - " " + "d=football.example.com; h=From:To:Subject:Date:Message-ID; s=brisbane;" + "\r\n" + - " " + "t=424242; v=1;" + "\r\n" + - " " + "b=iv+zd7iDETgqtf1BSyL++WgzTLNmG2msLIb5HupDqb9ynsNKWld1ApDJ0lsIxTBIyq/mKmSw" + "\r\n" + - " " + "+EnfA3YRP/CHBw==" + "\r\n" + +const signedEd25519MailString = "DKIM-Signature: a=ed25519-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;" + "\r\n" + + " " + "c=simple/simple; d=football.example.com;" + "\r\n" + + " " + "h=From:To:Subject:Date:Message-ID; s=brisbane; t=424242; v=1;" + "\r\n" + + " " + "b=k1LzRxs9/DfN/whlMICYKNIJhqUSmup0d5yw8tpi+Cfiqe6I3oSBmJ+HEp+moWy7/XvcUY/t" + "\r\n" + + " " + "ERHc3D2m7vw1AA==" + "\r\n" + mailHeaderString + "\r\n" + mailBodyString diff --git a/dkim/sign_test.go b/dkim/sign_test.go index 89e11d5..3cf7529 100644 --- a/dkim/sign_test.go +++ b/dkim/sign_test.go @@ -22,13 +22,12 @@ const mailBodyString = "Hi.\r\n" + const mailString = mailHeaderString + "\r\n" + mailBodyString -const signedMailString = "DKIM-Signature: a=rsa-sha256;" + "\r\n" + - " " + "bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=; c=simple/simple;" + "\r\n" + - " " + "d=example.org; h=From:To:Subject:Date:Message-ID; s=brisbane; t=424242;" + "\r\n" + - " " + "v=1;" + "\r\n" + - " " + "b=0SgMief3lYbcE4Ke+LxAOWwGD+wTYWvTMqHUY7QQtC05EcTIyHn3vwFUxESzq6l/zx5GY68C" + "\r\n" + - " " + "nkVuMmZ2JkxMtHffdpIAY0w7bkj7EsrJJVmpjk9GANXWPThJCbnVVBZXMViQoQ6hXfzNOunV4+C" + "\r\n" + - " " + "n2QWkBhpjPEJeDBSZ279qFnA=" + "\r\n" + +const signedMailString = "DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;" + "\r\n" + + " " + "c=simple/simple; d=example.org; h=From:To:Subject:Date:Message-ID;" + "\r\n" + + " " + "s=brisbane; t=424242; v=1;" + "\r\n" + + " " + "b=MobyyDTeHhMhNJCEI6ATNK63ZQ7deSXK9umyzAvYwFqE6oGGvlQBQwqr1aC11hWpktjMLP1/" + "\r\n" + + " " + "m0PBi9v7cRLKMXXBIv2O0B1mIWdZPqd9jveRJqKzCb7SpqH2u5kK6i2vZI639ENTQzRQdxSAGXc" + "\r\n" + + " " + "PcPYjrgkqj7xklnrNBs0aIUA=" + "\r\n" + mailHeaderString + "\r\n" + mailBodyString