github.com/lestrrat-go/jwx/v2@v2.0.21/jwx_test.go (about) 1 package jwx_test 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "crypto/rsa" 7 "fmt" 8 "strings" 9 "testing" 10 11 "github.com/lestrrat-go/jwx/v2" 12 "github.com/lestrrat-go/jwx/v2/internal/ecutil" 13 "github.com/lestrrat-go/jwx/v2/internal/jose" 14 "github.com/lestrrat-go/jwx/v2/internal/json" 15 "github.com/lestrrat-go/jwx/v2/internal/jwxtest" 16 "github.com/lestrrat-go/jwx/v2/jwa" 17 "github.com/lestrrat-go/jwx/v2/jwe" 18 "github.com/lestrrat-go/jwx/v2/jwk" 19 "github.com/lestrrat-go/jwx/v2/jws" 20 "github.com/stretchr/testify/assert" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestShowBuildInfo(t *testing.T) { 25 t.Logf("Running tests using JSON backend => %s\n", json.Engine()) 26 t.Logf("Available elliptic curves:") 27 for _, alg := range ecutil.AvailableAlgorithms() { 28 t.Logf(" %s", alg) 29 } 30 } 31 32 type jsonUnmarshalWrapper struct { 33 buf []byte 34 } 35 36 func (w jsonUnmarshalWrapper) Decode(v interface{}) error { 37 return json.Unmarshal(w.buf, v) 38 } 39 40 func TestDecoderSetting(t *testing.T) { 41 // DO NOT MAKE THIS TEST PARALLEL. This test uses features with global side effects 42 const src = `{"foo": 1}` 43 for _, useNumber := range []bool{true, false} { 44 useNumber := useNumber 45 t.Run(fmt.Sprintf("jwx.WithUseNumber(%t)", useNumber), func(t *testing.T) { 46 if useNumber { 47 jwx.DecoderSettings(jwx.WithUseNumber(useNumber)) 48 t.Cleanup(func() { 49 jwx.DecoderSettings(jwx.WithUseNumber(false)) 50 }) 51 } 52 53 // json.NewDecoder must be called AFTER the above jwx.DecoderSettings call 54 decoders := []struct { 55 Name string 56 Decoder interface{ Decode(interface{}) error } 57 }{ 58 {Name: "Decoder", Decoder: json.NewDecoder(strings.NewReader(src))}, 59 {Name: "Unmarshal", Decoder: jsonUnmarshalWrapper{buf: []byte(src)}}, 60 } 61 62 for _, tc := range decoders { 63 tc := tc 64 t.Run(tc.Name, func(t *testing.T) { 65 var m map[string]interface{} 66 if !assert.NoError(t, tc.Decoder.Decode(&m), `Decode should succeed`) { 67 return 68 } 69 70 v, ok := m["foo"] 71 if !assert.True(t, ok, `m["foo"] should exist`) { 72 return 73 } 74 75 if useNumber { 76 if !assert.Equal(t, json.Number("1"), v, `v should be a json.Number object`) { 77 return 78 } 79 } else { 80 if !assert.Equal(t, float64(1), v, `v should be a float64`) { 81 return 82 } 83 } 84 }) 85 } 86 }) 87 } 88 } 89 90 // Test compatibility against `jose` tool 91 func TestJoseCompatibility(t *testing.T) { 92 t.Parallel() 93 94 if testing.Short() { 95 t.Logf("Skipped during short tests") 96 return 97 } 98 99 if !jose.Available() { 100 t.Logf("`jose` binary not available, skipping tests") 101 return 102 } 103 104 t.Run("jwk", func(t *testing.T) { 105 t.Parallel() 106 testcases := []struct { 107 Name string 108 Raw interface{} 109 Template string 110 VerifyKey func(context.Context, *testing.T, jwk.Key) bool 111 }{ 112 { 113 Name: "RSA Private Key (256)", 114 Raw: rsa.PrivateKey{}, 115 Template: `{"alg": "RS256"}`, 116 }, 117 { 118 Name: "RSA Private Key (384)", 119 Raw: rsa.PrivateKey{}, 120 Template: `{"alg": "RS384"}`, 121 }, 122 { 123 Name: "RSA Private Key (512)", 124 Raw: rsa.PrivateKey{}, 125 Template: `{"alg": "RS512"}`, 126 }, 127 { 128 Name: "RSA Private Key with Private Parameters", 129 Raw: rsa.PrivateKey{}, 130 Template: `{"alg": "RS256", "x-jwx": 1234}`, 131 VerifyKey: func(ctx context.Context, t *testing.T, key jwk.Key) bool { 132 m, err := key.AsMap(ctx) 133 if !assert.NoError(t, err, `key.AsMap() should succeed`) { 134 return false 135 } 136 137 if !assert.Equal(t, float64(1234), m["x-jwx"], `private parameters should match`) { 138 return false 139 } 140 141 return true 142 }, 143 }, 144 } 145 146 for _, tc := range testcases { 147 tc := tc 148 t.Run(tc.Name, func(t *testing.T) { 149 t.Parallel() 150 151 ctx, cancel := context.WithCancel(context.Background()) 152 defer cancel() 153 154 keyfile, cleanup, err := jose.GenerateJwk(ctx, t, tc.Template) 155 if !assert.NoError(t, err, `jose.GenerateJwk should succeed`) { 156 return 157 } 158 defer cleanup() 159 160 webkey, err := jwxtest.ParseJwkFile(ctx, keyfile) 161 if !assert.NoError(t, err, `ParseJwkFile should succeed`) { 162 return 163 } 164 165 if vk := tc.VerifyKey; vk != nil { 166 if !vk(ctx, t, webkey) { 167 return 168 } 169 } 170 171 if !assert.NoError(t, webkey.Raw(&tc.Raw), `jwk.Raw should succeed`) { 172 return 173 } 174 }) 175 } 176 }) 177 t.Run("jwe", func(t *testing.T) { 178 // For some reason "jose" does not come with RSA-OAEP on some platforms. 179 // In order to avoid doing this in an ad-hoc way, we're just going to 180 // ask our jose package for the algorithms that it supports, and generate 181 // the list dynamically 182 183 t.Parallel() 184 ctx, cancel := context.WithCancel(context.Background()) 185 defer cancel() 186 set, err := jose.Algorithms(ctx, t) 187 require.NoError(t, err) 188 189 var tests []interopTest 190 191 for _, keyenc := range []jwa.KeyEncryptionAlgorithm{jwa.RSA1_5, jwa.RSA_OAEP, jwa.RSA_OAEP_256} { 192 if !set.Has(keyenc.String()) { 193 t.Logf("jose does not support key encryption algorithm %q: skipping", keyenc) 194 continue 195 } 196 for _, contentenc := range []jwa.ContentEncryptionAlgorithm{jwa.A128GCM, jwa.A128CBC_HS256, jwa.A256CBC_HS512} { 197 tests = append(tests, interopTest{keyenc, contentenc}) 198 } 199 } 200 201 for _, keyenc := range []jwa.KeyEncryptionAlgorithm{jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.A128KW, jwa.A128GCMKW, jwa.A256KW, jwa.A256GCMKW, jwa.PBES2_HS256_A128KW, jwa.DIRECT} { 202 if !set.Has(keyenc.String()) { 203 t.Logf("jose does not support key encryption algorithm %q: skipping", keyenc) 204 continue 205 } 206 for _, contentenc := range []jwa.ContentEncryptionAlgorithm{jwa.A128GCM, jwa.A128CBC_HS256} { 207 tests = append(tests, interopTest{keyenc, contentenc}) 208 } 209 } 210 211 for _, keyenc := range []jwa.KeyEncryptionAlgorithm{jwa.ECDH_ES, jwa.ECDH_ES_A256KW, jwa.A256KW, jwa.A256GCMKW, jwa.PBES2_HS512_A256KW, jwa.DIRECT} { 212 if !set.Has(keyenc.String()) { 213 t.Logf("jose does not support key encryption algorithm %q: skipping", keyenc) 214 continue 215 } 216 for _, contentenc := range []jwa.ContentEncryptionAlgorithm{jwa.A256GCM, jwa.A256CBC_HS512} { 217 tests = append(tests, interopTest{keyenc, contentenc}) 218 } 219 } 220 221 for _, keyenc := range []jwa.KeyEncryptionAlgorithm{jwa.PBES2_HS384_A192KW} { 222 if !set.Has(keyenc.String()) { 223 t.Logf("jose does not support key encryption algorithm %q: skipping", keyenc) 224 continue 225 } 226 for _, contentenc := range []jwa.ContentEncryptionAlgorithm{jwa.A192GCM, jwa.A192CBC_HS384} { 227 tests = append(tests, interopTest{keyenc, contentenc}) 228 } 229 } 230 231 for _, test := range tests { 232 test := test 233 t.Run(fmt.Sprintf("%s-%s", test.alg, test.enc), func(t *testing.T) { 234 t.Parallel() 235 ctx, cancel := context.WithCancel(context.Background()) 236 defer cancel() 237 joseInteropTest(ctx, test, t) 238 }) 239 } 240 }) 241 t.Run("jws", func(t *testing.T) { 242 t.Parallel() 243 tests := []jwa.SignatureAlgorithm{ 244 jwa.ES256, 245 //jwa.ES256K, 246 jwa.ES384, 247 jwa.ES512, 248 //jwa.EdDSA, 249 jwa.HS256, 250 jwa.HS384, 251 jwa.HS512, 252 jwa.PS256, 253 jwa.PS384, 254 jwa.PS512, 255 jwa.RS256, 256 jwa.RS384, 257 jwa.RS512, 258 } 259 for _, test := range tests { 260 test := test 261 t.Run(test.String(), func(t *testing.T) { 262 t.Parallel() 263 ctx, cancel := context.WithCancel(context.Background()) 264 defer cancel() 265 joseJwsInteropTest(ctx, test, t) 266 }) 267 } 268 }) 269 } 270 271 type interopTest struct { 272 alg jwa.KeyEncryptionAlgorithm 273 enc jwa.ContentEncryptionAlgorithm 274 } 275 276 func joseInteropTest(ctx context.Context, spec interopTest, t *testing.T) { 277 t.Helper() 278 279 expected := []byte("Lorem ipsum") 280 281 // let jose generate a key file 282 alg := spec.alg.String() 283 if spec.alg == jwa.DIRECT { 284 alg = spec.enc.String() 285 } 286 joseJwkFile, joseJwkCleanup, err := jose.GenerateJwk(ctx, t, fmt.Sprintf(`{"alg": "%s"}`, alg)) 287 if !assert.NoError(t, err, `jose.GenerateJwk should succeed`) { 288 return 289 } 290 defer joseJwkCleanup() 291 292 // Load the JWK generated by jose 293 jwxJwk, err := jwxtest.ParseJwkFile(ctx, joseJwkFile) 294 if !assert.NoError(t, err, `jwxtest.ParseJwkFile should succeed`) { 295 return 296 } 297 298 t.Run("Parse JWK via jwx", func(t *testing.T) { 299 switch spec.alg { 300 case jwa.RSA1_5, jwa.RSA_OAEP, jwa.RSA_OAEP_256: 301 var rawkey rsa.PrivateKey 302 if !assert.NoError(t, jwxJwk.Raw(&rawkey), `jwk.Raw should succeed`) { 303 return 304 } 305 case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW: 306 var rawkey ecdsa.PrivateKey 307 if !assert.NoError(t, jwxJwk.Raw(&rawkey), `jwk.Raw should succeed`) { 308 return 309 } 310 default: 311 var rawkey []byte 312 if !assert.NoError(t, jwxJwk.Raw(&rawkey), `jwk.Raw should succeed`) { 313 return 314 } 315 } 316 }) 317 t.Run("Encrypt with jose, Decrypt with jwx", func(t *testing.T) { 318 // let jose encrypt payload using the key file 319 joseCryptFile, joseCryptCleanup, err := jose.EncryptJwe(ctx, t, expected, spec.alg.String(), joseJwkFile, spec.enc.String(), true) 320 if !assert.NoError(t, err, `jose.EncryptJwe should succeed`) { 321 return 322 } 323 defer joseCryptCleanup() 324 325 jwxtest.DumpFile(t, joseCryptFile) 326 327 // let jwx decrypt the jose crypted file 328 payload, err := jwxtest.DecryptJweFile(ctx, joseCryptFile, spec.alg, joseJwkFile) 329 if !assert.NoError(t, err, `decryptFile.DecryptJwe should succeed`) { 330 return 331 } 332 333 if !assert.Equal(t, expected, payload, `decrypted payloads should match`) { 334 return 335 } 336 }) 337 t.Run("Encrypt with jwx, Decrypt with jose", func(t *testing.T) { 338 jwxCryptFile, jwxCryptCleanup, err := jwxtest.EncryptJweFile(ctx, expected, spec.alg, joseJwkFile, spec.enc, jwa.NoCompress) 339 if !assert.NoError(t, err, `jwxtest.EncryptJweFile should succeed`) { 340 return 341 } 342 defer jwxCryptCleanup() 343 344 payload, err := jose.DecryptJwe(ctx, t, jwxCryptFile, joseJwkFile) 345 if !assert.NoError(t, err, `jose.DecryptJwe should succeed`) { 346 return 347 } 348 349 if !assert.Equal(t, expected, payload, `decrypted payloads should match`) { 350 return 351 } 352 }) 353 } 354 355 func joseJwsInteropTest(ctx context.Context, alg jwa.SignatureAlgorithm, t *testing.T) { 356 t.Helper() 357 358 expected := []byte(`{"foo":"bar"}`) 359 360 joseJwkFile, joseJwkCleanup, err := jose.GenerateJwk(ctx, t, fmt.Sprintf(`{"alg": "%s"}`, alg)) 361 if !assert.NoError(t, err, `jose.GenerateJwk should succeed`) { 362 return 363 } 364 defer joseJwkCleanup() 365 366 // Load the JWK generated by jose 367 _, err = jwxtest.ParseJwkFile(ctx, joseJwkFile) 368 if !assert.NoError(t, err, `jwxtest.ParseJwkFile should succeed`) { 369 return 370 } 371 t.Run("Sign with jose, Verify with jwx", func(t *testing.T) { 372 // let jose encrypt payload using the key file 373 joseCryptFile, joseCryptCleanup, err := jose.SignJws(ctx, t, expected, joseJwkFile, true) 374 if !assert.NoError(t, err, `jose.SignJws should succeed`) { 375 return 376 } 377 defer joseCryptCleanup() 378 379 jwxtest.DumpFile(t, joseCryptFile) 380 381 // let jwx decrypt the jose crypted file 382 payload, err := jwxtest.VerifyJwsFile(ctx, joseCryptFile, alg, joseJwkFile) 383 if !assert.NoError(t, err, `jwxtest.VerifyJwsFile should succeed`) { 384 return 385 } 386 387 if !assert.Equal(t, expected, payload, `decrypted payloads should match`) { 388 return 389 } 390 }) 391 t.Run("Sign with jwx, Verify with jose", func(t *testing.T) { 392 jwxCryptFile, jwxCryptCleanup, err := jwxtest.SignJwsFile(ctx, expected, alg, joseJwkFile) 393 if !assert.NoError(t, err, `jwxtest.SignJwsFile should succeed`) { 394 return 395 } 396 defer jwxCryptCleanup() 397 398 payload, err := jose.VerifyJws(ctx, t, jwxCryptFile, joseJwkFile) 399 if !assert.NoError(t, err, `jose.VerifyJws should succeed`) { 400 return 401 } 402 403 if !assert.Equal(t, expected, payload, `decrypted payloads should match`) { 404 return 405 } 406 }) 407 } 408 409 func TestGHIssue230(t *testing.T) { 410 t.Parallel() 411 if !jose.Available() { 412 t.SkipNow() 413 } 414 415 data := "eyJhbGciOiJFQ0RILUVTIiwiY2xldmlzIjp7InBpbiI6InRhbmciLCJ0YW5nIjp7ImFkdiI6eyJrZXlzIjpbeyJhbGciOiJFQ01SIiwiY3J2IjoiUC01MjEiLCJrZXlfb3BzIjpbImRlcml2ZUtleSJdLCJrdHkiOiJFQyIsIngiOiJBZm5tR2xHRTFHRUZ5NEpUT2tGWmo5ZEhEUmdpVE5IeFBST3hpZDZLdm0xVGRFQkZ3bElsSVB6TG5lTjlnb3h6OUVGYmJLM3BoN0tWZS05aVF4MmxhOVNFIiwieSI6IkFmZGFaTVYzVzk1NE14elQxeXF3MWVaRU9xTFFZZnBXSGczMlJvekhyQjBEYmoxWWV3OVFvTDg1M2Y2aUw2REIyRC1nbEcxSFFsb3czdGRNdFhjN1pSY0IifSx7ImFsZyI6IkVTNTEyIiwiY3J2IjoiUC01MjEiLCJrZXlfb3BzIjpbInZlcmlmeSJdLCJrdHkiOiJFQyIsIngiOiJBR0drcXRPZzZqel9pZnhmVnVWQ01CalVySFhCTGtfS2hIb3lKRkU5NmJucTZKZVVHNFNMZnRrZ2FIYk5WT0U4Q3Mwd0JqR0ZkSWxDbnBmak94RGJfbFBoIiwieSI6IkFLU0laT0JYY1Jfa3RkWjZ6T3F3TGI5SEJzai0yYmRMUmw5dFZVbnVlV2N3aXg5X3NiekliSWx0SE9YUGhBTW9yaUlYMWVyNzc4Unh6Vkg5d0FtaUhGa1kifV19LCJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjM5NDIxIn19LCJlbmMiOiJBMjU2R0NNIiwiZXBrIjp7ImNydiI6IlAtNTIxIiwia3R5IjoiRUMiLCJ4IjoiQUJMUm9sQWotZFdVdzZLSjg2T3J6d1F6RjlGT09URFZBZnNWNkh0OU0zREhyQ045Q0N6dVJ1b3cwbWp6M3BjZnVCaFpYREpfN0dkdzE0LXdneV9fTFNrYyIsInkiOiJBT3NRMzlKZmFQVGhjc2FZTjhSMVBHXzIwYXZxRU1NRl9fM2RHQmI3c1BqNmktNEJORDVMdkZ3cVpJT1l4SS1kVWlvNzkyOWY1YnE0eEdJY0lGWWtlbllxIn0sImtpZCI6ImhlZmVpNzVqMkp4Sko3REZnSDAxUWlOVmlGayJ9..GH3-8v7wfxEsRnki.wns--EIYTRjM3Tb0HyA.EGn2Gq7PnSVvPaMN0oRi5A" 416 417 compactMsg, err := jwe.ParseString(data) 418 if !assert.NoError(t, err, `jwe.ParseString should succeed`) { 419 return 420 } 421 422 formatted, err := jose.FmtJwe(context.TODO(), t, []byte(data)) 423 if !assert.NoError(t, err, `jose.FmtJwe should succeed`) { 424 return 425 } 426 427 jsonMsg, err := jwe.Parse(formatted) 428 if !assert.NoError(t, err, `jwe.Parse should succeed`) { 429 return 430 } 431 432 if !assert.Equal(t, compactMsg, jsonMsg, `messages should match`) { 433 return 434 } 435 } 436 437 func TestGuessFormat(t *testing.T) { 438 testcases := []struct { 439 Name string 440 Expected jwx.FormatKind 441 Source []byte 442 }{ 443 { 444 Name: "Raw String", 445 Expected: jwx.InvalidFormat, 446 Source: []byte(`Hello, World`), 447 }, 448 { 449 Name: "Random JSON Object", 450 Expected: jwx.UnknownFormat, 451 Source: []byte(`{"random": "JSON"}`), 452 }, 453 { 454 Name: "Random JSON Array", 455 Expected: jwx.InvalidFormat, 456 Source: []byte(`["random", "JSON"]`), 457 }, 458 { 459 Name: "Random Broken JSON", 460 Expected: jwx.UnknownFormat, 461 Source: []byte(`{"aud": "foo", "x-customg": "extra semicolon after this string", }`), 462 }, 463 { 464 Name: "JWS", 465 Expected: jwx.JWS, 466 // from https://tools.ietf.org/html/rfc7515#appendix-A.1 467 Source: []byte(`eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk`), 468 }, 469 { 470 Name: "JWE", 471 Expected: jwx.JWE, 472 Source: []byte(`eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`), 473 }, 474 { 475 Name: "JWK", 476 Expected: jwx.JWK, 477 Source: []byte(`{"kty":"OKP","crv":"X25519","x":"3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"}`), 478 }, 479 { 480 Name: "JWKS", 481 Expected: jwx.JWKS, 482 Source: []byte(`{"keys":[{"kty":"OKP","crv":"X25519","x":"3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"}]}`), 483 }, 484 { 485 Name: "JWS (JSON)", 486 Expected: jwx.JWS, 487 Source: []byte(`{"signatures": [], "payload": ""}`), 488 }, 489 { 490 Name: "JWT", 491 Expected: jwx.JWT, 492 Source: []byte(`{"aud":"github.com/lestrrat-go/jwx/v2"}`), 493 }, 494 } 495 496 for _, tc := range testcases { 497 tc := tc 498 t.Run(tc.Name, func(t *testing.T) { 499 got := jwx.GuessFormat(tc.Source) 500 if !assert.Equal(t, got, tc.Expected, `value of jwx.GuessFormat should match (%s != %s)`, got, tc.Expected) { 501 return 502 } 503 }) 504 } 505 } 506 507 func TestFormat(t *testing.T) { 508 testcases := []struct { 509 Value jwx.FormatKind 510 Expected string 511 Error bool 512 }{ 513 { 514 Value: jwx.UnknownFormat, 515 Expected: "UnknownFormat", 516 }, 517 { 518 Value: jwx.JWE, 519 Expected: "JWE", 520 }, 521 { 522 Value: jwx.JWS, 523 Expected: "JWS", 524 }, 525 { 526 Value: jwx.JWK, 527 Expected: "JWK", 528 }, 529 { 530 Value: jwx.JWKS, 531 Expected: "JWKS", 532 }, 533 { 534 Value: jwx.JWT, 535 Expected: "JWT", 536 }, 537 { 538 Value: jwx.FormatKind(9999999), 539 Expected: "FormatKind(9999999)", 540 }, 541 } 542 for _, tc := range testcases { 543 tc := tc 544 t.Run(tc.Expected, func(t *testing.T) { 545 if !assert.Equal(t, tc.Expected, tc.Value.String(), `stringification should match`) { 546 return 547 } 548 }) 549 } 550 } 551 552 func TestGH996(t *testing.T) { 553 ecdsaKey, err := jwxtest.GenerateEcdsaKey(jwa.P256) 554 require.NoError(t, err, `jwxtest.GenerateEcdsaKey should succeed`) 555 556 rsaKey, err := jwxtest.GenerateRsaKey() 557 require.NoError(t, err, `jwxtest.GenerateRsaKey should succeed`) 558 559 okpKey, err := jwxtest.GenerateEd25519Key() 560 require.NoError(t, err, `jwxtest.GenerateEd25519Key should succeed`) 561 562 symmetricKey := []byte(`abracadabra`) 563 564 testcases := []struct { 565 Name string 566 Algorithm jwa.SignatureAlgorithm 567 ValidSigningKeys []interface{} 568 InvalidSigningKeys []interface{} 569 ValidVerificationKeys []interface{} 570 InvalidVerificationKeys []interface{} 571 }{ 572 { 573 Name: `ECDSA`, 574 Algorithm: jwa.ES256, 575 ValidSigningKeys: []interface{}{ecdsaKey}, 576 InvalidSigningKeys: []interface{}{rsaKey, okpKey, symmetricKey}, 577 ValidVerificationKeys: []interface{}{ecdsaKey.PublicKey}, 578 InvalidVerificationKeys: []interface{}{rsaKey.PublicKey, okpKey.Public(), symmetricKey}, 579 }, 580 { 581 Name: `RSA`, 582 Algorithm: jwa.RS256, 583 ValidSigningKeys: []interface{}{rsaKey}, 584 InvalidSigningKeys: []interface{}{ecdsaKey, okpKey, symmetricKey}, 585 ValidVerificationKeys: []interface{}{rsaKey.PublicKey}, 586 InvalidVerificationKeys: []interface{}{ecdsaKey.PublicKey, okpKey.Public(), symmetricKey}, 587 }, 588 { 589 Name: `OKP`, 590 Algorithm: jwa.EdDSA, 591 ValidSigningKeys: []interface{}{okpKey}, 592 InvalidSigningKeys: []interface{}{ecdsaKey, rsaKey, symmetricKey}, 593 ValidVerificationKeys: []interface{}{okpKey.Public()}, 594 InvalidVerificationKeys: []interface{}{ecdsaKey.PublicKey, rsaKey.PublicKey, symmetricKey}, 595 }, 596 } 597 598 for _, tc := range testcases { 599 tc := tc 600 t.Run(tc.Name, func(t *testing.T) { 601 for _, valid := range tc.ValidSigningKeys { 602 valid := valid 603 t.Run(fmt.Sprintf("Sign Valid(%T)", valid), func(t *testing.T) { 604 _, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(tc.Algorithm, valid)) 605 require.NoError(t, err, `signing with %T should succeed`, valid) 606 }) 607 } 608 609 for _, invalid := range tc.InvalidSigningKeys { 610 invalid := invalid 611 t.Run(fmt.Sprintf("Sign Invalid(%T)", invalid), func(t *testing.T) { 612 _, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(tc.Algorithm, invalid)) 613 require.Error(t, err, `signing with %T should fail`, invalid) 614 }) 615 } 616 617 signed, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(tc.Algorithm, tc.ValidSigningKeys[0])) 618 require.NoError(t, err, `jws.Sign with valid key should succeed`) 619 620 for _, valid := range tc.ValidVerificationKeys { 621 valid := valid 622 t.Run(fmt.Sprintf("Verify Valid(%T)", valid), func(t *testing.T) { 623 _, err := jws.Verify(signed, jws.WithKey(tc.Algorithm, valid)) 624 require.NoError(t, err, `verifying with %T should succeed`, valid) 625 }) 626 } 627 628 for _, invalid := range tc.InvalidVerificationKeys { 629 invalid := invalid 630 t.Run(fmt.Sprintf("Verify Invalid(%T)", invalid), func(t *testing.T) { 631 _, err := jws.Verify(signed, jws.WithKey(tc.Algorithm, invalid)) 632 require.Error(t, err, `verifying with %T should fail`, invalid) 633 }) 634 } 635 }) 636 } 637 }