github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/authcrypt/pack_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package authcrypt
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/ecdsa"
    12  	"crypto/ed25519"
    13  	"crypto/elliptic"
    14  	"encoding/json"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/go-jose/go-jose/v3"
    22  	"github.com/golang/protobuf/proto"
    23  	hybrid "github.com/google/tink/go/hybrid/subtle"
    24  	"github.com/google/tink/go/keyset"
    25  	commonpb "github.com/google/tink/go/proto/common_go_proto"
    26  	tinkpb "github.com/google/tink/go/proto/tink_go_proto"
    27  	"github.com/stretchr/testify/require"
    28  
    29  	ecdhpb "github.com/hyperledger/aries-framework-go/component/kmscrypto/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto"
    30  
    31  	"github.com/hyperledger/aries-framework-go/pkg/common/log"
    32  	cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto"
    33  	"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto"
    34  	"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite/keyio"
    35  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/transport"
    36  	afgjose "github.com/hyperledger/aries-framework-go/pkg/doc/jose"
    37  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey"
    38  	mockvdr "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/framework/aries/api/vdr"
    39  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    40  	"github.com/hyperledger/aries-framework-go/pkg/kms/localkms"
    41  	mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms"
    42  	mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider"
    43  	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
    44  	"github.com/hyperledger/aries-framework-go/pkg/secretlock/noop"
    45  	spilog "github.com/hyperledger/aries-framework-go/spi/log"
    46  )
    47  
    48  func TestAuthcryptPackerSuccess(t *testing.T) {
    49  	k := createKMS(t)
    50  
    51  	tests := []struct {
    52  		name    string
    53  		keyType kms.KeyType
    54  		encAlg  afgjose.EncAlg
    55  		cty     string
    56  	}{
    57  		{
    58  			name:    "authcrypt using NISTP256ECDHKW and AES128CBC+HMAC-SHA256",
    59  			keyType: kms.NISTP256ECDHKWType,
    60  			encAlg:  afgjose.A128CBCHS256,
    61  			cty:     transport.MediaTypeV1PlaintextPayload,
    62  		},
    63  		{
    64  			name:    "authcrypt using NISTP384ECDHKW and AES192CBC+HMAC-SHA384",
    65  			keyType: kms.NISTP384ECDHKWType,
    66  			encAlg:  afgjose.A192CBCHS384,
    67  			cty:     transport.MediaTypeV1PlaintextPayload,
    68  		},
    69  		{
    70  			name:    "authcrypt using NISTP521ECDHKW and AES256CBC+HMAC-SHA512",
    71  			keyType: kms.NISTP521ECDHKWType,
    72  			encAlg:  afgjose.A256CBCHS512,
    73  			cty:     transport.MediaTypeV1PlaintextPayload,
    74  		},
    75  		{
    76  			name:    "authcrypt using X25519ECDHKWType and AES128CBC+HMAC-SHA256",
    77  			keyType: kms.X25519ECDHKWType,
    78  			encAlg:  afgjose.A128CBCHS256ALG,
    79  			cty:     transport.MediaTypeV1PlaintextPayload,
    80  		},
    81  		{
    82  			name:    "authcrypt using NISTP256ECDHKW and XChacha20Poly1305",
    83  			keyType: kms.NISTP256ECDHKW,
    84  			encAlg:  afgjose.XC20P,
    85  			cty:     transport.MediaTypeV1PlaintextPayload,
    86  		},
    87  		{
    88  			name:    "authcrypt using NISTP384ECDHKW and XChacha20Poly1305",
    89  			keyType: kms.NISTP384ECDHKW,
    90  			encAlg:  afgjose.XC20P,
    91  			cty:     transport.MediaTypeV1PlaintextPayload,
    92  		},
    93  		{
    94  			name:    "authcrypt using NISTP521ECDHKW and XChacha20Poly1305",
    95  			keyType: kms.NISTP521ECDHKW,
    96  			encAlg:  afgjose.XC20P,
    97  			cty:     transport.MediaTypeV1PlaintextPayload,
    98  		},
    99  		{
   100  			name:    "authcrypt using X25519ECDHKWType and XChacha20Poly1305",
   101  			keyType: kms.X25519ECDHKWType,
   102  			encAlg:  afgjose.XC20P,
   103  			cty:     transport.MediaTypeV1PlaintextPayload,
   104  		},
   105  		{
   106  			name:    "authcrypt using NISTP256ECDHKW and AES192CBC+HMAC-SHA384",
   107  			keyType: kms.NISTP256ECDHKWType,
   108  			encAlg:  afgjose.A192CBCHS384,
   109  			cty:     transport.MediaTypeV1PlaintextPayload,
   110  		},
   111  		{
   112  			name:    "authcrypt using X25519ECDHKW and XChacha20Poly1305 without cty",
   113  			keyType: kms.X25519ECDHKWType,
   114  			encAlg:  afgjose.XC20P,
   115  			cty:     transport.MediaTypeV1PlaintextPayload,
   116  		},
   117  		{
   118  			name:    "authcrypt using NISTP256ECDHKW and XChacha20Poly1305 without cty",
   119  			keyType: kms.NISTP256ECDHKWType,
   120  			encAlg:  afgjose.XC20P,
   121  			cty:     transport.MediaTypeV1PlaintextPayload,
   122  		},
   123  		{
   124  			name:    "authcrypt using X25519ECDHKW and AES256-CBC+SHA512",
   125  			keyType: kms.X25519ECDHKWType,
   126  			encAlg:  afgjose.A256CBCHS512,
   127  			cty:     transport.MediaTypeV1PlaintextPayload,
   128  		},
   129  	}
   130  
   131  	t.Parallel()
   132  
   133  	for _, tt := range tests {
   134  		tc := tt
   135  		t.Run(fmt.Sprintf("running %s", tc.name), func(t *testing.T) {
   136  			t.Logf("authcrypt packing - creating kid %s key...", tc.keyType)
   137  			skid, sDIDKey, mSenderPubKey, _ := createAndMarshalKeyByKeyType(t, k, tc.keyType)
   138  
   139  			t.Logf("authcrypt packing - creating recipient %s keys...", tc.keyType)
   140  			_, recDIDKeys, recipientsKeys, keyHandles := createRecipientsByKeyType(t, k, 3, tc.keyType)
   141  
   142  			log.SetLevel("aries-framework/pkg/didcomm/packer/authcrypt", spilog.DEBUG)
   143  
   144  			cryptoSvc, err := tinkcrypto.New()
   145  			require.NoError(t, err)
   146  
   147  			authPacker, err := New(newMockProvider(k, cryptoSvc), tc.encAlg)
   148  			require.NoError(t, err)
   149  
   150  			origMsg := []byte("secret message")
   151  			ct, err := authPacker.Pack(tc.cty, origMsg, []byte(skid+"."+sDIDKey), recipientsKeys)
   152  			require.NoError(t, err)
   153  
   154  			jweStr, err := prettyPrint(ct)
   155  			require.NoError(t, err)
   156  			t.Logf("* authcrypt JWE: %s", jweStr)
   157  
   158  			msg, err := authPacker.Unpack(ct)
   159  			require.NoError(t, err)
   160  
   161  			recKey, err := exportPubKeyBytes(keyHandles[0], recDIDKeys[0])
   162  			require.NoError(t, err)
   163  
   164  			senderPubKey := &cryptoapi.PublicKey{}
   165  
   166  			err = json.Unmarshal(mSenderPubKey, senderPubKey)
   167  			require.NoError(t, err)
   168  
   169  			senderPubKey.KID = sDIDKey // match packer value.
   170  
   171  			mSenderPubKey, err = json.Marshal(senderPubKey)
   172  			require.NoError(t, err)
   173  
   174  			require.EqualValues(t, &transport.Envelope{Message: origMsg, FromKey: mSenderPubKey, ToKey: recKey}, msg)
   175  
   176  			jweJSON, err := afgjose.Deserialize(string(ct))
   177  			require.NoError(t, err)
   178  
   179  			verifyJWETypes(t, tc.cty, jweJSON.ProtectedHeaders)
   180  
   181  			// try with only 1 recipient to force compact JWE serialization
   182  			ct, err = authPacker.Pack(tc.cty, origMsg, []byte(skid+"."+sDIDKey), [][]byte{recipientsKeys[0]})
   183  			require.NoError(t, err)
   184  
   185  			t.Logf("* authcrypt JWE Compact serialization (using first recipient only): %s", ct)
   186  
   187  			jweJSON, err = afgjose.Deserialize(string(ct))
   188  			require.NoError(t, err)
   189  
   190  			jweStr, err = jweJSON.FullSerialize(json.Marshal)
   191  			require.NoError(t, err)
   192  			t.Logf("* authcrypt Flattened JWE JSON serialization (using first recipient only): %s", jweStr)
   193  
   194  			msg, err = authPacker.Unpack(ct)
   195  			require.NoError(t, err)
   196  
   197  			require.EqualValues(t, &transport.Envelope{Message: origMsg, FromKey: mSenderPubKey, ToKey: recKey}, msg)
   198  
   199  			verifyJWETypes(t, tc.cty, jweJSON.ProtectedHeaders)
   200  		})
   201  	}
   202  }
   203  
   204  func verifyJWETypes(t *testing.T, cty string, jweHeader afgjose.Headers) {
   205  	encodingType, ok := jweHeader.Type()
   206  	require.True(t, ok)
   207  
   208  	require.Equal(t, transport.MediaTypeV2EncryptedEnvelope, encodingType)
   209  
   210  	contentType, ok := jweHeader.ContentType()
   211  	require.True(t, contentType == "" || contentType != "" && ok)
   212  
   213  	require.Equal(t, cty, contentType)
   214  }
   215  
   216  func TestAuthcryptPackerUsingKeysWithDifferentCurvesSuccess(t *testing.T) {
   217  	k := createKMS(t)
   218  	_, recDIDKeys, recipientsKey1, keyHandles1 := createRecipients(t, k, 1)
   219  	// since authcrypt does ECDH kw using the sender key, the recipient keys must be on the same curve (for NIST P keys)
   220  	// and the same key type (for NIST P / X25519 keys) as the sender's.
   221  	// this is why recipient keys with different curves/type are not supported for authcrypt.
   222  	//nolint:dogsled
   223  	_, _, recipientsKey2, _ := createRecipients(t, k, 1) // can't create key with kms.NISTP384ECDHKW
   224  	//nolint:dogsled
   225  	_, _, recipientsKey3, _ := createRecipients(t, k, 1) // can't create key with kms.NISTP521ECDHKW
   226  
   227  	recipientsKeys := make([][]byte, 3)
   228  	recipientsKeys[0] = make([]byte, len(recipientsKey1[0]))
   229  	recipientsKeys[1] = make([]byte, len(recipientsKey2[0]))
   230  	recipientsKeys[2] = make([]byte, len(recipientsKey3[0]))
   231  
   232  	copy(recipientsKeys[0], recipientsKey1[0])
   233  	copy(recipientsKeys[1], recipientsKey2[0])
   234  	copy(recipientsKeys[2], recipientsKey3[0])
   235  
   236  	cty := transport.MediaTypeV1PlaintextPayload
   237  
   238  	skid, sDIDKey, mSenderPubKey, _ := createAndMarshalKey(t, k)
   239  
   240  	cryptoSvc, err := tinkcrypto.New()
   241  	require.NoError(t, err)
   242  
   243  	authPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A256CBCHS512)
   244  	require.NoError(t, err)
   245  
   246  	origMsg := []byte("secret message")
   247  	ct, err := authPacker.Pack(cty, origMsg, []byte(skid+"."+sDIDKey), recipientsKeys)
   248  	require.NoError(t, err)
   249  
   250  	t.Logf("authcrypt JWE: %s", ct)
   251  
   252  	msg, err := authPacker.Unpack(ct)
   253  	require.NoError(t, err)
   254  
   255  	recKey, err := exportPubKeyBytes(keyHandles1[0], recDIDKeys[0])
   256  	require.NoError(t, err)
   257  
   258  	senderPubKey := &cryptoapi.PublicKey{}
   259  
   260  	err = json.Unmarshal(mSenderPubKey, senderPubKey)
   261  	require.NoError(t, err)
   262  
   263  	senderPubKey.KID = sDIDKey // match packer value.
   264  
   265  	mSenderPubKey, err = json.Marshal(senderPubKey)
   266  	require.NoError(t, err)
   267  
   268  	require.EqualValues(t, &transport.Envelope{
   269  		Message: origMsg,
   270  		FromKey: mSenderPubKey,
   271  		ToKey:   recKey,
   272  	}, msg)
   273  
   274  	// try with only 1 recipient
   275  	ct, err = authPacker.Pack(cty, origMsg, []byte(skid+"."+sDIDKey), [][]byte{recipientsKeys[0]})
   276  	require.NoError(t, err)
   277  
   278  	msg, err = authPacker.Unpack(ct)
   279  	require.NoError(t, err)
   280  
   281  	senderPubKey = &cryptoapi.PublicKey{}
   282  
   283  	err = json.Unmarshal(mSenderPubKey, senderPubKey)
   284  	require.NoError(t, err)
   285  
   286  	senderPubKey.KID = sDIDKey // match packer value.
   287  
   288  	mSenderPubKey, err = json.Marshal(senderPubKey)
   289  	require.NoError(t, err)
   290  
   291  	require.EqualValues(t, &transport.Envelope{
   292  		Message: origMsg,
   293  		FromKey: mSenderPubKey,
   294  		ToKey:   recKey,
   295  	}, msg)
   296  
   297  	jweJSON, err := afgjose.Deserialize(string(ct))
   298  	require.NoError(t, err)
   299  
   300  	verifyJWETypes(t, cty, jweJSON.ProtectedHeaders)
   301  }
   302  
   303  func TestAuthcryptPackerFail(t *testing.T) {
   304  	cty := transport.MediaTypeV1PlaintextPayload
   305  	k := createKMS(t)
   306  
   307  	cryptoSvc, err := tinkcrypto.New()
   308  	require.NoError(t, err)
   309  
   310  	skid, sDIDKey, _, _ := createAndMarshalKey(t, k)
   311  	skidB := []byte(skid + "." + sDIDKey)
   312  
   313  	t.Run("new Pack fail with nil crypto service", func(t *testing.T) {
   314  		_, err = New(newMockProvider(k, nil), afgjose.A128CBCHS256)
   315  		require.EqualError(t, err, "authcrypt: failed to create packer because crypto service is empty")
   316  	})
   317  
   318  	t.Run("new Pack fail with invalid encryption algorithm", func(t *testing.T) {
   319  		_, err = New(newMockProvider(k, cryptoSvc), "invalidAlg")
   320  		require.EqualError(t, err, "authcrypt: unsupported content encrytpion algorithm: invalidAlg")
   321  	})
   322  
   323  	t.Run("new Pack fail with nil kms", func(t *testing.T) {
   324  		_, err = New(newMockProvider(nil, cryptoSvc), afgjose.A128CBCHS256)
   325  		require.EqualError(t, err, "authcrypt: failed to create packer because KMS is empty")
   326  	})
   327  
   328  	_, _, recipientsKeys, _ := createRecipients(t, k, 10) //nolint:dogsled
   329  	origMsg := []byte("secret message")
   330  	authPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A256CBCHS512)
   331  	require.NoError(t, err)
   332  
   333  	t.Run("unpack fail with bad recipient key", func(t *testing.T) {
   334  		_, _, keys, _ := createRecipients(t, k, 1)
   335  		keys[0] = []byte(strings.Replace(string(keys[0]), "did:key:", "invalid", 1))
   336  		var ct []byte
   337  		ct, err = authPacker.Pack(cty, origMsg, []byte(skid+"."+sDIDKey), keys)
   338  		require.NoError(t, err)
   339  		_, err = authPacker.Unpack(ct)
   340  		require.Contains(t, err.Error(), "invalid kid format, must be a did:key")
   341  	})
   342  
   343  	t.Run("pack fail with empty recipients keys", func(t *testing.T) {
   344  		_, err = authPacker.Pack(cty, origMsg, nil, nil)
   345  		require.EqualError(t, err, "authcrypt Pack: empty recipientsPubKeys")
   346  	})
   347  
   348  	t.Run("pack fail with invalid recipients keys", func(t *testing.T) {
   349  		_, err = authPacker.Pack(cty, origMsg, nil, [][]byte{[]byte("invalid")})
   350  		require.EqualError(t, err, "authcrypt Pack: failed to convert recipient keys: invalid character 'i' "+
   351  			"looking for beginning of value")
   352  	})
   353  
   354  	t.Run("pack fail with invalid encAlg", func(t *testing.T) {
   355  		invalidAlg := "invalidAlg"
   356  		invalidAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A256CBCHS512)
   357  		require.NoError(t, err)
   358  
   359  		invalidAuthPacker.encAlg = afgjose.EncAlg(invalidAlg)
   360  		_, err = invalidAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys)
   361  		require.EqualError(t, err, fmt.Sprintf("authcrypt Pack: failed to new JWEEncrypt instance: encryption"+
   362  			" algorithm '%s' not supported", invalidAlg))
   363  	})
   364  
   365  	t.Run("pack fail with KMS can't get kid key", func(t *testing.T) {
   366  		badKMSStoreProvider := mockstorage.NewCustomMockStoreProvider(
   367  			&mockstorage.MockStore{ErrGet: errors.New("bad fake key ID")})
   368  		p, err := mockkms.NewProviderForKMS(badKMSStoreProvider, &noop.NoLock{})
   369  		require.NoError(t, err)
   370  
   371  		badKMS, err := localkms.New("local-lock://test/key/uri", p)
   372  		require.NoError(t, err)
   373  
   374  		badAuthPacker, err := New(newMockProvider(badKMS, cryptoSvc), afgjose.A128CBCHS256)
   375  		require.NoError(t, err)
   376  
   377  		_, err = badAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys)
   378  		require.Contains(t, fmt.Sprintf("%v", err), "bad fake key ID")
   379  	})
   380  
   381  	t.Run("pack success but unpack fails with invalid payload format", func(t *testing.T) {
   382  		validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A192CBCHS384)
   383  		require.NoError(t, err)
   384  
   385  		_, err = validAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys)
   386  		require.NoError(t, err)
   387  
   388  		_, err = validAuthPacker.Unpack([]byte("invalid jwe envelope"))
   389  		require.Error(t, err)
   390  		require.Contains(t, err.Error(), "authcrypt Unpack: failed to deserialize JWE message: invalid compact "+
   391  			"JWE: it must have five parts")
   392  	})
   393  
   394  	t.Run("pack success but unpack fails with invalid payload auth (iv) data", func(t *testing.T) {
   395  		validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A192CBCHS384)
   396  		require.NoError(t, err)
   397  
   398  		var s []byte
   399  
   400  		s, err = validAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys)
   401  		require.NoError(t, err)
   402  
   403  		ivStartIndex := bytes.Index(s, []byte("\"iv\""))
   404  		ivEndIndex := ivStartIndex + 6 + bytes.Index(s[ivStartIndex+6:], []byte("\""))
   405  		sTrail := make([]byte, len(s[ivEndIndex:]))
   406  		copy(sTrail, s[ivEndIndex:])
   407  		s = append(s[:ivStartIndex+6], []byte("K3ORqVx392nLcdJveUl_Jg")...) // invalid base64 iv causes decryption error
   408  		s = append(s, sTrail...)
   409  
   410  		_, err = validAuthPacker.Unpack(s)
   411  		require.Error(t, err)
   412  		require.Contains(t, err.Error(), "authcrypt Unpack: failed to decrypt JWE envelope: ecdh_factory: "+
   413  			"decryption failed")
   414  	})
   415  
   416  	t.Run("pack success but unpack fails with missing keyID in protectedHeader", func(t *testing.T) {
   417  		validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A192CBCHS384)
   418  		require.NoError(t, err)
   419  
   420  		ct, err := validAuthPacker.Pack(cty, origMsg, skidB, [][]byte{recipientsKeys[0]})
   421  		require.NoError(t, err)
   422  
   423  		jwe, err := afgjose.Deserialize(string(ct))
   424  		require.NoError(t, err)
   425  
   426  		delete(jwe.ProtectedHeaders, afgjose.HeaderKeyID)
   427  
   428  		newCT, err := jwe.CompactSerialize(json.Marshal)
   429  		require.NoError(t, err)
   430  
   431  		_, err = validAuthPacker.Unpack([]byte(newCT))
   432  		require.EqualError(t, err, "authcrypt Unpack: single recipient missing 'KID' in jwe.ProtectHeaders")
   433  	})
   434  
   435  	t.Run("pack success but unpack fails with missing kid in kms", func(t *testing.T) {
   436  		kids, _, newRecKeys, _ := createRecipients(t, k, 2)
   437  		validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A128CBCHS256)
   438  		require.NoError(t, err)
   439  
   440  		ct, err := validAuthPacker.Pack(cty, origMsg, skidB, newRecKeys)
   441  		require.NoError(t, err)
   442  
   443  		// rotate keys to update keyID and force a failure
   444  		_, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[0])
   445  		require.NoError(t, err)
   446  
   447  		_, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[1])
   448  		require.NoError(t, err)
   449  
   450  		_, err = validAuthPacker.Unpack(ct)
   451  		require.EqualError(t, err, "authcrypt Unpack: no matching recipient in envelope")
   452  	})
   453  
   454  	t.Run("pack success but unpack fails with missing kms in packer", func(t *testing.T) {
   455  		kids, _, newRecKeys, _ := createRecipients(t, k, 2)
   456  		validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A128CBCHS256)
   457  		require.NoError(t, err)
   458  
   459  		ct, err := validAuthPacker.Pack(cty, origMsg, skidB, newRecKeys)
   460  		require.NoError(t, err)
   461  
   462  		// rotate keys to update keyID and force a failure
   463  		_, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[0])
   464  		require.NoError(t, err)
   465  
   466  		_, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[1])
   467  		require.NoError(t, err)
   468  
   469  		// mock kms get error
   470  		validAuthPacker.kms = &mockkms.KeyManager{GetKeyErr: errors.New("get error")}
   471  
   472  		_, err = validAuthPacker.Unpack(ct)
   473  		require.EqualError(t, err, "authcrypt Unpack: failed to get key from kms: get error")
   474  	})
   475  }
   476  
   477  func exportPubKeyBytes(keyHandle *keyset.Handle, kid string) ([]byte, error) {
   478  	pubKH, err := keyHandle.Public()
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  
   483  	buf := new(bytes.Buffer)
   484  	pubKeyWriter := keyio.NewWriter(buf)
   485  
   486  	err = pubKH.WriteWithNoSecrets(pubKeyWriter)
   487  	if err != nil {
   488  		return nil, err
   489  	}
   490  
   491  	pubKey := &cryptoapi.PublicKey{}
   492  
   493  	err = json.Unmarshal(buf.Bytes(), pubKey)
   494  	if err != nil {
   495  		return nil, err
   496  	}
   497  
   498  	pubKey.KID = kid
   499  
   500  	return json.Marshal(pubKey)
   501  }
   502  
   503  // createRecipients and return their public key, jwk kid, didKey and keyset.Handle.
   504  func createRecipients(t *testing.T, k *localkms.LocalKMS,
   505  	recipientsCount int) ([]string, []string, [][]byte, []*keyset.Handle) {
   506  	return createRecipientsByKeyType(t, k, recipientsCount, kms.NISTP256ECDHKW)
   507  }
   508  
   509  func createRecipientsByKeyType(t *testing.T, k *localkms.LocalKMS, recipientsCount int,
   510  	kt kms.KeyType) ([]string, []string, [][]byte, []*keyset.Handle) {
   511  	t.Helper()
   512  
   513  	var (
   514  		r       [][]byte
   515  		rKH     []*keyset.Handle
   516  		kids    []string
   517  		didKeys []string
   518  	)
   519  
   520  	for i := 0; i < recipientsCount; i++ {
   521  		kid, didKey, marshalledPubKey, kh := createAndMarshalKeyByKeyType(t, k, kt)
   522  
   523  		r = append(r, marshalledPubKey)
   524  		rKH = append(rKH, kh)
   525  		kids = append(kids, kid)
   526  		didKeys = append(didKeys, didKey)
   527  	}
   528  
   529  	return kids, didKeys, r, rKH
   530  }
   531  
   532  // createAndMarshalKey creates a new recipient keyset.Handle, extracts public key, marshals it and returns
   533  // both marshalled public key and original recipient keyset.Handle.
   534  func createAndMarshalKey(t *testing.T, k *localkms.LocalKMS) (string, string, []byte, *keyset.Handle) {
   535  	return createAndMarshalKeyByKeyType(t, k, kms.NISTP256ECDHKWType)
   536  }
   537  
   538  func createAndMarshalKeyByKeyType(t *testing.T, k *localkms.LocalKMS,
   539  	kt kms.KeyType) (string, string, []byte, *keyset.Handle) {
   540  	t.Helper()
   541  
   542  	kid, keyHandle, err := k.Create(kt)
   543  	require.NoError(t, err)
   544  
   545  	kh, ok := keyHandle.(*keyset.Handle)
   546  	require.True(t, ok)
   547  
   548  	pubKeyBytes, err := exportPubKeyBytes(kh, kid)
   549  	require.NoError(t, err)
   550  
   551  	key := &cryptoapi.PublicKey{}
   552  	err = json.Unmarshal(pubKeyBytes, key)
   553  	require.NoError(t, err)
   554  
   555  	// used with marshalled *crypto.PublicKey for encryption keys (it parses 'pubKeyBytes').
   556  	didKey, err := kmsdidkey.BuildDIDKeyByKeyType(pubKeyBytes, kt)
   557  	require.NoError(t, err)
   558  
   559  	key.KID = didKey
   560  	mKey, err := json.Marshal(key)
   561  	require.NoError(t, err)
   562  
   563  	printKey(t, mKey, kh, kid, didKey)
   564  
   565  	return kid, didKey, mKey, kh
   566  }
   567  
   568  func printKey(t *testing.T, mPubKey []byte, kh *keyset.Handle, kid, didKey string) {
   569  	t.Helper()
   570  
   571  	extractKey, err := extractPrivKey(kh)
   572  	require.NoError(t, err)
   573  
   574  	switch keyType := extractKey.(type) {
   575  	case *hybrid.ECPrivateKey:
   576  		t.Logf("** EC key: %s, \n\t kms kid: %s, \n\t jwe kid (did:key):%s", getPrintedECPrivKey(t, keyType), kid,
   577  			didKey)
   578  	case []byte:
   579  		pubKey := new(cryptoapi.PublicKey)
   580  		err := json.Unmarshal(mPubKey, pubKey)
   581  		require.NoError(t, err)
   582  
   583  		fullKey := append(keyType, pubKey.X...)
   584  		t.Logf("** X25519 key: %s, \n\t kms kid: %s, \n\t jwe kid (did:key):%s", getPrintedX25519PrivKey(t, fullKey), kid,
   585  			didKey)
   586  	default:
   587  		t.Errorf("not supported key type: %s", keyType)
   588  	}
   589  }
   590  
   591  func prettyPrint(msg []byte) (string, error) {
   592  	var prettyJSON bytes.Buffer
   593  
   594  	err := json.Indent(&prettyJSON, msg, "", "\t")
   595  	if err != nil {
   596  		return "", err
   597  	}
   598  
   599  	return prettyJSON.String(), nil
   600  }
   601  
   602  func getPrintedECPrivKey(t *testing.T, privKeyType *hybrid.ECPrivateKey) string {
   603  	jwk := jose.JSONWebKey{
   604  		Key: &ecdsa.PrivateKey{
   605  			PublicKey: ecdsa.PublicKey{
   606  				Curve: privKeyType.PublicKey.Curve,
   607  				X:     privKeyType.PublicKey.Point.X,
   608  				Y:     privKeyType.PublicKey.Point.Y,
   609  			},
   610  			D: privKeyType.D,
   611  		},
   612  	}
   613  
   614  	jwkByte, err := jwk.MarshalJSON()
   615  	require.NoError(t, err)
   616  
   617  	jwkStr, err := prettyPrint(jwkByte)
   618  	require.NoError(t, err)
   619  
   620  	return jwkStr
   621  }
   622  
   623  func getPrintedX25519PrivKey(t *testing.T, privKeyType ed25519.PrivateKey) string {
   624  	jwk := jose.JSONWebKey{
   625  		Key: privKeyType,
   626  	}
   627  
   628  	jwkByte, err := jwk.MarshalJSON()
   629  	require.NoError(t, err)
   630  
   631  	jwkStr, err := prettyPrint(jwkByte)
   632  	require.NoError(t, err)
   633  
   634  	return strings.Replace(jwkStr, "Ed25519", "X25519", 1)
   635  }
   636  
   637  func extractPrivKey(kh *keyset.Handle) (interface{}, error) {
   638  	nistPECDHKWPrivateKeyTypeURL := "type.hyperledger.org/hyperledger.aries.crypto.tink.NistPEcdhKwPrivateKey"
   639  	x25519ECDHKWPrivateKeyTypeURL := "type.hyperledger.org/hyperledger.aries.crypto.tink.X25519EcdhKwPrivateKey"
   640  	buf := new(bytes.Buffer)
   641  	w := &privKeyWriter{w: buf}
   642  	nAEAD := &noopAEAD{}
   643  
   644  	if kh == nil {
   645  		return nil, fmt.Errorf("extractPrivKey: kh is nil")
   646  	}
   647  
   648  	err := kh.Write(w, nAEAD)
   649  	if err != nil {
   650  		return nil, fmt.Errorf("extractPrivKey: retrieving private key failed: %w", err)
   651  	}
   652  
   653  	ks := new(tinkpb.Keyset)
   654  
   655  	err = proto.Unmarshal(buf.Bytes(), ks)
   656  	if err != nil {
   657  		return nil, errors.New("extractPrivKey: invalid private key")
   658  	}
   659  
   660  	primaryKey := ks.Key[0]
   661  
   662  	switch primaryKey.KeyData.TypeUrl {
   663  	case nistPECDHKWPrivateKeyTypeURL:
   664  		pbKey := new(ecdhpb.EcdhAeadPrivateKey)
   665  
   666  		err = proto.Unmarshal(primaryKey.KeyData.Value, pbKey)
   667  		if err != nil {
   668  			return nil, errors.New("extractPrivKey: invalid key in keyset")
   669  		}
   670  
   671  		var c elliptic.Curve
   672  
   673  		c, err = hybrid.GetCurve(pbKey.PublicKey.Params.KwParams.CurveType.String())
   674  		if err != nil {
   675  			return nil, fmt.Errorf("extractPrivKey: invalid key: %w", err)
   676  		}
   677  
   678  		return hybrid.GetECPrivateKey(c, pbKey.KeyValue), nil
   679  	case x25519ECDHKWPrivateKeyTypeURL:
   680  		pbKey := new(ecdhpb.EcdhAeadPrivateKey)
   681  
   682  		err = proto.Unmarshal(primaryKey.KeyData.Value, pbKey)
   683  		if err != nil {
   684  			return nil, errors.New("extractPrivKey: invalid key in keyset")
   685  		}
   686  
   687  		if pbKey.PublicKey.Params.KwParams.CurveType.String() != commonpb.EllipticCurveType_CURVE25519.String() {
   688  			return nil, errors.New("extractPrivKey: invalid key curve")
   689  		}
   690  
   691  		return pbKey.KeyValue, nil
   692  	}
   693  
   694  	return nil, fmt.Errorf("extractPrivKey: can't extract unsupported private key '%s'", primaryKey.KeyData.TypeUrl)
   695  }
   696  
   697  type noopAEAD struct{}
   698  
   699  func (n noopAEAD) Encrypt(plaintext, additionalData []byte) ([]byte, error) {
   700  	return plaintext, nil
   701  }
   702  
   703  func (n noopAEAD) Decrypt(ciphertext, additionalData []byte) ([]byte, error) {
   704  	return ciphertext, nil
   705  }
   706  
   707  type privKeyWriter struct {
   708  	w io.Writer
   709  }
   710  
   711  // Write writes the public keyset to the underlying w.Writer. It's not used in this implementation.
   712  func (p *privKeyWriter) Write(_ *tinkpb.Keyset) error {
   713  	return fmt.Errorf("privKeyWriter: write function not supported")
   714  }
   715  
   716  // WriteEncrypted writes the encrypted keyset to the underlying w.Writer.
   717  func (p *privKeyWriter) WriteEncrypted(ks *tinkpb.EncryptedKeyset) error {
   718  	return write(p.w, ks)
   719  }
   720  
   721  func write(w io.Writer, ks *tinkpb.EncryptedKeyset) error {
   722  	// we write EncryptedKeyset directly without decryption since noopAEAD was used to write *keyset.Handle
   723  	_, e := w.Write(ks.EncryptedKeyset)
   724  	return e
   725  }
   726  
   727  func createKMS(t *testing.T) *localkms.LocalKMS {
   728  	t.Helper()
   729  
   730  	p, err := mockkms.NewProviderForKMS(mockstorage.NewMockStoreProvider(), &noop.NoLock{})
   731  	require.NoError(t, err)
   732  
   733  	k, err := localkms.New("local-lock://test/key/uri", p)
   734  	require.NoError(t, err)
   735  
   736  	return k
   737  }
   738  
   739  func newMockProvider(customKMS kms.KeyManager,
   740  	customCrypto cryptoapi.Crypto) *mockprovider.Provider {
   741  	return &mockprovider.Provider{
   742  		KMSValue:        customKMS,
   743  		CryptoValue:     customCrypto,
   744  		VDRegistryValue: &mockvdr.MockRegistry{},
   745  	}
   746  }