github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/wrap_support_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package tinkcrypto
     8  
     9  import (
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	_ "embed"
    13  	"encoding/base64"
    14  	"encoding/hex"
    15  	"encoding/json"
    16  	"testing"
    17  
    18  	"github.com/stretchr/testify/require"
    19  	"golang.org/x/crypto/chacha20poly1305"
    20  	"golang.org/x/crypto/curve25519"
    21  
    22  	"github.com/trustbloc/kms-go/util/cryptoutil"
    23  )
    24  
    25  func Test_ecKWSupportFailures(t *testing.T) {
    26  	ecKW := &ecKWSupport{}
    27  
    28  	_, err := ecKW.wrap("badCipherBlockType", []byte(""))
    29  	require.EqualError(t, err, "wrap support: EC wrap with invalid cipher block type")
    30  
    31  	_, err = ecKW.unwrap("badCipherBlockType", []byte(""))
    32  	require.EqualError(t, err, "unwrap support: EC wrap with invalid cipher block type")
    33  
    34  	_, err = ecKW.deriveSender1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0)
    35  	require.EqualError(t, err, "deriveSender1Pu: ephemeral key not ECDSA type")
    36  
    37  	_, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{}, "badSenderPrivKeyType", nil, 0)
    38  	require.EqualError(t, err, "deriveSender1Pu: sender key not ECDSA type")
    39  
    40  	_, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{}, &ecdsa.PrivateKey{}, "badSenderPrivKeyType", 0)
    41  	require.EqualError(t, err, "deriveSender1Pu: recipient key not ECDSA type")
    42  
    43  	_, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{
    44  		PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()},
    45  	}, &ecdsa.PrivateKey{
    46  		PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()},
    47  	}, &ecdsa.PublicKey{Curve: elliptic.P521()}, 0)
    48  	require.EqualError(t, err, "deriveSender1Pu: recipient, sender and ephemeral key are not on the same curve")
    49  
    50  	_, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{
    51  		PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()},
    52  	}, &ecdsa.PrivateKey{
    53  		PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()},
    54  	}, &ecdsa.PublicKey{Curve: elliptic.P521()}, 0)
    55  	require.EqualError(t, err, "deriveSender1Pu: recipient, sender and ephemeral key are not on the same curve")
    56  
    57  	_, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0)
    58  	require.EqualError(t, err, "deriveRecipient1Pu: ephemeral key not ECDSA type")
    59  
    60  	_, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{}, "badSenderPrivKeyType", nil, 0)
    61  	require.EqualError(t, err, "deriveRecipient1Pu: sender key not ECDSA type")
    62  
    63  	_, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{}, &ecdsa.PublicKey{}, "badSenderPrivKeyType", 0)
    64  	require.EqualError(t, err, "deriveRecipient1Pu: recipient key not ECDSA type")
    65  
    66  	_, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{Curve: elliptic.P521()},
    67  		&ecdsa.PublicKey{Curve: elliptic.P521()}, &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()}}, 0)
    68  	require.EqualError(t, err, "deriveRecipient1Pu: recipient, sender and ephemeral key are not on the same curve")
    69  
    70  	_, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{Curve: elliptic.P521()},
    71  		&ecdsa.PublicKey{Curve: elliptic.P256()}, &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()}}, 0)
    72  	require.EqualError(t, err, "deriveRecipient1Pu: recipient, sender and ephemeral key are not on the same curve")
    73  }
    74  
    75  func Test_okpKWSupportFailures(t *testing.T) {
    76  	okpKW := &okpKWSupport{}
    77  
    78  	_, err := okpKW.getCurve("")
    79  	require.EqualError(t, err, "getCurve: not implemented for OKP KW support")
    80  
    81  	_, err = okpKW.createPrimitive([]byte("kekWithBadSize"))
    82  	require.EqualError(t, err, "createPrimitive: failed to create OKP primitive: chacha20poly1305: bad key length")
    83  
    84  	_, err = okpKW.wrap("badCipherBlockType", []byte(""))
    85  	require.EqualError(t, err, "wrap support: OKP wrap with invalid primitive type")
    86  
    87  	_, err = okpKW.unwrap("badCipherBlockType", []byte(""))
    88  	require.EqualError(t, err, "unwrap support: OKP unwrap with invalid primitive type")
    89  
    90  	kek, err := okpKW.generateKey(nil)
    91  	require.NoError(t, err)
    92  
    93  	kekBytes, ok := kek.([]byte)
    94  	require.True(t, ok)
    95  
    96  	XC20PPrimitive, err := okpKW.createPrimitive(kekBytes)
    97  	require.NoError(t, err)
    98  
    99  	_, err = okpKW.unwrap(XC20PPrimitive, []byte(""))
   100  	require.EqualError(t, err, "unwrap support: OKP unwrap invalid key")
   101  
   102  	_, err = okpKW.unwrap(XC20PPrimitive, []byte("badEncryptedKeyLargerThankNonceSize"))
   103  	require.EqualError(t, err, "unwrap support: OKP failed to unwrap key: chacha20poly1305: message authentication failed")
   104  
   105  	_, err = okpKW.deriveSender1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0)
   106  	require.EqualError(t, err, "deriveSender1Pu: ephemeral key not OKP type")
   107  
   108  	_, err = okpKW.deriveSender1Pu("", nil, nil, nil, []byte{}, "badSenderPrivKeyType", nil, 0)
   109  	require.EqualError(t, err, "deriveSender1Pu: sender key not OKP type")
   110  
   111  	_, err = okpKW.deriveSender1Pu("", nil, nil, nil, []byte{}, []byte{}, "badSenderPrivKeyType", 0)
   112  	require.EqualError(t, err, "deriveSender1Pu: recipient key not OKP type")
   113  
   114  	_, err = okpKW.deriveSender1Pu("", nil, nil, nil, []byte{}, []byte{}, []byte{}, 0)
   115  	require.EqualError(t, err,
   116  		"deriveSender1Pu: deriveECDHX25519: crypto/ecdh: bad X25519 remote ECDH input: low order point")
   117  
   118  	derivedKEK, err := curve25519.X25519(kekBytes, curve25519.Basepoint)
   119  	require.NoError(t, err)
   120  
   121  	// lowOrderPoint from golang.org/x/crypto/curve25519. Causes ED25519 key derivation to fail.
   122  	// https://github.com/golang/crypto/blob/f4817d981/curve25519/vectors_test.go#L10
   123  	lowOrderPoint := []byte{
   124  		0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   125  		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
   126  	}
   127  
   128  	_, err = okpKW.deriveSender1Pu("", nil, nil, nil, derivedKEK, kekBytes, lowOrderPoint, 0)
   129  	require.EqualError(t, err,
   130  		"deriveSender1Pu: deriveECDHX25519: crypto/ecdh: bad X25519 remote ECDH input: low order point")
   131  	// can't reproduce key derivation error with sender key because recipient public key as lowOrderPoint fails for
   132  	// ephemeral key derivation. ie sender key derivation failure only fails if ephemeral key derivation fails.
   133  
   134  	_, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0)
   135  	require.EqualError(t, err, "deriveRecipient1Pu: ephemeral key not OKP type")
   136  
   137  	_, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, []byte{}, "badSenderPrivKeyType", nil, 0)
   138  	require.EqualError(t, err, "deriveRecipient1Pu: sender key not OKP type")
   139  
   140  	_, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, []byte{}, []byte{}, "badSenderPrivKeyType", 0)
   141  	require.EqualError(t, err, "deriveRecipient1Pu: recipient key not OKP type")
   142  
   143  	_, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, []byte{}, []byte{}, []byte{}, 0)
   144  	require.EqualError(t, err,
   145  		"deriveRecipient1Pu: deriveECDHX25519: crypto/ecdh: bad X25519 remote ECDH input: low order point")
   146  }
   147  
   148  type mockKey struct {
   149  	Kty string `json:"kty,omitempty"`
   150  	Crv string `json:"crv,omitempty"`
   151  	X   string `json:"x,omitempty"`
   152  	Y   string `json:"y,omitempty"`
   153  	D   string `json:"d,omitempty"`
   154  }
   155  
   156  type mockProtectedHeader struct {
   157  	Alg string  `json:"alg,omitempty"`
   158  	Enc string  `json:"enc,omitempty"`
   159  	Apu string  `json:"apu,omitempty"`
   160  	Apv string  `json:"apv,omitempty"`
   161  	Epk mockKey `json:"epk,omitempty"`
   162  }
   163  
   164  type ref1PU struct {
   165  	ZeHex           string `json:"zeHex,omitempty"`
   166  	ZsHex           string `json:"zsHex,omitempty"`
   167  	ZHex            string `json:"zHex,omitempty"`
   168  	Sender1PUKDFHex string `json:"sender1puKdfHex,omitempty"`
   169  	Sender1PUKWB64  string `json:"sender1puKwB64,omitempty"`
   170  }
   171  
   172  func refJWKtoOKPKey(t *testing.T, jwkM string) (*[chacha20poly1305.KeySize]byte, *[chacha20poly1305.KeySize]byte) {
   173  	t.Helper()
   174  
   175  	jwk := &mockKey{}
   176  	err := json.Unmarshal([]byte(jwkM), jwk)
   177  	require.NoError(t, err)
   178  
   179  	x, err := base64.RawURLEncoding.DecodeString(jwk.X)
   180  	require.NoError(t, err)
   181  
   182  	d, err := base64.RawURLEncoding.DecodeString(jwk.D)
   183  	require.NoError(t, err)
   184  
   185  	x32 := new([chacha20poly1305.KeySize]byte)
   186  	copy(x32[:], x)
   187  
   188  	d32 := new([chacha20poly1305.KeySize]byte)
   189  	copy(d32[:], d)
   190  
   191  	return x32, d32
   192  }
   193  
   194  // nolint:gochecknoglobals // embedded test data
   195  var (
   196  	// test vector retrieved from:
   197  	//nolint:lll
   198  	// (github: https://github.com/NeilMadden/jose-ecdh-1pu/blob/master/draft-madden-jose-ecdh-1pu-04/draft-madden-jose-ecdh-1pu-04.txt#L740)
   199  	// (ietf draft: https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04#appendix-B)
   200  	//go:embed testdata/alice_key_ref.json
   201  	aliceKeyRef string
   202  	//go:embed testdata/bob_key_ref.json
   203  	bobKeyRef string
   204  	//go:embed testdata/charlie_key_ref.json
   205  	charlieKeyRef string
   206  	//go:embed testdata/alice_epk_ref.json
   207  	aliceEPKRef string
   208  	//go:embed testdata/protected_headers_ref.json
   209  	protectedHeadersRef string
   210  	//go:embed testdata/ecdh_1pu_bob.json
   211  	ecdh1puBobRef string
   212  	//go:embed testdata/ecdh_1pu_charlie.json
   213  	ecdh1puCharlieRef string
   214  )
   215  
   216  // TestDeriveReferenceKey uses the test vector in the 1PU draft found at:
   217  // (github: https://github.com/NeilMadden/jose-ecdh-1pu/blob/master/draft-madden-jose-ecdh-1pu-03.txt#L459)
   218  // (ietf draft: https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03#appendix-A)
   219  // to validate the ECDH-1PU key derivation.
   220  func TestDeriveReferenceKey(t *testing.T) {
   221  	tag, err := base64.RawURLEncoding.DecodeString("HLb4fTlm8spGmij3RyOs2gJ4DpHM4hhVRwdF_hGb3WQ")
   222  	require.NoError(t, err)
   223  
   224  	cek, err := hex.DecodeString("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8" +
   225  		"d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0")
   226  	require.NoError(t, err)
   227  
   228  	ref1PUBobData := &ref1PU{}
   229  	err = json.Unmarshal([]byte(ecdh1puBobRef), ref1PUBobData)
   230  	require.NoError(t, err)
   231  
   232  	ref1PUCharlieData := &ref1PU{}
   233  	err = json.Unmarshal([]byte(ecdh1puCharlieRef), ref1PUCharlieData)
   234  	require.NoError(t, err)
   235  
   236  	_, alicePrivKeyRefOKP := refJWKtoOKPKey(t, aliceKeyRef)
   237  	bobPubKeyRefOKP, _ := refJWKtoOKPKey(t, bobKeyRef)
   238  	charliePubKeyRefOKP, _ := refJWKtoOKPKey(t, charlieKeyRef)
   239  	_, alicePrivKeyEPKRefOKP := refJWKtoOKPKey(t, aliceEPKRef)
   240  
   241  	protectedHeaderRefJWK := &mockProtectedHeader{}
   242  	err = json.Unmarshal([]byte(protectedHeadersRef), protectedHeaderRefJWK)
   243  	require.NoError(t, err)
   244  
   245  	apuRef, err := base64.RawURLEncoding.DecodeString(protectedHeaderRefJWK.Apu) // "Alice"
   246  	require.NoError(t, err)
   247  
   248  	apvRef, err := base64.RawURLEncoding.DecodeString(protectedHeaderRefJWK.Apv) // "Bob and Charlie"
   249  	require.NoError(t, err)
   250  
   251  	zeBobRef, err := hex.DecodeString(ref1PUBobData.ZeHex)
   252  	require.NoError(t, err)
   253  
   254  	zeCharlieRef, err := hex.DecodeString(ref1PUCharlieData.ZeHex)
   255  	require.NoError(t, err)
   256  
   257  	t.Run("test derive Ze for Bob", func(t *testing.T) {
   258  		ze, e := cryptoutil.DeriveECDHX25519(alicePrivKeyEPKRefOKP, bobPubKeyRefOKP)
   259  		require.NoError(t, e)
   260  		require.EqualValues(t, zeBobRef, ze)
   261  
   262  		zeHEX := hex.EncodeToString(ze)
   263  		require.EqualValues(t, ref1PUBobData.ZeHex, zeHEX)
   264  	})
   265  
   266  	t.Run("test derive Ze for Charlie", func(t *testing.T) {
   267  		ze, e := cryptoutil.DeriveECDHX25519(alicePrivKeyEPKRefOKP, charliePubKeyRefOKP)
   268  		require.NoError(t, e)
   269  
   270  		zeHEX := hex.EncodeToString(ze)
   271  		require.EqualValues(t, ref1PUCharlieData.ZeHex, zeHEX)
   272  		require.EqualValues(t, zeCharlieRef, ze)
   273  	})
   274  
   275  	zsBobRef, err := hex.DecodeString(ref1PUBobData.ZsHex)
   276  	require.NoError(t, err)
   277  
   278  	t.Run("test derive Zs for Bob", func(t *testing.T) {
   279  		zs, e := cryptoutil.DeriveECDHX25519(alicePrivKeyRefOKP, bobPubKeyRefOKP)
   280  		require.NoError(t, e)
   281  
   282  		zsHEX := hex.EncodeToString(zs)
   283  		require.EqualValues(t, ref1PUBobData.ZsHex, zsHEX)
   284  		require.EqualValues(t, zsBobRef, zs)
   285  	})
   286  
   287  	zsCharlieRef, err := hex.DecodeString(ref1PUCharlieData.ZsHex)
   288  	require.NoError(t, err)
   289  
   290  	t.Run("test derive Zs for Charlie", func(t *testing.T) {
   291  		zs, e := cryptoutil.DeriveECDHX25519(alicePrivKeyRefOKP, charliePubKeyRefOKP)
   292  		require.NoError(t, e)
   293  
   294  		zsHEX := hex.EncodeToString(zs)
   295  		require.EqualValues(t, ref1PUCharlieData.ZsHex, zsHEX)
   296  		require.EqualValues(t, zsCharlieRef, zs)
   297  	})
   298  
   299  	zBob, err := hex.DecodeString(ref1PUBobData.ZHex)
   300  	require.NoError(t, err)
   301  	require.EqualValues(t, append(zeBobRef, zsBobRef...), zBob)
   302  
   303  	zCharlie, err := hex.DecodeString(ref1PUCharlieData.ZHex)
   304  	require.NoError(t, err)
   305  	require.EqualValues(t, append(zeCharlieRef, zsCharlieRef...), zCharlie)
   306  
   307  	onePUKDFBobFromHex, err := hex.DecodeString(ref1PUBobData.Sender1PUKDFHex)
   308  	require.NoError(t, err)
   309  
   310  	onePUKDFCharlieFromHex, err := hex.DecodeString(ref1PUCharlieData.Sender1PUKDFHex)
   311  	require.NoError(t, err)
   312  
   313  	okpWrapper := okpKWSupport{}
   314  
   315  	t.Run("test KDF for Bob", func(t *testing.T) {
   316  		sender1PUWithBobKDF, e := okpWrapper.deriveSender1Pu(protectedHeaderRefJWK.Alg, apuRef, apvRef, tag,
   317  			alicePrivKeyEPKRefOKP[:], alicePrivKeyRefOKP[:], bobPubKeyRefOKP[:], 32)
   318  		require.NoError(t, e)
   319  		require.EqualValues(t, onePUKDFBobFromHex, sender1PUWithBobKDF)
   320  	})
   321  
   322  	t.Run("test KDF for Charlie", func(t *testing.T) {
   323  		sender1PUWithCharlieKDF, e := okpWrapper.deriveSender1Pu(protectedHeaderRefJWK.Alg, apuRef, apvRef, tag,
   324  			alicePrivKeyEPKRefOKP[:], alicePrivKeyRefOKP[:], charliePubKeyRefOKP[:], 32)
   325  		require.NoError(t, e)
   326  		require.EqualValues(t, onePUKDFCharlieFromHex, sender1PUWithCharlieKDF)
   327  	})
   328  
   329  	// Appendix B example uses "A128KW" key wrapping.
   330  	ecKW := &ecKWSupport{}
   331  
   332  	t.Run("test key wrap for Bob", func(t *testing.T) {
   333  		bobAESBlock, err := ecKW.createPrimitive(onePUKDFBobFromHex)
   334  		require.NoError(t, err)
   335  
   336  		onePUKWBobFromB64, err := base64.RawURLEncoding.DecodeString(ref1PUBobData.Sender1PUKWB64)
   337  		require.NoError(t, err)
   338  
   339  		bobEncryptedKey, err := ecKW.wrap(bobAESBlock, cek)
   340  		require.NoError(t, err)
   341  		require.EqualValues(t, onePUKWBobFromB64, bobEncryptedKey)
   342  
   343  		bobDecryptedCEK, err := ecKW.unwrap(bobAESBlock, onePUKWBobFromB64)
   344  		require.NoError(t, err)
   345  		require.EqualValues(t, cek, bobDecryptedCEK)
   346  	})
   347  
   348  	t.Run("test key wrap for Charlie", func(t *testing.T) {
   349  		charlieAESBlock, err := ecKW.createPrimitive(onePUKDFCharlieFromHex)
   350  		require.NoError(t, err)
   351  
   352  		onePUKWCharlieFromB64, err := base64.RawURLEncoding.DecodeString(ref1PUCharlieData.Sender1PUKWB64)
   353  		require.NoError(t, err)
   354  
   355  		charlieEncryptedKey, err := ecKW.wrap(charlieAESBlock, cek)
   356  		require.NoError(t, err)
   357  		require.EqualValues(t, onePUKWCharlieFromB64, charlieEncryptedKey)
   358  
   359  		charlieDecryptedCEK, err := ecKW.unwrap(charlieAESBlock, onePUKWCharlieFromB64)
   360  		require.NoError(t, err)
   361  		require.EqualValues(t, cek, charlieDecryptedCEK)
   362  	})
   363  }