Files
ietf-wimse-ect/refimpl/go-lang/ect/verify_test.go
Christian Nennemann 884d2dc836 feat: migrate refimpls from draft-00 to draft-01 claim names
- Rename `par` to `pred` (predecessor) in types, serialization, tests
- Remove `pol`, `pol_decision` from core payload; move to `ect_ext`
- Remove `sub` from payload (not part of ECT spec)
- Update `typ` from `wimse-exec+jwt` to `exec+jwt` (accept both)
- Rename MaxParLength to MaxPredLength everywhere
- Update testdata, demos, READMEs with migration table
- All Go tests pass, all 56 Python tests pass (90% coverage)
2026-04-03 10:55:58 +02:00

238 lines
6.2 KiB
Go

package ect
import (
"crypto/ecdsa"
"errors"
"testing"
"time"
)
func TestParse(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss",
Aud: []string{"aud"},
Iat: now.Unix(),
Exp: now.Add(time.Hour).Unix(),
Jti: "jti-parse",
ExecAct: "act",
Pred: []string{},
}
compact, err := Create(payload, key, CreateOptions{KeyID: "kid"})
if err != nil {
t.Fatal(err)
}
parsed, err := Parse(compact)
if err != nil {
t.Fatal(err)
}
if parsed.Payload.Jti != "jti-parse" {
t.Errorf("jti: got %q", parsed.Payload.Jti)
}
if parsed.Raw != compact {
t.Error("Raw should match compact")
}
}
func TestVerify_InvalidTyp(t *testing.T) {
// Create token then tamper header to test typ check would need different alg or manual JWS;
// instead test Verify with bad token
_, err := Verify("not-a-jws", VerifyOptions{})
if err == nil {
t.Fatal("expected error for invalid JWS")
}
}
func TestVerify_Expired(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss",
Aud: []string{"verifier"},
Iat: now.Add(-1 * time.Hour).Unix(),
Exp: now.Add(-1 * time.Minute).Unix(),
Jti: "jti-exp",
ExecAct: "act",
Pred: []string{},
}
compact, _ := Create(payload, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
if kid == "kid" {
return &key.PublicKey, nil
}
return nil, nil
}
_, err := Verify(compact, VerifyOptions{
VerifierID: "verifier",
ResolveKey: resolver,
Now: now,
})
if err == nil {
t.Fatal("expected error for expired token")
}
}
func TestVerify_Replay(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss",
Aud: []string{"v"},
Iat: now.Unix(),
Exp: now.Add(time.Hour).Unix(),
Jti: "jti-replay",
ExecAct: "act",
Pred: []string{},
}
compact, _ := Create(payload, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
if kid == "kid" {
return &key.PublicKey, nil
}
return nil, nil
}
opts := VerifyOptions{
VerifierID: "v",
ResolveKey: resolver,
Now: now,
JTISeen: func(jti string) bool { return jti == "jti-replay" },
}
_, err := Verify(compact, opts)
if err == nil {
t.Fatal("expected error for replay")
}
}
func TestDefaultVerifyOptions(t *testing.T) {
opts := DefaultVerifyOptions()
if opts.IATMaxAge != 15*time.Minute {
t.Errorf("IATMaxAge: got %v", opts.IATMaxAge)
}
if opts.DAG.ClockSkewTolerance != DefaultClockSkewTolerance {
t.Errorf("DAG: got %d", opts.DAG.ClockSkewTolerance)
}
}
func TestVerify_WITSubjectMismatch(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss", Aud: []string{"v"}, Iat: now.Unix(), Exp: now.Add(time.Hour).Unix(),
Jti: "jti-wit", ExecAct: "act", Pred: []string{},
}
compact, _ := Create(payload, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
if kid == "kid" {
return &key.PublicKey, nil
}
return nil, nil
}
_, err := Verify(compact, VerifyOptions{
VerifierID: "v", ResolveKey: resolver, Now: now, WITSubject: "other-iss",
})
if err == nil {
t.Fatal("expected error for WIT subject mismatch")
}
}
func TestVerify_IATTooFarPast(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss", Aud: []string{"v"}, Iat: now.Add(-1 * time.Hour).Unix(), Exp: now.Add(time.Hour).Unix(),
Jti: "jti-iat", ExecAct: "act", Pred: []string{},
}
compact, _ := Create(payload, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
if kid == "kid" {
return &key.PublicKey, nil
}
return nil, nil
}
_, err := Verify(compact, VerifyOptions{
VerifierID: "v", ResolveKey: resolver, Now: now, IATMaxAge: 30 * time.Minute,
})
if err == nil {
t.Fatal("expected error for iat too far in past")
}
}
func TestVerify_IATInFuture(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss", Aud: []string{"v"}, Iat: now.Add(60 * time.Second).Unix(), Exp: now.Add(2 * time.Hour).Unix(),
Jti: "jti-fut", ExecAct: "act", Pred: []string{},
}
compact, _ := Create(payload, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
if kid == "kid" {
return &key.PublicKey, nil
}
return nil, nil
}
_, err := Verify(compact, VerifyOptions{
VerifierID: "v", ResolveKey: resolver, Now: now, IATMaxFuture: 30 * time.Second,
})
if err == nil {
t.Fatal("expected error for iat in future")
}
}
func TestVerify_ResolveKeyError(t *testing.T) {
key, _ := GenerateKey()
now := time.Now()
payload := &Payload{
Iss: "iss", Aud: []string{"v"}, Iat: now.Unix(), Exp: now.Add(time.Hour).Unix(),
Jti: "jti-err", ExecAct: "act", Pred: []string{},
}
compact, _ := Create(payload, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
return nil, errors.New("key lookup failed")
}
_, err := Verify(compact, VerifyOptions{
VerifierID: "v", ResolveKey: resolver, Now: now,
})
if err == nil {
t.Fatal("expected error from ResolveKey")
}
}
func TestVerify_WithDAG(t *testing.T) {
key, _ := GenerateKey()
ledger := NewMemoryLedger()
now := time.Now()
root := &Payload{
Iss: "iss", Aud: []string{"v"}, Iat: now.Unix(), Exp: now.Add(time.Hour).Unix(),
Jti: "jti-root", ExecAct: "act", Pred: []string{},
}
compactRoot, _ := Create(root, key, CreateOptions{KeyID: "kid"})
resolver := func(kid string) (*ecdsa.PublicKey, error) {
if kid == "kid" {
return &key.PublicKey, nil
}
return nil, nil
}
opts := VerifyOptions{
VerifierID: "v", ResolveKey: resolver, Store: ledger, Now: now,
}
parsed, err := Verify(compactRoot, opts)
if err != nil {
t.Fatal(err)
}
_, _ = ledger.Append(compactRoot, parsed.Payload)
child := &Payload{
Iss: "iss", Aud: []string{"v"}, Iat: now.Unix() + 1, Exp: now.Add(time.Hour).Unix(),
Jti: "jti-child", ExecAct: "act2", Pred: []string{"jti-root"},
}
compactChild, _ := Create(child, key, CreateOptions{KeyID: "kid"})
parsed2, err := Verify(compactChild, opts)
if err != nil {
t.Fatal(err)
}
if parsed2.Payload.Jti != "jti-child" {
t.Errorf("got %q", parsed2.Payload.Jti)
}
}