@@ -2,8 +2,6 @@ package encryption
22
33import (
44 "crypto/rand"
5− "crypto/sha1"
6− "crypto/sha256"
75 "encoding/base64"
86 "fmt"
97 "io"
@@ -12,146 +10,221 @@ import (
1210 "github.com/stretchr/testify/assert"
1311)
1412
15−func TestSecretBytesEncoded(t *testing.T) {
16− for _, secretSize := range []int{16, 24, 32} {
17− t.Run(fmt.Sprintf("%d", secretSize), func(t *testing.T) {
18− secret := make([]byte, secretSize)
19− _, err := io.ReadFull(rand.Reader, secret)
20− assert.Equal(t, nil, err)
13+func TestEncodeAndDecodeAccessToken(t *testing.T) {
14+ const secret = "0123456789abcdefghijklmnopqrstuv"
15+ const token = "my access token"
16+ c, err := NewBase64Cipher(NewCFBCipher, []byte(secret))
17+ assert.Equal(t, nil, err)
2118
22− // We test both padded & raw Base64 to ensure we handle both
23− // potential user input routes for Base64
24− base64Padded := base64.URLEncoding.EncodeToString(secret)
25− sb := SecretBytes(base64Padded)
26− assert.Equal(t, secret, sb)
27− assert.Equal(t, len(sb), secretSize)
28−
29− base64Raw := base64.RawURLEncoding.EncodeToString(secret)
30− sb = SecretBytes(base64Raw)
31− assert.Equal(t, secret, sb)
32− assert.Equal(t, len(sb), secretSize)
33− })
34− }
35−}
19+ encoded, err := c.Encrypt([]byte(token))
20+ assert.Equal(t, nil, err)
3621
37−// A string that isn't intended as Base64 and still decodes (but to unintended length)
38−// will return the original secret as bytes
39−func TestSecretBytesEncodedWrongSize(t *testing.T) {
40− for _, secretSize := range []int{15, 20, 28, 33, 44} {
41− t.Run(fmt.Sprintf("%d", secretSize), func(t *testing.T) {
42− secret := make([]byte, secretSize)
43− _, err := io.ReadFull(rand.Reader, secret)
44− assert.Equal(t, nil, err)
22+ decoded, err := c.Decrypt(encoded)
23+ assert.Equal(t, nil, err)
4524
46− // We test both padded & raw Base64 to ensure we handle both
47− // potential user input routes for Base64
48− base64Padded := base64.URLEncoding.EncodeToString(secret)
49− sb := SecretBytes(base64Padded)
50− assert.NotEqual(t, secret, sb)
51− assert.NotEqual(t, len(sb), secretSize)
52− // The given secret is returned as []byte
53− assert.Equal(t, base64Padded, string(sb))
54−
55− base64Raw := base64.RawURLEncoding.EncodeToString(secret)
56− sb = SecretBytes(base64Raw)
57− assert.NotEqual(t, secret, sb)
58− assert.NotEqual(t, len(sb), secretSize)
59− // The given secret is returned as []byte
60− assert.Equal(t, base64Raw, string(sb))
61− })
62− }
25+ assert.NotEqual(t, []byte(token), encoded)
26+ assert.Equal(t, []byte(token), decoded)
6327}
6428
65−func TestSecretBytesNonBase64(t *testing.T) {
66− trailer := "equals=========="
67− assert.Equal(t, trailer, string(SecretBytes(trailer)))
29+func TestEncodeAndDecodeAccessTokenB64(t *testing.T) {
30+ const secretBase64 = "A3Xbr6fu6Al0HkgrP1ztjb-mYiwmxgNPP-XbNsz1WBk="
31+ const token = "my access token"
6832
69− raw16 := "asdflkjhqwer)(*&"
70− sb16 := SecretBytes(raw16)
71− assert.Equal(t, raw16, string(sb16))
72− assert.Equal(t, 16, len(sb16))
33+ secret, err := base64.URLEncoding.DecodeString(secretBase64)
34+ assert.Equal(t, nil, err)
35+ c, err := NewBase64Cipher(NewCFBCipher, []byte(secret))
36+ assert.Equal(t, nil, err)
7337
74− raw24 := "asdflkjhqwer)(*&CJEN#$%^"
75− sb24 := SecretBytes(raw24)
76− assert.Equal(t, raw24, string(sb24))
77− assert.Equal(t, 24, len(sb24))
38+ encoded, err := c.Encrypt([]byte(token))
39+ assert.Equal(t, nil, err)
7840
79− raw32 := "asdflkjhqwer)(*&1234lkjhqwer)(*&"
80− sb32 := SecretBytes(raw32)
81− assert.Equal(t, raw32, string(sb32))
82− assert.Equal(t, 32, len(sb32))
41+ decoded, err := c.Decrypt(encoded)
42+ assert.Equal(t, nil, err)
43+
44+ assert.NotEqual(t, []byte(token), encoded)
45+ assert.Equal(t, []byte(token), decoded)
8346}
8447
85−func TestSignAndValidate(t *testing.T) {
86− seed := "0123456789abcdef"
87− key := "cookie-name"
88− value := base64.URLEncoding.EncodeToString([]byte("I am soooo encoded"))
89− epoch := "123456789"
48+func TestEncryptAndDecrypt(t *testing.T) {
49+ // Test our 2 cipher types
50+ cipherInits := map[string]func([]byte) (Cipher, error){
51+ "CFB": NewCFBCipher,
52+ "GCM": NewGCMCipher,
53+ }
54+ for name, initCipher := range cipherInits {
55+ t.Run(name, func(t *testing.T) {
56+ // Test all 3 valid AES sizes
57+ for _, secretSize := range []int{16, 24, 32} {
58+ t.Run(fmt.Sprintf("%d", secretSize), func(t *testing.T) {
59+ secret := make([]byte, secretSize)
60+ _, err := io.ReadFull(rand.Reader, secret)
61+ assert.Equal(t, nil, err)
62+
63+ // Test Standard & Base64 wrapped
64+ cstd, err := initCipher(secret)
65+ assert.Equal(t, nil, err)
66+
67+ cb64, err := NewBase64Cipher(initCipher, secret)
68+ assert.Equal(t, nil, err)
69+
70+ ciphers := map[string]Cipher{
71+ "Standard": cstd,
72+ "Base64": cb64,
73+ }
74+
75+ for cName, c := range ciphers {
76+ t.Run(cName, func(t *testing.T) {
77+ // Test various sizes sessions might be
78+ for _, dataSize := range []int{10, 100, 1000, 5000, 10000} {
79+ t.Run(fmt.Sprintf("%d", dataSize), func(t *testing.T) {
80+ runEncryptAndDecrypt(t, c, dataSize)
81+ })
82+ }
83+ })
84+ }
85+ })
86+ }
87+ })
88+ }
89+}
9090
91− sha256sig := cookieSignature(sha256.New, seed, key, value, epoch)
92− sha1sig := cookieSignature(sha1.New, seed, key, value, epoch)
91+func runEncryptAndDecrypt(t *testing.T, c Cipher, dataSize int) {
92+ data := make([]byte, dataSize)
93+ _, err := io.ReadFull(rand.Reader, data)
94+ assert.Equal(t, nil, err)
95+
96+ // Ensure our Encrypt function doesn't encrypt in place
97+ immutableData := make([]byte, len(data))
98+ copy(immutableData, data)
99+
100+ encrypted, err := c.Encrypt(data)
101+ assert.Equal(t, nil, err)
102+ assert.NotEqual(t, encrypted, data)
103+ // Encrypt didn't operate in-place on []byte
104+ assert.Equal(t, data, immutableData)
93105
94− assert.True(t, checkSignature(sha256sig, seed, key, value, epoch))
95− // This should be switched to False after fully deprecating SHA1
96− assert.True(t, checkSignature(sha1sig, seed, key, value, epoch))
106+ // Ensure our Decrypt function doesn't decrypt in place
107+ immutableEnc := make([]byte, len(encrypted))
108+ copy(immutableEnc, encrypted)
97109
98− assert.False(t, checkSignature(sha256sig, seed, key, "tampered", epoch))
99− assert.False(t, checkSignature(sha1sig, seed, key, "tampered", epoch))
110+ decrypted, err := c.Decrypt(encrypted)
111+ assert.Equal(t, nil, err)
112+ // Original data back
113+ assert.Equal(t, data, decrypted)
114+ // Decrypt didn't operate in-place on []byte
115+ assert.Equal(t, encrypted, immutableEnc)
116+ // Encrypt/Decrypt actually did something
117+ assert.NotEqual(t, encrypted, decrypted)
100118}
101119
102−func TestEncodeAndDecodeAccessToken(t *testing.T) {
103− const secret = "0123456789abcdefghijklmnopqrstuv"
104− const token = "my access token"
105− c, err := NewCipher([]byte(secret))
120+func TestDecryptCFBWrongSecret(t *testing.T) {
121+ secret1 := []byte("0123456789abcdefghijklmnopqrstuv")
122+ secret2 := []byte("9876543210abcdefghijklmnopqrstuv")
123+
124+ c1, err := NewCFBCipher(secret1)
106125 assert.Equal(t, nil, err)
107126
108− encoded, err := c.Encrypt(token)
127+ c2, err := NewCFBCipher(secret2)
109128 assert.Equal(t, nil, err)
110129
111− decoded, err := c.Decrypt(encoded)
130+ data := []byte("f3928pufm982374dj02y485dsl34890u2t9nd4028s94dm58y2394087dhmsyt29h8df")
131+
132+ ciphertext, err := c1.Encrypt(data)
112133 assert.Equal(t, nil, err)
113134
114− assert.NotEqual(t, token, encoded)
115− assert.Equal(t, token, decoded)
135+ wrongData, err := c2.Decrypt(ciphertext)
136+ assert.Equal(t, nil, err)
137+ assert.NotEqual(t, data, wrongData)
116138}
117139
118−func TestEncodeAndDecodeAccessTokenB64(t *testing.T) {
119− const secretBase64 = "A3Xbr6fu6Al0HkgrP1ztjb-mYiwmxgNPP-XbNsz1WBk="
120− const token = "my access token"
140+func TestDecryptGCMWrongSecret(t *testing.T) {
141+ secret1 := []byte("0123456789abcdefghijklmnopqrstuv")
142+ secret2 := []byte("9876543210abcdefghijklmnopqrstuv")
121143
122− secret, err := base64.URLEncoding.DecodeString(secretBase64)
123− assert.Equal(t, nil, err)
124− c, err := NewCipher([]byte(secret))
144+ c1, err := NewGCMCipher(secret1)
125145 assert.Equal(t, nil, err)
126146
127− encoded, err := c.Encrypt(token)
147+ c2, err := NewGCMCipher(secret2)
128148 assert.Equal(t, nil, err)
129149
130− decoded, err := c.Decrypt(encoded)
150+ data := []byte("f3928pufm982374dj02y485dsl34890u2t9nd4028s94dm58y2394087dhmsyt29h8df")
151+
152+ ciphertext, err := c1.Encrypt(data)
131153 assert.Equal(t, nil, err)
132154
133− assert.NotEqual(t, token, encoded)
134− assert.Equal(t, token, decoded)
155+ // GCM is authenticated - this should lead to message authentication failed
156+ _, err = c2.Decrypt(ciphertext)
157+ assert.Error(t, err)
135158}
136159
137−func TestEncodeIntoAndDecodeIntoAccessToken(t *testing.T) {
138− const secret = "0123456789abcdefghijklmnopqrstuv"
139− c, err := NewCipher([]byte(secret))
140− assert.Equal(t, nil, err)
160+// Encrypt with GCM, Decrypt with CFB: Results in Garbage data
161+func TestGCMtoCFBErrors(t *testing.T) {
162+ // Test all 3 valid AES sizes
163+ for _, secretSize := range []int{16, 24, 32} {
164+ t.Run(fmt.Sprintf("%d", secretSize), func(t *testing.T) {
165+ secret := make([]byte, secretSize)
166+ _, err := io.ReadFull(rand.Reader, secret)
167+ assert.Equal(t, nil, err)
141168
142− token := "my access token"
143− originalToken := token
169+ gcm, err := NewGCMCipher(secret)
170+ assert.Equal(t, nil, err)
144171
145− assert.Equal(t, nil, c.EncryptInto(&token))
146− assert.NotEqual(t, originalToken, token)
172+ cfb, err := NewCFBCipher(secret)
173+ assert.Equal(t, nil, err)
147174
148− assert.Equal(t, nil, c.DecryptInto(&token))
149− assert.Equal(t, originalToken, token)
175+ // Test various sizes sessions might be
176+ for _, dataSize := range []int{10, 100, 1000, 5000, 10000} {
177+ t.Run(fmt.Sprintf("%d", dataSize), func(t *testing.T) {
178+ data := make([]byte, dataSize)
179+ _, err := io.ReadFull(rand.Reader, data)
180+ assert.Equal(t, nil, err)
181+
182+ encrypted, err := gcm.Encrypt(data)
183+ assert.Equal(t, nil, err)
184+ assert.NotEqual(t, encrypted, data)
185+
186+ decrypted, err := cfb.Decrypt(encrypted)
187+ assert.Equal(t, nil, err)
188+ // Data is mangled
189+ assert.NotEqual(t, data, decrypted)
190+ assert.NotEqual(t, encrypted, decrypted)
191+ })
192+ }
193+ })
194+ }
195+}
150196
151− // Check no errors with empty or nil strings
152− empty := ""
153− assert.Equal(t, nil, c.EncryptInto(&empty))
154− assert.Equal(t, nil, c.DecryptInto(&empty))
155− assert.Equal(t, nil, c.EncryptInto(nil))
156− assert.Equal(t, nil, c.DecryptInto(nil))
197+// Encrypt with CFB, Decrypt with GCM: Results in errors
198+func TestCFBtoGCMErrors(t *testing.T) {
199+ // Test all 3 valid AES sizes
200+ for _, secretSize := range []int{16, 24, 32} {
201+ t.Run(fmt.Sprintf("%d", secretSize), func(t *testing.T) {
202+ secret := make([]byte, secretSize)
203+ _, err := io.ReadFull(rand.Reader, secret)
204+ assert.Equal(t, nil, err)
205+
206+ gcm, err := NewGCMCipher(secret)
207+ assert.Equal(t, nil, err)
208+
209+ cfb, err := NewCFBCipher(secret)
210+ assert.Equal(t, nil, err)
211+
212+ // Test various sizes sessions might be
213+ for _, dataSize := range []int{10, 100, 1000, 5000, 10000} {
214+ t.Run(fmt.Sprintf("%d", dataSize), func(t *testing.T) {
215+ data := make([]byte, dataSize)
216+ _, err := io.ReadFull(rand.Reader, data)
217+ assert.Equal(t, nil, err)
218+
219+ encrypted, err := cfb.Encrypt(data)
220+ assert.Equal(t, nil, err)
221+ assert.NotEqual(t, encrypted, data)
222+
223+ // GCM is authenticated - this should lead to message authentication failed
224+ _, err = gcm.Decrypt(encrypted)
225+ assert.Error(t, err)
226+ })
227+ }
228+ })
229+ }
157230}