| // 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" |
| ) |
| |
| func TestAESGCMSIVRejectsInvalidKeyLength(t *testing.T) { |
| invalidKeySizes := []uint32{4, 8, 12, 15, 17, 24, 30, 31, 33, 64, 128} |
| |
| for _, keySize := range invalidKeySizes { |
| key := random.GetRandomBytes(keySize) |
| |
| if _, err := subtle.NewAESGCMSIV(key); err == nil { |
| t.Errorf("expected error with invalid key-size %d", keySize) |
| } |
| } |
| } |
| |
| func TestAESGCMSIVRandomNonceProducesDifferentCiphertexts(t *testing.T) { |
| nSample := 1 << 17 |
| key := random.GetRandomBytes(16) |
| pt := []byte{} |
| ad := []byte{} |
| a, _ := subtle.NewAESGCMSIV(key) |
| ctSet := make(map[string]bool) |
| |
| for i := 0; i < nSample; i++ { |
| ct, _ := a.Encrypt(pt, ad) |
| ctHex := hex.EncodeToString(ct) |
| |
| if _, existed := ctSet[ctHex]; existed { |
| t.Errorf("nonce is repeated after %d samples", i) |
| } |
| ctSet[ctHex] = true |
| } |
| } |
| |
| func TestAESGCMSIVModifyCiphertext(t *testing.T) { |
| ad := random.GetRandomBytes(33) |
| key := random.GetRandomBytes(16) |
| pt := random.GetRandomBytes(32) |
| a, _ := subtle.NewAESGCMSIV(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 |
| } |
| } |
| } |
| |
| func TestAESGCMSIVWycheproofCases(t *testing.T) { |
| testutil.SkipTestIfTestSrcDirIsNotSet(t) |
| suite := new(AEADSuite) |
| if err := testutil.PopulateSuite(suite, "aes_gcm_siv_test.json"); err != nil { |
| t.Fatalf("failed populating suite: %s", err) |
| } |
| for _, group := range suite.TestGroups { |
| for _, test := range group.Tests { |
| caseName := fmt.Sprintf("%s-%s(%d):Case-%d", suite.Algorithm, group.Type, group.KeySize, test.CaseID) |
| t.Run("DecryptOnly/"+caseName, func(t *testing.T) { runWycheproofDecryptOnly(t, test) }) |
| t.Run("EncryptDecrypt/"+caseName, func(t *testing.T) { runWycheproofEncryptDecrypt(t, test) }) |
| } |
| } |
| } |
| |
| func runWycheproofDecryptOnly(t *testing.T, testCase *AEADCase) { |
| aead, err := subtle.NewAESGCMSIV(testCase.Key) |
| if err != nil { |
| t.Fatalf("cannot create aead, error: %v", err) |
| } |
| |
| var combinedCt []byte |
| combinedCt = append(combinedCt, testCase.Iv...) |
| combinedCt = append(combinedCt, testCase.Ct...) |
| combinedCt = append(combinedCt, testCase.Tag...) |
| decrypted, err := aead.Decrypt(combinedCt, testCase.Aad) |
| switch testCase.Result { |
| case "valid": |
| if err != nil { |
| t.Errorf("unexpected error in test-case: %v", err) |
| } else if !bytes.Equal(decrypted, testCase.Msg) { |
| t.Errorf( |
| "incorrect decryption: actual: %s; expected %s", |
| hex.EncodeToString(decrypted), hex.EncodeToString(testCase.Msg)) |
| } |
| case "invalid": |
| if err == nil && bytes.Equal(testCase.Ct, decrypted) { |
| t.Error("successfully decrypted invalid test-case") |
| } |
| default: |
| t.Errorf("unknown test-case result: %s", testCase.Result) |
| } |
| } |
| |
| func runWycheproofEncryptDecrypt(t *testing.T, testCase *AEADCase) { |
| aead, err := subtle.NewAESGCMSIV(testCase.Key) |
| if err != nil { |
| t.Fatalf("cannot create aead, error: %v", err) |
| } |
| |
| ct, err := aead.Encrypt(testCase.Msg, testCase.Aad) |
| if err != nil { |
| if testCase.Result != "invalid" { |
| t.Errorf("unexpected error in test-case: %v", err) |
| } |
| return |
| } |
| |
| decrypted, err := aead.Decrypt(ct, testCase.Aad) |
| switch testCase.Result { |
| case "valid": |
| if err != nil { |
| t.Errorf("unexpected error in test-case: %v", err) |
| } else if !bytes.Equal(decrypted, testCase.Msg) { |
| t.Errorf( |
| "incorrect decryption: actual: %s; expected %s", |
| hex.EncodeToString(decrypted), hex.EncodeToString(testCase.Msg)) |
| } |
| case "invalid": |
| if err == nil && bytes.Equal(ct, decrypted) { |
| t.Error("successfully decrypted invalid test-case") |
| } |
| default: |
| t.Errorf("unknown test-case result: %s", testCase.Result) |
| } |
| } |