new signature fro sign
This commit is contained in:
59
dkim.go
59
dkim.go
@@ -11,8 +11,9 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
//"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io/ioutil"
|
//"io/ioutil"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -74,61 +75,60 @@ type sigOptions struct {
|
|||||||
func NewSigOptions() sigOptions {
|
func NewSigOptions() sigOptions {
|
||||||
return sigOptions{
|
return sigOptions{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Canonicalization: "simple/simple",
|
Canonicalization: "relaxed/simple",
|
||||||
Algo: "rsa-sha256",
|
Algo: "rsa-sha256",
|
||||||
Headers: []string{"from"},
|
Headers: []string{"from"},
|
||||||
BodyLength: 0,
|
BodyLength: 0,
|
||||||
QueryMethods: []string{"dns/txt"},
|
QueryMethods: []string{"dns/txt"},
|
||||||
AddSignatureTimestamp: false,
|
AddSignatureTimestamp: true,
|
||||||
SignatureExpireIn: 0,
|
SignatureExpireIn: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs an email
|
// Sign signs an email
|
||||||
func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) {
|
func Sign(email *[]byte, options sigOptions) error {
|
||||||
var privateKey *rsa.PrivateKey
|
var privateKey *rsa.PrivateKey
|
||||||
|
|
||||||
// PrivateKey (required & TODO: valid)
|
// PrivateKey
|
||||||
if options.PrivateKey == "" {
|
if options.PrivateKey == "" {
|
||||||
return nil, ErrSignPrivateKeyRequired
|
return ErrSignPrivateKeyRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
d, _ := pem.Decode([]byte(options.PrivateKey))
|
d, _ := pem.Decode([]byte(options.PrivateKey))
|
||||||
key, err := x509.ParsePKCS1PrivateKey(d.Bytes)
|
key, err := x509.ParsePKCS1PrivateKey(d.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCandNotParsePrivateKey
|
return ErrCandNotParsePrivateKey
|
||||||
}
|
}
|
||||||
privateKey = key
|
privateKey = key
|
||||||
|
|
||||||
// Domain required
|
// Domain required
|
||||||
if options.Domain == "" {
|
if options.Domain == "" {
|
||||||
return nil, ErrSignDomainRequired
|
return ErrSignDomainRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selector required
|
// Selector required
|
||||||
if options.Selector == "" {
|
if options.Selector == "" {
|
||||||
return nil, ErrSignSelectorRequired
|
return ErrSignSelectorRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canonicalization
|
// Canonicalization
|
||||||
options.Canonicalization = strings.ToLower(options.Canonicalization)
|
options.Canonicalization = strings.ToLower(options.Canonicalization)
|
||||||
p := strings.Split(options.Canonicalization, "/")
|
p := strings.Split(options.Canonicalization, "/")
|
||||||
if len(p) > 2 {
|
if len(p) > 2 {
|
||||||
return nil, ErrSignBadCanonicalization
|
return ErrSignBadCanonicalization
|
||||||
}
|
}
|
||||||
if len(p) == 1 {
|
if len(p) == 1 {
|
||||||
options.Canonicalization = options.Canonicalization + "/simple"
|
options.Canonicalization = options.Canonicalization + "/simple"
|
||||||
}
|
}
|
||||||
for _, c := range p {
|
for _, c := range p {
|
||||||
if c != "simple" && c != "relaxed" {
|
if c != "simple" && c != "relaxed" {
|
||||||
return nil, ErrSignBadCanonicalization
|
return ErrSignBadCanonicalization
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Algo
|
// Algo
|
||||||
options.Algo = strings.ToLower(options.Algo)
|
options.Algo = strings.ToLower(options.Algo)
|
||||||
if options.Algo != "rsa-sha1" && options.Algo != "rsa-sha256" {
|
if options.Algo != "rsa-sha1" && options.Algo != "rsa-sha256" {
|
||||||
return nil, ErrSignBadAlgo
|
return ErrSignBadAlgo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header must contain "from"
|
// Header must contain "from"
|
||||||
@@ -142,13 +142,13 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !hasFrom {
|
if !hasFrom {
|
||||||
return nil, ErrSignHeaderShouldContainsFrom
|
return ErrSignHeaderShouldContainsFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize
|
// Normalize
|
||||||
headers, body, err := canonicalize(email, options)
|
headers, body, err := canonicalize(email, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash body
|
// hash body
|
||||||
@@ -169,7 +169,7 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) {
|
|||||||
// if l tag (body length)
|
// if l tag (body length)
|
||||||
if options.BodyLength != 0 {
|
if options.BodyLength != 0 {
|
||||||
if uint(len(body)) < options.BodyLength {
|
if uint(len(body)) < options.BodyLength {
|
||||||
return nil, ErrBadDKimTagLBodyTooShort
|
return ErrBadDKimTagLBodyTooShort
|
||||||
}
|
}
|
||||||
body = body[0:options.BodyLength]
|
body = body[0:options.BodyLength]
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) {
|
|||||||
canonicalizations := strings.Split(options.Canonicalization, "/")
|
canonicalizations := strings.Split(options.Canonicalization, "/")
|
||||||
dHeaderCanonicalized, err := canonicalizeHeader(dHeader, canonicalizations[0])
|
dHeaderCanonicalized, err := canonicalizeHeader(dHeader, canonicalizations[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
headers = append(headers, []byte(dHeaderCanonicalized)...)
|
headers = append(headers, []byte(dHeaderCanonicalized)...)
|
||||||
headers = bytes.TrimRight(headers, " \r\n")
|
headers = bytes.TrimRight(headers, " \r\n")
|
||||||
@@ -193,7 +193,7 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) {
|
|||||||
h2.Write(headers)
|
h2.Write(headers)
|
||||||
sig, err := rsa.SignPKCS1v15(rand.Reader, privateKey, h3, h2.Sum(nil))
|
sig, err := rsa.SignPKCS1v15(rand.Reader, privateKey, h3, h2.Sum(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
sig64 := base64.StdEncoding.EncodeToString(sig)
|
sig64 := base64.StdEncoding.EncodeToString(sig)
|
||||||
|
|
||||||
@@ -210,32 +210,17 @@ func Sign(email *bytes.Reader, options sigOptions) (*bytes.Reader, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dHeader += subh + CRLF
|
dHeader += subh + CRLF
|
||||||
|
*email = append([]byte(dHeader), *email...)
|
||||||
// Out
|
return nil
|
||||||
rawmail := []byte(dHeader)
|
|
||||||
t, err := ioutil.ReadAll(email)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rawmail = append(rawmail, t...)
|
|
||||||
return bytes.NewReader(rawmail), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// canonicalize returns canonicalized version of header and body
|
// canonicalize returns canonicalized version of header and body
|
||||||
func canonicalize(emailReader *bytes.Reader, options sigOptions) (headers, body []byte, err error) {
|
func canonicalize(email *[]byte, options sigOptions) (headers, body []byte, err error) {
|
||||||
var email []byte
|
|
||||||
body = []byte{}
|
body = []byte{}
|
||||||
rxReduceWS := regexp.MustCompile(`[ \t]+`)
|
rxReduceWS := regexp.MustCompile(`[ \t]+`)
|
||||||
|
|
||||||
email, err = ioutil.ReadAll(emailReader)
|
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: \n -> \r\n
|
// TODO: \n -> \r\n
|
||||||
parts := bytes.SplitN(email, []byte{13, 10, 13, 10}, 2)
|
parts := bytes.SplitN(*email, []byte{13, 10, 13, 10}, 2)
|
||||||
|
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
return headers, body, ErrBadMailFormat
|
return headers, body, ErrBadMailFormat
|
||||||
|
|||||||
86
dkim_test.go
86
dkim_test.go
@@ -1,8 +1,7 @@
|
|||||||
package dkim
|
package dkim
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
//"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -35,7 +34,7 @@ kS5vLkzRI84eiJrm3+IieUqIIicsO+WYxQs+JgVx5XhpPjX4SQjHtwEC2xKkWnEv
|
|||||||
selector = "test"
|
selector = "test"
|
||||||
)
|
)
|
||||||
|
|
||||||
var email = "Received: (qmail 28277 invoked from network); 1 May 2015 09:43:37 -0000" + CRLF +
|
var emailBase = "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 +
|
"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 +
|
" by mo51.mail-out.ovh.net (Postfix) with SMTP id A6E22FF8934" + CRLF +
|
||||||
@@ -86,122 +85,121 @@ var signedRelaxedRelaxed = "DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=rela
|
|||||||
" bh=GF+NsyJx/iX1Yab8k4suJkMG7DBO2lGAB9F2SCY4GWk=;" + CRLF +
|
" bh=GF+NsyJx/iX1Yab8k4suJkMG7DBO2lGAB9F2SCY4GWk=;" + CRLF +
|
||||||
" b=byhiFWd0lAM1sqD1tl8S1DZtKNqgiEZp8jrGds6RRydnZkdX9rCPeL0Q5MYWBQ/JmQrml5" + CRLF +
|
" b=byhiFWd0lAM1sqD1tl8S1DZtKNqgiEZp8jrGds6RRydnZkdX9rCPeL0Q5MYWBQ/JmQrml5" + CRLF +
|
||||||
" pIghLwl/EshDBmNy65O6qO8pSSGgZmM3T7SRLMloex8bnrBJ4KSYcHV46639gVEWcBOKW0" + CRLF +
|
" pIghLwl/EshDBmNy65O6qO8pSSGgZmM3T7SRLMloex8bnrBJ4KSYcHV46639gVEWcBOKW0" + CRLF +
|
||||||
" h1djZu2jaTuxGeJzlFVtw3Arf2B93cc=" + CRLF + email
|
" h1djZu2jaTuxGeJzlFVtw3Arf2B93cc=" + CRLF + emailBase
|
||||||
|
|
||||||
var signedSimpleSimple = "DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=simple/simple;" + CRLF +
|
var signedSimpleSimple = "DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=simple/simple;" + CRLF +
|
||||||
" s=test; d=tmail.io; l=5; h=from:date:mime-version:received:received;" + CRLF +
|
" s=test; d=tmail.io; l=5; h=from:date:mime-version:received:received;" + CRLF +
|
||||||
" bh=GF+NsyJx/iX1Yab8k4suJkMG7DBO2lGAB9F2SCY4GWk=;" + CRLF +
|
" bh=GF+NsyJx/iX1Yab8k4suJkMG7DBO2lGAB9F2SCY4GWk=;" + CRLF +
|
||||||
" b=SoEhlu1Emm2ASqo8jMhz6FIf2nNHt3ouY4Av/pFFEkQ048RqUFP437ap7RbtL2wh0N3Kkm" + CRLF +
|
" b=SoEhlu1Emm2ASqo8jMhz6FIf2nNHt3ouY4Av/pFFEkQ048RqUFP437ap7RbtL2wh0N3Kkm" + CRLF +
|
||||||
" AKF2TcTLZ++1nalq+djU+/aP4KYQd4RWWFBjkxDzvCH4bvB1M5AGp4Qz9ldmdMQBWOvvSp" + CRLF +
|
" AKF2TcTLZ++1nalq+djU+/aP4KYQd4RWWFBjkxDzvCH4bvB1M5AGp4Qz9ldmdMQBWOvvSp" + CRLF +
|
||||||
" DIpJW4XNA/uqLSswtjCYbJsSg9Ywv1o=" + CRLF + email
|
" DIpJW4XNA/uqLSswtjCYbJsSg9Ywv1o=" + CRLF + emailBase
|
||||||
|
|
||||||
func Test_NewSigOptions(t *testing.T) {
|
func Test_NewSigOptions(t *testing.T) {
|
||||||
options := NewSigOptions()
|
options := NewSigOptions()
|
||||||
assert.Equal(t, "rsa-sha256", options.Algo)
|
assert.Equal(t, "rsa-sha256", options.Algo)
|
||||||
assert.Equal(t, "simple/simple", options.Canonicalization)
|
assert.Equal(t, "relaxed/simple", options.Canonicalization)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_SignConfig(t *testing.T) {
|
/*func Test_SignConfig(t *testing.T) {
|
||||||
emailReader := bytes.NewReader([]byte(email))
|
email := []byte(emailBase)
|
||||||
|
emailToTest := append([]byte(nil), email...)
|
||||||
options := NewSigOptions()
|
options := NewSigOptions()
|
||||||
_, err := Sign(emailReader, options)
|
err := Sign(&emailToTest, options)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
// && err No private key
|
// && err No private key
|
||||||
assert.EqualError(t, err, ErrSignPrivateKeyRequired.Error())
|
assert.EqualError(t, err, ErrSignPrivateKeyRequired.Error())
|
||||||
options.PrivateKey = privKey
|
options.PrivateKey = privKey
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
emailReader.Seek(0, 0)
|
err = Sign(&emailToTest, options)
|
||||||
|
|
||||||
// Domain
|
// Domain
|
||||||
assert.EqualError(t, err, ErrSignDomainRequired.Error())
|
assert.EqualError(t, err, ErrSignDomainRequired.Error())
|
||||||
options.Domain = "toorop.fr"
|
options.Domain = "toorop.fr"
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
emailReader.Seek(0, 0)
|
err = Sign(&emailToTest, options)
|
||||||
|
|
||||||
// Selector
|
// Selector
|
||||||
assert.Error(t, err, ErrSignSelectorRequired.Error())
|
assert.Error(t, err, ErrSignSelectorRequired.Error())
|
||||||
options.Selector = "default"
|
options.Selector = "default"
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
|
err = Sign(&emailToTest, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
|
|
||||||
// Canonicalization
|
// Canonicalization
|
||||||
options.Canonicalization = "simple/relaxed/simple"
|
options.Canonicalization = "simple/relaxed/simple"
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
|
err = Sign(&emailToTest, options)
|
||||||
assert.EqualError(t, err, ErrSignBadCanonicalization.Error())
|
assert.EqualError(t, err, ErrSignBadCanonicalization.Error())
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
|
|
||||||
options.Canonicalization = "simple/relax"
|
options.Canonicalization = "simple/relax"
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
emailReader.Seek(0, 0)
|
err = Sign(&emailToTest, options)
|
||||||
assert.EqualError(t, err, ErrSignBadCanonicalization.Error())
|
assert.EqualError(t, err, ErrSignBadCanonicalization.Error())
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
|
|
||||||
options.Canonicalization = "relaxed"
|
options.Canonicalization = "relaxed"
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
|
err = Sign(&emailToTest, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
|
|
||||||
options.Canonicalization = "SiMple/relAxed"
|
options.Canonicalization = "SiMple/relAxed"
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
|
err = Sign(&emailToTest, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
|
|
||||||
// header
|
// header
|
||||||
options.Headers = []string{"toto"}
|
options.Headers = []string{"toto"}
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
|
err = Sign(&emailToTest, options)
|
||||||
assert.EqualError(t, err, ErrSignHeaderShouldContainsFrom.Error())
|
assert.EqualError(t, err, ErrSignHeaderShouldContainsFrom.Error())
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
|
|
||||||
options.Headers = []string{"To", "From"}
|
options.Headers = []string{"To", "From"}
|
||||||
_, err = Sign(emailReader, options)
|
emailToTest = append([]byte(nil), email...)
|
||||||
|
err = Sign(&emailToTest, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_canonicalize(t *testing.T) {
|
func Test_canonicalize(t *testing.T) {
|
||||||
emailReader := bytes.NewReader([]byte(email))
|
email := []byte(emailBase)
|
||||||
|
emailToTest := append([]byte(nil), email...)
|
||||||
options := NewSigOptions()
|
options := NewSigOptions()
|
||||||
options.Headers = []string{"from", "date", "mime-version", "received", "received", "In-Reply-To"}
|
options.Headers = []string{"from", "date", "mime-version", "received", "received", "In-Reply-To"}
|
||||||
// simple/simple
|
// simple/simple
|
||||||
options.Canonicalization = "simple/simple"
|
options.Canonicalization = "simple/simple"
|
||||||
header, body, err := canonicalize(emailReader, options)
|
header, body, err := canonicalize(&emailToTest, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, []byte(headerSimple), header)
|
assert.Equal(t, []byte(headerSimple), header)
|
||||||
assert.Equal(t, []byte(bodySimple), body)
|
assert.Equal(t, []byte(bodySimple), body)
|
||||||
|
|
||||||
// relaxed/relaxed
|
// relaxed/relaxed
|
||||||
|
emailToTest = append([]byte(nil), email...)
|
||||||
options.Canonicalization = "relaxed/relaxed"
|
options.Canonicalization = "relaxed/relaxed"
|
||||||
header, body, err = canonicalize(emailReader, options)
|
header, body, err = canonicalize(&emailToTest, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, []byte(headerRelaxed), header)
|
assert.Equal(t, []byte(headerRelaxed), header)
|
||||||
assert.Equal(t, []byte(bodyRelaxed), body)
|
assert.Equal(t, []byte(bodyRelaxed), body)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
func Test_Sign(t *testing.T) {
|
func Test_Sign(t *testing.T) {
|
||||||
emailReader := bytes.NewReader([]byte(email))
|
email := []byte(emailBase)
|
||||||
|
emailRelaxed := append([]byte(nil), email...)
|
||||||
options := NewSigOptions()
|
options := NewSigOptions()
|
||||||
options.PrivateKey = privKey
|
options.PrivateKey = privKey
|
||||||
options.Domain = domain
|
options.Domain = domain
|
||||||
options.Selector = selector
|
options.Selector = selector
|
||||||
//options.AddSignatureTimestamp = true
|
|
||||||
//options.SignatureExpireIn = 3600
|
//options.SignatureExpireIn = 3600
|
||||||
options.BodyLength = 5
|
options.BodyLength = 5
|
||||||
options.Headers = []string{"from", "date", "mime-version", "received", "received"}
|
options.Headers = []string{"from", "date", "mime-version", "received", "received"}
|
||||||
|
options.AddSignatureTimestamp = false
|
||||||
options.Canonicalization = "relaxed/relaxed"
|
options.Canonicalization = "relaxed/relaxed"
|
||||||
signedEmailReader, err := Sign(emailReader, options)
|
err := Sign(&emailRelaxed, options)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
raw, _ := ioutil.ReadAll(signedEmailReader)
|
assert.Equal(t, []byte(signedRelaxedRelaxed), emailRelaxed)
|
||||||
emailReader.Seek(0, 0)
|
|
||||||
assert.Equal(t, []byte(signedRelaxedRelaxed), raw)
|
|
||||||
|
|
||||||
options.Canonicalization = "simple/simple"
|
options.Canonicalization = "simple/simple"
|
||||||
emailReader, err = Sign(emailReader, options)
|
emailSimple := append([]byte(nil), email...)
|
||||||
assert.NoError(t, err)
|
err = Sign(&emailSimple, options)
|
||||||
raw, _ = ioutil.ReadAll(emailReader)
|
assert.Equal(t, []byte(signedSimpleSimple), emailSimple)
|
||||||
assert.Equal(t, []byte(signedSimpleSimple), raw)
|
|
||||||
|
|
||||||
//raw, _ = ioutil.ReadAll(emailReader)
|
|
||||||
//fmt.Println(string(raw))
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user