| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| package aead_test |
| |
| import ( |
| "bytes" |
| "encoding/hex" |
| "testing" |
| |
| "github.com/google/tink/go/subtle/aead" |
| "github.com/google/tink/go/subtle/mac" |
| "github.com/google/tink/go/subtle/random" |
| "github.com/google/tink/go/tink" |
| ) |
| |
| func createAEADWithKeys(encryptionKey []byte, ivSize int, hashAlgo string, macKey []byte, tagSize int) (tink.AEAD, error) { |
| ctr, err := aead.NewAESCTR(encryptionKey, ivSize) |
| if err != nil { |
| return nil, err |
| } |
| |
| mac, err := mac.NewHMAC(hashAlgo, macKey, uint32(tagSize)) |
| if err != nil { |
| return nil, err |
| } |
| |
| cipher, err := aead.NewEncryptThenAuthenticate(ctr, mac, tagSize) |
| if err != nil { |
| return nil, err |
| } |
| return cipher, nil |
| } |
| |
| func createAEAD(keySize, ivSize int, hashAlgo string, macKeySize int, tagSize int) (tink.AEAD, error) { |
| encryptionKey := random.GetRandomBytes(uint32(keySize)) |
| ctr, err := aead.NewAESCTR(encryptionKey, ivSize) |
| if err != nil { |
| return nil, err |
| } |
| |
| macKey := random.GetRandomBytes(uint32(macKeySize)) |
| mac, err := mac.NewHMAC(hashAlgo, macKey, uint32(tagSize)) |
| if err != nil { |
| return nil, err |
| } |
| |
| cipher, err := aead.NewEncryptThenAuthenticate(ctr, mac, tagSize) |
| if err != nil { |
| return nil, err |
| } |
| return cipher, nil |
| } |
| |
| // Copied from |
| // https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. |
| // We use CTR but the RFC uses CBC mode, so it's not possible to compare |
| // plaintexts. However, the tests are still valueable to ensure that we correcly |
| // compute HMAC over ciphertext and aad. |
| var rfcTestVectors = []struct { |
| macKey string |
| encryptionKey string |
| ciphertext string |
| aad string |
| hashAlgo string |
| ivSize int |
| tagSize int |
| }{ |
| {"000102030405060708090a0b0c0d0e0f", |
| "101112131415161718191a1b1c1d1e1f", |
| "1af38c2dc2b96ffdd86694092341bc04" + |
| "c80edfa32ddf39d5ef00c0b468834279" + |
| "a2e46a1b8049f792f76bfe54b903a9c9" + |
| "a94ac9b47ad2655c5f10f9aef71427e2" + |
| "fc6f9b3f399a221489f16362c7032336" + |
| "09d45ac69864e3321cf82935ac4096c8" + |
| "6e133314c54019e8ca7980dfa4b9cf1b" + |
| "384c486f3a54c51078158ee5d79de59f" + |
| "bd34d848b3d69550a67646344427ade5" + |
| "4b8851ffb598f7f80074b9473c82e2db" + |
| "652c3fa36b0a7c5b3219fab3a30bc1c4", |
| "546865207365636f6e64207072696e63" + |
| "69706c65206f66204175677573746520" + |
| "4b6572636b686f666673", |
| "SHA256", 16, 16}, |
| {"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", |
| "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", |
| "1af38c2dc2b96ffdd86694092341bc04" + |
| "4affaaadb78c31c5da4b1b590d10ffbd" + |
| "3dd8d5d302423526912da037ecbcc7bd" + |
| "822c301dd67c373bccb584ad3e9279c2" + |
| "e6d12a1374b77f077553df829410446b" + |
| "36ebd97066296ae6427ea75c2e0846a1" + |
| "1a09ccf5370dc80bfecbad28c73f09b3" + |
| "a3b75e662a2594410ae496b2e2e6609e" + |
| "31e6e02cc837f053d21f37ff4f51950b" + |
| "be2638d09dd7a4930930806d0703b1f6" + |
| "4dd3b4c088a7f45c216839645b2012bf" + |
| "2e6269a8c56a816dbc1b267761955bc5", |
| "546865207365636f6e64207072696e63" + |
| "69706c65206f66204175677573746520" + |
| "4b6572636b686f666673", |
| "SHA512", 16, 32}, |
| } |
| |
| func hexDecodeOrDie(data string) []byte { |
| decoded, err := hex.DecodeString(data) |
| if err != nil { |
| panic(err) |
| } |
| return decoded |
| } |
| |
| func TestETARFCTestVectors(t *testing.T) { |
| for _, v := range rfcTestVectors { |
| macKey := hexDecodeOrDie(v.macKey) |
| encryptionKey := hexDecodeOrDie(v.encryptionKey) |
| ciphertext := hexDecodeOrDie(v.ciphertext) |
| aad := hexDecodeOrDie(v.aad) |
| |
| cipher, err := createAEADWithKeys(encryptionKey, v.ivSize, v.hashAlgo, macKey, v.tagSize) |
| if err != nil { |
| t.Fatalf("failed to create AEAD from RFC test vector: %v", v) |
| } |
| |
| if _, err := cipher.Decrypt(ciphertext, aad); err != nil { |
| t.Errorf("decryption failed to RFC test vector: %v, error: %v", v, err) |
| } |
| } |
| } |
| |
| func TestETAEncryptDecrypt(t *testing.T) { |
| const keySize = 16 |
| const ivSize = 12 |
| const macKeySize = 16 |
| const tagSize = 16 |
| |
| cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) |
| if err != nil { |
| t.Fatalf("got: %v, want: success", err) |
| } |
| |
| message := []byte("Some data to encrypt.") |
| aad := []byte("Some data to authenticate.") |
| |
| ciphertext, err := cipher.Encrypt(message, aad) |
| if err != nil { |
| t.Fatalf("encryption failed, error: %v", err) |
| } |
| |
| if len(ciphertext) != len(message)+ivSize+tagSize { |
| t.Errorf("invalid ciphertext size, got: %d, want: %d", len(ciphertext), len(message)+ivSize+tagSize) |
| } |
| |
| plaintext, err := cipher.Decrypt(ciphertext, aad) |
| if err != nil { |
| t.Fatalf("decryption failed, error: %v", err) |
| } |
| |
| if !bytes.Equal(plaintext, message) { |
| t.Errorf("invalid plaintext, got: %q, want: %q", plaintext, message) |
| } |
| } |
| |
| func TestETAEncryptDecryptRandomMessage(t *testing.T) { |
| const keySize = 16 |
| const ivSize = 12 |
| const macKeySize = 16 |
| const tagSize = 16 |
| |
| cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) |
| if err != nil { |
| t.Fatalf("got: %v, want: success", err) |
| } |
| |
| for i := 0; i < 256; i++ { |
| message := random.GetRandomBytes(uint32(i)) |
| aad := random.GetRandomBytes(uint32(i)) |
| |
| ciphertext, err := cipher.Encrypt(message, aad) |
| if err != nil { |
| t.Fatalf("encryption failed, error: %v", err) |
| } |
| |
| if len(ciphertext) != len(message)+ivSize+tagSize { |
| t.Errorf("invalid ciphertext size, got: %d, want: %d", len(ciphertext), len(message)+ivSize+tagSize) |
| } |
| |
| plaintext, err := cipher.Decrypt(ciphertext, aad) |
| if err != nil { |
| t.Fatalf("decryption failed, error: %v", err) |
| } |
| |
| if !bytes.Equal(plaintext, message) { |
| t.Errorf("invalid plaintext, got: %q, want: %q", plaintext, message) |
| } |
| } |
| } |
| |
| func TestETAMultipleEncrypt(t *testing.T) { |
| const keySize = 16 |
| const ivSize = 12 |
| const macKeySize = 16 |
| const tagSize = 16 |
| |
| cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) |
| if err != nil { |
| t.Fatalf("got: %v, want: success", err) |
| } |
| |
| message := []byte("Some data to encrypt.") |
| aad := []byte("Some data to authenticate.") |
| |
| ciphertext1, err := cipher.Encrypt(message, aad) |
| if err != nil { |
| t.Fatalf("encryption failed, error: %v", err) |
| } |
| |
| ciphertext2, err := cipher.Encrypt(message, aad) |
| if err != nil { |
| t.Fatalf("encryption failed, error: %v", err) |
| } |
| |
| if bytes.Equal(ciphertext1, ciphertext2) { |
| t.Error("ciphertexts must not be the same") |
| } |
| } |
| |
| func TestETAInvalidTagSize(t *testing.T) { |
| const keySize = 16 |
| const ivSize = 12 |
| const macKeySize = 16 |
| const tagSize = 9 // Invalid! |
| |
| if _, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize); err == nil { |
| t.Error("got: success, want: error invalid tag size") |
| } |
| } |
| |
| func TestETADecryptModifiedCiphertext(t *testing.T) { |
| const keySize = 16 |
| const ivSize = 12 |
| const macKeySize = 16 |
| const tagSize = 16 |
| |
| cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) |
| if err != nil { |
| t.Fatalf("got: %v, want: success", err) |
| } |
| |
| message := []byte("Some data to encrypt.") |
| aad := []byte("Some data to authenticate.") |
| ciphertext, err := cipher.Encrypt(message, aad) |
| if err != nil { |
| t.Fatalf("encryption failed, error: %v", err) |
| } |
| |
| // Modify the ciphertext and try to decrypt. |
| modct := make([]byte, len(ciphertext)) |
| copy(modct, ciphertext) |
| for i := 0; i < len(ciphertext)*8; i++ { |
| // Save the byte to be modified. |
| b := modct[i/8] |
| modct[i/8] ^= (1 << uint(i%8)) |
| if bytes.Equal(ciphertext, modct) { |
| t.Errorf("modified ciphertext shouldn't be the same as aad") |
| } |
| if _, err := cipher.Decrypt(modct, aad); err == nil { |
| t.Errorf("successfully decrypted modified ciphertext (i = %d)", i) |
| } |
| // Restore the modified byte. |
| modct[i/8] = b |
| } |
| |
| // Modify the additional authenticated data. |
| modaad := make([]byte, len(aad)) |
| copy(modaad, aad) |
| for i := 0; i < len(aad)*8; i++ { |
| // Save the byte to be modified. |
| b := modaad[i/8] |
| modaad[i/8] ^= (1 << uint(i%8)) |
| if bytes.Equal(aad, modaad) { |
| t.Errorf("modified aad shouldn't be the same as aad") |
| } |
| if _, err := cipher.Decrypt(ciphertext, modaad); err == nil { |
| t.Errorf("successfully decrypted with modified aad (i = %d)", i) |
| } |
| // Restore the modified byte. |
| modaad[i/8] = b |
| } |
| |
| // Truncate the ciphertext. |
| trunct := make([]byte, len(ciphertext)) |
| copy(trunct, ciphertext) |
| for i := 1; i <= len(ciphertext); i++ { |
| trunct = trunct[:len(ciphertext)-i] |
| if _, err := cipher.Decrypt(trunct, aad); err == nil { |
| t.Errorf("successfully decrypted truncated ciphertext (i = %d)", i) |
| } |
| } |
| } |
| |
| func TestETAEmptyParams(t *testing.T) { |
| const keySize = 16 |
| const ivSize = 12 |
| const macKeySize = 16 |
| const tagSize = 16 |
| |
| cipher, err := createAEAD(keySize, ivSize, "SHA1", macKeySize, tagSize) |
| if err != nil { |
| t.Fatalf("got: %v, want: success", err) |
| } |
| |
| message := []byte("Some data to encrypt.") |
| if _, err := cipher.Encrypt(message, []byte{}); err != nil { |
| t.Errorf("encryption failed with empty aad") |
| } |
| if _, err := cipher.Encrypt([]byte{}, []byte{}); err != nil { |
| t.Errorf("encryption failed with empty ciphertext and aad") |
| } |
| } |