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

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package jwk
     8  
     9  import (
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	"crypto/rand"
    13  	"crypto/sha256"
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/btcsuite/btcd/btcec/v2"
    18  	"github.com/go-jose/go-jose/v3"
    19  	"github.com/go-jose/go-jose/v3/json"
    20  	"github.com/stretchr/testify/require"
    21  	"github.com/trustbloc/bbs-signature-go/bbs12381g2pub"
    22  
    23  	"github.com/trustbloc/kms-go/spi/kms"
    24  )
    25  
    26  func TestDecodePublicKey(t *testing.T) {
    27  	t.Run("Test decode public key failure", func(t *testing.T) {
    28  		tests := []struct {
    29  			name    string
    30  			jwkJSON string
    31  			err     string
    32  		}{
    33  			{
    34  				name:    "attempt public key bytes from invalid JSON bytes",
    35  				jwkJSON: `}`,
    36  				err:     "invalid character",
    37  			},
    38  			{
    39  				name: "attempt public key bytes from invalid curve",
    40  				jwkJSON: `{
    41  							"kty": "EC",
    42      						"use": "enc",
    43      						"crv": "sec12341",
    44      						"kid": "sample@sample.id",
    45      						"x": "wQehEGTVCu32yp8IwTaBCqPUIYslyd-WoFRsfDKE9II",
    46      						"y": "rIJO8RmkExUecJ5i15L9OC7rl7pwmYFR8QQgdM1ERWI",
    47     						 	"alg": "ES256"
    48  						}`,
    49  				err: "unsupported elliptic curve 'sec12341'",
    50  			},
    51  			{
    52  				name: "attempt public key bytes from invalid JSON bytes",
    53  				jwkJSON: `{
    54  							"kty": "EC",
    55      						"use": "enc",
    56      						"crv": "secp256k1",
    57      						"kid": "sample@sample.id",
    58      						"x": "",
    59      						"y": "",
    60     						 	"alg": "ES256"
    61  						}`,
    62  				err: "unable to read JWK: invalid JWK",
    63  			},
    64  			{
    65  				name: "attempt public key bytes from invalid JSON bytes",
    66  				jwkJSON: `{
    67  							"kty": "EC",
    68      						"use": "enc",
    69      						"crv": "secp256k1",
    70      						"kid": "sample@sample.id",
    71      						"x": "wQehEGTVCu32yp8IwTaBCqPUIYslyd-WoFRsfDKE9II",
    72      						"y": "",
    73     						 	"alg": "ES256"
    74  						}`,
    75  				err: "unable to read JWK: invalid JWK",
    76  			},
    77  			{
    78  				name: "attempt public key bytes from invalid JSON bytes",
    79  				jwkJSON: `{
    80  							"kty": "EC",
    81      						"use": "enc",
    82      						"crv": "secp256k1",
    83      						"kid": "sample@sample.id",
    84      						"x": "x",
    85      						"y": "y",
    86     						 	"alg": "ES256"
    87  						}`,
    88  				err: "unable to read JWK",
    89  			},
    90  			{
    91  				name: "X is not defined",
    92  				jwkJSON: `{
    93  							"kty": "EC",
    94      						"use": "enc",
    95      						"crv": "secp256k1",
    96      						"kid": "sample@sample.id",
    97      						"y": "rIJO8RmkExUecJ5i15L9OC7rl7pwmYFR8QQgdM1ERWI",
    98     						 	"alg": "ES256"
    99  						}`,
   100  				err: "invalid JWK",
   101  			},
   102  			{
   103  				name: "X is not defined X25519",
   104  				jwkJSON: `{
   105      						"kty": "OKP",
   106      						"use": "enc",
   107      						"crv": "X25519",
   108      						"kid": "sample@sample.id"
   109  						}`,
   110  				err: "invalid JWK",
   111  			},
   112  			{
   113  				name: "Y is not defined",
   114  				jwkJSON: `{
   115  							"kty": "EC",
   116      						"use": "enc",
   117      						"crv": "secp256k1",
   118      						"kid": "sample@sample.id",
   119      						"x": "wQehEGTVCu32yp8IwTaBCqPUIYslyd-WoFRsfDKE9II",
   120     						 	"alg": "ES256"
   121  						}`,
   122  				err: "invalid JWK",
   123  			},
   124  			{
   125  				name: "D is not defined",
   126  				jwkJSON: `{
   127  							"kty": "EC",
   128      						"use": "enc",
   129      						"crv": "secp256k1",
   130      						"kid": "sample@sample.id",
   131      						"x": "wQehEGTVCu32yp8IwTaBCqPUIYslyd-WoFRsfDKE9II",
   132      						"y": "rIJO8RmkExUecJ5i15L9OC7rl7pwmYFR8QQgdM1ERWI",
   133  							"d": "",
   134  							"alg": "ES256"
   135  						}`,
   136  				err: "invalid JWK",
   137  			},
   138  			{
   139  				name: "Y is not defined",
   140  				jwkJSON: `{
   141  							"kty": "EC",
   142      						"use": "enc",
   143      						"crv": "secp256k1",
   144      						"kid": "sample@sample.id",
   145      						"x": "wQehEGTVCu32yp8IwTaBCqPUIYslyd-WoFRsfDKE9II",
   146      						"y": "rIJO8RmkExUecJ5i15L9OC7rl7pwmYFR8QQgdM1ERWO",
   147  							"alg": "ES256"
   148  						}`,
   149  				err: "unable to read JWK: invalid JWK",
   150  			},
   151  			{
   152  				name: "attempt public key bytes from invalid JSON bytes",
   153  				jwkJSON: `{
   154  							"kty": "EC",
   155      						"use": "enc",
   156      						"crv": "secp256k1",
   157      						"kid": "sample@sample.id",
   158      						"x": "{",
   159      						"y": "y",
   160     						 	"alg": "ES256"
   161  						}`,
   162  				err: "unable to read JWK",
   163  			},
   164  			{
   165  				name: "invalid X25519",
   166  				jwkJSON: `{
   167      						"kty": "OKP",
   168      						"use": "enc",
   169      						"crv": "X25519",
   170      						"x": "wQehEGTVCu32yp8IwTaBCqPUIYslyd-WoFRsfDKE9IIrIJO8RmkExUecJ5i15L9OC7rl7pwmYFR8QQgdM1ERWO",
   171      						"kid": "sample@sample.id"
   172  						}`,
   173  				err: "unable to read X25519 JWE: invalid JWK",
   174  			},
   175  		}
   176  
   177  		t.Parallel()
   178  
   179  		for _, test := range tests {
   180  			tc := test
   181  			t.Run(tc.name, func(t *testing.T) {
   182  				var j JWK
   183  				err := json.Unmarshal([]byte(tc.jwkJSON), &j)
   184  				require.Error(t, err)
   185  				require.Contains(t, err.Error(), tc.err)
   186  			})
   187  		}
   188  	})
   189  }
   190  
   191  func TestByteBufferUnmarshalFailure(t *testing.T) {
   192  	bb := &byteBuffer{}
   193  	err := bb.UnmarshalJSON([]byte("{"))
   194  	require.Error(t, err)
   195  }
   196  
   197  func TestCurveSize(t *testing.T) {
   198  	require.Equal(t, 32, curveSize(btcec.S256()))
   199  	require.Equal(t, 32, curveSize(elliptic.P256()))
   200  	require.Equal(t, 28, curveSize(elliptic.P224()))
   201  	require.Equal(t, 48, curveSize(elliptic.P384()))
   202  	require.Equal(t, 66, curveSize(elliptic.P521()))
   203  }
   204  
   205  func TestJWKFromX25519KeyFailure(t *testing.T) {
   206  	key := &JWK{
   207  		JSONWebKey: jose.JSONWebKey{
   208  			Key: "abc", // try to create an invalid X25519 key type (string instead of []byte)
   209  		},
   210  	}
   211  
   212  	_, err := marshalX25519(key)
   213  	require.EqualError(t, err, "marshalX25519: invalid key")
   214  
   215  	invalidKey := make([]byte, 10)
   216  
   217  	n, err := rand.Read(invalidKey)
   218  	require.NoError(t, err)
   219  	require.Equal(t, 10, n)
   220  
   221  	key.Key = invalidKey // try with key larger than X25519 key length
   222  
   223  	_, err = marshalX25519(key)
   224  	require.EqualError(t, err, "marshalX25519: invalid key")
   225  }
   226  
   227  func TestJWK_PublicKeyBytesValidation(t *testing.T) {
   228  	jwk := &JWK{
   229  		JSONWebKey: jose.JSONWebKey{
   230  			Key:   "key of invalid type",
   231  			KeyID: "pubkey#123",
   232  		},
   233  	}
   234  
   235  	// unsupported public key type
   236  	pkBytes, err := jwk.PublicKeyBytes()
   237  	require.Error(t, err)
   238  	require.Contains(t, err.Error(), "unsupported public key type in kid 'pubkey#123'")
   239  	require.Empty(t, pkBytes)
   240  }
   241  
   242  func TestJWK_BBSKeyValidation(t *testing.T) {
   243  	_, privateKey, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil)
   244  	require.NoError(t, err)
   245  
   246  	jwkKey := &JWK{
   247  		JSONWebKey: jose.JSONWebKey{
   248  			Key: privateKey,
   249  		},
   250  		Kty: ecKty,
   251  		Crv: bls12381G2Crv,
   252  	}
   253  
   254  	t.Run("test MarshalJSON/UnmarshalJSON", func(t *testing.T) {
   255  		var mJWK []byte
   256  
   257  		mJWK, err = jwkKey.MarshalJSON()
   258  		require.NoError(t, err)
   259  
   260  		t.Logf("marshaled JWK: %s", mJWK)
   261  
   262  		jwk2 := &JWK{}
   263  		err = jwk2.UnmarshalJSON(mJWK)
   264  		require.NoError(t, err)
   265  		require.EqualValues(t, jwkKey, jwk2)
   266  	})
   267  
   268  	t.Run("test BBS private key jwk.PublicKeyBytes()", func(t *testing.T) {
   269  		var pubKeyBytes []byte
   270  
   271  		pubKeyBytes, err = jwkKey.PublicKeyBytes()
   272  		require.NoError(t, err)
   273  		require.NotEmpty(t, pubKeyBytes)
   274  	})
   275  
   276  	t.Run("test UnmarshalJSON of valid BBS private key JWK - with both x and d headers", func(t *testing.T) {
   277  		//nolint:lll
   278  		goodJWK := `{
   279  	"kty":"EC",
   280  	"crv":"BLS12381_G2",
   281  	"x":"oUd1c-NsWZy2oCaST4CRW1naLjgYY3OhHgTMie4uzgrB5VuVqx0pdYf4XWWlnEkZERnpMhgo2re4tQtdCguhI4OIGyAXFaML8D6E1ZYO8B0WmysMZUnC5BWWEfOid1lu",
   282  	"d":"MhYilAbhICa8T6m0U2gLAgLvPEsF05XN1yYHZgkfAK4"
   283  }`
   284  
   285  		jwk4 := &JWK{}
   286  
   287  		err = jwk4.UnmarshalJSON([]byte(goodJWK))
   288  		require.NoError(t, err)
   289  	})
   290  
   291  	t.Run("test UnmarshalJSON of invalid BBS private key JWK - no x header", func(t *testing.T) {
   292  		goodJWK := `{
   293  	"kty":"EC",
   294  	"crv":"BLS12381_G2",
   295  	"d":"MhYilAbhICa8T6m0U2gLAgLvPEsF05XN1yYHZgkfAK4"
   296  }`
   297  
   298  		jwk4 := &JWK{}
   299  
   300  		err = jwk4.UnmarshalJSON([]byte(goodJWK))
   301  		require.EqualError(t, err, "unable to read BBS+ JWE: invalid JWK")
   302  	})
   303  
   304  	t.Run("test UnmarshalJSON of valid BBS public key JWK", func(t *testing.T) {
   305  		//nolint:lll
   306  		goodJWK := `{
   307  	"kty":"EC",
   308  	"crv":"BLS12381_G2",
   309  	"x":"oUd1c-NsWZy2oCaST4CRW1naLjgYY3OhHgTMie4uzgrB5VuVqx0pdYf4XWWlnEkZERnpMhgo2re4tQtdCguhI4OIGyAXFaML8D6E1ZYO8B0WmysMZUnC5BWWEfOid1lu"
   310  }`
   311  
   312  		jwk4 := &JWK{}
   313  
   314  		err = jwk4.UnmarshalJSON([]byte(goodJWK))
   315  		require.NoError(t, err)
   316  	})
   317  
   318  	t.Run("test UnmarshalJSON of invalid BBS public key JWK - x wrong size", func(t *testing.T) {
   319  		goodJWK := `{
   320  	"kty":"EC",
   321  	"crv":"BLS12381_G2",
   322  	"x":"oUd1"
   323  }`
   324  
   325  		jwk4 := &JWK{}
   326  
   327  		err = jwk4.UnmarshalJSON([]byte(goodJWK))
   328  		require.EqualError(t, err, "unable to read BBS+ JWE: invalid JWK")
   329  	})
   330  
   331  	t.Run("test UnmarshalJSON of invalid BBS private key JWK - d wrong size", func(t *testing.T) {
   332  		//nolint:lll
   333  		goodJWK := `{
   334  	"kty":"EC",
   335  	"crv":"BLS12381_G2",
   336  	"x":"oUd1c-NsWZy2oCaST4CRW1naLjgYY3OhHgTMie4uzgrB5VuVqx0pdYf4XWWlnEkZERnpMhgo2re4tQtdCguhI4OIGyAXFaML8D6E1ZYO8B0WmysMZUnC5BWWEfOid1lu",
   337  	"d":"MhYi"
   338  }`
   339  
   340  		jwk4 := &JWK{}
   341  
   342  		err = jwk4.UnmarshalJSON([]byte(goodJWK))
   343  		require.EqualError(t, err, "unable to read BBS+ JWE: invalid JWK")
   344  	})
   345  
   346  	t.Run("test UnmarshalJSON of invalid BBS public key JWK - x wrong value", func(t *testing.T) {
   347  		//nolint:lll
   348  		goodJWK := `{
   349  	"kty":"EC",
   350  	"crv":"BLS12381_G2",
   351  	"x":"pUd1c-NsWZy2oCaST4CRW1naLjgYY3OhHgTMie4uzgrB5VuVqx0pdYf4XWWlnEkZERnpMhko2re4tQtdCguhI4OIGyAXFaML8D6E1ZYO8B0WmysMZUnC5BWWEfOhc6tv"
   352  }`
   353  
   354  		jwk4 := &JWK{}
   355  
   356  		err = jwk4.UnmarshalJSON([]byte(goodJWK))
   357  		require.EqualError(t, err, "unable to read BBS+ JWE: jwk invalid public key unmarshal: deserialize "+
   358  			"public key: failure [set bytes failed [point is not on curve]]")
   359  	})
   360  }
   361  
   362  func TestJWK_KeyType(t *testing.T) {
   363  	t.Run("success: get KeyType from JWK", func(t *testing.T) {
   364  		testCases := []struct {
   365  			jwk     string
   366  			keyType kms.KeyType
   367  		}{
   368  			{
   369  				jwk: `{
   370  					"kty": "OKP",
   371  					"use": "enc",
   372  					"crv": "Ed25519",
   373  					"kid": "sample@sample.id",
   374  					"x": "sEHL6KXs8bUz9Ss2qSWWjhhRMHVjrog0lzFENM132R8",
   375  					"alg": "EdDSA"
   376  				}`,
   377  				keyType: kms.ED25519Type,
   378  			},
   379  			{
   380  				jwk: `{
   381  					"kty": "OKP",
   382  					"use": "enc",
   383  					"crv": "X25519",
   384  					"kid": "sample@sample.id",
   385  					"x": "sEHL6KXs8bUz9Ss2qSWWjhhRMHVjrog0lzFENM132R8"
   386  				}`,
   387  				keyType: kms.X25519ECDHKWType,
   388  			},
   389  			{
   390  				//nolint:lll
   391  				jwk: `{
   392  					"kty": "EC",
   393  					"use": "enc",
   394  					"crv": "BLS12381_G2",
   395  					"kid": "sample@sample.id",
   396  					"x": "tKWJu0SOY7onl4tEyOOH11XBriQN2JgzV-UmjgBMSsNkcAx3_l97SVYViSDBouTVBkBfrLh33C5icDD-4UEDxNO3Wn1ijMHvn2N63DU4pkezA3kGN81jGbwbrsMPpiOF"
   397  				}`,
   398  				keyType: kms.BLS12381G2Type,
   399  			},
   400  			{
   401  				jwk: `{
   402  					"kty": "EC",
   403  					"use": "enc",
   404  					"crv": "secp256k1",
   405  					"kid": "sample@sample.id",
   406  					"x": "YRrvJocKf39GpdTnd-zBFE0msGDqawR-Cmtc6yKoFsM",
   407  					"y": "kE-dMH9S3mxnTXo0JFEhraCU_tVYFDfpu9tpP1LfVKQ",
   408  					"alg": "ES256K"
   409  				}`,
   410  				keyType: kms.ECDSASecp256k1TypeIEEEP1363,
   411  			},
   412  			{
   413  				jwk: `{
   414  					"kty": "EC",
   415  					"use": "enc",
   416  					"crv": "P-256",
   417  					"kid": "sample@sample.id",
   418  					"x": "JR7nhI47w7bxrNkp7Xt1nbmozNn-RB2Q-PWi7KHT8J0",
   419  					"y": "iXmKtH0caOgB1vV0CQwinwK999qdDvrssKhdbiAz9OI",
   420  					"alg": "ES256"
   421  				}`,
   422  				keyType: kms.ECDSAP256TypeIEEEP1363,
   423  			},
   424  			{
   425  				jwk: `{
   426  					"kty": "EC",
   427  					"kid": "sample@sample.id",
   428  					"crv": "P-384",
   429  					"x": "SNJT8Q-irydV5yppI-blGNuRTPf8sCYuL_tO92SLrufdlEgDll9cRuBLACrlBz2x",
   430  					"y": "zIYfra2_y2hnc35sIwA1jiDx5rKmG3mX6162HkAodTJIpUYxw2rz1qHiwVcaU2tY",
   431  					"alg": "ES384"
   432  				}`,
   433  				keyType: kms.ECDSAP384TypeIEEEP1363,
   434  			},
   435  			{
   436  				jwk: `{
   437  					"kty": "EC",
   438  					"kid": "sample@sample.id",
   439  					"crv": "P-521",
   440  					"d": "AfcmEHp9Nd_X005hBoKEs8bvMzIH0OMYodQUw8xRWpUGOq31cyXV1dUvX-S8uSaBIbh2w-fy_OaolBmvTe3Il5Rw",
   441  					"x": "AMIjmQpOT7oz5e8CJZQVi3cxCdF0gdmnNE8qmi5Y3_1-6gRzHoaXGs_TBcAvNgD8UCYhk3FWA8aLChJ9BjEUi44m",
   442  					"y": "AIfNzFdbyI1rfRrcY7orl3wTXT-C_kWhyWdr3K3rSS8WbwXhqg9jb29iEoE8izpCnuoJbC_FsMf2WbI_1iNomfB4",
   443  					"alg": "ES512"
   444  				}`,
   445  				keyType: kms.ECDSAP521TypeIEEEP1363,
   446  			},
   447  			{
   448  				jwk: `{
   449  					"kty": "RSA",
   450  					"e": "AQAB",
   451  					"use": "enc",
   452  					"kid": "sample@sample.id",
   453  					"alg": "RS256",
   454  					"n": "1hOl09BUnwY7jFBqoZKa4XDmIuc0YFb4y_5ThiHhLRW68aNG5Vo23n3ugND2GK3PsguZqJ_HrWCGVuVlKTmFg` +
   455  					`JWQD9ZnVcYqScgHpQRhxMBi86PIvXR01D_PWXZZjvTRakpvQxUT5bVBdWnaBHQoxDBt0YIVi5a7x-gXB1aDlts4RTMpfS9BPmEjX` +
   456  					`4lciozwS6Ow_wTO3C2YGa_Our0ptIxr-x_3sMbPCN8Fe_iaBDezeDAm39xCNjFa1E735ipXA4eUW_6SzFJ5-bM2UKba2WE6xUaEa5G1` +
   457  					`MDDHCG5LKKd6Mhy7SSAzPOR2FTKYj89ch2asCPlbjHTu8jS6Iy8"
   458  				}`,
   459  				keyType: kms.RSAPS256Type,
   460  			},
   461  		}
   462  
   463  		t.Parallel()
   464  
   465  		for _, testCase := range testCases {
   466  			t.Run(fmt.Sprintf("KeyType %s", testCase.keyType), func(t *testing.T) {
   467  				j := JWK{}
   468  				e := j.UnmarshalJSON([]byte(testCase.jwk))
   469  				require.NoError(t, e)
   470  
   471  				kt, e := j.KeyType()
   472  				require.NoError(t, e)
   473  				require.Equal(t, testCase.keyType, kt)
   474  
   475  				mJWK, err := j.MarshalJSON()
   476  				require.NoError(t, err)
   477  				require.NotEmpty(t, mJWK)
   478  
   479  				keyBytes, err := j.PublicKeyBytes()
   480  				require.NoError(t, err)
   481  				require.NotEmpty(t, keyBytes)
   482  			})
   483  		}
   484  	})
   485  
   486  	t.Run("test ed25519 with []byte key material", func(t *testing.T) {
   487  		jwkJSON := `{
   488  			"kty": "OKP",
   489  			"use": "enc",
   490  			"crv": "Ed25519",
   491  			"kid": "sample@sample.id",
   492  			"x": "sEHL6KXs8bUz9Ss2qSWWjhhRMHVjrog0lzFENM132R8",
   493  			"alg": "EdDSA"
   494  		}`
   495  
   496  		j := JWK{}
   497  		e := j.UnmarshalJSON([]byte(jwkJSON))
   498  		require.NoError(t, e)
   499  
   500  		k, err := j.PublicKeyBytes()
   501  		require.NoError(t, err)
   502  
   503  		j.Key = k
   504  
   505  		kt, e := j.KeyType()
   506  		require.NoError(t, e)
   507  		require.Equal(t, kms.ED25519Type, kt)
   508  	})
   509  
   510  	t.Run("test secp256k1 with []byte key material", func(t *testing.T) {
   511  		jwkJSON := `{
   512  			"kty": "EC",
   513  			"use": "enc",
   514  			"crv": "secp256k1",
   515  			"kid": "sample@sample.id",
   516  			"x": "YRrvJocKf39GpdTnd-zBFE0msGDqawR-Cmtc6yKoFsM",
   517  			"y": "kE-dMH9S3mxnTXo0JFEhraCU_tVYFDfpu9tpP1LfVKQ",
   518  			"alg": "ES256K"
   519  		}`
   520  
   521  		j := JWK{}
   522  		e := j.UnmarshalJSON([]byte(jwkJSON))
   523  		require.NoError(t, e)
   524  
   525  		pkb, err := j.PublicKeyBytes()
   526  		require.NoError(t, err)
   527  
   528  		j.Key = pkb
   529  
   530  		kt, e := j.KeyType()
   531  		require.NoError(t, e)
   532  		require.Equal(t, kms.ECDSASecp256k1TypeIEEEP1363, kt)
   533  	})
   534  
   535  	t.Run("fail to get ecdsa keytype for (unsupported) p-224", func(t *testing.T) {
   536  		eckey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
   537  		require.NoError(t, err)
   538  
   539  		kt, err := ecdsaPubKeyType(&eckey.PublicKey)
   540  		require.Error(t, err)
   541  		require.Contains(t, err.Error(), "no keytype recognized for ecdsa jwk")
   542  		require.Equal(t, kms.KeyType(""), kt)
   543  	})
   544  }