Skip to content

Commit ad9cd20

Browse files
authored
Merge pull request #450 from wneessen/feature/449_fork-golangorgxcryptopbkdf2
Fork pbkdf2 package from x/crypto/pbkdf2
2 parents 8e4c7da + 3944b3e commit ad9cd20

File tree

7 files changed

+281
-11
lines changed

7 files changed

+281
-11
lines changed

.golangci.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,9 @@ linters = ["gosec"]
7979
path = "quicksend_test.go"
8080
text = "G402:"
8181

82+
## These are tests which intentionally test SHA1 and SHA256
83+
[[issues.exclude-rules]]
84+
linters = ["gosec"]
85+
path = "internal/pbkdf2/pbkdf2_test.go"
86+
text = "G505:"
87+

go.mod

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,4 @@ module github.com/wneessen/go-mail
66

77
go 1.16
88

9-
require (
10-
golang.org/x/crypto v0.33.0
11-
golang.org/x/text v0.22.0
12-
)
9+
require golang.org/x/text v0.22.0

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
55
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
66
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
77
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
8-
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
9-
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
108
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
119
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
1210
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -37,7 +35,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3735
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3836
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
3937
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
40-
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
4138
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
4239
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
4340
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -46,7 +43,6 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
4643
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
4744
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
4845
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
49-
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
5046
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
5147
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
5248
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=

internal/pbkdf2/pbkdf2.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// SPDX-FileCopyrightText: Copyright 2012 The Go Authors. All rights reserved.
2+
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
3+
//
4+
// Original crypto/pbkdf2 code from the Go extended library by the Go Authors.
5+
// Use of this source code is governed by a BSD-style
6+
// LICENSE file that can be found in this directory.
7+
//
8+
// go-mail specific modifications by the go-mail Authors.
9+
// Licensed under the MIT License.
10+
// See [PROJECT ROOT]/LICENSES directory for more information.
11+
//
12+
// SPDX-License-Identifier: BSD-3-Clause AND MIT
13+
14+
/*
15+
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
16+
2898 / PKCS #5 v2.0.
17+
18+
A key derivation function is useful when encrypting data based on a password
19+
or any other not-fully-random data. It uses a pseudorandom function to derive
20+
a secure encryption key based on the password.
21+
22+
While v2.0 of the standard defines only one pseudorandom function to use,
23+
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
24+
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
25+
choose, you can pass the `New` functions from the different SHA packages to
26+
pbkdf2.Key.
27+
*/
28+
package pbkdf2
29+
30+
import (
31+
"crypto/hmac"
32+
"hash"
33+
)
34+
35+
// Key derives a key from the password, salt and iteration count, returning a
36+
// []byte of length keylen that can be used as cryptographic key. The key is
37+
// derived based on the method described as PBKDF2 with the HMAC variant using
38+
// the supplied hash function.
39+
//
40+
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
41+
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
42+
// doing:
43+
//
44+
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
45+
//
46+
// Remember to get a good random salt. At least 8 bytes is recommended by the
47+
// RFC.
48+
//
49+
// Using a higher iteration count will increase the cost of an exhaustive
50+
// search but will also make derivation proportionally slower.
51+
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
52+
prf := hmac.New(h, password)
53+
hashLen := prf.Size()
54+
numBlocks := (keyLen + hashLen - 1) / hashLen
55+
56+
var buf [4]byte
57+
dk := make([]byte, 0, numBlocks*hashLen)
58+
U := make([]byte, hashLen)
59+
for block := 1; block <= numBlocks; block++ {
60+
// N.B.: || means concatenation, ^ means XOR
61+
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
62+
// U_1 = PRF(password, salt || uint(i))
63+
prf.Reset()
64+
prf.Write(salt)
65+
buf[0] = byte(block >> 24)
66+
buf[1] = byte(block >> 16)
67+
buf[2] = byte(block >> 8)
68+
buf[3] = byte(block)
69+
prf.Write(buf[:4])
70+
dk = prf.Sum(dk)
71+
T := dk[len(dk)-hashLen:]
72+
copy(U, T)
73+
74+
// U_n = PRF(password, U_(n-1))
75+
for n := 2; n <= iter; n++ {
76+
prf.Reset()
77+
prf.Write(U)
78+
U = U[:0]
79+
U = prf.Sum(U)
80+
for x := range U {
81+
T[x] ^= U[x]
82+
}
83+
}
84+
}
85+
return dk[:keyLen]
86+
}

internal/pbkdf2/pbkdf2_test.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// SPDX-FileCopyrightText: Copyright 2012 The Go Authors. All rights reserved.
2+
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
3+
//
4+
// Original crypto/pbkdf2 code from the Go extended library by the Go Authors.
5+
// Use of this source code is governed by a BSD-style
6+
// LICENSE file that can be found in this directory.
7+
//
8+
// go-mail specific modifications by the go-mail Authors.
9+
// Licensed under the MIT License.
10+
// See [PROJECT ROOT]/LICENSES directory for more information.
11+
//
12+
// SPDX-License-Identifier: BSD-3-Clause AND MIT
13+
14+
package pbkdf2
15+
16+
import (
17+
"bytes"
18+
"crypto/sha1"
19+
"crypto/sha256"
20+
"hash"
21+
"testing"
22+
)
23+
24+
type testVector struct {
25+
password string
26+
salt string
27+
iter int
28+
output []byte
29+
}
30+
31+
// Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070
32+
var sha1TestVectors = []testVector{
33+
{
34+
"password",
35+
"salt",
36+
1,
37+
[]byte{
38+
0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
39+
0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
40+
0x2f, 0xe0, 0x37, 0xa6,
41+
},
42+
},
43+
{
44+
"password",
45+
"salt",
46+
2,
47+
[]byte{
48+
0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
49+
0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
50+
0xd8, 0xde, 0x89, 0x57,
51+
},
52+
},
53+
{
54+
"password",
55+
"salt",
56+
4096,
57+
[]byte{
58+
0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
59+
0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
60+
0x65, 0xa4, 0x29, 0xc1,
61+
},
62+
},
63+
// // This one takes too long
64+
// {
65+
// "password",
66+
// "salt",
67+
// 16777216,
68+
// []byte{
69+
// 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
70+
// 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
71+
// 0x26, 0x34, 0xe9, 0x84,
72+
// },
73+
// },
74+
{
75+
"passwordPASSWORDpassword",
76+
"saltSALTsaltSALTsaltSALTsaltSALTsalt",
77+
4096,
78+
[]byte{
79+
0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
80+
0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
81+
0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
82+
0x38,
83+
},
84+
},
85+
{
86+
"pass\000word",
87+
"sa\000lt",
88+
4096,
89+
[]byte{
90+
0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
91+
0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3,
92+
},
93+
},
94+
}
95+
96+
// Test vectors from
97+
// http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
98+
var sha256TestVectors = []testVector{
99+
{
100+
"password",
101+
"salt",
102+
1,
103+
[]byte{
104+
0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c,
105+
0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37,
106+
0xa8, 0x65, 0x48, 0xc9,
107+
},
108+
},
109+
{
110+
"password",
111+
"salt",
112+
2,
113+
[]byte{
114+
0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3,
115+
0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0,
116+
0x2a, 0x30, 0x3f, 0x8e,
117+
},
118+
},
119+
{
120+
"password",
121+
"salt",
122+
4096,
123+
[]byte{
124+
0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41,
125+
0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d,
126+
0x96, 0x28, 0x93, 0xa0,
127+
},
128+
},
129+
{
130+
"passwordPASSWORDpassword",
131+
"saltSALTsaltSALTsaltSALTsaltSALTsalt",
132+
4096,
133+
[]byte{
134+
0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f,
135+
0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf,
136+
0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18,
137+
0x1c,
138+
},
139+
},
140+
{
141+
"pass\000word",
142+
"sa\000lt",
143+
4096,
144+
[]byte{
145+
0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89,
146+
0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87,
147+
},
148+
},
149+
}
150+
151+
func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) {
152+
for i, v := range vectors {
153+
o := Key([]byte(v.password), []byte(v.salt), v.iter, len(v.output), h)
154+
if !bytes.Equal(o, v.output) {
155+
t.Errorf("%s %d: expected %x, got %x", hashName, i, v.output, o)
156+
}
157+
}
158+
}
159+
160+
func TestWithHMACSHA1(t *testing.T) {
161+
testHash(t, sha1.New, "SHA1", sha1TestVectors)
162+
}
163+
164+
func TestWithHMACSHA256(t *testing.T) {
165+
testHash(t, sha256.New, "SHA256", sha256TestVectors)
166+
}
167+
168+
var sink uint8
169+
170+
func benchmark(b *testing.B, h func() hash.Hash) {
171+
password := make([]byte, h().Size())
172+
salt := make([]byte, 8)
173+
for i := 0; i < b.N; i++ {
174+
password = Key(password, salt, 4096, len(password), h)
175+
}
176+
sink += password[0]
177+
}
178+
179+
func BenchmarkHMACSHA1(b *testing.B) {
180+
benchmark(b, sha1.New)
181+
}
182+
183+
func BenchmarkHMACSHA256(b *testing.B) {
184+
benchmark(b, sha256.New)
185+
}

smtp/auth_scram.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import (
1919
"strconv"
2020
"strings"
2121

22-
"golang.org/x/crypto/pbkdf2"
2322
"golang.org/x/text/secure/precis"
23+
24+
"github.com/wneessen/go-mail/internal/pbkdf2"
2425
)
2526

2627
// scramAuth represents a SCRAM (Salted Challenge Response Authentication Mechanism) client and

smtp/smtp_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ import (
3737
"testing"
3838
"time"
3939

40-
"golang.org/x/crypto/pbkdf2"
41-
40+
"github.com/wneessen/go-mail/internal/pbkdf2"
4241
"github.com/wneessen/go-mail/log"
4342
)
4443

0 commit comments

Comments
 (0)