github.com/trustbloc/kms-go@v1.1.2/doc/jose/encrypt_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package jose
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/elliptic"
    12  	"encoding/base64"
    13  	"encoding/json"
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/google/tink/go/aead"
    18  	"github.com/google/tink/go/keyset"
    19  	"github.com/stretchr/testify/require"
    20  
    21  	"github.com/trustbloc/kms-go/crypto/tinkcrypto"
    22  	"github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/composite/ecdh"
    23  	"github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/composite/keyio"
    24  
    25  	cryptoapi "github.com/trustbloc/kms-go/spi/crypto"
    26  )
    27  
    28  func TestFailConvertRecKeyToMarshalledJWK(t *testing.T) {
    29  	t.Run("test convertRecEPKToMarshalledJWK using EPK with bad curve", func(t *testing.T) {
    30  		recKey := &cryptoapi.RecipientWrappedKey{
    31  			EPK: cryptoapi.PublicKey{
    32  				Curve: "badCurveName",
    33  				Type:  "EC",
    34  			},
    35  		}
    36  
    37  		_, err := convertRecEPKToMarshalledJWK(&recKey.EPK)
    38  		require.EqualError(t, err, "unsupported curve")
    39  	})
    40  
    41  	t.Run("test convertRecEPKToMarshalledJWK and buildRecipientHeaders using EPK with bad key type", func(t *testing.T) {
    42  		recKey := &cryptoapi.RecipientWrappedKey{
    43  			EPK: cryptoapi.PublicKey{
    44  				Curve: "P-256",
    45  			},
    46  		}
    47  
    48  		_, err := convertRecEPKToMarshalledJWK(&recKey.EPK)
    49  		require.EqualError(t, err, "invalid key type")
    50  
    51  		_, err = buildRecipientHeaders(recKey, false)
    52  		require.EqualError(t, err, "failed to convert recipient key to marshalled JWK: invalid key type")
    53  	})
    54  }
    55  
    56  func TestBuildRecsWithEPKMissingKeyTypeFailure(t *testing.T) {
    57  	recKey := &cryptoapi.RecipientWrappedKey{
    58  		EPK: cryptoapi.PublicKey{
    59  			Curve: "P-256",
    60  		},
    61  	}
    62  
    63  	c, err := tinkcrypto.New()
    64  	require.NoError(t, err)
    65  
    66  	recipients, recsKH := createRecipients(t, 2)
    67  
    68  	enc, err := NewJWEEncrypt(A256GCM, testEncType, testPayloadType,
    69  		"0", recsKH["0"], recipients, c)
    70  	require.NoError(t, err)
    71  
    72  	_, _, err = enc.buildRecs([]*cryptoapi.RecipientWrappedKey{recKey}, true)
    73  	require.EqualError(t, err, "failed to convert recipient key to marshalled JWK: invalid key type")
    74  }
    75  
    76  func TestBadSenderKeyType(t *testing.T) {
    77  	c, err := tinkcrypto.New()
    78  	require.NoError(t, err)
    79  
    80  	// create a keyset.Handle that fails JWE/ECDH primitive execution (must come from an ECDH key template).
    81  	aeadKT := aead.AES256GCMKeyTemplate()
    82  	aeadKH, err := keyset.NewHandle(aeadKT)
    83  	require.NoError(t, err)
    84  
    85  	recipients, _ := createRecipients(t, 2)
    86  
    87  	// create jweEncrypter manually with a bad sender type
    88  	jweEncrypter := JWEEncrypt{
    89  		skid:           "123",
    90  		senderKH:       aeadKH,
    91  		recipientsKeys: recipients,
    92  		crypto:         c,
    93  		encAlg:         A256GCM,
    94  	}
    95  
    96  	_, err = jweEncrypter.Encrypt([]byte{})
    97  	require.EqualError(t, err, "jweencryptWithSender: failed to wrap cek: wrapCEKForRecipientsWithTagAndEPK: "+
    98  		"wrapKey: 1 failed: wrapKey: deriveKEKAndWrap: error ECDH-1PU kek derivation: derive1PUKEK: EC key derivation "+
    99  		"error derive1PUWithECKey: failed to retrieve sender key: ksToPrivateECDSAKey: failed to extract sender key: "+
   100  		"extractPrivKey: can't extract unsupported private key 'type.googleapis.com/google.crypto.tink.AesGcmKey'")
   101  }
   102  
   103  func TestMergeSingleRecipientsHeadersFailureWithUnsetCurve(t *testing.T) {
   104  	aad := map[string]string{"enc": "test"}
   105  
   106  	mAAD, err := json.Marshal(aad)
   107  	require.NoError(t, err)
   108  
   109  	wk := &cryptoapi.RecipientWrappedKey{
   110  		EPK: cryptoapi.PublicKey{Type: "EC"},
   111  	}
   112  
   113  	// fail with aad not base64URL encoded
   114  	_, err = mergeSingleRecipientHeaders(wk, []byte("aad not base64URL encoded"), json.Marshal)
   115  	require.EqualError(t, err, "illegal base64 data at input byte 3")
   116  
   117  	badAAD := base64.RawURLEncoding.EncodeToString([]byte("aad not a json format"))
   118  
   119  	// fail with aad not being a marshalled json
   120  	_, err = mergeSingleRecipientHeaders(wk, []byte(badAAD), json.Marshal)
   121  	require.EqualError(t, err, "invalid character 'a' looking for beginning of value")
   122  
   123  	// fail with epk curve not set
   124  	_, err = mergeSingleRecipientHeaders(wk, []byte(base64.RawURLEncoding.EncodeToString(mAAD)), json.Marshal)
   125  	require.EqualError(t, err, "unsupported curve")
   126  
   127  	// set epk curve for subsequent tests
   128  	wk.EPK.Curve = elliptic.P256().Params().Name
   129  
   130  	fm := &failingMarshaller{
   131  		numTimesMarshalCalledBeforeReturnErr: 0,
   132  	}
   133  
   134  	// fail KID marshalling
   135  	_, err = mergeSingleRecipientHeaders(wk, []byte(base64.RawURLEncoding.EncodeToString(mAAD)), fm.failingMarshal)
   136  	require.EqualError(t, err, errFailingMarshal.Error())
   137  
   138  	fm = &failingMarshaller{
   139  		numTimesMarshalCalledBeforeReturnErr: 1,
   140  	}
   141  
   142  	// fail Alg marshalling
   143  	_, err = mergeSingleRecipientHeaders(wk, []byte(base64.RawURLEncoding.EncodeToString(mAAD)), fm.failingMarshal)
   144  	require.EqualError(t, err, errFailingMarshal.Error())
   145  
   146  	fm = &failingMarshaller{
   147  		numTimesMarshalCalledBeforeReturnErr: 1,
   148  	}
   149  
   150  	// fail EPK marshalling
   151  	_, err = mergeSingleRecipientHeaders(wk, []byte(base64.RawURLEncoding.EncodeToString(mAAD)), fm.failingMarshal)
   152  	require.EqualError(t, err, errFailingMarshal.Error())
   153  }
   154  
   155  func TestEmptyComputeAuthData(t *testing.T) {
   156  	protecteHeaders := new(map[string]interface{})
   157  	aad := []byte("")
   158  	_, err := computeAuthData(*protecteHeaders, "", aad)
   159  	require.NoError(t, err, "computeAuthData with empty protectedHeaders and empty aad should not fail")
   160  }
   161  
   162  // createRecipients and return their public key and keyset.Handle.
   163  func createRecipients(t *testing.T, numberOfEntities int) ([]*cryptoapi.PublicKey, map[string]*keyset.Handle) {
   164  	t.Helper()
   165  
   166  	r := make([]*cryptoapi.PublicKey, 0)
   167  	rKH := make(map[string]*keyset.Handle)
   168  
   169  	for i := 0; i < numberOfEntities; i++ {
   170  		mrKey, kh := createAndMarshalEntityKey(t)
   171  		ecPubKey := new(cryptoapi.PublicKey)
   172  		err := json.Unmarshal(mrKey, ecPubKey)
   173  		require.NoError(t, err)
   174  
   175  		ecPubKey.KID = fmt.Sprint(i)
   176  
   177  		r = append(r, ecPubKey)
   178  		rKH[fmt.Sprint(i)] = kh
   179  	}
   180  
   181  	return r, rKH
   182  }
   183  
   184  // createAndMarshalEntityKey creates a new recipient keyset.Handle, extracts public key, marshals it and returns
   185  // both marshalled public key and original recipient keyset.Handle.
   186  func createAndMarshalEntityKey(t *testing.T) ([]byte, *keyset.Handle) {
   187  	t.Helper()
   188  
   189  	tmpl := ecdh.NISTP256ECDHKWKeyTemplate()
   190  
   191  	kh, err := keyset.NewHandle(tmpl)
   192  	require.NoError(t, err)
   193  
   194  	pubKH, err := kh.Public()
   195  	require.NoError(t, err)
   196  
   197  	buf := new(bytes.Buffer)
   198  	pubKeyWriter := keyio.NewWriter(buf)
   199  	require.NotEmpty(t, pubKeyWriter)
   200  
   201  	err = pubKH.WriteWithNoSecrets(pubKeyWriter)
   202  	require.NoError(t, err)
   203  
   204  	return buf.Bytes(), kh
   205  }
   206  
   207  const (
   208  	testEncType     = "application/test-encrypted+json"
   209  	testPayloadType = "application/test-content+json"
   210  )
   211  
   212  func TestFailJWEEncrypt(t *testing.T) {
   213  	c, err := tinkcrypto.New()
   214  	require.NoError(t, err)
   215  
   216  	recipients, recsKH := createRecipients(t, 2)
   217  
   218  	enc, err := NewJWEEncrypt(A256GCM, testEncType, testPayloadType,
   219  		"0", recsKH["0"], recipients, c)
   220  	require.NoError(t, err)
   221  
   222  	enc.encAlg = "Undefined"
   223  
   224  	_, err = enc.Encrypt([]byte("test"))
   225  	require.EqualError(t, err, "jweencrypt: failed to get encryption primitive: getECDHEncPrimitive: encAlg"+
   226  		" not supported: 'Undefined'")
   227  }
   228  
   229  func TestFailJWEDecrypt(t *testing.T) {
   230  	c, err := tinkcrypto.New()
   231  	require.NoError(t, err)
   232  
   233  	recipients, _ := createRecipients(t, 2)
   234  
   235  	// encrypt using local jose package
   236  	jweEncrypter, err := NewJWEEncrypt(A256GCM, testEncType, testPayloadType,
   237  		"", nil, recipients, c)
   238  	require.NoError(t, err, "NewJWEEncrypt should not fail with non empty recipientPubKeys")
   239  
   240  	pt := []byte("some msg")
   241  	jwe, err := jweEncrypter.Encrypt(pt)
   242  	require.NoError(t, err)
   243  	require.Equal(t, len(recipients), len(jwe.Recipients))
   244  
   245  	dec := NewJWEDecrypt(nil, c, nil)
   246  	require.NotEmpty(t, dec)
   247  
   248  	jwe.ProtectedHeaders[HeaderEncryption] = "Undefined"
   249  
   250  	_, err = dec.decryptJWE(jwe, []byte(""))
   251  	require.EqualError(t, err, "jwedecrypt: failed to get decryption primitive: invalid content encAlg: 'Undefined'")
   252  
   253  	delete(jwe.ProtectedHeaders, HeaderEncryption)
   254  
   255  	_, err = dec.decryptJWE(jwe, []byte(""))
   256  	require.EqualError(t, err, "jwedecrypt: JWE 'enc' protected header is missing")
   257  }
   258  
   259  func TestBuildAPUAPVFailures(t *testing.T) {
   260  	c, err := tinkcrypto.New()
   261  	require.NoError(t, err)
   262  
   263  	recipients, _ := createRecipients(t, 3)
   264  
   265  	// encrypt using local jose package
   266  	jweEncrypter, err := NewJWEEncrypt(A256GCM, testEncType, testPayloadType,
   267  		"", nil, recipients, c)
   268  	require.NoError(t, err, "NewJWEEncrypt should not fail with non empty recipientPubKeys")
   269  
   270  	t.Run("call encryptWithSender and  buildAPUAPV with JWEEncrypter having empty skid", func(t *testing.T) {
   271  		_, err = jweEncrypter.encryptWithSender(nil, nil, nil, nil, nil)
   272  		require.EqualError(t, err, "jweencryptWithSender: cannot create APU/APV with empty sender skid")
   273  
   274  		_, _, err = jweEncrypter.buildAPUAPV()
   275  		require.EqualError(t, err, "cannot create APU/APV with empty sender skid")
   276  	})
   277  
   278  	t.Run("call buildAPUAPV with JWEEncrypter having empty recipients", func(t *testing.T) {
   279  		jweEncrypter.skid = "skid"
   280  		jweEncrypter.recipientsKeys = nil
   281  
   282  		_, _, err = jweEncrypter.buildAPUAPV()
   283  		require.EqualError(t, err, "cannot create APU/APV with empty recipient keys")
   284  	})
   285  }
   286  
   287  func TestGenerateEPKAndUpdateAuthDataFor1PUFailure(t *testing.T) {
   288  	c, err := tinkcrypto.New()
   289  	require.NoError(t, err)
   290  
   291  	recipients, _ := createRecipients(t, 5)
   292  
   293  	recipients[0].Type = "invalidType"
   294  
   295  	// encrypt using local jose package
   296  	jweEncrypter, err := NewJWEEncrypt(A256GCM, testEncType, testPayloadType,
   297  		"", nil, recipients, c)
   298  	require.NoError(t, err, "NewJWEEncrypt should not fail with non empty recipientPubKeys")
   299  
   300  	epk, authData, authJSON, err := jweEncrypter.generateEPKAndUpdateAuthDataFor1PU(nil, nil, nil, nil)
   301  	require.EqualError(t, err, "generateEPKAndUpdateAuthDataFor1PU: newEPK: invalid key type 'invalidType'")
   302  	require.Empty(t, epk)
   303  	require.Empty(t, authData)
   304  	require.Empty(t, authJSON)
   305  
   306  	recipients[0].Type = "EC"
   307  	recipients[0].Curve = "invalid"
   308  
   309  	_, _, err = jweEncrypter.newEPK([]byte(""))
   310  	require.EqualError(t, err, "newEPK: ecEPKAndAlg: getCurve: unsupported curve")
   311  }
   312  
   313  func TestBuildCommonAuthDataFailure(t *testing.T) {
   314  	c, err := tinkcrypto.New()
   315  	require.NoError(t, err)
   316  
   317  	recipients, _ := createRecipients(t, 5)
   318  
   319  	recipients[0].Type = "invalid"
   320  
   321  	// encrypt using local jose package
   322  	jweEncrypter, err := NewJWEEncrypt(A256GCM, testEncType, testPayloadType,
   323  		"", nil, recipients, c)
   324  	require.NoError(t, err, "NewJWEEncrypt should not fail with non empty recipientPubKeys")
   325  
   326  	t.Run("buildCommonAuthData with authData having invalid b64 format", func(t *testing.T) {
   327  		epk, authData, authJSON, err := jweEncrypter.buildCommonAuthData(0, "", "==Badb64Str$$#^",
   328  			nil, nil, nil, nil)
   329  		require.EqualError(t, err, "buildCommonAuthData: authdata decode: illegal base64 data at input byte 0")
   330  		require.Empty(t, epk)
   331  		require.Empty(t, authData)
   332  		require.Empty(t, authJSON)
   333  	})
   334  
   335  	t.Run("buildCommonAuthData with authData having invalid json format", func(t *testing.T) {
   336  		epk, authData, authJSON, err := jweEncrypter.buildCommonAuthData(0, "", "badjsonunmarshal",
   337  			nil, nil, nil, nil)
   338  		require.EqualError(t, err, "buildCommonAuthData: authData unmarshall: invalid character 'm' looking "+
   339  			"for beginning of value")
   340  		require.Empty(t, epk)
   341  		require.Empty(t, authData)
   342  		require.Empty(t, authJSON)
   343  	})
   344  
   345  	t.Run("buildCommonAuthData with epk having invalid public key type", func(t *testing.T) {
   346  		epk, authData, authJSON, err := jweEncrypter.buildCommonAuthData(0, "", "eyJ0ZXN0IjoidGVzdCJ9",
   347  			nil, nil, nil, &cryptoapi.PrivateKey{
   348  				PublicKey: cryptoapi.PublicKey{Type: "invalid"},
   349  				D:         nil,
   350  			})
   351  		require.EqualError(t, err, "buildCommonAuthData: epk marshall: invalid key type")
   352  		require.Empty(t, epk)
   353  		require.Empty(t, authData)
   354  		require.Empty(t, authJSON)
   355  	})
   356  }
   357  
   358  func TestDecodeAPUAPVFailure(t *testing.T) {
   359  	t.Run("decodeAPUAPV with recipient APU header as invalid b64 []byte", func(t *testing.T) {
   360  		_, _, err := decodeAPUAPV(&RecipientHeaders{APU: "=Badb64Str$$#^"})
   361  		require.EqualError(t, err, "illegal base64 data at input byte 0")
   362  	})
   363  
   364  	t.Run("decodeAPUAPV with recipient APV header as invalid b64 []byte", func(t *testing.T) {
   365  		_, _, err := decodeAPUAPV(&RecipientHeaders{APV: "=Badb64Str$$#^"})
   366  		require.EqualError(t, err, "illegal base64 data at input byte 0")
   367  	})
   368  }
   369  
   370  func TestJWKMarshalEPKFailure(t *testing.T) {
   371  	t.Run("jwkMarshalEPK with protectedHeadersJSON having 'epk' with invalid json format", func(t *testing.T) {
   372  		err := jwkMarshalEPK(map[string]json.RawMessage{HeaderEPK: []byte("badjsonunmarshal")})
   373  		require.EqualError(t, err, "unable to read JWK: invalid character 'b' looking for beginning of value")
   374  	})
   375  }