github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/primitive/aead/aead_factory_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package aead_test 8 9 import ( 10 "crypto/aes" 11 "testing" 12 13 "github.com/golang/protobuf/proto" 14 tinkaead "github.com/google/tink/go/aead" 15 "github.com/google/tink/go/core/cryptofmt" 16 "github.com/google/tink/go/keyset" 17 commonpb "github.com/google/tink/go/proto/common_go_proto" 18 hmacpb "github.com/google/tink/go/proto/hmac_go_proto" 19 tinkpb "github.com/google/tink/go/proto/tink_go_proto" 20 "github.com/google/tink/go/signature" 21 "github.com/google/tink/go/subtle/random" 22 "github.com/google/tink/go/testkeyset" 23 "github.com/google/tink/go/testutil" 24 "github.com/google/tink/go/tink" 25 "github.com/stretchr/testify/require" 26 27 "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/aead" 28 "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/aead/subtle" 29 aescbcpb "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/proto/aes_cbc_go_proto" 30 aeadpb "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/proto/aes_cbc_hmac_aead_go_proto" 31 ) 32 33 func TestFactoryMultipleKeys(t *testing.T) { 34 // encrypt with non-raw key 35 ks := NewTestAESCBCHMACKeyset(t, tinkpb.OutputPrefixType_TINK) 36 primaryKey := ks.Key[0] 37 require.NotEqualf(t, tinkpb.OutputPrefixType_RAW, primaryKey.OutputPrefixType, "expect a non-raw key") 38 39 keysetHandle, err := testkeyset.NewHandle(ks) 40 require.NoError(t, err) 41 42 a, err := tinkaead.New(keysetHandle) 43 require.NoError(t, err) 44 45 expectedPrefix, err := cryptofmt.OutputPrefix(primaryKey) 46 require.NoError(t, err) 47 48 validateAEADFactoryCipher(t, a, a, expectedPrefix, true) 49 50 // encrypt with a non-primary RAW key and decrypt with the keyset 51 rawKey := ks.Key[1] 52 require.Equalf(t, tinkpb.OutputPrefixType_RAW, rawKey.OutputPrefixType, "expect a raw key") 53 54 keyset2 := testutil.NewKeyset(rawKey.KeyId, []*tinkpb.Keyset_Key{rawKey}) 55 keysetHandle2, err := testkeyset.NewHandle(keyset2) 56 require.NoError(t, err) 57 58 a2, err := tinkaead.New(keysetHandle2) 59 require.NoError(t, err) 60 61 validateAEADFactoryCipher(t, a2, a, cryptofmt.RawPrefix, true) 62 63 // encrypt with a random key not in the keyset, decrypt with the keyset should fail 64 keyset2 = NewTestAESCBCHMACKeyset(t, tinkpb.OutputPrefixType_TINK) 65 primaryKey = keyset2.Key[0] 66 expectedPrefix, err = cryptofmt.OutputPrefix(primaryKey) 67 require.NoError(t, err) 68 69 keysetHandle2, err = testkeyset.NewHandle(keyset2) 70 require.NoError(t, err) 71 72 a2, err = tinkaead.New(keysetHandle2) 73 require.NoErrorf(t, err, "aead.New failed") 74 75 validateAEADFactoryCipher(t, a2, a, expectedPrefix, false) 76 } 77 78 func TestFactoryRawKeyAsPrimary(t *testing.T) { 79 ks := NewTestAESCBCHMACKeyset(t, tinkpb.OutputPrefixType_RAW) 80 require.Equalf(t, tinkpb.OutputPrefixType_RAW, ks.Key[0].OutputPrefixType, "primary key is not a raw key") 81 82 keysetHandle, err := testkeyset.NewHandle(ks) 83 require.NoError(t, err) 84 85 a, err := tinkaead.New(keysetHandle) 86 require.NoError(t, err) 87 88 validateAEADFactoryCipher(t, a, a, cryptofmt.RawPrefix, true) 89 } 90 91 func validateAEADFactoryCipher(t *testing.T, encryptCipher tink.AEAD, decryptCipher tink.AEAD, expectedPrefix string, 92 noError bool) { 93 prefixSize := len(expectedPrefix) 94 // regular plaintext 95 pt := random.GetRandomBytes(20) 96 ad := random.GetRandomBytes(20) 97 ct, err := encryptCipher.Encrypt(pt, ad) 98 require.NoErrorf(t, err, "encryption failed with regular plaintext") 99 100 decrypted, err := decryptCipher.Decrypt(ct, ad) 101 if !noError { 102 require.Error(t, err) 103 require.Contains(t, err.Error(), "decryption failed") 104 105 return 106 } 107 108 require.NoError(t, err) 109 require.EqualValues(t, pt, decrypted, "decryption not equal to plaintext: %s", pt) 110 111 require.Equalf(t, expectedPrefix, string(ct[:prefixSize]), "incorrect prefix with regular plaintext") 112 113 padding := aes.BlockSize - (len(pt) % aes.BlockSize) 114 115 // Tink's CBC+HMAC ciphertext output is comprised of: 116 // Tink Prefix + 117 // plaintext + 118 // AES Block padding + 119 // IV/nonce + 120 // authentication Tag + 121 require.Equalf(t, len(ct), prefixSize+len(pt)+padding+subtle.AESCBCIVSize+subtle.AES128Size, 122 "lengths of plaintext and ciphertext don't match with regular plaintext") 123 124 // short plaintext 125 pt = random.GetRandomBytes(1) 126 ct, err = encryptCipher.Encrypt(pt, ad) 127 require.NoError(t, err, "encryption failed with short plaintext") 128 129 decrypted, err = decryptCipher.Decrypt(ct, ad) 130 require.NoError(t, err) 131 require.EqualValuesf(t, pt, decrypted, "decryption failed with short plaintext: %s", pt) 132 133 require.Equalf(t, expectedPrefix, string(ct[:prefixSize]), "incorrect prefix with short plaintext") 134 135 padding = aes.BlockSize - (len(pt) % aes.BlockSize) 136 137 require.Equalf(t, len(ct), prefixSize+len(pt)+padding+subtle.AESCBCIVSize+subtle.AES128Size, 138 "lengths of plaintext and ciphertext don't match with short plaintext") 139 } 140 141 func TestFactoryWithInvalidPrimitiveSetType(t *testing.T) { 142 wrongKH, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) 143 if err != nil { 144 t.Fatalf("failed to build *keyset.Handle: %s", err) 145 } 146 147 _, err = tinkaead.New(wrongKH) 148 if err == nil { 149 t.Fatalf("calling New() with wrong *keyset.Handle should fail") 150 } 151 } 152 153 func TestFactoryWithValidPrimitiveSetType(t *testing.T) { 154 goodKH, err := keyset.NewHandle(aead.AES128CBCHMACSHA256KeyTemplate()) 155 require.NoError(t, err, "failed to build *keyset.Handle") 156 157 _, err = tinkaead.New(goodKH) 158 require.NoError(t, err, "calling New() with good *keyset.Handle failed") 159 } 160 161 func TestFactoryAndPrimitiveSetWithBadCiphertext(t *testing.T) { 162 goodKH, err := keyset.NewHandle(aead.AES128CBCHMACSHA256KeyTemplate()) 163 require.NoError(t, err, "failed to build *keyset.Handle") 164 165 aeadPrimitive, err := tinkaead.New(goodKH) 166 require.NoError(t, err, "calling New() with good *keyset.Handle failed") 167 168 pt := []byte("test plaintext") 169 ad := []byte("aad") 170 171 ct, err := aeadPrimitive.Encrypt(pt, ad) 172 require.NoError(t, err) 173 174 plaintext, err := aeadPrimitive.Decrypt(ct, ad) 175 require.NoError(t, err) 176 require.EqualValues(t, pt, plaintext) 177 178 _, err = aeadPrimitive.Decrypt([]byte("bad ciphertext"), ad) 179 require.EqualError(t, err, "aead_factory: decryption failed") 180 } 181 182 // NewTestAESCBCHMACKeyset creates a new Keyset containing an AESCBC+HMAC aead. 183 func NewTestAESCBCHMACKeyset(t *testing.T, primaryOutputPrefixType tinkpb.OutputPrefixType) *tinkpb.Keyset { 184 keyData := NewCBCHMACKeyData(t, subtle.AES128Size, subtle.AES128Size, commonpb.HashType_SHA256) 185 return testutil.NewTestKeyset(keyData, primaryOutputPrefixType) 186 } 187 188 // NewCBCHMACKeyData creates a KeyData containing a randomly generated AESCBC+HMAC key. 189 func NewCBCHMACKeyData(t *testing.T, keySize, tagSize uint32, hashType commonpb.HashType) *tinkpb.KeyData { 190 serializedKey, err := proto.Marshal(NewCBCHMACKey(0, keySize, tagSize, hashType)) 191 require.NoError(t, err) 192 193 return testutil.NewKeyData(AESCBCHMACAEADTypeURL, serializedKey, tinkpb.KeyData_SYMMETRIC) 194 } 195 196 // NewAESGCMKey creates a randomly generated AESCBC+HMAC key. 197 func NewCBCHMACKey(keyVersion, keySize, tagSize uint32, hashType commonpb.HashType) *aeadpb.AesCbcHmacAeadKey { 198 cbcKeyValue := random.GetRandomBytes(keySize) 199 hmacKeyValue := random.GetRandomBytes(keySize) 200 201 return &aeadpb.AesCbcHmacAeadKey{ 202 Version: keyVersion, 203 AesCbcKey: &aescbcpb.AesCbcKey{ 204 Version: keyVersion, 205 KeyValue: cbcKeyValue, 206 }, 207 HmacKey: &hmacpb.HmacKey{ 208 Version: keyVersion, 209 Params: &hmacpb.HmacParams{ 210 Hash: hashType, 211 TagSize: tagSize, 212 }, 213 KeyValue: hmacKeyValue, 214 }, 215 } 216 }