1// Copyright 2020 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 subtle_test 18 19import ( 20 "bytes" 21 "encoding/hex" 22 "testing" 23 24 "github.com/google/tink/go/aead/subtle" 25 subtleMac "github.com/google/tink/go/mac/subtle" 26 "github.com/google/tink/go/subtle/random" 27 "github.com/google/tink/go/tink" 28) 29 30func createAEADWithKeys(encryptionKey []byte, ivSize int, hashAlgo string, macKey []byte, tagSize int) (tink.AEAD, error) { 31 ctr, err := subtle.NewAESCTR(encryptionKey, ivSize) 32 if err != nil { 33 return nil, err 34 } 35 36 mac, err := subtleMac.NewHMAC(hashAlgo, macKey, uint32(tagSize)) 37 if err != nil { 38 return nil, err 39 } 40 41 cipher, err := subtle.NewEncryptThenAuthenticate(ctr, mac, tagSize) 42 if err != nil { 43 return nil, err 44 } 45 return cipher, nil 46} 47 48func createAEAD(keySize, ivSize int, hashAlgo string, macKeySize int, tagSize int) (tink.AEAD, error) { 49 encryptionKey := random.GetRandomBytes(uint32(keySize)) 50 ctr, err := subtle.NewAESCTR(encryptionKey, ivSize) 51 if err != nil { 52 return nil, err 53 } 54 55 macKey := random.GetRandomBytes(uint32(macKeySize)) 56 mac, err := subtleMac.NewHMAC(hashAlgo, macKey, uint32(tagSize)) 57 if err != nil { 58 return nil, err 59 } 60 61 cipher, err := subtle.NewEncryptThenAuthenticate(ctr, mac, tagSize) 62 if err != nil { 63 return nil, err 64 } 65 return cipher, nil 66} 67 68// Copied from 69// https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. 70// 71// We use CTR but the RFC uses CBC mode, so it's not possible to compare 72// plaintexts. However, the tests are still valueable to ensure that we 73// correcly compute HMAC over ciphertext and associatedData. 74var rfcTestVectors = []struct { 75 macKey string 76 encryptionKey string 77 ciphertext string 78 associatedData string 79 hashAlgo string 80 ivSize int 81 tagSize int 82}{ 83 { 84 macKey: "000102030405060708090a0b0c0d0e0f", 85 encryptionKey: "101112131415161718191a1b1c1d1e1f", 86 ciphertext: "" + 87 "1af38c2dc2b96ffdd86694092341bc04" + 88 "c80edfa32ddf39d5ef00c0b468834279" + 89 "a2e46a1b8049f792f76bfe54b903a9c9" + 90 "a94ac9b47ad2655c5f10f9aef71427e2" + 91 "fc6f9b3f399a221489f16362c7032336" + 92 "09d45ac69864e3321cf82935ac4096c8" + 93 "6e133314c54019e8ca7980dfa4b9cf1b" + 94 "384c486f3a54c51078158ee5d79de59f" + 95 "bd34d848b3d69550a67646344427ade5" + 96 "4b8851ffb598f7f80074b9473c82e2db" + 97 "652c3fa36b0a7c5b3219fab3a30bc1c4", 98 associatedData: "" + 99 "546865207365636f6e64207072696e63" + 100 "69706c65206f66204175677573746520" + 101 "4b6572636b686f666673", 102 hashAlgo: "SHA256", 103 ivSize: 16, 104 tagSize: 16, 105 }, 106 { 107 macKey: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 108 encryptionKey: "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", 109 ciphertext: "" + 110 "1af38c2dc2b96ffdd86694092341bc04" + 111 "4affaaadb78c31c5da4b1b590d10ffbd" + 112 "3dd8d5d302423526912da037ecbcc7bd" + 113 "822c301dd67c373bccb584ad3e9279c2" + 114 "e6d12a1374b77f077553df829410446b" + 115 "36ebd97066296ae6427ea75c2e0846a1" + 116 "1a09ccf5370dc80bfecbad28c73f09b3" + 117 "a3b75e662a2594410ae496b2e2e6609e" + 118 "31e6e02cc837f053d21f37ff4f51950b" + 119 "be2638d09dd7a4930930806d0703b1f6" + 120 "4dd3b4c088a7f45c216839645b2012bf" + 121 "2e6269a8c56a816dbc1b267761955bc5", 122 associatedData: "" + 123 "546865207365636f6e64207072696e63" + 124 "69706c65206f66204175677573746520" + 125 "4b6572636b686f666673", 126 hashAlgo: "SHA512", 127 ivSize: 16, 128 tagSize: 32, 129 }, 130} 131 132func hexDecode(t *testing.T, data string) []byte { 133 t.Helper() 134 decoded, err := hex.DecodeString(data) 135 if err != nil { 136 t.Fatal(err) 137 } 138 return decoded 139} 140 141func TestETARFCTestVectors(t *testing.T) { 142 for _, v := range rfcTestVectors { 143 macKey := hexDecode(t, v.macKey) 144 encryptionKey := hexDecode(t, v.encryptionKey) 145 ciphertext := hexDecode(t, v.ciphertext) 146 associatedData := hexDecode(t, v.associatedData) 147 148 cipher, err := createAEADWithKeys(encryptionKey, v.ivSize, v.hashAlgo, macKey, v.tagSize) 149 if err != nil { 150 t.Fatalf("failed to create AEAD from RFC test vector: %v", v) 151 } 152 153 if _, err := cipher.Decrypt(ciphertext, associatedData); err != nil { 154 t.Errorf("decryption failed to RFC test vector: %v, error: %v", v, err) 155 } 156 } 157} 158 159func TestETAEncryptDecrypt(t *testing.T) { 160 const keySize = 16 161 const ivSize = 12 162 const macKeySize = 16 163 const tagSize = 16 164 165 cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) 166 if err != nil { 167 t.Fatalf("got: %v, want: success", err) 168 } 169 170 message := []byte("Some data to encrypt.") 171 associatedData := []byte("Some data to authenticate.") 172 173 ciphertext, err := cipher.Encrypt(message, associatedData) 174 if err != nil { 175 t.Fatalf("encryption failed, error: %v", err) 176 } 177 178 if len(ciphertext) != len(message)+ivSize+tagSize { 179 t.Errorf("invalid ciphertext size, got: %d, want: %d", len(ciphertext), len(message)+ivSize+tagSize) 180 } 181 182 plaintext, err := cipher.Decrypt(ciphertext, associatedData) 183 if err != nil { 184 t.Fatalf("decryption failed, error: %v", err) 185 } 186 187 if !bytes.Equal(plaintext, message) { 188 t.Errorf("invalid plaintext, got: %q, want: %q", plaintext, message) 189 } 190} 191 192func TestETAWithAssociatedDataSlice(t *testing.T) { 193 const keySize = 16 194 const ivSize = 12 195 const macKeySize = 16 196 const tagSize = 16 197 cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) 198 if err != nil { 199 t.Fatalf("got: %v, want: success", err) 200 } 201 202 message := []byte("message") 203 largeData := []byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 204 associatedData := largeData[:1] 205 206 _, err = cipher.Encrypt(message, associatedData) 207 if err != nil { 208 t.Fatalf("encryption failed, error: %v", err) 209 } 210 211 wantLargeData := []byte("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 212 if !bytes.Equal(largeData, wantLargeData) { 213 t.Errorf("largeData = %q, want: %q", largeData, wantLargeData) 214 } 215} 216 217func TestETAEncryptDecryptRandomMessage(t *testing.T) { 218 const keySize = 16 219 const ivSize = 12 220 const macKeySize = 16 221 const tagSize = 16 222 223 cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) 224 if err != nil { 225 t.Fatalf("got: %v, want: success", err) 226 } 227 228 for i := 0; i < 256; i++ { 229 message := random.GetRandomBytes(uint32(i)) 230 associatedData := random.GetRandomBytes(uint32(i)) 231 232 ciphertext, err := cipher.Encrypt(message, associatedData) 233 if err != nil { 234 t.Fatalf("encryption failed, error: %v", err) 235 } 236 237 if len(ciphertext) != len(message)+ivSize+tagSize { 238 t.Errorf("invalid ciphertext size, got: %d, want: %d", len(ciphertext), len(message)+ivSize+tagSize) 239 } 240 241 plaintext, err := cipher.Decrypt(ciphertext, associatedData) 242 if err != nil { 243 t.Fatalf("decryption failed, error: %v", err) 244 } 245 246 if !bytes.Equal(plaintext, message) { 247 t.Errorf("invalid plaintext, got: %q, want: %q", plaintext, message) 248 } 249 } 250} 251 252func TestETAMultipleEncrypt(t *testing.T) { 253 const keySize = 16 254 const ivSize = 12 255 const macKeySize = 16 256 const tagSize = 16 257 258 cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) 259 if err != nil { 260 t.Fatalf("got: %v, want: success", err) 261 } 262 263 message := []byte("Some data to encrypt.") 264 associatedData := []byte("Some data to authenticate.") 265 266 ciphertext1, err := cipher.Encrypt(message, associatedData) 267 if err != nil { 268 t.Fatalf("encryption failed, error: %v", err) 269 } 270 271 ciphertext2, err := cipher.Encrypt(message, associatedData) 272 if err != nil { 273 t.Fatalf("encryption failed, error: %v", err) 274 } 275 276 if bytes.Equal(ciphertext1, ciphertext2) { 277 t.Error("ciphertexts must not be the same") 278 } 279} 280 281func TestETAInvalidTagSize(t *testing.T) { 282 const keySize = 16 283 const ivSize = 12 284 const macKeySize = 16 285 const tagSize = 9 // Invalid! 286 287 if _, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize); err == nil { 288 t.Error("got: success, want: error invalid tag size") 289 } 290} 291 292func TestETADecryptModifiedCiphertext(t *testing.T) { 293 const keySize = 16 294 const ivSize = 12 295 const macKeySize = 16 296 const tagSize = 16 297 298 cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) 299 if err != nil { 300 t.Fatalf("got: %v, want: success", err) 301 } 302 303 message := []byte("Some data to encrypt.") 304 associatedData := []byte("Some data to authenticate.") 305 ciphertext, err := cipher.Encrypt(message, associatedData) 306 if err != nil { 307 t.Fatalf("encryption failed, error: %v", err) 308 } 309 310 // Modify the ciphertext and try to decrypt. 311 modCiphertext := make([]byte, len(ciphertext)) 312 copy(modCiphertext, ciphertext) 313 for i := 0; i < len(ciphertext)*8; i++ { 314 // Save the byte to be modified. 315 b := modCiphertext[i/8] 316 modCiphertext[i/8] ^= (1 << uint(i%8)) 317 if bytes.Equal(ciphertext, modCiphertext) { 318 t.Errorf("modCiphertext shouldn't be the same as ciphertext") 319 } 320 if _, err := cipher.Decrypt(modCiphertext, associatedData); err == nil { 321 t.Errorf("successfully decrypted modified ciphertext (i = %d)", i) 322 } 323 // Restore the modified byte. 324 modCiphertext[i/8] = b 325 } 326 327 // Modify the associated data. 328 modAssociatedData := make([]byte, len(associatedData)) 329 copy(modAssociatedData, associatedData) 330 for i := 0; i < len(associatedData)*8; i++ { 331 // Save the byte to be modified. 332 b := modAssociatedData[i/8] 333 modAssociatedData[i/8] ^= (1 << uint(i%8)) 334 if bytes.Equal(associatedData, modAssociatedData) { 335 t.Errorf("modAssociatedData shouldn't be the same as associatedData") 336 } 337 if _, err := cipher.Decrypt(ciphertext, modAssociatedData); err == nil { 338 t.Errorf("successfully decrypted with modified associated data (i = %d)", i) 339 } 340 // Restore the modified byte. 341 modAssociatedData[i/8] = b 342 } 343 344 // Truncate the ciphertext. 345 truncatedCiphertext := make([]byte, len(ciphertext)) 346 copy(truncatedCiphertext, ciphertext) 347 for i := 1; i <= len(ciphertext); i++ { 348 truncatedCiphertext = truncatedCiphertext[:len(ciphertext)-i] 349 if _, err := cipher.Decrypt(truncatedCiphertext, associatedData); err == nil { 350 t.Errorf("successfully decrypted truncated ciphertext (i = %d)", i) 351 } 352 } 353} 354 355func TestETAEmptyParams(t *testing.T) { 356 const keySize = 16 357 const ivSize = 12 358 const macKeySize = 16 359 const tagSize = 16 360 361 cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) 362 if err != nil { 363 t.Fatalf("got: %v, want: success", err) 364 } 365 366 message := []byte("Some data to encrypt.") 367 if _, err := cipher.Encrypt(message, []byte{}); err != nil { 368 t.Errorf("encryption failed with empty associatedData") 369 } 370 if _, err := cipher.Encrypt([]byte{}, []byte{}); err != nil { 371 t.Errorf("encryption failed with empty ciphertext and associatedData") 372 } 373} 374