package ect import ( "encoding/base64" "encoding/json" "regexp" ) // 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}$`) // base64urlRegex matches a non-empty base64url string without padding. var base64urlRegex = regexp.MustCompile(`^[A-Za-z0-9_-]+$`) // 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 is plain base64url (no padding) // per draft-nennemann-wimse-ect-01 and RFC 9449 (no algorithm prefix). func ValidateHashFormat(s string) error { if s == "" { return nil } if !base64urlRegex.MatchString(s) { return ErrHashFormat } _, err := base64.RawURLEncoding.DecodeString(s) if err != nil { return ErrHashFormat } return nil }