mirror of
https://github.com/therootcompany/golib.git
synced 2026-03-29 03:24:07 +00:00
661 lines
19 KiB
Go
661 lines
19 KiB
Go
// Package josert_test verifies interoperability between this library and
|
|
// github.com/go-jose/go-jose/v4 (JWS, JWK, JWT). It covers sign/verify,
|
|
// JWK serialization, thumbprint consistency, JWKS, audience, custom claims,
|
|
// NumericDate precision, and stress tests.
|
|
package josert_test
|
|
|
|
import (
|
|
"crypto"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
jose "github.com/go-jose/go-jose/v4"
|
|
josejwt "github.com/go-jose/go-jose/v4/jwt"
|
|
|
|
"github.com/therootcompany/golib/auth/jwt"
|
|
"github.com/therootcompany/golib/auth/jwt/tests/testkeys"
|
|
)
|
|
|
|
var longTests = flag.Bool("long", false, "run extended stress tests (100 RSA iterations instead of 10)")
|
|
|
|
// joseAlg maps our algorithm name to a go-jose SignatureAlgorithm constant.
|
|
func joseAlg(name string) jose.SignatureAlgorithm {
|
|
switch name {
|
|
case "EdDSA":
|
|
return jose.EdDSA
|
|
case "ES256":
|
|
return jose.ES256
|
|
case "ES384":
|
|
return jose.ES384
|
|
case "ES512":
|
|
return jose.ES512
|
|
case "RS256":
|
|
return jose.RS256
|
|
}
|
|
panic("unknown alg: " + name)
|
|
}
|
|
|
|
// --- helpers ---
|
|
|
|
func assertOurSignGoJoseVerify(t *testing.T, ks testkeys.KeySet, sub string) {
|
|
t.Helper()
|
|
|
|
signer, err := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
if err != nil {
|
|
t.Fatalf("NewSigner: %v", err)
|
|
}
|
|
tokenStr, err := signer.SignToString(testkeys.TestClaims(sub))
|
|
if err != nil {
|
|
t.Fatalf("SignToString: %v", err)
|
|
}
|
|
|
|
// Parse and verify with go-jose.
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{joseAlg(ks.AlgName)})
|
|
if err != nil {
|
|
t.Fatalf("go-jose ParseSigned: %v", err)
|
|
}
|
|
|
|
var claims josejwt.Claims
|
|
if err := tok.Claims(ks.RawPub, &claims); err != nil {
|
|
t.Fatalf("go-jose Claims: %v", err)
|
|
}
|
|
if claims.Subject != sub {
|
|
t.Errorf("sub: got %q, want %q", claims.Subject, sub)
|
|
}
|
|
if claims.Issuer != "https://example.com" {
|
|
t.Errorf("iss: got %q, want %q", claims.Issuer, "https://example.com")
|
|
}
|
|
}
|
|
|
|
func assertGoJoseSignOurVerify(t *testing.T, ks testkeys.KeySet, sub string) {
|
|
t.Helper()
|
|
|
|
// Use JSONWebKey wrapper to get kid in the JWS header.
|
|
sigKey := jose.SigningKey{
|
|
Algorithm: joseAlg(ks.AlgName),
|
|
Key: jose.JSONWebKey{
|
|
Key: ks.RawPriv,
|
|
KeyID: ks.KID,
|
|
},
|
|
}
|
|
joseSigner, err := jose.NewSigner(sigKey, nil)
|
|
if err != nil {
|
|
t.Fatalf("go-jose NewSigner: %v", err)
|
|
}
|
|
|
|
now := time.Now()
|
|
claims := josejwt.Claims{
|
|
Issuer: "https://example.com",
|
|
Subject: sub,
|
|
Expiry: josejwt.NewNumericDate(now.Add(time.Hour)),
|
|
IssuedAt: josejwt.NewNumericDate(now),
|
|
}
|
|
tokenStr, err := josejwt.Signed(joseSigner).Claims(claims).Serialize()
|
|
if err != nil {
|
|
t.Fatalf("go-jose Serialize: %v", err)
|
|
}
|
|
|
|
verifier, _ := jwt.NewVerifier([]jwt.PublicKey{ks.PubKey})
|
|
verifiedJWS, err := verifier.VerifyJWT(tokenStr)
|
|
if err != nil {
|
|
t.Fatalf("our verify: %v", err)
|
|
}
|
|
|
|
var decoded jwt.TokenClaims
|
|
if err := verifiedJWS.UnmarshalClaims(&decoded); err != nil {
|
|
t.Fatalf("UnmarshalClaims: %v", err)
|
|
}
|
|
if decoded.Sub != sub {
|
|
t.Errorf("sub: got %q, want %q", decoded.Sub, sub)
|
|
}
|
|
}
|
|
|
|
// --- Our sign, go-jose verify (all algorithms) ---
|
|
|
|
func TestOurSignGoJoseVerify_EdDSA(t *testing.T) {
|
|
assertOurSignGoJoseVerify(t, testkeys.GenerateEdDSA("k1"), "user-eddsa")
|
|
}
|
|
|
|
func TestOurSignGoJoseVerify_ES256(t *testing.T) {
|
|
assertOurSignGoJoseVerify(t, testkeys.GenerateES256("k1"), "user-es256")
|
|
}
|
|
|
|
func TestOurSignGoJoseVerify_ES384(t *testing.T) {
|
|
assertOurSignGoJoseVerify(t, testkeys.GenerateES384("k1"), "user-es384")
|
|
}
|
|
|
|
func TestOurSignGoJoseVerify_ES512(t *testing.T) {
|
|
assertOurSignGoJoseVerify(t, testkeys.GenerateES512("k1"), "user-es512")
|
|
}
|
|
|
|
func TestOurSignGoJoseVerify_RS256(t *testing.T) {
|
|
assertOurSignGoJoseVerify(t, testkeys.GenerateRS256("k1"), "user-rs256")
|
|
}
|
|
|
|
// --- go-jose sign, our verify (all algorithms) ---
|
|
|
|
func TestGoJoseSignOurVerify_EdDSA(t *testing.T) {
|
|
assertGoJoseSignOurVerify(t, testkeys.GenerateEdDSA("k1"), "user-eddsa")
|
|
}
|
|
|
|
func TestGoJoseSignOurVerify_ES256(t *testing.T) {
|
|
assertGoJoseSignOurVerify(t, testkeys.GenerateES256("k1"), "user-es256")
|
|
}
|
|
|
|
func TestGoJoseSignOurVerify_ES384(t *testing.T) {
|
|
assertGoJoseSignOurVerify(t, testkeys.GenerateES384("k1"), "user-es384")
|
|
}
|
|
|
|
func TestGoJoseSignOurVerify_ES512(t *testing.T) {
|
|
assertGoJoseSignOurVerify(t, testkeys.GenerateES512("k1"), "user-es512")
|
|
}
|
|
|
|
func TestGoJoseSignOurVerify_RS256(t *testing.T) {
|
|
assertGoJoseSignOurVerify(t, testkeys.GenerateRS256("k1"), "user-rs256")
|
|
}
|
|
|
|
// --- JWK serialization interop ---
|
|
|
|
func TestJWKInterop_OurJSONToGoJose(t *testing.T) {
|
|
for _, ag := range testkeys.AllAlgorithms() {
|
|
t.Run(ag.Name+"_Public", func(t *testing.T) {
|
|
ks := ag.Generate("jwk-" + ag.Name)
|
|
|
|
// Marshal our public key to JSON.
|
|
ourJSON, err := json.Marshal(ks.PubKey)
|
|
if err != nil {
|
|
t.Fatalf("marshal our pubkey: %v", err)
|
|
}
|
|
|
|
// Parse with go-jose.
|
|
var joseKey jose.JSONWebKey
|
|
if err := json.Unmarshal(ourJSON, &joseKey); err != nil {
|
|
t.Fatalf("go-jose unmarshal from our JSON: %v", err)
|
|
}
|
|
|
|
// Verify a token signed by us, using the go-jose-parsed key.
|
|
signer, err := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tokenStr, err := signer.SignToString(testkeys.TestClaims("jwk-interop"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{joseAlg(ks.AlgName)})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var claims josejwt.Claims
|
|
if err := tok.Claims(joseKey.Key, &claims); err != nil {
|
|
t.Fatalf("go-jose verify with our-JSON-parsed key: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run(ag.Name+"_Private", func(t *testing.T) {
|
|
ks := ag.Generate("jwk-priv-" + ag.Name)
|
|
|
|
// Marshal our private key to JSON.
|
|
ourJSON, err := json.Marshal(ks.PrivKey)
|
|
if err != nil {
|
|
t.Fatalf("marshal our privkey: %v", err)
|
|
}
|
|
|
|
// Parse with go-jose.
|
|
var joseKey jose.JSONWebKey
|
|
if err := json.Unmarshal(ourJSON, &joseKey); err != nil {
|
|
t.Fatalf("go-jose unmarshal from our private JSON: %v", err)
|
|
}
|
|
|
|
// Sign with the go-jose-parsed key, verify with our lib.
|
|
joseKey.KeyID = ks.KID
|
|
sigKey := jose.SigningKey{
|
|
Algorithm: joseAlg(ks.AlgName),
|
|
Key: joseKey,
|
|
}
|
|
joseSigner, err := jose.NewSigner(sigKey, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
claims := josejwt.Claims{
|
|
Subject: "jwk-priv-interop",
|
|
Expiry: josejwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
}
|
|
tokenStr, err := josejwt.Signed(joseSigner).Claims(claims).Serialize()
|
|
if err != nil {
|
|
t.Fatalf("go-jose sign with our-JSON-parsed key: %v", err)
|
|
}
|
|
|
|
verifier, _ := jwt.NewVerifier([]jwt.PublicKey{ks.PubKey})
|
|
if _, err := verifier.VerifyJWT(tokenStr); err != nil {
|
|
t.Fatalf("our verify: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJWKInterop_GoJoseJSONToOur(t *testing.T) {
|
|
for _, ag := range testkeys.AllAlgorithms() {
|
|
t.Run(ag.Name, func(t *testing.T) {
|
|
ks := ag.Generate("jose-to-our-" + ag.Name)
|
|
|
|
// Create go-jose JWK and serialize.
|
|
joseKey := jose.JSONWebKey{
|
|
Key: ks.RawPub,
|
|
KeyID: ks.KID,
|
|
Algorithm: ks.AlgName,
|
|
Use: "sig",
|
|
}
|
|
joseJSON, err := json.Marshal(joseKey)
|
|
if err != nil {
|
|
t.Fatalf("marshal go-jose key: %v", err)
|
|
}
|
|
|
|
// Parse with our library.
|
|
var recovered jwt.PublicKey
|
|
if err := json.Unmarshal(joseJSON, &recovered); err != nil {
|
|
t.Fatalf("our unmarshal of go-jose JSON: %v", err)
|
|
}
|
|
|
|
// Sign with our signer, verify with the recovered key.
|
|
signer, err := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tokenStr, err := signer.SignToString(testkeys.TestClaims("jose-json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
verifier, _ := jwt.NewVerifier([]jwt.PublicKey{recovered})
|
|
if _, err := verifier.VerifyJWT(tokenStr); err != nil {
|
|
t.Fatalf("verify with go-jose-JSON-parsed key: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- Thumbprint consistency (RFC 7638) ---
|
|
|
|
func TestThumbprintConsistency_GoJose(t *testing.T) {
|
|
for _, ag := range testkeys.AllAlgorithms() {
|
|
t.Run(ag.Name, func(t *testing.T) {
|
|
ks := ag.Generate("thumb-" + ag.Name)
|
|
|
|
// Our thumbprint (returns base64url string).
|
|
ourThumb, err := ks.PubKey.Thumbprint()
|
|
if err != nil {
|
|
t.Fatalf("our Thumbprint: %v", err)
|
|
}
|
|
|
|
// go-jose thumbprint (returns raw bytes).
|
|
joseKey := jose.JSONWebKey{Key: ks.RawPub}
|
|
joseRaw, err := joseKey.Thumbprint(crypto.SHA256)
|
|
if err != nil {
|
|
t.Fatalf("go-jose Thumbprint: %v", err)
|
|
}
|
|
joseThumb := base64.RawURLEncoding.EncodeToString(joseRaw)
|
|
|
|
if ourThumb != joseThumb {
|
|
t.Errorf("thumbprint mismatch:\n ours: %s\n go-jose: %s", ourThumb, joseThumb)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- JWKS interop ---
|
|
|
|
func TestJWKSInterop_OurToGoJose(t *testing.T) {
|
|
// Build a signer with all 5 key types.
|
|
var keys []*jwt.PrivateKey
|
|
var sets []testkeys.KeySet
|
|
for _, ag := range testkeys.AllAlgorithms() {
|
|
ks := ag.Generate("jwks-" + ag.Name)
|
|
keys = append(keys, ks.PrivKey)
|
|
sets = append(sets, ks)
|
|
}
|
|
signer, err := jwt.NewSigner(keys)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Serialize our JWKS.
|
|
jwksData, err := json.Marshal(&signer)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Parse with go-jose.
|
|
var joseJWKS jose.JSONWebKeySet
|
|
if err := json.Unmarshal(jwksData, &joseJWKS); err != nil {
|
|
t.Fatalf("go-jose unmarshal JWKS: %v", err)
|
|
}
|
|
if len(joseJWKS.Keys) != 5 {
|
|
t.Fatalf("expected 5 keys, got %d", len(joseJWKS.Keys))
|
|
}
|
|
|
|
// Sign tokens with each key and verify with the go-jose-parsed set.
|
|
for i, ks := range sets {
|
|
tokenStr, err := signer.SignToString(testkeys.TestClaims(fmt.Sprintf("jwks-%d", i)))
|
|
if err != nil {
|
|
t.Fatalf("sign[%d]: %v", i, err)
|
|
}
|
|
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{joseAlg(ks.AlgName)})
|
|
if err != nil {
|
|
t.Errorf("parse[%d] (%s): %v", i, ks.AlgName, err)
|
|
continue
|
|
}
|
|
// Find the matching key from the parsed JWKS.
|
|
matching := joseJWKS.Key(ks.KID)
|
|
if len(matching) == 0 {
|
|
t.Errorf("no key found for kid %q", ks.KID)
|
|
continue
|
|
}
|
|
var claims josejwt.Claims
|
|
if err := tok.Claims(matching[0].Key, &claims); err != nil {
|
|
t.Errorf("go-jose verify[%d] (%s) with parsed JWKS: %v", i, ks.AlgName, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestJWKSInterop_GoJoseToOur(t *testing.T) {
|
|
// Build a go-jose key set.
|
|
var joseJWKS jose.JSONWebKeySet
|
|
var sets []testkeys.KeySet
|
|
for _, ag := range testkeys.AllAlgorithms() {
|
|
ks := ag.Generate("jose-jwks-" + ag.Name)
|
|
sets = append(sets, ks)
|
|
joseJWKS.Keys = append(joseJWKS.Keys, jose.JSONWebKey{
|
|
Key: ks.RawPub,
|
|
KeyID: ks.KID,
|
|
Algorithm: ks.AlgName,
|
|
Use: "sig",
|
|
})
|
|
}
|
|
|
|
// Serialize go-jose JWKS.
|
|
jwksData, err := json.Marshal(joseJWKS)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Parse with our library.
|
|
var ourJWKS jwt.WellKnownJWKs
|
|
if err := json.Unmarshal(jwksData, &ourJWKS); err != nil {
|
|
t.Fatalf("our unmarshal of go-jose JWKS: %v", err)
|
|
}
|
|
if len(ourJWKS.Keys) != 5 {
|
|
t.Fatalf("expected 5 keys, got %d", len(ourJWKS.Keys))
|
|
}
|
|
|
|
verifier, _ := jwt.NewVerifier(ourJWKS.Keys)
|
|
|
|
// Sign tokens with go-jose, verify with our library.
|
|
for _, ks := range sets {
|
|
sigKey := jose.SigningKey{
|
|
Algorithm: joseAlg(ks.AlgName),
|
|
Key: jose.JSONWebKey{
|
|
Key: ks.RawPriv,
|
|
KeyID: ks.KID,
|
|
},
|
|
}
|
|
joseSigner, err := jose.NewSigner(sigKey, nil)
|
|
if err != nil {
|
|
t.Fatalf("go-jose signer %s: %v", ks.AlgName, err)
|
|
}
|
|
claims := josejwt.Claims{
|
|
Subject: "jose-to-our",
|
|
Expiry: josejwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
}
|
|
tokenStr, err := josejwt.Signed(joseSigner).Claims(claims).Serialize()
|
|
if err != nil {
|
|
t.Fatalf("go-jose sign %s: %v", ks.AlgName, err)
|
|
}
|
|
if _, err := verifier.VerifyJWT(tokenStr); err != nil {
|
|
t.Errorf("our verify %s from go-jose JWKS: %v", ks.AlgName, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Audience interop ---
|
|
|
|
func TestAudienceStringInterop_GoJose(t *testing.T) {
|
|
ks := testkeys.GenerateEdDSA("aud-test")
|
|
|
|
// Our library: single aud marshals as plain string "single-aud".
|
|
signer, err := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
claims := testkeys.ListishClaims("aud-str", jwt.Listish{"single-aud"})
|
|
tokenStr, err := signer.SignToString(claims)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{jose.EdDSA})
|
|
if err != nil {
|
|
t.Fatalf("go-jose parse: %v", err)
|
|
}
|
|
var joseClaims josejwt.Claims
|
|
if err := tok.Claims(ks.RawPub, &joseClaims); err != nil {
|
|
t.Fatalf("go-jose Claims: %v", err)
|
|
}
|
|
if len(joseClaims.Audience) != 1 || joseClaims.Audience[0] != "single-aud" {
|
|
t.Errorf("aud: got %v, want [single-aud]", joseClaims.Audience)
|
|
}
|
|
|
|
// Reverse: go-jose signs with single aud, our library parses.
|
|
sigKey := jose.SigningKey{
|
|
Algorithm: jose.EdDSA,
|
|
Key: jose.JSONWebKey{Key: ks.RawPriv, KeyID: ks.KID},
|
|
}
|
|
joseSigner, _ := jose.NewSigner(sigKey, nil)
|
|
jClaims := josejwt.Claims{
|
|
Subject: "aud-str-rev",
|
|
Audience: josejwt.Listish{"single-aud"},
|
|
Expiry: josejwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
}
|
|
joseToken, _ := josejwt.Signed(joseSigner).Claims(jClaims).Serialize()
|
|
|
|
verifier, _ := jwt.NewVerifier([]jwt.PublicKey{ks.PubKey})
|
|
verifiedJWS, err := verifier.VerifyJWT(joseToken)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var decoded jwt.TokenClaims
|
|
verifiedJWS.UnmarshalClaims(&decoded)
|
|
if len(decoded.Aud) == 0 || decoded.Aud[0] != "single-aud" {
|
|
t.Errorf("reverse aud: got %v, want [single-aud]", decoded.Aud)
|
|
}
|
|
}
|
|
|
|
func TestAudienceArrayInterop_GoJose(t *testing.T) {
|
|
ks := testkeys.GenerateEdDSA("aud-arr")
|
|
|
|
signer, _ := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
claims := testkeys.ListishClaims("aud-arr", jwt.Listish{"aud1", "aud2"})
|
|
tokenStr, _ := signer.SignToString(claims)
|
|
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{jose.EdDSA})
|
|
if err != nil {
|
|
t.Fatalf("go-jose parse: %v", err)
|
|
}
|
|
var joseClaims josejwt.Claims
|
|
if err := tok.Claims(ks.RawPub, &joseClaims); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(joseClaims.Audience) != 2 || joseClaims.Audience[0] != "aud1" || joseClaims.Audience[1] != "aud2" {
|
|
t.Errorf("aud: got %v, want [aud1 aud2]", joseClaims.Audience)
|
|
}
|
|
}
|
|
|
|
// --- Custom claims interop ---
|
|
|
|
func TestCustomClaimsInterop_GoJose(t *testing.T) {
|
|
ks := testkeys.GenerateEdDSA("custom")
|
|
signer, _ := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
claims := &testkeys.CustomClaims{
|
|
TokenClaims: *testkeys.TestClaims("custom-user"),
|
|
Email: "user@example.com",
|
|
Roles: []string{"admin", "editor"},
|
|
Metadata: map[string]string{"team": "platform"},
|
|
}
|
|
tokenStr, err := signer.SignToString(claims)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{jose.EdDSA})
|
|
if err != nil {
|
|
t.Fatalf("go-jose parse: %v", err)
|
|
}
|
|
|
|
// go-jose extracts into an arbitrary struct.
|
|
var extracted struct {
|
|
josejwt.Claims
|
|
Email string `json:"email"`
|
|
Roles []string `json:"roles"`
|
|
Metadata map[string]string `json:"metadata"`
|
|
}
|
|
if err := tok.Claims(ks.RawPub, &extracted); err != nil {
|
|
t.Fatalf("go-jose Claims: %v", err)
|
|
}
|
|
if extracted.Email != "user@example.com" {
|
|
t.Errorf("email: got %q, want %q", extracted.Email, "user@example.com")
|
|
}
|
|
if len(extracted.Roles) != 2 || extracted.Roles[0] != "admin" {
|
|
t.Errorf("roles: got %v, want [admin editor]", extracted.Roles)
|
|
}
|
|
if extracted.Metadata["team"] != "platform" {
|
|
t.Errorf("metadata.team: got %v, want %q", extracted.Metadata["team"], "platform")
|
|
}
|
|
}
|
|
|
|
// --- NumericDate precision ---
|
|
|
|
func TestNumericDatePrecision_GoJose(t *testing.T) {
|
|
ks := testkeys.GenerateEdDSA("nd")
|
|
|
|
// Use fixed future timestamps to test precision without triggering
|
|
// expiration validation. 2000000000 = 2033-05-18.
|
|
var wantExp int64 = 2000000000
|
|
var wantIat int64 = 1999999000
|
|
claims := &jwt.TokenClaims{
|
|
Iss: "https://example.com",
|
|
Sub: "numdate",
|
|
Exp: wantExp,
|
|
IAt: wantIat,
|
|
}
|
|
signer, _ := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
tokenStr, _ := signer.SignToString(claims)
|
|
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{jose.EdDSA})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var joseClaims josejwt.Claims
|
|
if err := tok.Claims(ks.RawPub, &joseClaims); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if joseClaims.Expiry.Time().Unix() != wantExp {
|
|
t.Errorf("exp: got %d, want %d", joseClaims.Expiry.Time().Unix(), wantExp)
|
|
}
|
|
if joseClaims.IssuedAt.Time().Unix() != wantIat {
|
|
t.Errorf("iat: got %d, want %d", joseClaims.IssuedAt.Time().Unix(), wantIat)
|
|
}
|
|
|
|
// Reverse: go-jose signs with specific times, our library reads.
|
|
var wantExp2 int64 = 2100000000
|
|
var wantIat2 int64 = 2099999000
|
|
sigKey := jose.SigningKey{
|
|
Algorithm: jose.EdDSA,
|
|
Key: jose.JSONWebKey{Key: ks.RawPriv, KeyID: ks.KID},
|
|
}
|
|
joseSigner, _ := jose.NewSigner(sigKey, nil)
|
|
jClaims := josejwt.Claims{
|
|
Subject: "numdate-rev",
|
|
Expiry: josejwt.NewNumericDate(time.Unix(wantExp2, 0)),
|
|
IssuedAt: josejwt.NewNumericDate(time.Unix(wantIat2, 0)),
|
|
}
|
|
joseToken, _ := josejwt.Signed(joseSigner).Claims(jClaims).Serialize()
|
|
|
|
verifier, _ := jwt.NewVerifier([]jwt.PublicKey{ks.PubKey})
|
|
verifiedJWS, _ := verifier.VerifyJWT(joseToken)
|
|
var decoded jwt.TokenClaims
|
|
verifiedJWS.UnmarshalClaims(&decoded)
|
|
if decoded.Exp != wantExp2 {
|
|
t.Errorf("rev exp: got %d, want %d", decoded.Exp, wantExp2)
|
|
}
|
|
if decoded.IAt != wantIat2 {
|
|
t.Errorf("rev iat: got %d, want %d", decoded.IAt, wantIat2)
|
|
}
|
|
}
|
|
|
|
// --- Stress tests ---
|
|
|
|
func TestStress_GoJose(t *testing.T) {
|
|
for _, ag := range testkeys.AllAlgorithms() {
|
|
ag := ag
|
|
t.Run(ag.Name, func(t *testing.T) {
|
|
t.Parallel()
|
|
n := 1000
|
|
if ag.Name == "RS256" {
|
|
n = 10
|
|
if *longTests {
|
|
n = 100
|
|
}
|
|
}
|
|
for i := range n {
|
|
ks := ag.Generate(fmt.Sprintf("s%d", i))
|
|
sub := fmt.Sprintf("stress-%d", i)
|
|
|
|
// Our sign, go-jose verify.
|
|
signer, err := jwt.NewSigner([]*jwt.PrivateKey{ks.PrivKey})
|
|
if err != nil {
|
|
t.Fatalf("iter %d: NewSigner: %v", i, err)
|
|
}
|
|
tokenStr, err := signer.SignToString(testkeys.TestClaims(sub))
|
|
if err != nil {
|
|
t.Fatalf("iter %d: SignToString: %v", i, err)
|
|
}
|
|
tok, err := josejwt.ParseSigned(tokenStr, []jose.SignatureAlgorithm{joseAlg(ks.AlgName)})
|
|
if err != nil {
|
|
t.Fatalf("iter %d: go-jose parse: %v", i, err)
|
|
}
|
|
var claims josejwt.Claims
|
|
if err := tok.Claims(ks.RawPub, &claims); err != nil {
|
|
t.Fatalf("iter %d: go-jose verify: %v", i, err)
|
|
}
|
|
|
|
// go-jose sign, our verify.
|
|
sigKey := jose.SigningKey{
|
|
Algorithm: joseAlg(ks.AlgName),
|
|
Key: jose.JSONWebKey{Key: ks.RawPriv, KeyID: ks.KID},
|
|
}
|
|
joseSigner, err := jose.NewSigner(sigKey, nil)
|
|
if err != nil {
|
|
t.Fatalf("iter %d: go-jose NewSigner: %v", i, err)
|
|
}
|
|
jClaims := josejwt.Claims{
|
|
Subject: sub,
|
|
Expiry: josejwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
}
|
|
joseToken, err := josejwt.Signed(joseSigner).Claims(jClaims).Serialize()
|
|
if err != nil {
|
|
t.Fatalf("iter %d: go-jose sign: %v", i, err)
|
|
}
|
|
verifier, _ := jwt.NewVerifier([]jwt.PublicKey{ks.PubKey})
|
|
if _, err := verifier.VerifyJWT(joseToken); err != nil {
|
|
t.Fatalf("iter %d: our verify: %v", i, err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|