// Copyright 2020 Google LLC // // 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 subtle_test import ( "bytes" "encoding/hex" "fmt" "testing" "github.com/google/tink/go/aead/subtle" "github.com/google/tink/go/subtle/random" "github.com/google/tink/go/testutil" ) var aesKeySizes = []uint32{ 16, /*AES-128*/ 32, /*AES-256*/ } // Since the tag size depends on the Seal() function of crypto library, // this test checks that the tag size is always 128 bit. func TestAESGCMTagLength(t *testing.T) { for _, keySize := range aesKeySizes { key := random.GetRandomBytes(keySize) a, _ := subtle.NewAESGCM(key) ad := random.GetRandomBytes(32) pt := random.GetRandomBytes(32) ct, _ := a.Encrypt(pt, ad) actualTagSize := len(ct) - subtle.AESGCMIVSize - len(pt) if actualTagSize != subtle.AESGCMTagSize { t.Errorf("tag size is not 128 bit, it is %d bit", actualTagSize*8) } } } func TestAESGCMKeySize(t *testing.T) { for _, keySize := range aesKeySizes { if _, err := subtle.NewAESGCM(make([]byte, keySize)); err != nil { t.Errorf("unexpected error when key size is %d btyes", keySize) } if _, err := subtle.NewAESGCM(make([]byte, keySize+1)); err == nil { t.Errorf("expect an error when key size is not supported %d", keySize) } } } func TestAESGCMEncryptDecrypt(t *testing.T) { for _, keySize := range aesKeySizes { key := random.GetRandomBytes(keySize) a, err := subtle.NewAESGCM(key) if err != nil { t.Errorf("unexpected error when creating new cipher: %s", err) } ad := random.GetRandomBytes(5) for ptSize := 0; ptSize < 75; ptSize++ { pt := random.GetRandomBytes(uint32(ptSize)) ct, err := a.Encrypt(pt, ad) if err != nil { t.Errorf("unexpected error in encryption: keySize %v, ptSize %v", keySize, ptSize) } decrypted, err := a.Decrypt(ct, ad) if err != nil { t.Errorf("unexpected error in decryption: keySize %v, ptSize %v", keySize, ptSize) } if !bytes.Equal(pt, decrypted) { t.Errorf("decrypted text and plaintext don't match: keySize %v, ptSize %v", keySize, ptSize) } } } } func TestAESGCMLongMessages(t *testing.T) { ptSize := 16 for ptSize <= 1<<24 { pt := random.GetRandomBytes(uint32(ptSize)) ad := random.GetRandomBytes(uint32(ptSize / 3)) for _, keySize := range aesKeySizes { key := random.GetRandomBytes(keySize) a, _ := subtle.NewAESGCM(key) ct, _ := a.Encrypt(pt, ad) decrypted, _ := a.Decrypt(ct, ad) if !bytes.Equal(pt, decrypted) { t.Errorf("decrypted text and plaintext don't match: keySize %v, ptSize %v", keySize, ptSize) } } ptSize += 5 * ptSize / 11 } } func TestAESGCMModifyCiphertext(t *testing.T) { ad := random.GetRandomBytes(33) key := random.GetRandomBytes(16) pt := random.GetRandomBytes(32) a, _ := subtle.NewAESGCM(key) ct, _ := a.Encrypt(pt, ad) // flipping bits for i := 0; i < len(ct); i++ { tmp := ct[i] for j := 0; j < 8; j++ { ct[i] ^= 1 << uint8(j) if _, err := a.Decrypt(ct, ad); err == nil { t.Errorf("expect an error when flipping bit of ciphertext: byte %d, bit %d", i, j) } ct[i] = tmp } } // truncated ciphertext for i := 1; i < len(ct); i++ { if _, err := a.Decrypt(ct[:i], ad); err == nil { t.Errorf("expect an error ciphertext is truncated until byte %d", i) } } // modify associated data for i := 0; i < len(ad); i++ { tmp := ad[i] for j := 0; j < 8; j++ { ad[i] ^= 1 << uint8(j) if _, err := a.Decrypt(ct, ad); err == nil { t.Errorf("expect an error when flipping bit of ad: byte %d, bit %d", i, j) } ad[i] = tmp } } // replace ciphertext with a random string with a small, unacceptable size for _, ctSize := range []uint32{subtle.AESGCMIVSize / 2, subtle.AESGCMIVSize - 1} { smallCT := random.GetRandomBytes(ctSize) emptyAD := []byte{} if _, err := a.Decrypt(smallCT, emptyAD); err == nil { t.Error("Decrypt: got success, want err") } } } /** * This is a very simple test for the randomness of the nonce. * The test simply checks that the multiple ciphertexts of the same * message are distinct. */ func TestAESGCMRandomNonce(t *testing.T) { nSample := 1 << 17 key := random.GetRandomBytes(16) pt := []byte{} ad := []byte{} a, _ := subtle.NewAESGCM(key) ctSet := make(map[string]bool) for i := 0; i < nSample; i++ { ct, _ := a.Encrypt(pt, ad) ctHex := hex.EncodeToString(ct) _, existed := ctSet[ctHex] if existed { t.Errorf("nonce is repeated after %d samples", i) } ctSet[ctHex] = true } } func TestAESGCMWycheproofCases(t *testing.T) { testutil.SkipTestIfTestSrcDirIsNotSet(t) suite := new(AEADSuite) if err := testutil.PopulateSuite(suite, "aes_gcm_test.json"); err != nil { t.Fatalf("failed populating suite: %s", err) } for _, group := range suite.TestGroups { if err := subtle.ValidateAESKeySize(group.KeySize / 8); err != nil { continue } if group.IvSize != subtle.AESGCMIVSize*8 { continue } for _, test := range group.Tests { caseName := fmt.Sprintf("%s-%s(%d,%d):Case-%d", suite.Algorithm, group.Type, group.KeySize, group.TagSize, test.CaseID) t.Run(caseName, func(t *testing.T) { runAESGCMWycheproofCase(t, test) }) } } } func runAESGCMWycheproofCase(t *testing.T, tc *AEADCase) { var combinedCt []byte combinedCt = append(combinedCt, tc.Iv...) combinedCt = append(combinedCt, tc.Ct...) combinedCt = append(combinedCt, tc.Tag...) // create cipher and do encryption cipher, err := subtle.NewAESGCM(tc.Key) if err != nil { t.Fatalf("cannot create new instance of AESGCM in test case: %s", err) } decrypted, err := cipher.Decrypt(combinedCt, tc.Aad) if err != nil { if tc.Result == "valid" { t.Errorf("unexpected error in test case: %s", err) } } else { if tc.Result == "invalid" { t.Error("decrypted invalid test case") } if !bytes.Equal(decrypted, tc.Msg) { t.Error("incorrect decryption in test case") } } }