// 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 streamingaead_test import ( "bytes" "fmt" "io" "log" "strings" "testing" "google.golang.org/protobuf/proto" "github.com/google/tink/go/keyset" "github.com/google/tink/go/mac" "github.com/google/tink/go/streamingaead" "github.com/google/tink/go/subtle/random" "github.com/google/tink/go/testkeyset" "github.com/google/tink/go/testutil" "github.com/google/tink/go/tink" ghpb "github.com/google/tink/go/proto/aes_gcm_hkdf_streaming_go_proto" commonpb "github.com/google/tink/go/proto/common_go_proto" tinkpb "github.com/google/tink/go/proto/tink_go_proto" ) const ( aesGCMHKDFTypeURL = "type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey" ) func TestFactoryMultipleKeys(t *testing.T) { keyset := testutil.NewTestAESGCMHKDFKeyset() keysetHandle, err := testkeyset.NewHandle(keyset) if err != nil { log.Fatal(err) } a, err := streamingaead.New(keysetHandle) if err != nil { t.Errorf("streamingaead.New failed: %s", err) } t.Run("Encrypt with a primary RAW key and decrypt with the keyset", func(t *testing.T) { if err := validateFactoryCipher(a, a); err != nil { t.Errorf("invalid cipher: %s", err) } }) t.Run("Encrypt with a non-primary RAW key and decrypt with the keyset", func(t *testing.T) { rawKey := keyset.Key[1] if rawKey.OutputPrefixType != tinkpb.OutputPrefixType_RAW { t.Errorf("expect a raw key") } keyset2 := testutil.NewKeyset(rawKey.KeyId, []*tinkpb.Keyset_Key{rawKey}) keysetHandle2, _ := testkeyset.NewHandle(keyset2) a2, err := streamingaead.New(keysetHandle2) if err != nil { t.Errorf("streamingaead.New failed: %s", err) } if err := validateFactoryCipher(a2, a); err != nil { t.Errorf("invalid cipher: %s", err) } }) t.Run("Encrypt with a random key not in the keyset, decrypt with the keyset should fail", func(t *testing.T) { keyset2 := testutil.NewTestAESGCMHKDFKeyset() keysetHandle2, _ := testkeyset.NewHandle(keyset2) a2, err := streamingaead.New(keysetHandle2) if err != nil { t.Errorf("streamingaead.New failed: %s", err) } err = validateFactoryCipher(a2, a) if err == nil || !strings.Contains(err.Error(), "decryption failed") { t.Errorf("expect decryption to fail with random key: %s", err) } }) } func validateFactoryCipher(encryptCipher tink.StreamingAEAD, decryptCipher tink.StreamingAEAD) error { tt := []int{1, 16, 4095, 4096, 4097, 16384} for _, t := range tt { if err := encryptDecrypt(encryptCipher, decryptCipher, t, 32); err != nil { return fmt.Errorf("failed plaintext-size=%d: %s", t, err) } } return nil } func encryptDecrypt(encryptCipher, decryptCipher tink.StreamingAEAD, ptSize, aadSize int) error { pt := random.GetRandomBytes(uint32(ptSize)) aad := random.GetRandomBytes(uint32(aadSize)) buf := &bytes.Buffer{} w, err := encryptCipher.NewEncryptingWriter(buf, aad) if err != nil { return fmt.Errorf("cannot create encrypt writer: %v", err) } if _, err := w.Write(pt); err != nil { return fmt.Errorf("error writing data: %v", err) } if err := w.Close(); err != nil { return fmt.Errorf("error closing writer: %v", err) } r, err := decryptCipher.NewDecryptingReader(buf, aad) if err != nil { return fmt.Errorf("cannot create decrypt reader: %v", err) } ptGot := make([]byte, len(pt)+1) n, err := io.ReadFull(r, ptGot) if err != nil && err != io.ErrUnexpectedEOF { return fmt.Errorf("decryption failed: %v", err) } ptGot = ptGot[:n] if !bytes.Equal(pt, ptGot) { return fmt.Errorf("decryption failed") } return nil } func TestFactoryWithInvalidPrimitiveSetType(t *testing.T) { wrongKH, err := keyset.NewHandle(mac.HMACSHA256Tag128KeyTemplate()) if err != nil { t.Fatalf("failed to build *keyset.Handle: %s", err) } _, err = streamingaead.New(wrongKH) if err == nil { t.Fatal("New() should fail with wrong *keyset.Handle") } } func TestFactoryWithValidPrimitiveSetType(t *testing.T) { goodKH, err := keyset.NewHandle(streamingaead.AES128GCMHKDF4KBKeyTemplate()) if err != nil { t.Fatalf("failed to build *keyset.Handle: %s", err) } _, err = streamingaead.New(goodKH) if err != nil { t.Fatalf("New() failed with good *keyset.Handle: %s", err) } } func TestFactoryWithKeysetWithTinkKeys(t *testing.T) { key := &ghpb.AesGcmHkdfStreamingKey{ Version: 0, KeyValue: []byte("0123456789abcdef"), Params: &ghpb.AesGcmHkdfStreamingParams{ CiphertextSegmentSize: 512, DerivedKeySize: 16, HkdfHashType: commonpb.HashType_SHA1, }, } value1, err := proto.Marshal(key) if err != nil { t.Fatalf("proto.Marshal(key) err = %q, want nil", err) } key.KeyValue = []byte("ABCDEF0123456789") value2, err := proto.Marshal(key) if err != nil { t.Fatalf("proto.Marshal(key) err = %q, want nil", err) } keyset := &tinkpb.Keyset{ PrimaryKeyId: 1, Key: []*tinkpb.Keyset_Key{{ KeyData: &tinkpb.KeyData{ TypeUrl: aesGCMHKDFTypeURL, Value: value1, KeyMaterialType: tinkpb.KeyData_SYMMETRIC, }, OutputPrefixType: tinkpb.OutputPrefixType_TINK, KeyId: 1, Status: tinkpb.KeyStatusType_ENABLED, }, &tinkpb.Keyset_Key{ KeyData: &tinkpb.KeyData{ TypeUrl: aesGCMHKDFTypeURL, Value: value2, KeyMaterialType: tinkpb.KeyData_SYMMETRIC, }, OutputPrefixType: tinkpb.OutputPrefixType_RAW, KeyId: 2, Status: tinkpb.KeyStatusType_ENABLED, }}, } keysetHandle, err := testkeyset.NewHandle(keyset) if err != nil { t.Fatalf("testkeyset.NewHandle(keyset) err = %q, want nil", err) } a, err := streamingaead.New(keysetHandle) if err != nil { t.Errorf("streamingaead.New(keysetHandle) err = %q, want nil", err) } if err := validateFactoryCipher(a, a); err != nil { t.Errorf("Encryption & Decryption with TINK key should succeed") } }