github.com/lestrrat-go/jwx/v2@v2.0.21/jws/jws_test.go (about) 1 package jws_test 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "crypto" 8 "crypto/ecdsa" 9 "crypto/ed25519" 10 "crypto/rsa" 11 "crypto/sha256" 12 "crypto/sha512" 13 "encoding/asn1" 14 "errors" 15 "fmt" 16 "io" 17 "math/big" 18 "net/http" 19 "net/http/httptest" 20 "os" 21 "sort" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/lestrrat-go/httprc" 27 "github.com/lestrrat-go/jwx/v2/internal/base64" 28 "github.com/lestrrat-go/jwx/v2/internal/json" 29 "github.com/lestrrat-go/jwx/v2/internal/jwxtest" 30 "github.com/lestrrat-go/jwx/v2/jwa" 31 "github.com/lestrrat-go/jwx/v2/jwk" 32 "github.com/lestrrat-go/jwx/v2/jws" 33 "github.com/lestrrat-go/jwx/v2/jwt" 34 "github.com/lestrrat-go/jwx/v2/x25519" 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 ) 38 39 const examplePayload = `{"iss":"joe",` + "\r\n" + ` "exp":1300819380,` + "\r\n" + ` "http://example.com/is_root":true}` 40 const exampleCompactSerialization = `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` 41 const badValue = "%badvalue%" 42 43 var hasES256K bool 44 45 func TestSanity(t *testing.T) { 46 t.Run("sanity: Verify with single key", func(t *testing.T) { 47 key, err := jwk.ParseKey([]byte(`{ 48 "kty": "oct", 49 "k": "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" 50 }`)) 51 require.NoError(t, err, `jwk.ParseKey should succeed`) 52 payload, err := jws.Verify([]byte(exampleCompactSerialization), jws.WithKey(jwa.HS256, key)) 53 require.NoError(t, err, `jws.Verify should succeed`) 54 require.Equal(t, []byte(examplePayload), payload, `payloads should match`) 55 }) 56 } 57 58 func TestParseReader(t *testing.T) { 59 t.Parallel() 60 t.Run("Empty []byte", func(t *testing.T) { 61 t.Parallel() 62 _, err := jws.Parse(nil) 63 require.Error(t, err, "Parsing an empty byte slice should result in an error") 64 }) 65 t.Run("Empty bytes.Buffer", func(t *testing.T) { 66 t.Parallel() 67 _, err := jws.ParseReader(&bytes.Buffer{}) 68 require.Error(t, err, "Parsing an empty buffer should result in an error") 69 }) 70 t.Run("Compact detached payload", func(t *testing.T) { 71 t.Parallel() 72 split := strings.Split(exampleCompactSerialization, ".") 73 incoming := strings.Join([]string{split[0], "", split[2]}, ".") 74 _, err := jws.ParseString(incoming) 75 require.NoError(t, err, `jws.ParseString should succeed`) 76 }) 77 t.Run("Compact missing header", func(t *testing.T) { 78 t.Parallel() 79 incoming := strings.Join( 80 (strings.Split( 81 exampleCompactSerialization, 82 ".", 83 ))[:2], 84 ".", 85 ) 86 87 for _, useReader := range []bool{true, false} { 88 var err error 89 if useReader { 90 // Force ParseReader() to choose un-optimized path by using bufio.NewReader 91 _, err = jws.ParseReader(bufio.NewReader(strings.NewReader(incoming))) 92 } else { 93 _, err = jws.ParseString(incoming) 94 } 95 require.Error(t, err, "Parsing compact serialization with less than 3 parts should be an error") 96 } 97 }) 98 t.Run("Compact bad header", func(t *testing.T) { 99 t.Parallel() 100 parts := strings.Split(exampleCompactSerialization, ".") 101 parts[0] = badValue 102 incoming := strings.Join(parts, ".") 103 104 for _, useReader := range []bool{true, false} { 105 var err error 106 if useReader { 107 _, err = jws.ParseReader(bufio.NewReader(strings.NewReader(incoming))) 108 } else { 109 _, err = jws.ParseString(incoming) 110 } 111 require.Error(t, err, "Parsing compact serialization with bad header should be an error") 112 } 113 }) 114 t.Run("Compact bad payload", func(t *testing.T) { 115 t.Parallel() 116 parts := strings.Split(exampleCompactSerialization, ".") 117 parts[1] = badValue 118 incoming := strings.Join(parts, ".") 119 120 for _, useReader := range []bool{true, false} { 121 var err error 122 if useReader { 123 _, err = jws.ParseReader(bufio.NewReader(strings.NewReader(incoming))) 124 } else { 125 _, err = jws.ParseString(incoming) 126 } 127 require.Error(t, err, "Parsing compact serialization with bad payload should be an error") 128 } 129 }) 130 t.Run("Compact bad signature", func(t *testing.T) { 131 t.Parallel() 132 parts := strings.Split(exampleCompactSerialization, ".") 133 parts[2] = badValue 134 incoming := strings.Join(parts, ".") 135 136 for _, useReader := range []bool{true, false} { 137 var err error 138 if useReader { 139 _, err = jws.ParseReader(bufio.NewReader(strings.NewReader(incoming))) 140 } else { 141 _, err = jws.ParseString(incoming) 142 } 143 require.Error(t, err, "Parsing compact serialization with bad signature should be an error") 144 } 145 }) 146 } 147 148 type dummyCryptoSigner struct { 149 raw crypto.Signer 150 } 151 152 func (s *dummyCryptoSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { 153 return s.raw.Sign(rand, digest, opts) 154 } 155 156 func (s *dummyCryptoSigner) Public() crypto.PublicKey { 157 return s.raw.Public() 158 } 159 160 var _ crypto.Signer = &dummyCryptoSigner{} 161 162 type dummyECDSACryptoSigner struct { 163 raw *ecdsa.PrivateKey 164 } 165 166 func (es *dummyECDSACryptoSigner) Public() crypto.PublicKey { 167 return es.raw.Public() 168 } 169 170 func (es *dummyECDSACryptoSigner) Sign(rand io.Reader, digest []byte, _ crypto.SignerOpts) ([]byte, error) { 171 // The implementation is the same as ecdsaCryptoSigner. 172 // This is just here to test the interface conversion 173 r, s, err := ecdsa.Sign(rand, es.raw, digest) 174 if err != nil { 175 return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err) 176 } 177 178 return asn1.Marshal(struct { 179 R *big.Int 180 S *big.Int 181 }{R: r, S: s}) 182 } 183 184 var _ crypto.Signer = &dummyECDSACryptoSigner{} 185 186 func testRoundtrip(t *testing.T, payload []byte, alg jwa.SignatureAlgorithm, signKey interface{}, keys map[string]interface{}) { 187 jwkKey, err := jwk.FromRaw(signKey) 188 require.NoError(t, err, `jwk.New should succeed`) 189 signKeys := []struct { 190 Name string 191 Key interface{} 192 }{ 193 { 194 Name: "Raw Key", 195 Key: signKey, 196 }, 197 { 198 Name: "JWK Key", 199 Key: jwkKey, 200 }, 201 } 202 203 if es, ok := signKey.(*ecdsa.PrivateKey); ok { 204 signKeys = append(signKeys, struct { 205 Name string 206 Key interface{} 207 }{ 208 Name: "crypto.Hash", 209 Key: &dummyECDSACryptoSigner{raw: es}, 210 }) 211 } else if cs, ok := signKey.(crypto.Signer); ok { 212 signKeys = append(signKeys, struct { 213 Name string 214 Key interface{} 215 }{ 216 Name: "crypto.Hash", 217 Key: &dummyCryptoSigner{raw: cs}, 218 }) 219 } 220 221 for _, key := range signKeys { 222 key := key 223 t.Run(key.Name, func(t *testing.T) { 224 signed, err := jws.Sign(payload, jws.WithKey(alg, key.Key)) 225 require.NoError(t, err, "jws.Sign should succeed") 226 227 parsers := map[string]func([]byte) (*jws.Message, error){ 228 "ParseReader(io.Reader)": func(b []byte) (*jws.Message, error) { return jws.ParseReader(bufio.NewReader(bytes.NewReader(b))) }, 229 "Parse([]byte)": func(b []byte) (*jws.Message, error) { return jws.Parse(b) }, 230 "ParseString(string)": func(b []byte) (*jws.Message, error) { return jws.ParseString(string(b)) }, 231 } 232 for name, f := range parsers { 233 name := name 234 f := f 235 t.Run(name, func(t *testing.T) { 236 t.Parallel() 237 m, err := f(signed) 238 require.NoError(t, err, "(%s) %s is successful", alg, name) 239 require.Equal(t, payload, m.Payload(), "(%s) %s: Payload is decoded", alg, name) 240 }) 241 } 242 243 for name, testKey := range keys { 244 name := name 245 testKey := testKey 246 t.Run(name, func(t *testing.T) { 247 verified, err := jws.Verify(signed, jws.WithKey(alg, testKey)) 248 require.NoError(t, err, "(%s) Verify is successful", alg) 249 require.Equal(t, payload, verified, "(%s) Verified payload is the same", alg) 250 }) 251 } 252 }) 253 } 254 } 255 256 func TestRoundtrip(t *testing.T) { 257 t.Parallel() 258 payload := []byte("Lorem ipsum") 259 260 t.Run("HMAC", func(t *testing.T) { 261 t.Parallel() 262 sharedkey := []byte("Avracadabra") 263 jwkKey, _ := jwk.FromRaw(sharedkey) 264 keys := map[string]interface{}{ 265 "[]byte": sharedkey, 266 "jwk.Key": jwkKey, 267 } 268 hmacAlgorithms := []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} 269 for _, alg := range hmacAlgorithms { 270 alg := alg 271 t.Run(alg.String(), func(t *testing.T) { 272 t.Parallel() 273 testRoundtrip(t, payload, alg, sharedkey, keys) 274 }) 275 } 276 }) 277 t.Run("ECDSA", func(t *testing.T) { 278 t.Parallel() 279 key, err := jwxtest.GenerateEcdsaKey(jwa.P521) 280 require.NoError(t, err, "ECDSA key generated") 281 jwkKey, _ := jwk.FromRaw(key.PublicKey) 282 keys := map[string]interface{}{ 283 "Verify(ecdsa.PublicKey)": key.PublicKey, 284 "Verify(*ecdsa.PublicKey)": &key.PublicKey, 285 "Verify(jwk.Key)": jwkKey, 286 } 287 for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} { 288 alg := alg 289 t.Run(alg.String(), func(t *testing.T) { 290 t.Parallel() 291 testRoundtrip(t, payload, alg, key, keys) 292 }) 293 } 294 }) 295 t.Run("RSA", func(t *testing.T) { 296 t.Parallel() 297 key, err := jwxtest.GenerateRsaKey() 298 require.NoError(t, err, "RSA key generated") 299 jwkKey, _ := jwk.FromRaw(key.PublicKey) 300 keys := map[string]interface{}{ 301 "Verify(rsa.PublicKey)": key.PublicKey, 302 "Verify(*rsa.PublicKey)": &key.PublicKey, 303 "Verify(jwk.Key)": jwkKey, 304 } 305 for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} { 306 alg := alg 307 t.Run(alg.String(), func(t *testing.T) { 308 t.Parallel() 309 testRoundtrip(t, payload, alg, key, keys) 310 }) 311 } 312 }) 313 t.Run("EdDSA", func(t *testing.T) { 314 t.Parallel() 315 key, err := jwxtest.GenerateEd25519Key() 316 require.NoError(t, err, "ed25519 key generated") 317 pubkey := key.Public() 318 jwkKey, _ := jwk.FromRaw(pubkey) 319 keys := map[string]interface{}{ 320 "Verify(ed25519.Public())": pubkey, 321 // Meh, this doesn't work 322 // "Verify(*ed25519.Public())": &pubkey, 323 "Verify(jwk.Key)": jwkKey, 324 } 325 for _, alg := range []jwa.SignatureAlgorithm{jwa.EdDSA} { 326 alg := alg 327 t.Run(alg.String(), func(t *testing.T) { 328 t.Parallel() 329 testRoundtrip(t, payload, alg, key, keys) 330 }) 331 } 332 }) 333 } 334 335 func TestSignMulti2(t *testing.T) { 336 sharedkey := []byte("Avracadabra") 337 payload := []byte("Lorem ipsum") 338 hmacAlgorithms := []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} 339 var signed []byte 340 t.Run("Sign", func(t *testing.T) { 341 var options = []jws.SignOption{jws.WithJSON()} 342 for _, alg := range hmacAlgorithms { 343 options = append(options, jws.WithKey(alg, sharedkey)) // (signer, sharedkey, nil, nil)) 344 } 345 var err error 346 signed, err = jws.Sign(payload, options...) 347 require.NoError(t, err, `jws.SignMulti should succeed`) 348 }) 349 for _, alg := range hmacAlgorithms { 350 alg := alg 351 t.Run("Verify "+alg.String(), func(t *testing.T) { 352 m := jws.NewMessage() 353 verified, err := jws.Verify(signed, jws.WithKey(alg, sharedkey), jws.WithMessage(m)) 354 require.NoError(t, err, "Verify succeeded") 355 require.Equal(t, payload, verified, "verified payload matches") 356 357 // XXX This actally doesn't really test much, but if there was anything 358 // wrong, the process should have failed well before reaching here 359 require.Equal(t, payload, m.Payload(), "message payload matches") 360 }) 361 } 362 } 363 364 func TestEncode(t *testing.T) { 365 t.Parallel() 366 367 // HS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.1 works 368 t.Run("HS256Compact", func(t *testing.T) { 369 t.Parallel() 370 const hdr = `{"typ":"JWT",` + "\r\n" + ` "alg":"HS256"}` 371 const hmacKey = `AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow` 372 const expected = `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk` 373 374 hmacKeyDecoded, err := base64.DecodeString(hmacKey) 375 require.NoError(t, err, "HMAC base64 decoded successful") 376 377 hdrbuf := base64.Encode([]byte(hdr)) 378 payload := base64.Encode([]byte(examplePayload)) 379 380 signingInput := bytes.Join( 381 [][]byte{ 382 hdrbuf, 383 payload, 384 }, 385 []byte{'.'}, 386 ) 387 388 sign, err := jws.NewSigner(jwa.HS256) 389 require.NoError(t, err, "HMAC signer created successfully") 390 391 signature, err := sign.Sign(signingInput, hmacKeyDecoded) 392 require.NoError(t, err, "PayloadSign is successful") 393 sigbuf := base64.Encode(signature) 394 395 encoded := bytes.Join( 396 [][]byte{ 397 signingInput, 398 sigbuf, 399 }, 400 []byte{'.'}, 401 ) 402 require.Equal(t, expected, string(encoded), "generated compact serialization should match") 403 404 msg, err := jws.ParseReader(bytes.NewReader(encoded)) 405 require.NoError(t, err, "Parsing compact encoded serialization succeeds") 406 407 signatures := msg.Signatures() 408 require.Len(t, signatures, 1, `there should be exactly one signature`) 409 410 algorithm := signatures[0].ProtectedHeaders().Algorithm() 411 if algorithm != jwa.HS256 { 412 t.Fatal("Algorithm in header does not match") 413 } 414 415 v, err := jws.NewVerifier(jwa.HS256) 416 require.NoError(t, err, "HmacVerify created") 417 418 require.NoError(t, v.Verify(signingInput, signature, hmacKeyDecoded), "Verify succeeds") 419 }) 420 t.Run("ES512Compact", func(t *testing.T) { 421 t.Parallel() 422 // ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works 423 hdr := []byte{123, 34, 97, 108, 103, 34, 58, 34, 69, 83, 53, 49, 50, 34, 125} 424 const jwksrc = `{ 425 "kty":"EC", 426 "crv":"P-521", 427 "x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk", 428 "y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2", 429 "d":"AY5pb7A0UFiB3RELSD64fTLOSV_jazdF7fLYyuTw8lOfRhWg6Y6rUrPAxerEzgdRhajnu0ferB0d53vM9mE15j2C" 430 }` 431 432 // "Payload" 433 jwsPayload := []byte{80, 97, 121, 108, 111, 97, 100} 434 435 standardHeaders := jws.NewHeaders() 436 require.NoError(t, json.Unmarshal(hdr, standardHeaders), `parsing headers should succeed`) 437 438 alg := standardHeaders.Algorithm() 439 440 jwkKey, err := jwk.ParseKey([]byte(jwksrc)) 441 if err != nil { 442 t.Fatal("Failed to parse JWK") 443 } 444 var key interface{} 445 require.NoError(t, jwkKey.Raw(&key), `jwk.Raw should succeed`) 446 var jwsCompact []byte 447 jwsCompact, err = jws.Sign(jwsPayload, jws.WithKey(alg, key)) 448 if err != nil { 449 t.Fatal("Failed to sign message") 450 } 451 452 // Verify with standard ecdsa library 453 _, _, jwsSignature, err := jws.SplitCompact(jwsCompact) 454 if err != nil { 455 t.Fatal("Failed to split compact JWT") 456 } 457 458 decodedJwsSignature, err := base64.Decode(jwsSignature) 459 require.NoError(t, err, `base64.Decode should succeed`) 460 r, s := &big.Int{}, &big.Int{} 461 n := len(decodedJwsSignature) / 2 462 r.SetBytes(decodedJwsSignature[:n]) 463 s.SetBytes(decodedJwsSignature[n:]) 464 signingHdr := base64.Encode(hdr) 465 signingPayload := base64.Encode(jwsPayload) 466 467 jwsSigningInput := bytes.Join( 468 [][]byte{ 469 signingHdr, 470 signingPayload, 471 }, 472 []byte{'.'}, 473 ) 474 hashed512 := sha512.Sum512(jwsSigningInput) 475 ecdsaPrivateKey := key.(*ecdsa.PrivateKey) 476 require.True(t, ecdsa.Verify(&ecdsaPrivateKey.PublicKey, hashed512[:], r, s), "ecdsa.Verify should succeed") 477 478 // Verify with API library 479 publicKey, err := jwk.PublicRawKeyOf(key) 480 require.NoError(t, err, `jwk.PublicRawKeyOf should succeed`) 481 verifiedPayload, err := jws.Verify(jwsCompact, jws.WithKey(alg, publicKey)) 482 if err != nil || string(verifiedPayload) != string(jwsPayload) { 483 t.Fatal("Failed to verify message") 484 } 485 }) 486 t.Run("RS256Compact", func(t *testing.T) { 487 t.Parallel() 488 // RS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.2 works 489 const hdr = `{"alg":"RS256"}` 490 const expected = `eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw` 491 const jwksrc = `{ 492 "kty":"RSA", 493 "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", 494 "e":"AQAB", 495 "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ", 496 "p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc", 497 "q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc", 498 "dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0", 499 "dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU", 500 "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U" 501 }` 502 503 privkey, err := jwk.ParseKey([]byte(jwksrc)) 504 require.NoError(t, err, `parsing jwk should be successful`) 505 506 var rawkey rsa.PrivateKey 507 require.NoError(t, privkey.Raw(&rawkey), `obtaining raw key should succeed`) 508 509 sign, err := jws.NewSigner(jwa.RS256) 510 require.NoError(t, err, "RsaSign created successfully") 511 512 hdrbuf := base64.Encode([]byte(hdr)) 513 payload := base64.Encode([]byte(examplePayload)) 514 signingInput := bytes.Join( 515 [][]byte{ 516 hdrbuf, 517 payload, 518 }, 519 []byte{'.'}, 520 ) 521 signature, err := sign.Sign(signingInput, rawkey) 522 require.NoError(t, err, "PayloadSign is successful") 523 sigbuf := base64.Encode(signature) 524 525 encoded := bytes.Join( 526 [][]byte{ 527 signingInput, 528 sigbuf, 529 }, 530 []byte{'.'}, 531 ) 532 533 require.Equal(t, expected, string(encoded), "generated compact serialization should match") 534 535 msg, err := jws.ParseReader(bytes.NewReader(encoded)) 536 require.NoError(t, err, "Parsing compact encoded serialization succeeds") 537 538 signatures := msg.Signatures() 539 require.Len(t, signatures, 1, `there should be exactly one signature`) 540 541 algorithm := signatures[0].ProtectedHeaders().Algorithm() 542 if algorithm != jwa.RS256 { 543 t.Fatal("Algorithm in header does not match") 544 } 545 546 v, err := jws.NewVerifier(jwa.RS256) 547 require.NoError(t, err, "Verify created") 548 require.NoError(t, v.Verify(signingInput, signature, rawkey.PublicKey), "Verify succeeds") 549 }) 550 t.Run("ES256Compact", func(t *testing.T) { 551 t.Parallel() 552 // ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works 553 const hdr = `{"alg":"ES256"}` 554 const jwksrc = `{ 555 "kty":"EC", 556 "crv":"P-256", 557 "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", 558 "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", 559 "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI" 560 }` 561 privkey, err := jwk.ParseKey([]byte(jwksrc)) 562 require.NoError(t, err, `parsing jwk should succeed`) 563 564 var rawkey ecdsa.PrivateKey 565 require.NoError(t, privkey.Raw(&rawkey), `obtaining raw key should succeed`) 566 567 signer, err := jws.NewSigner(jwa.ES256) 568 require.NoError(t, err, "RsaSign created successfully") 569 570 hdrbuf := base64.Encode([]byte(hdr)) 571 payload := base64.Encode([]byte(examplePayload)) 572 signingInput := bytes.Join( 573 [][]byte{ 574 hdrbuf, 575 payload, 576 }, 577 []byte{'.'}, 578 ) 579 signature, err := signer.Sign(signingInput, &rawkey) 580 require.NoError(t, err, "PayloadSign is successful") 581 sigbuf := base64.Encode(signature) 582 require.NoError(t, err, "base64 encode successful") 583 584 encoded := bytes.Join( 585 [][]byte{ 586 signingInput, 587 sigbuf, 588 }, 589 []byte{'.'}, 590 ) 591 592 // The signature contains random factor, so unfortunately we can't match 593 // the output against a fixed expected outcome. We'll wave doing an 594 // exact match, and just try to verify using the signature 595 596 msg, err := jws.ParseReader(bytes.NewReader(encoded)) 597 require.NoError(t, err, "Parsing compact encoded serialization succeeds") 598 599 signatures := msg.Signatures() 600 require.Len(t, signatures, 1, `there should be exactly one signature`) 601 602 algorithm := signatures[0].ProtectedHeaders().Algorithm() 603 if algorithm != jwa.ES256 { 604 t.Fatal("Algorithm in header does not match") 605 } 606 607 v, err := jws.NewVerifier(jwa.ES256) 608 require.NoError(t, err, "EcdsaVerify created") 609 require.NoError(t, v.Verify(signingInput, signature, rawkey.PublicKey), "Verify succeeds") 610 }) 611 t.Run("EdDSACompact", func(t *testing.T) { 612 t.Parallel() 613 // EdDSACompact tests that https://tools.ietf.org/html/rfc8037#appendix-A.1-5 works 614 const hdr = `{"alg":"EdDSA"}` 615 const jwksrc = `{ 616 "kty":"OKP", 617 "crv":"Ed25519", 618 "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A", 619 "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" 620 }` 621 const examplePayload = `Example of Ed25519 signing` 622 const expected = `hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg` 623 expectedDecoded, err := base64.Decode([]byte(expected)) 624 require.NoError(t, err, "Expected Signature decode successful") 625 626 privkey, err := jwk.ParseKey([]byte(jwksrc)) 627 require.NoError(t, err, `parsing jwk should succeed`) 628 629 var rawkey ed25519.PrivateKey 630 require.NoError(t, privkey.Raw(&rawkey), `obtaining raw key should succeed`) 631 632 signer, err := jws.NewSigner(jwa.EdDSA) 633 require.NoError(t, err, "EdDSASign created successfully") 634 635 hdrbuf := base64.Encode([]byte(hdr)) 636 payload := base64.Encode([]byte(examplePayload)) 637 signingInput := bytes.Join( 638 [][]byte{ 639 hdrbuf, 640 payload, 641 }, 642 []byte{'.'}, 643 ) 644 645 signature, err := signer.Sign(signingInput, rawkey) 646 require.NoError(t, err, "PayloadSign is successful") 647 sigbuf := base64.Encode(signature) 648 encoded := bytes.Join( 649 [][]byte{ 650 signingInput, 651 sigbuf, 652 }, 653 []byte{'.'}, 654 ) 655 656 // The signature contains random factor, so unfortunately we can't match 657 // the output against a fixed expected outcome. We'll wave doing an 658 // exact match, and just try to verify using the signature 659 660 msg, err := jws.ParseReader(bytes.NewReader(encoded)) 661 require.NoError(t, err, "Parsing compact encoded serialization succeeds") 662 663 signatures := msg.Signatures() 664 require.Len(t, signatures, 1, `there should be exactly one signature`) 665 666 algorithm := signatures[0].ProtectedHeaders().Algorithm() 667 if algorithm != jwa.EdDSA { 668 t.Fatal("Algorithm in header does not match") 669 } 670 671 v, err := jws.NewVerifier(jwa.EdDSA) 672 require.NoError(t, err, "EcdsaVerify created") 673 require.NoError(t, v.Verify(signingInput, signature, rawkey.Public()), "Verify succeeds") 674 require.Equal(t, signature, expectedDecoded, "signatures match") 675 }) 676 t.Run("UnsecuredCompact", func(t *testing.T) { 677 t.Parallel() 678 s := `eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.` 679 680 m, err := jws.ParseReader(strings.NewReader(s)) 681 require.NoError(t, err, "Parsing compact serialization") 682 683 { 684 v := map[string]interface{}{} 685 require.NoError(t, json.Unmarshal(m.Payload(), &v), "Unmarshal payload") 686 require.Equal(t, v["iss"], "joe", "iss matches") 687 require.Equal(t, int(v["exp"].(float64)), 1300819380, "exp matches") 688 require.Equal(t, v["http://example.com/is_root"], true, "'http://example.com/is_root' matches") 689 } 690 691 require.Len(t, m.Signatures(), 1, "There should be 1 signature") 692 693 signatures := m.Signatures() 694 algorithm := signatures[0].ProtectedHeaders().Algorithm() 695 if algorithm != jwa.NoSignature { 696 t.Fatal("Algorithm in header does not match") 697 } 698 699 require.Empty(t, signatures[0].Signature(), "Signature should be empty") 700 }) 701 t.Run("CompleteJSON", func(t *testing.T) { 702 t.Parallel() 703 s := `{ 704 "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", 705 "signatures":[ 706 { 707 "header": {"kid":"2010-12-29"}, 708 "protected":"eyJhbGciOiJSUzI1NiJ9", 709 "signature": "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw" 710 }, 711 { 712 "header": {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"}, 713 "protected":"eyJhbGciOiJFUzI1NiJ9", 714 "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q" 715 } 716 ] 717 }` 718 719 m, err := jws.ParseReader(strings.NewReader(s)) 720 require.NoError(t, err, "Unmarshal complete json serialization") 721 require.Len(t, m.Signatures(), 2, "There should be 2 signatures") 722 723 sigs := m.LookupSignature("2010-12-29") 724 require.Len(t, sigs, 1, "There should be 1 signature with kid = '2010-12-29'") 725 }) 726 t.Run("Protected Header lookup", func(t *testing.T) { 727 t.Parallel() 728 s := `{ 729 "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", 730 "signatures":[ 731 { 732 "header": {"cty":"example"}, 733 "protected":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImU5YmMwOTdhLWNlNTEtNDAzNi05NTYyLWQyYWRlODgyZGIwZCJ9", 734 "signature": "JcLb1udPAV72TayGv6eawZKlIQQ3K1NzB0fU7wwYoFypGxEczdCQU-V9jp4WwY2ueJKYeE4fF6jigB0PdSKR0Q" 735 } 736 ] 737 }` 738 739 // Protected Header is {"alg":"ES256","kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"} 740 // This protected header combination forces the parser/unmarshal to go trough the code path to populate and look for protected header fields. 741 // The signature is valid. 742 743 m, err := jws.ParseReader(strings.NewReader(s)) 744 require.NoError(t, err, "Unmarshal complete json serialization") 745 require.Len(t, m.Signatures(), 1, "There should be 1 signature") 746 747 sigs := m.LookupSignature("e9bc097a-ce51-4036-9562-d2ade882db0d") 748 require.Len(t, sigs, 1, "There should be 1 signature with kid = '2010-12-29'") 749 }) 750 t.Run("FlattenedJSON", func(t *testing.T) { 751 t.Parallel() 752 s := `{ 753 "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", 754 "protected":"eyJhbGciOiJFUzI1NiJ9", 755 "header": { 756 "kid":"e9bc097a-ce51-4036-9562-d2ade882db0d" 757 }, 758 "signature": "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q" 759 }` 760 761 m, err := jws.ParseReader(strings.NewReader(s)) 762 require.NoError(t, err, "Parsing flattened json serialization") 763 require.Len(t, m.Signatures(), 1, "There should be 1 signature") 764 765 jsonbuf, _ := json.MarshalIndent(m, "", " ") 766 t.Logf("%s", jsonbuf) 767 }) 768 t.Run("SplitCompact", func(t *testing.T) { 769 testcases := []struct { 770 Name string 771 Size int 772 }{ 773 {Name: "Short", Size: 100}, 774 {Name: "Long", Size: 8000}, 775 } 776 for _, tc := range testcases { 777 size := tc.Size 778 t.Run(tc.Name, func(t *testing.T) { 779 t.Parallel() 780 // Create payload with X.Y.Z 781 var payload []byte 782 for i := 0; i < size; i++ { 783 payload = append(payload, 'X') 784 } 785 payload = append(payload, '.') 786 for i := 0; i < size; i++ { 787 payload = append(payload, 'Y') 788 } 789 payload = append(payload, '.') 790 791 for i := 0; i < size; i++ { 792 payload = append(payload, 'Y') 793 } 794 795 // Test using bytes, reader optimized and non-optimized path 796 for _, method := range []int{0, 1, 2} { 797 var x, y, z []byte 798 var err error 799 switch method { 800 case 0: // bytes 801 x, y, z, err = jws.SplitCompact(payload) 802 case 1: // un-optimized io.Reader 803 x, y, z, err = jws.SplitCompactReader(bytes.NewReader(payload)) 804 default: // optimized io.Reader 805 x, y, z, err = jws.SplitCompactReader(bufio.NewReader(bytes.NewReader(payload))) 806 } 807 require.NoError(t, err, "SplitCompact should succeed") 808 require.Len(t, x, size, "Length of header") 809 require.Len(t, y, size, "Length of payload") 810 require.Len(t, z, size, "Length of signature") 811 } 812 }) 813 } 814 }) 815 } 816 817 func TestPublicHeaders(t *testing.T) { 818 key, err := jwxtest.GenerateRsaKey() 819 require.NoError(t, err, "GenerateKey should succeed") 820 821 signer, err := jws.NewSigner(jwa.RS256) 822 require.NoError(t, err, "jws.NewSigner should succeed") 823 _ = signer // TODO 824 825 pubkey := key.PublicKey 826 pubjwk, err := jwk.FromRaw(&pubkey) 827 require.NoError(t, err, "NewRsaPublicKey should succeed") 828 _ = pubjwk // TODO 829 } 830 831 func TestDecode_ES384Compact_NoSigTrim(t *testing.T) { 832 incoming := "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCIsImtpZCI6IjE5MzFmZTQ0YmFhMWNhZTkyZWUzNzYzOTQ0MDU1OGMwODdlMTRlNjk5ZWU5NjVhM2Q1OGU1MmU2NGY4MDE0NWIifQ.eyJpc3MiOiJicmt0LWNsaS0xLjAuN3ByZTEiLCJpYXQiOjE0ODQ2OTU1MjAsImp0aSI6IjgxYjczY2Y3In0.DdFi0KmPHSv4PfIMGcWGMSRLmZsfRPQ3muLFW6Ly2HpiLFFQWZ0VEanyrFV263wjlp3udfedgw_vrBLz3XC8CkbvCo_xeHMzaTr_yfhjoheSj8gWRLwB-22rOnUX_M0A" 833 t.Logf("incoming = '%s'", incoming) 834 const jwksrc = `{ 835 "kty":"EC", 836 "crv":"P-384", 837 "x":"YHVZ4gc1RDoqxKm4NzaN_Y1r7R7h3RM3JMteC478apSKUiLVb4UNytqWaLoE6ygH", 838 "y":"CRKSqP-aYTIsqJfg_wZEEYUayUR5JhZaS2m4NLk2t1DfXZgfApAJ2lBO0vWKnUMp" 839 }` 840 841 pubkey, err := jwk.ParseKey([]byte(jwksrc)) 842 require.NoError(t, err, `parsing jwk should be successful`) 843 844 var rawkey ecdsa.PublicKey 845 require.NoError(t, pubkey.Raw(&rawkey), `obtaining raw key should succeed`) 846 847 v, err := jws.NewVerifier(jwa.ES384) 848 require.NoError(t, err, "EcdsaVerify created") 849 850 protected, payload, signature, err := jws.SplitCompact([]byte(incoming)) 851 require.NoError(t, err, `jws.SplitCompact should succeed`) 852 853 var buf bytes.Buffer 854 buf.Write(protected) 855 buf.WriteByte('.') 856 buf.Write(payload) 857 858 decodedSignature, err := base64.Decode(signature) 859 require.NoError(t, err, `decoding signature should succeed`) 860 require.NoError(t, v.Verify(buf.Bytes(), decodedSignature, rawkey), "Verify succeeds") 861 } 862 863 func TestReadFile(t *testing.T) { 864 t.Parallel() 865 866 f, err := os.CreateTemp("", "test-read-file-*.jws") 867 require.NoError(t, err, `io.CreateTemp should succeed`) 868 defer f.Close() 869 870 fmt.Fprintf(f, "%s", exampleCompactSerialization) 871 872 if _, err := jws.ReadFile(f.Name()); !assert.NoError(t, err, `jws.ReadFile should succeed`) { 873 return 874 } 875 } 876 877 func TestVerifyNonUniqueKid(t *testing.T) { 878 const payload = "Lorem ipsum" 879 const kid = "notUniqueKid" 880 privateKey, err := jwxtest.GenerateRsaJwk() 881 require.NoError(t, err, "jwxtest.GenerateJwk should succeed") 882 _ = privateKey.Set(jwk.KeyIDKey, kid) 883 signed, err := jws.Sign([]byte(payload), jws.WithKey(jwa.RS256, privateKey)) 884 require.NoError(t, err, `jws.Sign should succeed`) 885 correctKey, _ := jwk.PublicKeyOf(privateKey) 886 _ = correctKey.Set(jwk.AlgorithmKey, jwa.RS256) 887 888 makeSet := func(keys ...jwk.Key) jwk.Set { 889 set := jwk.NewSet() 890 for _, key := range keys { 891 _ = set.AddKey(key) 892 } 893 return set 894 } 895 896 testcases := []struct { 897 Name string 898 Key func() jwk.Key // Generates the "wrong" key 899 }{ 900 { 901 Name: `match 2 keys via same "kid"`, 902 Key: func() jwk.Key { 903 privateKey, _ := jwxtest.GenerateRsaJwk() 904 wrongKey, _ := jwk.PublicKeyOf(privateKey) 905 _ = wrongKey.Set(jwk.KeyIDKey, kid) 906 _ = wrongKey.Set(jwk.AlgorithmKey, jwa.RS256) 907 return wrongKey 908 }, 909 }, 910 { 911 Name: `match 2 keys via same "kid", same key value but different alg`, 912 Key: func() jwk.Key { 913 wrongKey, _ := correctKey.Clone() 914 _ = wrongKey.Set(jwk.KeyIDKey, kid) 915 _ = wrongKey.Set(jwk.AlgorithmKey, jwa.RS512) 916 return wrongKey 917 }, 918 }, 919 { 920 Name: `match 2 keys via same "kid", same key type but different alg`, 921 Key: func() jwk.Key { 922 privateKey, _ := jwxtest.GenerateRsaJwk() 923 wrongKey, _ := jwk.PublicKeyOf(privateKey) 924 _ = wrongKey.Set(jwk.KeyIDKey, kid) 925 _ = wrongKey.Set(jwk.AlgorithmKey, jwa.RS512) 926 return wrongKey 927 }, 928 }, 929 { 930 Name: `match 2 keys via same "kid" and different key type / alg`, 931 Key: func() jwk.Key { 932 privateKey, _ := jwxtest.GenerateEcdsaKey(jwa.P256) 933 wrongKey, err := jwk.PublicKeyOf(privateKey) 934 require.NoError(t, err, `jwk.PublicKeyOf should succeed`) 935 _ = wrongKey.Set(jwk.KeyIDKey, kid) 936 _ = wrongKey.Set(jwk.AlgorithmKey, jwa.ES256K) 937 return wrongKey 938 }, 939 }, 940 } 941 942 for _, tc := range testcases { 943 tc := tc 944 wrongKey, err := tc.Key().Clone() 945 require.NoError(t, err, `cloning wrong key should succeed`) 946 for _, set := range []jwk.Set{makeSet(wrongKey, correctKey), makeSet(correctKey, wrongKey)} { 947 set := set 948 t.Run(tc.Name, func(t *testing.T) { 949 // Try matching in different orders 950 var usedKey jwk.Key 951 _, err = jws.Verify(signed, jws.WithKeySet(set, jws.WithMultipleKeysPerKeyID(true)), jws.WithKeyUsed(&usedKey)) 952 require.NoError(t, err, `jws.Verify should succeed`) 953 require.Equal(t, usedKey, correctKey) 954 }) 955 } 956 } 957 } 958 959 func TestVerifySet(t *testing.T) { 960 t.Parallel() 961 const payload = "Lorem ipsum" 962 963 makeSet := func(privkey jwk.Key) jwk.Set { 964 set := jwk.NewSet() 965 k1, _ := jwk.FromRaw([]byte("abracadabra")) 966 set.AddKey(k1) 967 k2, _ := jwk.FromRaw([]byte("opensesame")) 968 set.AddKey(k2) 969 pubkey, _ := jwk.PublicKeyOf(privkey) 970 pubkey.Set(jwk.AlgorithmKey, jwa.RS256) 971 set.AddKey(pubkey) 972 return set 973 } 974 975 for _, useJSON := range []bool{true, false} { 976 useJSON := useJSON 977 t.Run(fmt.Sprintf("useJSON=%t", useJSON), func(t *testing.T) { 978 t.Parallel() 979 t.Run(`match via "alg"`, func(t *testing.T) { 980 t.Parallel() 981 key, err := jwxtest.GenerateRsaJwk() 982 require.NoError(t, err, "jwxtest.GenerateJwk should succeed") 983 984 set := makeSet(key) 985 signed, err := jws.Sign([]byte(payload), jws.WithKey(jwa.RS256, key)) 986 require.NoError(t, err, `jws.Sign should succeed`) 987 if useJSON { 988 m, err := jws.Parse(signed) 989 require.NoError(t, err, `jws.Parse should succeed`) 990 signed, err = json.Marshal(m) 991 require.NoError(t, err, `json.Marshal should succeed`) 992 } 993 994 var used jwk.Key 995 verified, err := jws.Verify(signed, jws.WithKeySet(set, jws.WithRequireKid(false)), jws.WithKeyUsed(&used)) 996 require.NoError(t, err, `jws.Verify should succeed`) 997 require.Equal(t, []byte(payload), verified, `payload should match`) 998 expected, _ := jwk.PublicKeyOf(key) 999 thumb1, _ := expected.Thumbprint(crypto.SHA1) 1000 thumb2, _ := used.Thumbprint(crypto.SHA1) 1001 require.Equal(t, thumb1, thumb2, `keys should match`) 1002 }) 1003 t.Run(`match via "kid"`, func(t *testing.T) { 1004 t.Parallel() 1005 1006 key, err := jwxtest.GenerateRsaJwk() 1007 require.NoError(t, err, "jwxtest.GenerateJwk should succeed") 1008 key.Set(jwk.KeyIDKey, `mykey`) 1009 1010 set := makeSet(key) 1011 signed, err := jws.Sign([]byte(payload), jws.WithKey(jwa.RS256, key)) 1012 require.NoError(t, err, `jws.Sign should succeed`) 1013 if useJSON { 1014 m, err := jws.Parse(signed) 1015 require.NoError(t, err, `jws.Parse should succeed`) 1016 signed, err = json.Marshal(m) 1017 require.NoError(t, err, `json.Marshal should succeed`) 1018 } 1019 1020 var used jwk.Key 1021 verified, err := jws.Verify(signed, jws.WithKeySet(set), jws.WithKeyUsed(&used)) 1022 require.NoError(t, err, `jws.Verify should succeed`) 1023 require.Equal(t, []byte(payload), verified, `payload should match`) 1024 expected, _ := jwk.PublicKeyOf(key) 1025 thumb1, _ := expected.Thumbprint(crypto.SHA1) 1026 thumb2, _ := used.Thumbprint(crypto.SHA1) 1027 require.Equal(t, thumb1, thumb2, `keys should match`) 1028 }) 1029 }) 1030 } 1031 } 1032 1033 func TestCustomField(t *testing.T) { 1034 // XXX has global effect!!! 1035 jws.RegisterCustomField(`x-birthday`, time.Time{}) 1036 defer jws.RegisterCustomField(`x-birthday`, nil) 1037 1038 expected := time.Date(2015, 11, 4, 5, 12, 52, 0, time.UTC) 1039 bdaybytes, _ := expected.MarshalText() // RFC3339 1040 1041 payload := "Hello, World!" 1042 privkey, err := jwxtest.GenerateRsaJwk() 1043 require.NoError(t, err, `jwxtest.GenerateRsaJwk() should succeed`) 1044 1045 hdrs := jws.NewHeaders() 1046 hdrs.Set(`x-birthday`, string(bdaybytes)) 1047 1048 signed, err := jws.Sign([]byte(payload), jws.WithKey(jwa.RS256, privkey, jws.WithProtectedHeaders(hdrs))) 1049 require.NoError(t, err, `jws.Sign should succeed`) 1050 1051 t.Run("jws.Parse + json.Unmarshal", func(t *testing.T) { 1052 msg, err := jws.Parse(signed) 1053 require.NoError(t, err, `jws.Parse should succeed`) 1054 1055 v, ok := msg.Signatures()[0].ProtectedHeaders().Get(`x-birthday`) 1056 require.True(t, ok, `msg.Signatures()[0].ProtectedHeaders().Get("x-birthday") should succeed`) 1057 require.Equal(t, expected, v, `values should match`) 1058 1059 // Create JSON from jws.Message 1060 buf, err := json.Marshal(msg) 1061 require.NoError(t, err, `json.Marshal should succeed`) 1062 1063 var msg2 jws.Message 1064 require.NoError(t, json.Unmarshal(buf, &msg2), `json.Unmarshal should succeed`) 1065 1066 v, ok = msg2.Signatures()[0].ProtectedHeaders().Get(`x-birthday`) 1067 require.True(t, ok, `msg2.Signatures()[0].ProtectedHeaders().Get("x-birthday") should succeed`) 1068 require.Equal(t, expected, v, `values should match`) 1069 }) 1070 } 1071 1072 func TestWithMessage(t *testing.T) { 1073 key, err := jwxtest.GenerateRsaKey() 1074 require.NoError(t, err, "jwxtest.Generate should succeed") 1075 1076 const text = "hello, world" 1077 signed, err := jws.Sign([]byte(text), jws.WithKey(jwa.RS256, key)) 1078 require.NoError(t, err, `jws.Sign should succeed`) 1079 1080 m := jws.NewMessage() 1081 payload, err := jws.Verify(signed, jws.WithKey(jwa.RS256, key.PublicKey), jws.WithMessage(m)) 1082 require.NoError(t, err, `jws.Verify should succeed`) 1083 require.Equal(t, payload, []byte(text), `jws.Verify should produce the correct payload`) 1084 1085 parsed, err := jws.Parse(signed) 1086 require.NoError(t, err, `jws.Parse should succeed`) 1087 1088 // The result of using jws.WithMessage should match the result of jws.Parse 1089 buf1, _ := json.Marshal(m) 1090 buf2, _ := json.Marshal(parsed) 1091 1092 require.Equal(t, buf1, buf2, `result of jws.PArse and jws.Verify(..., jws.WithMessage()) should match`) 1093 } 1094 1095 func TestRFC7797(t *testing.T) { 1096 const keysrc = `{"kty":"oct", 1097 "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow" 1098 }` 1099 1100 key, err := jwk.ParseKey([]byte(keysrc)) 1101 require.NoError(t, err, `jwk.Parse should succeed`) 1102 1103 t.Run("Invalid payload when b64 = false and NOT detached", func(t *testing.T) { 1104 const payload = `$.02` 1105 hdrs := jws.NewHeaders() 1106 hdrs.Set("b64", false) 1107 hdrs.Set("crit", "b64") 1108 1109 _, err := jws.Sign([]byte(payload), jws.WithKey(jwa.HS256, key, jws.WithProtectedHeaders(hdrs))) 1110 require.Error(t, err, `jws.Sign should fail`) 1111 }) 1112 t.Run("Invalid usage when b64 = false and NOT detached", func(t *testing.T) { 1113 const payload = `$.02` 1114 hdrs := jws.NewHeaders() 1115 hdrs.Set("b64", false) 1116 hdrs.Set("crit", "b64") 1117 1118 _, err := jws.Sign([]byte(payload), jws.WithKey(jwa.HS256, key, jws.WithProtectedHeaders(hdrs)), jws.WithDetachedPayload([]byte(payload))) 1119 require.Error(t, err, `jws.Sign should fail`) 1120 }) 1121 t.Run("Valid payload when b64 = false", func(t *testing.T) { 1122 testcases := []struct { 1123 Name string 1124 Payload []byte 1125 Detached bool 1126 }{ 1127 { 1128 Name: `(Detached) payload contains a period`, 1129 Payload: []byte(`$.02`), 1130 Detached: true, 1131 }, 1132 { 1133 Name: `(NOT detached) payload does not contain a period`, 1134 Payload: []byte(`hell0w0rld`), 1135 }, 1136 } 1137 1138 for _, tc := range testcases { 1139 tc := tc 1140 t.Run(tc.Name, func(t *testing.T) { 1141 hdrs := jws.NewHeaders() 1142 hdrs.Set("b64", false) 1143 hdrs.Set("crit", "b64") 1144 1145 payload := tc.Payload 1146 signOptions := []jws.SignOption{jws.WithKey(jwa.HS256, key, jws.WithProtectedHeaders(hdrs))} 1147 var verifyOptions []jws.VerifyOption 1148 verifyOptions = append(verifyOptions, jws.WithKey(jwa.HS256, key)) 1149 if tc.Detached { 1150 signOptions = append(signOptions, jws.WithDetachedPayload(payload)) 1151 verifyOptions = append(verifyOptions, jws.WithDetachedPayload(payload)) 1152 payload = nil 1153 } 1154 signed, err := jws.Sign(payload, signOptions...) 1155 require.NoError(t, err, `jws.Sign should succeed`) 1156 1157 verified, err := jws.Verify(signed, verifyOptions...) 1158 require.NoError(t, err, `jws.Verify should succeed`) 1159 require.Equal(t, tc.Payload, verified, `payload should match`) 1160 }) 1161 } 1162 }) 1163 1164 t.Run("Verify", func(t *testing.T) { 1165 detached := []byte(`$.02`) 1166 testcases := []struct { 1167 Name string 1168 Input []byte 1169 VerifyOptions []jws.VerifyOption 1170 Error bool 1171 }{ 1172 { 1173 Name: "JSON format", 1174 Input: []byte(`{ 1175 "protected": "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19", 1176 "payload": "$.02", 1177 "signature": "A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY" 1178 }`), 1179 }, 1180 { 1181 Name: "JSON format (detached payload)", 1182 VerifyOptions: []jws.VerifyOption{ 1183 jws.WithDetachedPayload(detached), 1184 }, 1185 Input: []byte(`{ 1186 "protected": "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19", 1187 "signature": "A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY" 1188 }`), 1189 }, 1190 { 1191 Name: "JSON Format (b64 does not match)", 1192 Error: true, 1193 Input: []byte(`{ 1194 "signatures": [ 1195 { 1196 "protected": "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19", 1197 "signature": "A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY" 1198 }, 1199 { 1200 "protected": "eyJhbGciOiJIUzI1NiIsImI2NCI6dHJ1ZSwiY3JpdCI6WyJiNjQiXX0", 1201 "signature": "6BjugbC8MfrT_yy5WxWVFZrEHVPDtpdsV9u-wbzQDV8" 1202 } 1203 ], 1204 "payload":"$.02" 1205 }`), 1206 }, 1207 { 1208 Name: "Compact (detached payload)", 1209 Input: []byte(`eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..A5dxf2s96_n5FLueVuW1Z_vh161FwXZC4YLPff6dmDY`), 1210 VerifyOptions: []jws.VerifyOption{ 1211 jws.WithDetachedPayload(detached), 1212 }, 1213 }, 1214 } 1215 1216 for _, tc := range testcases { 1217 tc := tc 1218 t.Run(tc.Name, func(t *testing.T) { 1219 options := tc.VerifyOptions 1220 options = append(options, jws.WithKey(jwa.HS256, key)) 1221 payload, err := jws.Verify(tc.Input, options...) 1222 if tc.Error { 1223 require.Error(t, err, `jws.Verify should fail`) 1224 require.False(t, jws.IsVerificationError(err), `jws.IsVerifyError should return false`) 1225 } else { 1226 require.NoError(t, err, `jws.Verify should succeed`) 1227 require.Equal(t, detached, payload, `payload should match`) 1228 } 1229 }) 1230 } 1231 }) 1232 } 1233 1234 func TestGH485(t *testing.T) { 1235 const payload = `eyJhIjoiYiJ9` 1236 const protected = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImNyaXQiOlsiZXhwIl0sImV4cCI6MCwiaXNzIjoiZm9vIiwibmJmIjowLCJpYXQiOjB9` 1237 const signature = `qM0CdRcyR4hw03J2ThJDat3Af40U87wVCF3Tp3xsyOg` 1238 const expected = `{"a":"b"}` 1239 signed := fmt.Sprintf(`{ 1240 "payload": %q, 1241 "signatures": [{"protected": %q, "signature": %q}] 1242 }`, payload, protected, signature) 1243 1244 verified, err := jws.Verify([]byte(signed), jws.WithKey(jwa.HS256, []byte("secret"))) 1245 require.NoError(t, err, `jws.Verify should succeed`) 1246 require.Equal(t, expected, string(verified), `verified payload should match`) 1247 1248 compact := strings.Join([]string{protected, payload, signature}, ".") 1249 verified, err = jws.Verify([]byte(compact), jws.WithKey(jwa.HS256, []byte("secret"))) 1250 require.NoError(t, err, `jws.Verify should succeed`) 1251 require.Equal(t, expected, string(verified), `verified payload should match`) 1252 } 1253 1254 func TestJKU(t *testing.T) { 1255 key, err := jwxtest.GenerateRsaJwk() 1256 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 1257 1258 key.Set(jwk.KeyIDKey, `my-awesome-key`) 1259 1260 pubkey, err := jwk.PublicKeyOf(key) 1261 require.NoError(t, err, `jwk.PublicKeyOf should succeed`) 1262 set := jwk.NewSet() 1263 set.AddKey(pubkey) 1264 srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1265 w.WriteHeader(http.StatusOK) 1266 json.NewEncoder(w).Encode(set) 1267 })) 1268 defer srv.Close() 1269 1270 payload := []byte("Lorem Ipsum") 1271 1272 t.Run("Compact", func(t *testing.T) { 1273 testcases := []struct { 1274 Name string 1275 Error bool 1276 Query string 1277 Fetcher func() jwk.Fetcher 1278 FetchOptions func() []jwk.FetchOption 1279 }{ 1280 { 1281 Name: "Fail without whitelist", 1282 Error: true, 1283 FetchOptions: func() []jwk.FetchOption { 1284 return []jwk.FetchOption{jwk.WithHTTPClient(srv.Client())} 1285 }, 1286 }, 1287 { 1288 Name: "Success", 1289 FetchOptions: func() []jwk.FetchOption { 1290 return []jwk.FetchOption{ 1291 jwk.WithFetchWhitelist(jwk.InsecureWhitelist{}), 1292 jwk.WithHTTPClient(srv.Client()), 1293 } 1294 }, 1295 }, 1296 { 1297 Name: "Rejected by whitelist", 1298 Error: true, 1299 FetchOptions: func() []jwk.FetchOption { 1300 wl := jwk.NewMapWhitelist().Add(`https://github.com/lestrrat-go/jwx/v2`) 1301 return []jwk.FetchOption{ 1302 jwk.WithFetchWhitelist(wl), 1303 jwk.WithHTTPClient(srv.Client()), 1304 } 1305 }, 1306 }, 1307 { 1308 Name: "JWKFetcher", 1309 Fetcher: func() jwk.Fetcher { 1310 c := jwk.NewCache(context.TODO()) 1311 return jwk.FetchFunc(func(ctx context.Context, u string, options ...jwk.FetchOption) (jwk.Set, error) { 1312 var cacheopts []jwk.RegisterOption 1313 for _, option := range options { 1314 cacheopts = append(cacheopts, option) 1315 } 1316 cacheopts = append(cacheopts, jwk.WithHTTPClient(srv.Client())) 1317 cacheopts = append(cacheopts, jwk.WithFetchWhitelist(httprc.InsecureWhitelist{})) 1318 c.Register(u, cacheopts...) 1319 1320 return c.Get(ctx, u) 1321 }) 1322 }, 1323 }, 1324 } 1325 1326 for _, tc := range testcases { 1327 tc := tc 1328 t.Run(tc.Name, func(t *testing.T) { 1329 hdr := jws.NewHeaders() 1330 u := srv.URL 1331 if tc.Query != "" { 1332 u += "?" + tc.Query 1333 } 1334 hdr.Set(jws.JWKSetURLKey, u) 1335 signed, err := jws.Sign(payload, jws.WithKey(jwa.RS256, key, jws.WithProtectedHeaders(hdr))) 1336 require.NoError(t, err, `jws.Sign should succeed`) 1337 1338 var options []jwk.FetchOption 1339 if f := tc.FetchOptions; f != nil { 1340 options = append(options, f()...) 1341 } 1342 1343 var fetcher jwk.Fetcher 1344 if f := tc.Fetcher; f != nil { 1345 fetcher = f() 1346 } 1347 decoded, err := jws.Verify(signed, jws.WithVerifyAuto(fetcher, options...)) 1348 if tc.Error { 1349 require.Error(t, err, `jws.Verify should fail`) 1350 } else { 1351 require.NoError(t, err, `jws.Verify should succeed`) 1352 require.Equal(t, payload, decoded, `decoded payload should match`) 1353 } 1354 }) 1355 } 1356 }) 1357 t.Run("JSON", func(t *testing.T) { 1358 // scenario: create a JSON message, which contains 3 signature entries. 1359 // 1st and 3rd signatures are valid, but signed using keys that are not 1360 // present in the JWKS. 1361 // Only the second signature uses a key found in the JWKS 1362 var keys []jwk.Key 1363 for i := 0; i < 3; i++ { 1364 key, err := jwxtest.GenerateRsaJwk() 1365 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 1366 key.Set(jwk.KeyIDKey, fmt.Sprintf(`used-%d`, i)) 1367 keys = append(keys, key) 1368 } 1369 1370 var unusedKeys []jwk.Key 1371 for i := 0; i < 2; i++ { 1372 key, err := jwxtest.GenerateRsaJwk() 1373 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 1374 key.Set(jwk.KeyIDKey, fmt.Sprintf(`unused-%d`, i)) 1375 unusedKeys = append(unusedKeys, key) 1376 } 1377 1378 // The set should contain unused key, used key, and unused key. 1379 // ...but they need to be public keys 1380 set := jwk.NewSet() 1381 for _, key := range []jwk.Key{unusedKeys[0], keys[1], unusedKeys[1]} { 1382 pubkey, err := jwk.PublicKeyOf(key) 1383 require.NoError(t, err, `jwk.PublicKeyOf should succeed`) 1384 require.Equal(t, pubkey.KeyID(), key.KeyID(), `key ID should be populated`) 1385 set.AddKey(pubkey) 1386 } 1387 srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1388 w.WriteHeader(http.StatusOK) 1389 json.NewEncoder(w).Encode(set) 1390 })) 1391 defer srv.Close() 1392 1393 // Sign the payload using the three keys 1394 var signOptions = []jws.SignOption{jws.WithJSON()} 1395 for _, key := range keys { 1396 hdr := jws.NewHeaders() 1397 hdr.Set(jws.JWKSetURLKey, srv.URL) 1398 signOptions = append(signOptions, jws.WithKey(jwa.RS256, key, jws.WithProtectedHeaders(hdr))) 1399 } 1400 1401 signed, err := jws.Sign(payload, signOptions...) 1402 require.NoError(t, err, `jws.SignMulti should succeed`) 1403 1404 testcases := []struct { 1405 Name string 1406 FetchOptions func() []jwk.FetchOption 1407 Error bool 1408 }{ 1409 { 1410 Name: "Fail without whitelist", 1411 Error: true, 1412 }, 1413 { 1414 Name: "Success", 1415 FetchOptions: func() []jwk.FetchOption { 1416 return []jwk.FetchOption{ 1417 jwk.WithFetchWhitelist(jwk.InsecureWhitelist{}), 1418 } 1419 }, 1420 }, 1421 { 1422 Name: "Rejected by whitelist", 1423 Error: true, 1424 FetchOptions: func() []jwk.FetchOption { 1425 wl := jwk.NewMapWhitelist().Add(`https://github.com/lestrrat-go/jwx/v2`) 1426 return []jwk.FetchOption{ 1427 jwk.WithFetchWhitelist(wl), 1428 } 1429 }, 1430 }, 1431 } 1432 1433 for _, tc := range testcases { 1434 tc := tc 1435 t.Run(tc.Name, func(t *testing.T) { 1436 m := jws.NewMessage() 1437 var options []jwk.FetchOption 1438 if fn := tc.FetchOptions; fn != nil { 1439 options = fn() 1440 } 1441 options = append(options, jwk.WithHTTPClient(srv.Client())) 1442 1443 decoded, err := jws.Verify(signed, jws.WithVerifyAuto(nil, options...), jws.WithMessage(m)) 1444 if tc.Error { 1445 require.Error(t, err, `jws.Verify should fail`) 1446 } else { 1447 if !assert.NoError(t, err, `jws.Verify should succeed`) { 1448 set, _ := jwk.Fetch(context.Background(), srv.URL, options...) 1449 { 1450 buf, _ := json.MarshalIndent(set, "", " ") 1451 t.Logf("%s", buf) 1452 } 1453 return 1454 } 1455 require.Equal(t, payload, decoded, `decoded payload should match`) 1456 // XXX This actally doesn't really test much, but if there was anything 1457 // wrong, the process should have failed well before reaching here 1458 require.Equal(t, payload, m.Payload(), "message payload matches") 1459 } 1460 }) 1461 } 1462 }) 1463 } 1464 1465 func TestAlgorithmsForKey(t *testing.T) { 1466 rsaprivkey, err := jwxtest.GenerateRsaJwk() 1467 require.NoError(t, err, `jwxtest.GenerateRsaPrivateKey should succeed`) 1468 rsapubkey, err := rsaprivkey.PublicKey() 1469 require.NoError(t, err, `jwk (RSA) PublicKey() should succeed`) 1470 1471 ecdsaprivkey, err := jwxtest.GenerateEcdsaJwk() 1472 require.NoError(t, err, `jwxtest.GenerateEcdsaPrivateKey should succeed`) 1473 ecdsapubkey, err := ecdsaprivkey.PublicKey() 1474 require.NoError(t, err, `jwk (ECDSA) PublicKey() should succeed`) 1475 1476 testcases := []struct { 1477 Name string 1478 Key interface{} 1479 Expected []jwa.SignatureAlgorithm 1480 }{ 1481 { 1482 Name: "Octet sequence", 1483 Key: []byte("hello"), 1484 Expected: []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512}, 1485 }, 1486 { 1487 Name: "rsa.PublicKey", 1488 Key: rsa.PublicKey{}, 1489 Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512}, 1490 }, 1491 { 1492 Name: "*rsa.PublicKey", 1493 Key: &rsa.PublicKey{}, 1494 Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512}, 1495 }, 1496 { 1497 Name: "jwk.RSAPublicKey", 1498 Key: rsapubkey, 1499 Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512}, 1500 }, 1501 { 1502 Name: "ecdsa.PublicKey", 1503 Key: ecdsa.PublicKey{}, 1504 Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512}, 1505 }, 1506 { 1507 Name: "*ecdsa.PublicKey", 1508 Key: &ecdsa.PublicKey{}, 1509 Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512}, 1510 }, 1511 { 1512 Name: "jwk.ECDSAPublicKey", 1513 Key: ecdsapubkey, 1514 Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512}, 1515 }, 1516 { 1517 Name: "rsa.PrivateKey", 1518 Key: rsa.PrivateKey{}, 1519 Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512}, 1520 }, 1521 { 1522 Name: "*rsa.PrivateKey", 1523 Key: &rsa.PrivateKey{}, 1524 Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512}, 1525 }, 1526 { 1527 Name: "jwk.RSAPrivateKey", 1528 Key: rsapubkey, 1529 Expected: []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512}, 1530 }, 1531 { 1532 Name: "ecdsa.PrivateKey", 1533 Key: ecdsa.PrivateKey{}, 1534 Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512}, 1535 }, 1536 { 1537 Name: "*ecdsa.PrivateKey", 1538 Key: &ecdsa.PrivateKey{}, 1539 Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512}, 1540 }, 1541 { 1542 Name: "jwk.ECDSAPrivateKey", 1543 Key: ecdsaprivkey, 1544 Expected: []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512}, 1545 }, 1546 { 1547 Name: "ed25519.PublicKey", 1548 Key: ed25519.PublicKey(nil), 1549 Expected: []jwa.SignatureAlgorithm{jwa.EdDSA}, 1550 }, 1551 { 1552 Name: "x25519.PublicKey", 1553 Key: x25519.PublicKey(nil), 1554 Expected: []jwa.SignatureAlgorithm{jwa.EdDSA}, 1555 }, 1556 } 1557 1558 for _, tc := range testcases { 1559 tc := tc 1560 1561 if hasES256K { 1562 if strings.Contains(strings.ToLower(tc.Name), `ecdsa`) { 1563 tc.Expected = append(tc.Expected, jwa.ES256K) 1564 } 1565 } 1566 1567 sort.Slice(tc.Expected, func(i, j int) bool { 1568 return tc.Expected[i].String() < tc.Expected[j].String() 1569 }) 1570 t.Run(tc.Name, func(t *testing.T) { 1571 algs, err := jws.AlgorithmsForKey(tc.Key) 1572 require.NoError(t, err, `jws.AlgorithmsForKey should succeed`) 1573 1574 sort.Slice(algs, func(i, j int) bool { 1575 return algs[i].String() < algs[j].String() 1576 }) 1577 require.Equal(t, tc.Expected, algs, `results should match`) 1578 }) 1579 } 1580 } 1581 1582 func TestGH681(t *testing.T) { 1583 privkey, err := jwxtest.GenerateRsaKey() 1584 require.NoError(t, err, "failed to create private key") 1585 1586 buf, err := jws.Sign(nil, jws.WithKey(jwa.RS256, privkey), jws.WithDetachedPayload([]byte("Lorem ipsum"))) 1587 require.NoError(t, err, "failed to sign payload") 1588 1589 t.Logf("%s", buf) 1590 1591 _, err = jws.Verify(buf, jws.WithKey(jwa.RS256, &privkey.PublicKey), jws.WithDetachedPayload([]byte("Lorem ipsum"))) 1592 require.NoError(t, err, "failed to verify JWS message") 1593 } 1594 1595 func TestGH840(t *testing.T) { 1596 // Go 1.19+ panics if elliptic curve operations are called against 1597 // a point that's _NOT_ on the curve 1598 untrustedJWK := []byte(`{ 1599 "kty": "EC", 1600 "crv": "P-256", 1601 "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqx7D4", 1602 "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", 1603 "d": "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" 1604 }`) 1605 1606 // Parse, serialize, slice and dice JWKs! 1607 privkey, err := jwk.ParseKey(untrustedJWK) 1608 require.NoError(t, err, `jwk.ParseKey should succeed`) 1609 1610 pubkey, err := jwk.PublicKeyOf(privkey) 1611 require.NoError(t, err, `jwk.PublicKeyOf should succeed`) 1612 1613 tok, err := jwt.NewBuilder(). 1614 Issuer(`github.com/lestrrat-go/jwx`). 1615 IssuedAt(time.Now()). 1616 Build() 1617 require.NoError(t, err, `jwt.NewBuilder should succeed`) 1618 1619 signed, err := jwt.Sign(tok, jwt.WithKey(jwa.ES256, privkey)) 1620 require.NoError(t, err, `jwt.Sign should succeed`) 1621 1622 _, err = jwt.Parse(signed, jwt.WithKey(jwa.ES256, pubkey)) 1623 require.Error(t, err, `jwt.Parse should FAIL`) // pubkey's X/Y is not on the curve 1624 } 1625 1626 func TestGH888(t *testing.T) { 1627 // This should fail because we're passing multiple keys (i.e. multiple signatures) 1628 // and yet we haven't specified JSON serialization 1629 _, err := jws.Sign([]byte(`foo`), jws.WithInsecureNoSignature(), jws.WithKey(jwa.HS256, []byte(`bar`))) 1630 require.Error(t, err, `jws.Sign with multiple keys (including alg=none) should fail`) 1631 1632 // This should pass because we can now have multiple signaures with JSON serialization 1633 signed, err := jws.Sign([]byte(`foo`), jws.WithInsecureNoSignature(), jws.WithKey(jwa.HS256, []byte(`bar`)), jws.WithJSON()) 1634 require.NoError(t, err, `jws.Sign should succeed`) 1635 1636 message, err := jws.Parse(signed) 1637 require.NoError(t, err, `jws.Parse should succeed`) 1638 1639 // Look for alg=none signature 1640 var foundNoSignature bool 1641 for _, sig := range message.Signatures() { 1642 if sig.ProtectedHeaders().Algorithm() != jwa.NoSignature { 1643 continue 1644 } 1645 1646 require.Nil(t, sig.Signature(), `signature must be nil for alg=none`) 1647 foundNoSignature = true 1648 } 1649 require.True(t, foundNoSignature, `signature with no signature was found`) 1650 1651 _, err = jws.Verify(signed) 1652 require.Error(t, err, `jws.Verify should fail`) 1653 1654 _, err = jws.Verify(signed, jws.WithKey(jwa.NoSignature, nil)) 1655 require.Error(t, err, `jws.Verify should fail`) 1656 1657 // Note: you can't do jws.Verify(..., jws.WithInsecureNoSignature()) 1658 1659 verified, err := jws.Verify(signed, jws.WithKey(jwa.HS256, []byte(`bar`))) 1660 require.NoError(t, err, `jws.Verify should succeed`) 1661 require.Equal(t, []byte(`foo`), verified) 1662 } 1663 1664 // Some stuff required for testing #910 1665 // The original code used an external library to sign/verify, but here 1666 // we just use a simple SHA256 digest here so that we don't force 1667 // users to download an optional dependency 1668 type s256SignerVerifier struct{} 1669 1670 const sha256Algo jwa.SignatureAlgorithm = "SillyTest256" 1671 1672 func (s256SignerVerifier) Algorithm() jwa.SignatureAlgorithm { 1673 return sha256Algo 1674 } 1675 1676 func (s256SignerVerifier) Sign(payload []byte, _ interface{}) ([]byte, error) { 1677 h := sha256.Sum256(payload) 1678 return h[:], nil 1679 } 1680 1681 func (s256SignerVerifier) Verify(payload, signature []byte, _ interface{}) error { 1682 h := sha256.Sum256(payload) 1683 if !bytes.Equal(h[:], signature) { 1684 return errors.New("invalid signature") 1685 } 1686 return nil 1687 } 1688 1689 func TestGH910(t *testing.T) { 1690 // Note: This has global effect. You can't run this in parallel with other tests 1691 jws.RegisterSigner(sha256Algo, jws.SignerFactoryFn(func() (jws.Signer, error) { 1692 return s256SignerVerifier{}, nil 1693 })) 1694 defer jws.UnregisterSigner(sha256Algo) 1695 1696 jws.RegisterVerifier(sha256Algo, jws.VerifierFactoryFn(func() (jws.Verifier, error) { 1697 return s256SignerVerifier{}, nil 1698 })) 1699 defer jws.UnregisterVerifier(sha256Algo) 1700 defer jwa.UnregisterSignatureAlgorithm(sha256Algo) 1701 1702 var sa jwa.SignatureAlgorithm 1703 require.NoError(t, sa.Accept(sha256Algo.String()), `jwa.SignatureAlgorithm.Accept should succeed`) 1704 1705 // Now that we have established that the signature algorithm works, 1706 // we can proceed with the test 1707 const src = `Lorem Ipsum` 1708 signed, err := jws.Sign([]byte(src), jws.WithKey(sha256Algo, nil)) 1709 require.NoError(t, err, `jws.Sign should succeed`) 1710 1711 verified, err := jws.Verify(signed, jws.WithKey(sha256Algo, nil)) 1712 require.NoError(t, err, `jws.Verify should succeed`) 1713 1714 require.Equal(t, src, string(verified), `verified payload should match`) 1715 1716 jws.UnregisterSigner(sha256Algo) 1717 1718 // Now try after unregistering the signer for the algorithm 1719 _, err = jws.Sign([]byte(src), jws.WithKey(sha256Algo, nil)) 1720 require.Error(t, err, `jws.Sign should succeed`) 1721 1722 jws.RegisterSigner(sha256Algo, jws.SignerFactoryFn(func() (jws.Signer, error) { 1723 return s256SignerVerifier{}, nil 1724 })) 1725 1726 _, err = jws.Sign([]byte(src), jws.WithKey(sha256Algo, nil)) 1727 require.NoError(t, err, `jws.Sign should succeed`) 1728 } 1729 1730 func TestUnpaddedSignatureR(t *testing.T) { 1731 // I brute-forced generating a key and signature where the R portion 1732 // of the signature was not padded by using the following code in the 1733 // first run, then copied the result to the test 1734 /* 1735 for i := 0; i < 10000; i++ { 1736 rawKey, err := jwxtest.GenerateEcdsaKey(jwa.P256) 1737 require.NoError(t, err, `jwxtest.GenerateEcdsaJwk should succeed`) 1738 1739 key, err := jwk.FromRaw(rawKey) 1740 require.NoError(t, err, `jwk.FromRaw should succeed`) 1741 1742 pubkey, _ := key.PublicKey() 1743 1744 signed, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(jwa.ES256, key)) 1745 require.NoError(t, err, `jws.Sign should succeed`) 1746 1747 message, err := jws.Parse(signed) 1748 require.NoError(t, err, `jws.Parse should succeed`) 1749 1750 asJson, _ := json.Marshal(message) 1751 t.Logf("%s", asJson) 1752 1753 for _, sig := range message.Signatures() { 1754 sigBytes := sig.Signature() 1755 if sigBytes[0] == 0x00 { 1756 // Found it! 1757 t.Logf("Found signature that can be unpadded.") 1758 t.Logf("Original signature: %q", base64.EncodeToString(sigBytes)) 1759 1760 // unpaddedSig := append(sigBytes[1:31], sigBytes[32:]...) 1761 unpaddedSig := sigBytes[1:] 1762 t.Logf("Signature with first byte of R removed: %q", base64.EncodeToString(unpaddedSig)) 1763 t.Logf("Original JWS payload: %q", signed) 1764 require.Len(t, unpaddedSig, 63) 1765 1766 i := bytes.LastIndexByte(signed, '.') 1767 modified := append(signed[:i+1], base64.Encode(unpaddedSig)...) 1768 t.Logf("JWS payload with unpadded signature: %q", modified) 1769 1770 // jws.Verify for sanity 1771 verified, err := jws.Verify(modified, jws.WithKey(jwa.ES256, pubkey)) 1772 require.NoError(t, err, `jws.Verify should succeed`) 1773 t.Logf("verified payload: %q", verified) 1774 1775 buf, _ := json.Marshal(key) 1776 t.Logf("Private JWK: %s", buf) 1777 return 1778 } 1779 } 1780 } 1781 */ 1782 // Padded has R with a leading 0 (as it should) 1783 padded := "eyJhbGciOiJFUzI1NiJ9.TG9yZW0gSXBzdW0.ALFru4CRZDiAlVKyyHtlLGtXIAWxC3lXIlZuYO8G8a5ePzCwyw6c2FzWBZwrLaoLFZb_TcYs3TcZ8mhONPaavQ" 1784 // Unpadded has R with a leading 0 removed (31 bytes, WRONG) 1785 unpadded := "eyJhbGciOiJFUzI1NiJ9.TG9yZW0gSXBzdW0.sWu7gJFkOICVUrLIe2Usa1cgBbELeVciVm5g7wbxrl4_MLDLDpzYXNYFnCstqgsVlv9NxizdNxnyaE409pq9" 1786 1787 // This is the private key used to sign the payload 1788 keySrc := `{"crv":"P-256","d":"MqGwMl-dlJFrMnu7rFyslPV8EdsVC7I4V19N-ADVqaU","kty":"EC","x":"Anf1p2lRrcXgZKpVRRC1xLxPiw_45PbOlygfbxvD8Es","y":"d0HiZq-aurVVLLtK-xqXPpzpWloZJNwKNve7akBDuvg"}` 1789 1790 privKey, err := jwk.ParseKey([]byte(keySrc)) 1791 require.NoError(t, err, `jwk.ParseKey should succeed`) 1792 1793 pubKey, err := jwk.PublicKeyOf(privKey) 1794 require.NoError(t, err, `jwk.PublicKeyOf should succeed`) 1795 1796 // Should always succeed 1797 payload, err := jws.Verify([]byte(padded), jws.WithKey(jwa.ES256, pubKey)) 1798 require.NoError(t, err, `jws.Verify should succeed`) 1799 require.Equal(t, "Lorem Ipsum", string(payload)) 1800 1801 // Should fail 1802 _, err = jws.Verify([]byte(unpadded), jws.WithKey(jwa.ES256, pubKey)) 1803 require.Error(t, err, `jws.Verify should fail`) 1804 } 1805 1806 func TestValidateKey(t *testing.T) { 1807 privKey, err := jwxtest.GenerateRsaJwk() 1808 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 1809 1810 signed, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(jwa.RS256, privKey), jws.WithValidateKey(true)) 1811 require.NoError(t, err, `jws.Sign should succeed`) 1812 1813 // This should fail because D is empty 1814 require.NoError(t, privKey.Set(jwk.RSADKey, []byte(nil)), `jwk.Set should succeed`) 1815 _, err = jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(jwa.RS256, privKey), jws.WithValidateKey(true)) 1816 require.Error(t, err, `jws.Sign should fail`) 1817 1818 pubKey, err := jwk.PublicKeyOf(privKey) 1819 require.NoError(t, err, `jwk.PublicKeyOf should succeed`) 1820 1821 n := pubKey.(jwk.RSAPublicKey).N() 1822 1823 // Set N to an empty value 1824 require.NoError(t, pubKey.Set(jwk.RSANKey, []byte(nil)), `jwk.Set should succeed`) 1825 1826 // This is going to fail regardless, because the public key is now 1827 // invalid (empty N), but we want to make sure that it fails because 1828 // of the validation failing 1829 _, err = jws.Verify(signed, jws.WithKey(jwa.RS256, pubKey), jws.WithValidateKey(true)) 1830 require.Error(t, err, `jws.Verify should fail`) 1831 require.True(t, jwk.IsKeyValidationError(err), `jwk.IsKeyValidationError should return true`) 1832 1833 // The following should now succeed, because N has been reinstated 1834 require.NoError(t, pubKey.Set(jwk.RSANKey, n), `jwk.Set should succeed`) 1835 _, err = jws.Verify(signed, jws.WithKey(jwa.RS256, pubKey), jws.WithValidateKey(true)) 1836 require.NoError(t, err, `jws.Verify should succeed`) 1837 } 1838 1839 func TestEmptyProtectedField(t *testing.T) { 1840 // MEMO: this was the only test case from the original report 1841 // This passes. It should produce an invalid JWS message, but 1842 // that's not `jws.Parse`'s problem. 1843 _, err := jws.Parse([]byte(`{"signature": ""}`)) 1844 require.NoError(t, err, `jws.Parse should fail`) 1845 1846 // Also test that non-flattened serialization passes. 1847 _, err = jws.Parse([]byte(`{"signatures": [{}]}`)) 1848 require.NoError(t, err, `jws.Parse should fail`) 1849 1850 // MEMO: rest of the cases are present to be extra pedantic about it 1851 1852 privKey, err := jwxtest.GenerateRsaJwk() 1853 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 1854 1855 // This fails. `jws.Parse` works, but the subsequent verification 1856 // workflow fails to verify anything without the presence of a signature or 1857 // a protected header. 1858 _, err = jws.Verify([]byte(`{"signature": ""}`), jws.WithKey(jwa.RS256, privKey)) 1859 require.Error(t, err, `jws.Parse should fail`) 1860 1861 // Create a valid signatre. 1862 signed, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(jwa.RS256, privKey)) 1863 require.NoError(t, err, `jws.Sign should succeed`) 1864 1865 _, payload, signature, err := jws.SplitCompact(signed) 1866 require.NoError(t, err, `jws.SplitCompact should succeed`) 1867 1868 // This fails as well. we have a valid signature and a valid 1869 // key to verify it, but no protected headers 1870 _, err = jws.Verify( 1871 []byte(fmt.Sprintf(`{"signature": "%s"}`, signature)), 1872 jws.WithKey(jwa.RS256, privKey), 1873 ) 1874 require.Error(t, err, `jws.Verify should fail`) 1875 1876 // Test for cases when we have an incomplete compact form JWS 1877 var buf bytes.Buffer 1878 buf.WriteRune('.') 1879 buf.Write(payload) 1880 buf.WriteRune('.') 1881 buf.Write(signature) 1882 invalidMessage := buf.Bytes() 1883 1884 // This is an error because the format is simply wrong. 1885 // Whereas in the other JSON-based JWS's case the lack of protected field 1886 // is not a SYNTAX error, this one is, and therefore we barf. 1887 _, err = jws.Parse(invalidMessage) 1888 require.Error(t, err, `jws.Parse should fail`) 1889 } 1890 1891 func TestParseFormat(t *testing.T) { 1892 privKey, err := jwxtest.GenerateRsaJwk() 1893 require.NoError(t, err, `jwxtest.GenerateRsaJwk should succeed`) 1894 1895 signedCompact, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(jwa.RS256, privKey), jws.WithValidateKey(true)) 1896 require.NoError(t, err, `jws.Sign should succeed`) 1897 1898 signedJSON, err := jws.Sign([]byte("Lorem Ipsum"), jws.WithKey(jwa.RS256, privKey), jws.WithValidateKey(true), jws.WithJSON()) 1899 require.NoError(t, err, `jws.Sign should succeed`) 1900 1901 // Only compact formats should succeed 1902 _, err = jws.Verify(signedCompact, jws.WithKey(jwa.RS256, privKey), jws.WithCompact()) 1903 require.NoError(t, err, `jws.Verify should succeed`) 1904 _, err = jws.Verify(signedJSON, jws.WithKey(jwa.RS256, privKey), jws.WithCompact()) 1905 require.Error(t, err, `jws.Verify should fail`) 1906 _, err = jws.Parse(signedCompact, jws.WithCompact()) 1907 require.NoError(t, err, `jws.Parse should succeed`) 1908 _, err = jws.Parse(signedJSON, jws.WithCompact()) 1909 require.Error(t, err, `jws.Parse should fail`) 1910 1911 // Only JSON formats should succeed 1912 _, err = jws.Verify(signedCompact, jws.WithKey(jwa.RS256, privKey), jws.WithJSON()) 1913 require.Error(t, err, `jws.Verify should fail`) 1914 _, err = jws.Verify(signedJSON, jws.WithKey(jwa.RS256, privKey), jws.WithJSON()) 1915 require.NoError(t, err, `jws.Verify should succeed`) 1916 _, err = jws.Parse(signedJSON, jws.WithJSON()) 1917 require.NoError(t, err, `jws.Parse should succeed`) 1918 _, err = jws.Parse(signedCompact, jws.WithJSON()) 1919 require.Error(t, err, `jws.Parse should fail`) 1920 1921 // Either format should succeed 1922 _, err = jws.Verify(signedCompact, jws.WithKey(jwa.RS256, privKey)) 1923 require.NoError(t, err, `jws.Verify should succeed`) 1924 _, err = jws.Verify(signedCompact, jws.WithKey(jwa.RS256, privKey), jws.WithJSON(), jws.WithCompact()) 1925 require.NoError(t, err, `jws.Verify should succeed`) 1926 _, err = jws.Parse(signedCompact) 1927 require.NoError(t, err, `jws.Parse should succeed`) 1928 _, err = jws.Parse(signedCompact, jws.WithJSON(), jws.WithCompact()) 1929 require.NoError(t, err, `jws.Parse should succeed`) 1930 1931 _, err = jws.Verify(signedJSON, jws.WithKey(jwa.RS256, privKey)) 1932 require.NoError(t, err, `jws.Verify should succeed`) 1933 _, err = jws.Verify(signedJSON, jws.WithKey(jwa.RS256, privKey), jws.WithJSON(), jws.WithCompact()) 1934 require.NoError(t, err, `jws.Verify should succeed`) 1935 _, err = jws.Parse(signedJSON) 1936 require.NoError(t, err, `jws.Parse should succeed`) 1937 _, err = jws.Parse(signedJSON, jws.WithJSON(), jws.WithCompact()) 1938 require.NoError(t, err, `jws.Parse should succeed`) 1939 }