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 }