1// Copyright 2022 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15//////////////////////////////////////////////////////////////////////////////// 16 17package aead_test 18 19import ( 20 "bytes" 21 "encoding/hex" 22 "fmt" 23 "math/rand" 24 "testing" 25 26 "golang.org/x/crypto/chacha20poly1305" 27 "github.com/google/tink/go/internal/aead" 28 "github.com/google/tink/go/subtle/random" 29 "github.com/google/tink/go/testutil" 30) 31 32// TODO(b/201070904): Improve tests and rename aad to ad. 33 34func TestChaCha20Poly1305EncryptDecrypt(t *testing.T) { 35 for i, test := range chaCha20Poly1305Tests { 36 key, _ := hex.DecodeString(test.key) 37 pt, _ := hex.DecodeString(test.plaintext) 38 aad, _ := hex.DecodeString(test.aad) 39 nonce, _ := hex.DecodeString(test.nonce) 40 ct, _ := hex.DecodeString(test.out) 41 42 ca, err := aead.NewChaCha20Poly1305InsecureNonce(key) 43 if err != nil { 44 t.Errorf("#%d, cannot create new instance of ChaCha20Poly1305: %s", i, err) 45 continue 46 } 47 48 _, err = ca.Encrypt(nonce, pt, aad) 49 if err != nil { 50 t.Errorf("#%d, unexpected encryption error: %s", i, err) 51 continue 52 } 53 54 if got, err := ca.Decrypt(nonce, ct, aad); err != nil { 55 t.Errorf("#%d, unexpected decryption error: %s", i, err) 56 continue 57 } else if !bytes.Equal(pt, got) { 58 t.Errorf("#%d, plaintext's don't match: got %x vs %x", i, got, pt) 59 continue 60 } 61 } 62} 63 64func TestChaCha20Poly1305EmptyAssociatedData(t *testing.T) { 65 key := random.GetRandomBytes(chacha20poly1305.KeySize) 66 ca, err := aead.NewChaCha20Poly1305InsecureNonce(key) 67 if err != nil { 68 t.Fatal(err) 69 } 70 71 for i := 0; i < 75; i++ { 72 pt := random.GetRandomBytes(uint32(i)) 73 74 emptyAADs := [][]byte{[]byte{}, nil} 75 for _, encAAD := range emptyAADs { 76 nonce := random.GetRandomBytes(chacha20poly1305.NonceSize) 77 ct, err := ca.Encrypt(nonce, pt, encAAD) 78 if err != nil { 79 t.Errorf("Encrypt() err = %v, want nil", err) 80 continue 81 } 82 83 for _, decAAD := range emptyAADs { 84 got, err := ca.Decrypt(nonce, ct, decAAD) 85 if err != nil { 86 t.Errorf("Decrypt() err = %v, want nil", err) 87 } 88 if want := pt; !bytes.Equal(want, got) { 89 t.Errorf("Decrypt() = %x, want %x", got, want) 90 } 91 } 92 93 badAAD := []byte{1, 2, 3} 94 if _, err := ca.Decrypt(nonce, ct, badAAD); err == nil { 95 t.Errorf("Decrypt() err = nil, want error") 96 } 97 } 98 } 99} 100 101func TestChaCha20Poly1305LongMessages(t *testing.T) { 102 dataSize := uint32(16) 103 // Encrypts and decrypts messages of size <= 8192. 104 for dataSize <= 1<<24 { 105 pt := random.GetRandomBytes(dataSize) 106 aad := random.GetRandomBytes(dataSize / 3) 107 key := random.GetRandomBytes(chacha20poly1305.KeySize) 108 109 ca, err := aead.NewChaCha20Poly1305InsecureNonce(key) 110 if err != nil { 111 t.Fatal(err) 112 } 113 114 nonce := random.GetRandomBytes(chacha20poly1305.NonceSize) 115 ct, err := ca.Encrypt(nonce, pt, aad) 116 if err != nil { 117 t.Errorf("Encrypt(%x, %x) failed", pt, aad) 118 continue 119 } 120 121 if got, err := ca.Decrypt(nonce, ct, aad); err != nil || !bytes.Equal(pt, got) { 122 t.Errorf("Decrypt(Encrypt(pt, %x)): plaintext's don't match: got %x vs %x; error: %v", aad, got, pt, err) 123 } 124 125 dataSize += 5 * dataSize / 11 126 } 127} 128 129func TestChaCha20Poly1305ModifyCiphertext(t *testing.T) { 130 for i, test := range chaCha20Poly1305Tests { 131 key, _ := hex.DecodeString(test.key) 132 pt, _ := hex.DecodeString(test.plaintext) 133 aad, _ := hex.DecodeString(test.aad) 134 135 ca, err := aead.NewChaCha20Poly1305InsecureNonce(key) 136 if err != nil { 137 t.Fatal(err) 138 } 139 140 nonce := random.GetRandomBytes(chacha20poly1305.NonceSize) 141 ct, err := ca.Encrypt(nonce, pt, aad) 142 if err != nil { 143 t.Errorf("#%d: Encrypt failed", i) 144 continue 145 } 146 147 if len(aad) > 0 { 148 alterAadIdx := rand.Intn(len(aad)) 149 aad[alterAadIdx] ^= 0x80 150 if _, err := ca.Decrypt(nonce, ct, aad); err == nil { 151 t.Errorf("#%d: Decrypt was successful after altering additional data", i) 152 continue 153 } 154 aad[alterAadIdx] ^= 0x80 155 } 156 157 alterCtIdx := rand.Intn(len(ct)) 158 ct[alterCtIdx] ^= 0x80 159 if _, err := ca.Decrypt(nonce, ct, aad); err == nil { 160 t.Errorf("#%d: Decrypt was successful after altering ciphertext", i) 161 continue 162 } 163 ct[alterCtIdx] ^= 0x80 164 } 165} 166 167// This is a very simple test for the randomness of the nonce. 168// The test simply checks that the multiple ciphertexts of the same message are distinct. 169func TestChaCha20Poly1305RandomNonce(t *testing.T) { 170 key := random.GetRandomBytes(chacha20poly1305.KeySize) 171 ca, err := aead.NewChaCha20Poly1305InsecureNonce(key) 172 if err != nil { 173 t.Fatal(err) 174 } 175 176 cts := make(map[string]bool) 177 pt, aad := []byte{}, []byte{} 178 for i := 0; i < 1<<10; i++ { 179 nonce := random.GetRandomBytes(chacha20poly1305.NonceSize) 180 ct, err := ca.Encrypt(nonce, pt, aad) 181 ctHex := hex.EncodeToString(ct) 182 if err != nil || cts[ctHex] { 183 t.Errorf("TestRandomNonce failed: %v", err) 184 } else { 185 cts[ctHex] = true 186 } 187 } 188} 189 190func TestChaCha20Poly1305WycheproofCases(t *testing.T) { 191 testutil.SkipTestIfTestSrcDirIsNotSet(t) 192 suite := new(AEADSuite) 193 if err := testutil.PopulateSuite(suite, "chacha20_poly1305_test.json"); err != nil { 194 t.Fatalf("failed populating suite: %s", err) 195 } 196 for _, group := range suite.TestGroups { 197 if group.KeySize/8 != chacha20poly1305.KeySize { 198 continue 199 } 200 if group.IVSize/8 != chacha20poly1305.NonceSize { 201 continue 202 } 203 204 for _, test := range group.Tests { 205 caseName := fmt.Sprintf("%s-%s:Case-%d", suite.Algorithm, group.Type, test.CaseID) 206 t.Run(caseName, func(t *testing.T) { runChaCha20Poly1305WycheproofCase(t, test) }) 207 } 208 } 209} 210 211func runChaCha20Poly1305WycheproofCase(t *testing.T, tc *AEADCase) { 212 ca, err := aead.NewChaCha20Poly1305InsecureNonce(tc.Key) 213 if err != nil { 214 t.Fatalf("cannot create new instance of ChaCha20Poly1305: %s", err) 215 } 216 217 nonce := random.GetRandomBytes(chacha20poly1305.NonceSize) 218 _, err = ca.Encrypt(nonce, tc.Message, tc.AD) 219 if err != nil { 220 t.Fatalf("unexpected encryption error: %s", err) 221 } 222 223 ct := append(tc.CT, tc.Tag...) 224 decrypted, err := ca.Decrypt(tc.IV, ct, tc.AD) 225 if err != nil { 226 if tc.Result == "valid" { 227 t.Errorf("unexpected error: %s", err) 228 } 229 } else { 230 if tc.Result == "invalid" { 231 t.Error("decrypted invalid") 232 } 233 if !bytes.Equal(decrypted, tc.Message) { 234 t.Error("incorrect decryption") 235 } 236 } 237} 238