1
2
3
4
5 package chacha20poly1305
6
7 import (
8 "bytes"
9 "crypto/cipher"
10 cryptorand "crypto/rand"
11 "encoding/hex"
12 "fmt"
13 mathrand "math/rand"
14 "strconv"
15 "testing"
16 )
17
18 func TestVectors(t *testing.T) {
19 for i, test := range chacha20Poly1305Tests {
20 key, _ := hex.DecodeString(test.key)
21 nonce, _ := hex.DecodeString(test.nonce)
22 ad, _ := hex.DecodeString(test.aad)
23 plaintext, _ := hex.DecodeString(test.plaintext)
24
25 var (
26 aead cipher.AEAD
27 err error
28 )
29 switch len(nonce) {
30 case NonceSize:
31 aead, err = New(key)
32 case NonceSizeX:
33 aead, err = NewX(key)
34 default:
35 t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
36 }
37 if err != nil {
38 t.Fatal(err)
39 }
40
41 ct := aead.Seal(nil, nonce, plaintext, ad)
42 if ctHex := hex.EncodeToString(ct); ctHex != test.out {
43 t.Errorf("#%d: got %s, want %s", i, ctHex, test.out)
44 continue
45 }
46
47 plaintext2, err := aead.Open(nil, nonce, ct, ad)
48 if err != nil {
49 t.Errorf("#%d: Open failed", i)
50 continue
51 }
52
53 if !bytes.Equal(plaintext, plaintext2) {
54 t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
55 continue
56 }
57
58 if len(ad) > 0 {
59 alterAdIdx := mathrand.Intn(len(ad))
60 ad[alterAdIdx] ^= 0x80
61 if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
62 t.Errorf("#%d: Open was successful after altering additional data", i)
63 }
64 ad[alterAdIdx] ^= 0x80
65 }
66
67 alterNonceIdx := mathrand.Intn(aead.NonceSize())
68 nonce[alterNonceIdx] ^= 0x80
69 if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
70 t.Errorf("#%d: Open was successful after altering nonce", i)
71 }
72 nonce[alterNonceIdx] ^= 0x80
73
74 alterCtIdx := mathrand.Intn(len(ct))
75 ct[alterCtIdx] ^= 0x80
76 if _, err := aead.Open(nil, nonce, ct, ad); err == nil {
77 t.Errorf("#%d: Open was successful after altering ciphertext", i)
78 }
79 ct[alterCtIdx] ^= 0x80
80 }
81 }
82
83 func TestRandom(t *testing.T) {
84
85 f := func(t *testing.T, nonceSize int) {
86 for i := 0; i < 256; i++ {
87 var nonce = make([]byte, nonceSize)
88 var key [32]byte
89
90 al := mathrand.Intn(128)
91 pl := mathrand.Intn(16384)
92 ad := make([]byte, al)
93 plaintext := make([]byte, pl)
94 cryptorand.Read(key[:])
95 cryptorand.Read(nonce[:])
96 cryptorand.Read(ad)
97 cryptorand.Read(plaintext)
98
99 var (
100 aead cipher.AEAD
101 err error
102 )
103 switch len(nonce) {
104 case NonceSize:
105 aead, err = New(key[:])
106 case NonceSizeX:
107 aead, err = NewX(key[:])
108 default:
109 t.Fatalf("#%d: wrong nonce length: %d", i, len(nonce))
110 }
111 if err != nil {
112 t.Fatal(err)
113 }
114
115 ct := aead.Seal(nil, nonce[:], plaintext, ad)
116
117 plaintext2, err := aead.Open(nil, nonce[:], ct, ad)
118 if err != nil {
119 t.Errorf("Random #%d: Open failed", i)
120 continue
121 }
122
123 if !bytes.Equal(plaintext, plaintext2) {
124 t.Errorf("Random #%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
125 continue
126 }
127
128 if len(ad) > 0 {
129 alterAdIdx := mathrand.Intn(len(ad))
130 ad[alterAdIdx] ^= 0x80
131 if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
132 t.Errorf("Random #%d: Open was successful after altering additional data", i)
133 }
134 ad[alterAdIdx] ^= 0x80
135 }
136
137 alterNonceIdx := mathrand.Intn(aead.NonceSize())
138 nonce[alterNonceIdx] ^= 0x80
139 if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
140 t.Errorf("Random #%d: Open was successful after altering nonce", i)
141 }
142 nonce[alterNonceIdx] ^= 0x80
143
144 alterCtIdx := mathrand.Intn(len(ct))
145 ct[alterCtIdx] ^= 0x80
146 if _, err := aead.Open(nil, nonce[:], ct, ad); err == nil {
147 t.Errorf("Random #%d: Open was successful after altering ciphertext", i)
148 }
149 ct[alterCtIdx] ^= 0x80
150 }
151 }
152 t.Run("Standard", func(t *testing.T) { f(t, NonceSize) })
153 t.Run("X", func(t *testing.T) { f(t, NonceSizeX) })
154 }
155
156 func benchamarkChaCha20Poly1305Seal(b *testing.B, buf []byte, nonceSize int) {
157 b.ReportAllocs()
158 b.SetBytes(int64(len(buf)))
159
160 var key [32]byte
161 var nonce = make([]byte, nonceSize)
162 var ad [13]byte
163 var out []byte
164
165 var aead cipher.AEAD
166 switch len(nonce) {
167 case NonceSize:
168 aead, _ = New(key[:])
169 case NonceSizeX:
170 aead, _ = NewX(key[:])
171 }
172
173 b.ResetTimer()
174 for i := 0; i < b.N; i++ {
175 out = aead.Seal(out[:0], nonce[:], buf[:], ad[:])
176 }
177 }
178
179 func benchamarkChaCha20Poly1305Open(b *testing.B, buf []byte, nonceSize int) {
180 b.ReportAllocs()
181 b.SetBytes(int64(len(buf)))
182
183 var key [32]byte
184 var nonce = make([]byte, nonceSize)
185 var ad [13]byte
186 var ct []byte
187 var out []byte
188
189 var aead cipher.AEAD
190 switch len(nonce) {
191 case NonceSize:
192 aead, _ = New(key[:])
193 case NonceSizeX:
194 aead, _ = NewX(key[:])
195 }
196 ct = aead.Seal(ct[:0], nonce[:], buf[:], ad[:])
197
198 b.ResetTimer()
199 for i := 0; i < b.N; i++ {
200 out, _ = aead.Open(out[:0], nonce[:], ct[:], ad[:])
201 }
202 }
203
204 func BenchmarkChacha20Poly1305(b *testing.B) {
205 for _, length := range []int{64, 1350, 8 * 1024} {
206 b.Run("Open-"+strconv.Itoa(length), func(b *testing.B) {
207 benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSize)
208 })
209 b.Run("Seal-"+strconv.Itoa(length), func(b *testing.B) {
210 benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSize)
211 })
212
213 b.Run("Open-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
214 benchamarkChaCha20Poly1305Open(b, make([]byte, length), NonceSizeX)
215 })
216 b.Run("Seal-"+strconv.Itoa(length)+"-X", func(b *testing.B) {
217 benchamarkChaCha20Poly1305Seal(b, make([]byte, length), NonceSizeX)
218 })
219 }
220 }
221
222 func ExampleNewX() {
223
224 key := make([]byte, KeySize)
225 if _, err := cryptorand.Read(key); err != nil {
226 panic(err)
227 }
228
229 aead, err := NewX(key)
230 if err != nil {
231 panic(err)
232 }
233
234
235 var encryptedMsg []byte
236 {
237 msg := []byte("Gophers, gophers, gophers everywhere!")
238
239
240 nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(msg)+aead.Overhead())
241 if _, err := cryptorand.Read(nonce); err != nil {
242 panic(err)
243 }
244
245
246 encryptedMsg = aead.Seal(nonce, nonce, msg, nil)
247 }
248
249
250 {
251 if len(encryptedMsg) < aead.NonceSize() {
252 panic("ciphertext too short")
253 }
254
255
256 nonce, ciphertext := encryptedMsg[:aead.NonceSize()], encryptedMsg[aead.NonceSize():]
257
258
259 plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
260 if err != nil {
261 panic(err)
262 }
263
264 fmt.Printf("%s\n", plaintext)
265 }
266
267
268 }
269
View as plain text