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  }