...

Source file src/github.com/lestrrat-go/jwx/jws/jws_test.go

Documentation: github.com/lestrrat-go/jwx/jws

     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  				// Force ParseReader() to choose un-optimized path by using bufio.NewReader
    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  	// The implementation is the same as ecdsaCryptoSigner.
   171  	// This is just here to test the interface conversion
   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  			// XXX This actally doesn't really test much, but if there was anything
   389  			// wrong, the process should have failed well before reaching here
   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  	// HS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.1 works
   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  		// ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works
   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  		// "Payload"
   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  		// Verify with standard ecdsa library
   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  		// Verify with API library
   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  		// RS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.2 works
   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  		// ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works
   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  		// The signature contains random factor, so unfortunately we can't match
   681  		// the output against a fixed expected outcome. We'll wave doing an
   682  		// exact match, and just try to verify using the signature
   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  		// EdDSACompact tests that https://tools.ietf.org/html/rfc8037#appendix-A.1-5 works
   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  		// The signature contains random factor, so unfortunately we can't match
   763  		// the output against a fixed expected outcome. We'll wave doing an
   764  		// exact match, and just try to verify using the signature
   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  		// Protected Header is {"alg":"ES256","kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"}
   877  		// This protected header combination forces the parser/unmarshal to go trough the code path to populate and look for protected header fields.
   878  		// The signature is valid.
   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  				// Create payload with X.Y.Z
   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  				// Test using bytes, reader optimized and non-optimized path
   944  				for _, method := range []int{0, 1, 2} {
   945  					var x, y, z []byte
   946  					var err error
   947  					switch method {
   948  					case 0: // bytes
   949  						x, y, z, err = jws.SplitCompact(payload)
   950  					case 1: // un-optimized io.Reader
   951  						x, y, z, err = jws.SplitCompactReader(bytes.NewReader(payload))
   952  					default: // optimized io.Reader
   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 // TODO
   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 // TODO
   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  	// XXX has global effect!!!
  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() // RFC3339
  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  		// Create JSON from jws.Message
  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  	// The result of using jws.WithMessage should match the result of jws.Parse
  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  				// Note: this test doesn't really test "backoff", but it tests
  1598  				// that jku can be fetched again upon seeing a non-200 response.
  1599  				// to be preceise we should be also checking for the timing, but
  1600  				// I think that can be deferred to the tests in jwk package
  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  		// scenario: create a JSON message, which contains 3 signature entries.
  1663  		// 1st and 3rd signatures are valid, but signed using keys that are not
  1664  		// present in the JWKS.
  1665  		// Only the second signature uses a key found in the JWKS
  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  		// The set should contain unused key, used key, and unused key.
  1687  		// ...but they need to be public keys
  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  		// Sign the payload using the three keys
  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  					// XXX This actally doesn't really test much, but if there was anything
  1772  					// wrong, the process should have failed well before reaching here
  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