github.com/lestrrat-go/jwx/v2@v2.0.21/jwx_test.go (about)

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