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)
This commit is contained in:
@@ -44,8 +44,8 @@ type VerifyOptions struct {
|
||||
WITSubject string
|
||||
// ValidateUUIDs when true requires jti and wid (if set) to be UUID format.
|
||||
ValidateUUIDs bool
|
||||
// MaxParLength caps par length (0 = no limit). Applied before DAG; DAG may also enforce via DAG.MaxParLength.
|
||||
MaxParLength int
|
||||
// MaxPredLength caps pred length (0 = no limit). Applied before DAG; DAG may also enforce via DAG.MaxPredLength.
|
||||
MaxPredLength int
|
||||
// LogVerify if set is called after verification with jti and any error (for observability).
|
||||
LogVerify func(jti string, err error)
|
||||
}
|
||||
@@ -104,12 +104,13 @@ func Verify(compact string, opts VerifyOptions) (parsed *ParsedECT, err error) {
|
||||
sig := jws.Signatures[0]
|
||||
header := &sig.Header
|
||||
|
||||
// 2. typ must be wimse-exec+jwt (constant-time compare)
|
||||
// 2. typ must be exec+jwt (preferred) or wimse-exec+jwt (legacy); constant-time compare
|
||||
typ, _ := header.ExtraHeaders["typ"].(string)
|
||||
if typ == "" {
|
||||
typ, _ = header.ExtraHeaders[jose.HeaderType].(string)
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(typ), []byte(ECTType)) != 1 {
|
||||
if subtle.ConstantTimeCompare([]byte(typ), []byte(ECTType)) != 1 &&
|
||||
subtle.ConstantTimeCompare([]byte(typ), []byte(ECTTypeLegacy)) != 1 {
|
||||
return nil, ErrInvalidTyp
|
||||
}
|
||||
|
||||
@@ -182,15 +183,15 @@ func Verify(compact string, opts VerifyOptions) (parsed *ParsedECT, err error) {
|
||||
return nil, ErrIATInFuture
|
||||
}
|
||||
|
||||
// 12. Required claims present (jti, exec_act, par)
|
||||
// 12. Required claims present (jti, exec_act, pred)
|
||||
if p.Jti == "" || p.ExecAct == "" {
|
||||
return nil, ErrMissingClaims
|
||||
}
|
||||
if p.Par == nil {
|
||||
p.Par = []string{}
|
||||
if p.Pred == nil {
|
||||
p.Pred = []string{}
|
||||
}
|
||||
if opts.MaxParLength > 0 && len(p.Par) > opts.MaxParLength {
|
||||
return nil, ErrParLength
|
||||
if opts.MaxPredLength > 0 && len(p.Pred) > opts.MaxPredLength {
|
||||
return nil, ErrPredLength
|
||||
}
|
||||
if opts.ValidateUUIDs {
|
||||
if !ValidUUID(p.Jti) {
|
||||
@@ -211,24 +212,14 @@ func Verify(compact string, opts VerifyOptions) (parsed *ParsedECT, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 13. If pol or pol_decision present, both must be present and pol_decision in registry
|
||||
if p.Pol != "" || p.PolDecision != "" {
|
||||
if p.Pol == "" || p.PolDecision == "" {
|
||||
return nil, ErrPolPolDecisionPair
|
||||
}
|
||||
if !ValidPolDecision(p.PolDecision) {
|
||||
return nil, ErrInvalidPolDecision
|
||||
}
|
||||
}
|
||||
|
||||
// 14. DAG validation
|
||||
// 13. DAG validation
|
||||
if opts.Store != nil {
|
||||
if err := ValidateDAG(&p, opts.Store, opts.DAG); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 15. Replay (jti seen)
|
||||
// 14. Replay (jti seen)
|
||||
if opts.JTISeen != nil && opts.JTISeen(p.Jti) {
|
||||
return nil, ErrReplay
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user