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