- 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)
84 lines
1.9 KiB
Go
84 lines
1.9 KiB
Go
package ect
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// ExtMaxSize is the recommended max serialized size of ext (Section 4.2.7).
|
|
const ExtMaxSize = 4096
|
|
|
|
// ExtMaxDepth is the recommended max JSON nesting depth in ext.
|
|
const ExtMaxDepth = 5
|
|
|
|
// DefaultMaxPredLength is the recommended max number of predecessor references.
|
|
const DefaultMaxPredLength = 100
|
|
|
|
// uuidRegex matches RFC 9562 UUID: 8-4-4-4-12 hex.
|
|
var uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
|
|
|
|
// allowedHashAlgs are the spec-recommended hash algorithm prefixes for inp_hash/out_hash.
|
|
var allowedHashAlgs = map[string]bool{"sha-256": true, "sha-384": true, "sha-512": true}
|
|
|
|
// ValidateExt returns an error if ext exceeds ExtMaxSize or ExtMaxDepth.
|
|
func ValidateExt(ext map[string]interface{}) error {
|
|
if ext == nil || len(ext) == 0 {
|
|
return nil
|
|
}
|
|
b, err := json.Marshal(ext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(b) > ExtMaxSize {
|
|
return ErrExtSize
|
|
}
|
|
if depth(b) > ExtMaxDepth {
|
|
return ErrExtDepth
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func depth(b []byte) int {
|
|
var max, level int
|
|
for _, c := range b {
|
|
switch c {
|
|
case '{', '[':
|
|
level++
|
|
if level > max {
|
|
max = level
|
|
}
|
|
case '}', ']':
|
|
level--
|
|
}
|
|
}
|
|
return max
|
|
}
|
|
|
|
// ValidUUID returns true if s is a UUID string (RFC 9562).
|
|
func ValidUUID(s string) bool {
|
|
return uuidRegex.MatchString(s)
|
|
}
|
|
|
|
// ValidateHashFormat returns nil if s is empty or matches "algorithm:base64url" (sha-256, sha-384, sha-512).
|
|
func ValidateHashFormat(s string) error {
|
|
if s == "" {
|
|
return nil
|
|
}
|
|
idx := strings.Index(s, ":")
|
|
if idx <= 0 {
|
|
return ErrHashFormat
|
|
}
|
|
alg := strings.ToLower(s[:idx])
|
|
if !allowedHashAlgs[alg] {
|
|
return ErrHashFormat
|
|
}
|
|
encoded := s[idx+1:]
|
|
if encoded == "" {
|
|
return ErrHashFormat
|
|
}
|
|
_, err := base64.RawURLEncoding.DecodeString(encoded)
|
|
return err
|
|
}
|