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