Documentation
¶
Overview ¶
Package cap implements the ZAP capability runtime.
A Capability is a signed, attenuable token of authority. It grants a holder permission to perform a bitmask of operations on a target, with optional caveats. Caps form a chain: a parent's holder can issue an attenuated child cap whose permissions are a subset of the parent's. VerifyChain walks the chain back to a root, checking each signature, the intersection of permissions, expiry, revocation, and caveats.
Wire format is canonical ZAP — magic+version header, object data, length-prefixed list of Caveat sub-messages. The bytes are produced and read via the generated zero-copy views in capabilities_zap.go. This file is the thin idiomatic-Go wrapper that the IAM/KMS/MPC consumers see: Wrap / Cap.Field / Verifier.
Signature scope: the full ZAP buffer with the SigSize-byte Sig field zeroed (SigSize is 3408 bytes at v1.1, wide enough to hold any supported primitive). Verifier reconstructs the signing payload by zeroing the sig field in a local copy and verifying the captured Signature() against it. See SPEC.md "Signature scope".
Index ¶
- Constants
- Variables
- func EncodeRevocation(r Revocation) []byte
- func Hash32(b []byte) [32]byte
- func NewCapProofView(in CapProofViewInput) []byte
- func NewCapabilityView(in CapabilityViewInput) []byte
- func NewCaveatView(in CaveatViewInput) []byte
- func NewRevocationView(in RevocationViewInput) []byte
- func VerifyRevocation(r Revocation, issuerPub []byte) error
- type Cap
- func (c Cap) Bytes() []byte
- func (c Cap) CaveatAt(i int) Caveat
- func (c Cap) Caveats() []Caveat
- func (c Cap) ExpiresAt() uint64
- func (c Cap) Holder() [32]byte
- func (c Cap) ID() [32]byte
- func (c Cap) IssuedAt() uint64
- func (c Cap) Issuer() [32]byte
- func (c Cap) Kind() uint32
- func (c Cap) NumCaveats() int
- func (c Cap) Parent() [32]byte
- func (c Cap) Permissions() uint64
- func (c Cap) Signature() [SigSize]byte
- func (c Cap) SignedBytes() []byte
- func (c Cap) Target() [32]byte
- type CapKind
- type CapProofView
- type CapProofViewInput
- type CapabilityView
- func (t CapabilityView) Caveats() zap.List
- func (t CapabilityView) ExpiresAt() uint64
- func (t CapabilityView) Holder() [32]byte
- func (t CapabilityView) IssuedAt() uint64
- func (t CapabilityView) Issuer() [32]byte
- func (t CapabilityView) Kind() uint32
- func (t CapabilityView) Parent() [32]byte
- func (t CapabilityView) Permissions() uint64
- func (t CapabilityView) Sig() [3408]byte
- func (t CapabilityView) Target() [32]byte
- type CapabilityViewInput
- type Caveat
- type CaveatKind
- type CaveatView
- type CaveatViewInput
- type Ed25519Signer
- type Issuance
- type Revocation
- type RevocationView
- type RevocationViewInput
- type Scheme
- type Signer
- type Verifier
Constants ¶
const AlgTagOffset = SigSize - 1
AlgTagOffset is the offset of the algorithm-tag byte within the SigSize signature footer. The byte at [SigSize-1] identifies which signature primitive a verifier MUST use; it is part of the signed payload, so a tag flip changes the signature and is caught by verifier mismatch.
const SigSize = 3408
SigSize is the fixed signature footer width in bytes. Sized at v1.1 to hold any of:
- secp256k1 ECDSA (65 bytes)
- Ed25519 (64 bytes)
- ML-DSA-65 (3309 bytes, FIPS 204 §5.2 Level-3)
- hybrid Ed25519+ML-DSA-65 (3373 bytes + 16-byte separator)
3408 is the smallest 16-byte-aligned size that fits FIPS 204 ML-DSA-65 (3309 B) with a 99-byte headroom. Schemes shorter than SigSize are zero-padded on the right; verifiers identify the scheme via the algorithm tag in the final byte (Sig[SigSize-1]) and decode the leading L_scheme bytes accordingly. See zap-spec/capabilities.zap and capabilities_kinds.md for the canonical tag table.
Variables ¶
var ( ErrTooShort = errors.New("cap: buffer too short") ErrBadMagic = errors.New("cap: bad magic") ErrBadCaveats = errors.New("cap: caveat block malformed") ErrSigMismatch = errors.New("cap: signature does not verify") ErrExpired = errors.New("cap: expired") ErrRevoked = errors.New("cap: revoked") ErrChainBroken = errors.New("cap: chain link broken") ErrPermsExceedPar = errors.New("cap: permissions exceed parent") ErrOpNotPermitted = errors.New("cap: op not in permission mask") ErrTargetMismatch = errors.New("cap: target does not match") ErrHolderMismatch = errors.New("cap: holder does not match") ErrIssuerUnknown = errors.New("cap: issuer key unknown") ErrCaveatViolation = errors.New("cap: caveat violated") // ErrUnhandledScheme is returned by a Verifier's SchemeVerify hook to // indicate it does not recognise the algorithm-tag byte; the // dispatcher then falls through to its built-in ed25519 path. // Consumers that want strict fail-closed semantics on unknown schemes // should NOT return this and should instead return ErrSigMismatch. ErrUnhandledScheme = errors.New("cap: signature scheme not handled by SchemeVerify") )
Errors returned by Wrap and chain validation.
Functions ¶
func EncodeRevocation ¶
func EncodeRevocation(r Revocation) []byte
EncodeRevocation marshals a Revocation into canonical ZAP wire bytes via the generated RevocationView builder. Symmetric to DecodeRevocation.
func Hash32 ¶
Hash32 is the package's canonical 32-byte hash function. Exposed so signers and verifiers agree on the digest construction. SHA-256 today, BLAKE3 in v1.1 (see ID).
func NewCapProofView ¶
func NewCapProofView(in CapProofViewInput) []byte
NewCapProofView builds a ZAP-encoded CapProofView message from in and returns the bytes.
func NewCapabilityView ¶
func NewCapabilityView(in CapabilityViewInput) []byte
NewCapabilityView builds a ZAP-encoded CapabilityView message from in and returns the bytes.
func NewCaveatView ¶
func NewCaveatView(in CaveatViewInput) []byte
NewCaveatView builds a ZAP-encoded CaveatView message from in and returns the bytes.
func NewRevocationView ¶
func NewRevocationView(in RevocationViewInput) []byte
NewRevocationView builds a ZAP-encoded RevocationView message from in and returns the bytes.
func VerifyRevocation ¶
func VerifyRevocation(r Revocation, issuerPub []byte) error
VerifyRevocation checks that r is a valid revocation under issuerPub. The caller is expected to have resolved the original cap's Issuer hash to a public key (via the same IssuerKey lookup the Verifier uses) and then call this with the resolved key.
Types ¶
type Cap ¶
type Cap struct {
// contains filtered or unexported fields
}
Cap is a zero-copy view over a capability buffer. Constructed by Wrap. All accessors read directly from raw without allocating; Value slices returned from CaveatAt / Caveats alias the underlying buffer.
func Attenuate ¶
func Attenuate(parent Cap, holder [32]byte, permissions uint64, caveats []Caveat, expiresAt int64, signer Signer) (Cap, error)
Attenuate derives a child cap from parent by intersecting permissions and adding caveats. The child's Issuer = parent's Holder; signer must hold the parent's holder key (this is the basis for chain validation: each link is signed by the previous holder's key).
The child target equals the parent's target — attenuation never broadens scope. permissions is intersected with the parent's permissions; passing 0xFFFF... is equivalent to "inherit all". caveats are appended (not replaced); chain validation evaluates them in conjunction with the parent's.
expiresAt of 0 inherits the parent's expiry; non-zero overrides downward (the child cannot outlive the parent).
func Issue ¶
Issue mints a new root capability signed by signer. The signer's Public() becomes the cap's Issuer field. Parent stays as supplied (zero for a true root; non-zero for re-issuing under an existing parent at the cost of caller asserting the chain).
To derive a child cap from an existing parent, use Attenuate.
func Wrap ¶
Wrap parses a capability buffer and returns a typed zero-copy view. Validates ZAP framing (magic, version, size) plus capability-specific structural checks (sig field within bounds). Cryptographic verification lives in Verifier.
func (Cap) CaveatAt ¶
CaveatAt returns the i-th caveat. Out-of-range returns a zero Caveat. The Value slice aliases the underlying buffer; callers must not mutate it. Each call re-walks the variable-element list: O(i).
func (Cap) Caveats ¶
Caveats returns the slice of caveats decoded in one walk. Values alias the buffer; do not mutate.
func (Cap) ID ¶
ID returns the canonical 32-byte identifier of this cap: SHA-256 of the full buffer (including signature). Revocation records key on ID.
BLAKE3 swap-point: replace sha256.Sum256 with blake3.Sum256 in v1.1 once luxfi/crypto exposes a stable BLAKE3 entry point. Both are 256-bit; consumers should treat the ID as opaque bytes.
func (Cap) NumCaveats ¶
NumCaveats returns the number of caveats attached to this cap.
func (Cap) Permissions ¶
Permissions reads the permission bitmask.
func (Cap) SignedBytes ¶
SignedBytes returns the bytes that were signed when this cap was issued: the full ZAP buffer with the 96-byte Sig field replaced by zeros. Allocates a copy of len(c.raw) bytes — the underlying buffer is not mutated. Used by Verifier and by signers in Issue/Attenuate.
type CapKind ¶
type CapKind uint32
CapKind enumerates the kinds of authority a capability can confer.
type CapProofView ¶
type CapProofView struct {
// contains filtered or unexported fields
}
CapProofView is a zero-copy view into a ZAP-encoded CapProofView message.
func WrapCapProofView ¶
func WrapCapProofView(b []byte) (CapProofView, error)
WrapCapProofView parses b and returns a typed view. Returns an error if the wire-level checks (magic, version, size) fail.
func (CapProofView) Chain ¶
func (t CapProofView) Chain() zap.List
func (CapProofView) Leaf ¶
func (t CapProofView) Leaf() CapabilityView
type CapProofViewInput ¶
CapProofViewInput collects the field values for NewCapProofView.
type CapabilityView ¶
type CapabilityView struct {
// contains filtered or unexported fields
}
CapabilityView is a zero-copy view into a ZAP-encoded CapabilityView message.
func WrapCapabilityView ¶
func WrapCapabilityView(b []byte) (CapabilityView, error)
WrapCapabilityView parses b and returns a typed view. Returns an error if the wire-level checks (magic, version, size) fail.
func (CapabilityView) Caveats ¶
func (t CapabilityView) Caveats() zap.List
func (CapabilityView) ExpiresAt ¶
func (t CapabilityView) ExpiresAt() uint64
func (CapabilityView) Holder ¶
func (t CapabilityView) Holder() [32]byte
func (CapabilityView) IssuedAt ¶
func (t CapabilityView) IssuedAt() uint64
func (CapabilityView) Issuer ¶
func (t CapabilityView) Issuer() [32]byte
func (CapabilityView) Kind ¶
func (t CapabilityView) Kind() uint32
func (CapabilityView) Parent ¶
func (t CapabilityView) Parent() [32]byte
func (CapabilityView) Permissions ¶
func (t CapabilityView) Permissions() uint64
func (CapabilityView) Sig ¶
func (t CapabilityView) Sig() [3408]byte
func (CapabilityView) Target ¶
func (t CapabilityView) Target() [32]byte
type CapabilityViewInput ¶
type CapabilityViewInput struct {
Kind uint32
Target [32]byte
Holder [32]byte
Issuer [32]byte
Permissions uint64
Parent [32]byte
IssuedAt uint64
ExpiresAt uint64
Caveats [][]byte
Sig [3408]byte
}
CapabilityViewInput collects the field values for NewCapabilityView.
type Caveat ¶
type Caveat struct {
Kind CaveatKind
Value []byte
}
Caveat is one constraint attached to a capability. Value bytes alias the underlying ZAP buffer when produced via Cap.CaveatAt / Cap.Caveats; callers must not mutate Value in-place. Caveat literals passed into Issue/Attenuate are copied during build.
type CaveatKind ¶
type CaveatKind uint32
CaveatKind enumerates the kinds of caveat that can be attached.
const ( CaveatExpiresAt CaveatKind = 0x00 CaveatMaxAmount CaveatKind = 0x01 CaveatDestChain CaveatKind = 0x02 CaveatRateLimit CaveatKind = 0x03 CaveatIPCIDR CaveatKind = 0x04 CaveatAssetID CaveatKind = 0x05 CaveatOpAllow CaveatKind = 0x06 CaveatMaxDepth CaveatKind = 0x07 CaveatAudience CaveatKind = 0x08 CaveatNonceHash CaveatKind = 0x09 )
type CaveatView ¶
type CaveatView struct {
// contains filtered or unexported fields
}
CaveatView is a zero-copy view into a ZAP-encoded CaveatView message.
func WrapCaveatView ¶
func WrapCaveatView(b []byte) (CaveatView, error)
WrapCaveatView parses b and returns a typed view. Returns an error if the wire-level checks (magic, version, size) fail.
func (CaveatView) Kind ¶
func (t CaveatView) Kind() uint32
func (CaveatView) Value ¶
func (t CaveatView) Value() []byte
type CaveatViewInput ¶
CaveatViewInput collects the field values for NewCaveatView.
type Ed25519Signer ¶
type Ed25519Signer struct {
// contains filtered or unexported fields
}
Ed25519Signer is a Signer backed by an ed25519 key. ed25519's native signature is 64 bytes; this stub places it at the leading bytes of the SigSize footer with the remaining bytes zero-padded and the algorithm tag (SchemeEd25519 = 0x02) at sig[AlgTagOffset]. The matching verifier (verifyEd25519) reads the leading 64 bytes back out and ignores the pad and tag byte.
This is intended for tests and bootstrap. Production PQ deployments plug an ML-DSA-65 Signer via the runtime's consumer (e.g. github.com/hanzoai/iam/capauth.MLDSA65Signer).
func NewEd25519Signer ¶
func NewEd25519Signer() (*Ed25519Signer, error)
NewEd25519Signer generates a fresh keypair.
func (*Ed25519Signer) Public ¶
func (s *Ed25519Signer) Public() [32]byte
Public returns the 32-byte hash of the ed25519 public key.
func (*Ed25519Signer) PublicKey ¶
func (s *Ed25519Signer) PublicKey() ed25519.PublicKey
PublicKey returns the raw 32-byte ed25519 public key. Used in tests to register the signer with a Verifier's IssuerKey lookup.
type Issuance ¶
type Issuance struct {
Kind uint32
Target [32]byte
Holder [32]byte
Permissions uint64
Parent [32]byte // zero = root
IssuedAt int64 // 0 = use time.Now()
ExpiresAt int64 // 0 = no expiry
Caveats []Caveat
}
Issuance describes the request to mint a new root capability.
type Revocation ¶
Revocation is the on-the-wire record stating that a particular cap is no longer valid. Revocations are gossiped/published out-of-band; the canonical store is a hash-set keyed on CapID.
The signature is over a 40-byte canonical payload:
[32]byte CapID || uint64 RevokedAt (little-endian)
The signature occupies the SigSize footer in the same shape as cap signatures (algorithm tag at byte SigSize-1, primitive's bytes at the leading L_scheme bytes, zero pad in between) so the verifier can reuse one signing primitive. The wire encoding uses ZAP framing via the generated RevocationView; this struct is the idiomatic Go container used at the boundaries.
func DecodeRevocation ¶
func DecodeRevocation(b []byte) (Revocation, error)
DecodeRevocation parses a ZAP-framed Revocation buffer back into the idiomatic Go struct. Returns ErrBadMagic / ErrTooShort on framing errors. Symmetric to EncodeRevocation.
type RevocationView ¶
type RevocationView struct {
// contains filtered or unexported fields
}
RevocationView is a zero-copy view into a ZAP-encoded RevocationView message.
func WrapRevocationView ¶
func WrapRevocationView(b []byte) (RevocationView, error)
WrapRevocationView parses b and returns a typed view. Returns an error if the wire-level checks (magic, version, size) fail.
func (RevocationView) CapID ¶
func (t RevocationView) CapID() [32]byte
func (RevocationView) RevokedAt ¶
func (t RevocationView) RevokedAt() uint64
func (RevocationView) RevokerSig ¶
func (t RevocationView) RevokerSig() [3408]byte
type RevocationViewInput ¶
RevocationViewInput collects the field values for NewRevocationView.
type Scheme ¶ added in v1.1.0
type Scheme uint8
Scheme is the wire-level signature algorithm tag. The numeric values MUST match capabilities_kinds.md "Wire schemes" table; the byte written at Sig[AlgTagOffset] is one of these constants.
Verifiers fail-closed on SchemeReserved and on values not enumerated here. Consumers (e.g. github.com/hanzoai/iam/capauth) re-export a typed alias whose constants reuse these numeric values one-for-one so the wire byte and the typed enum agree.
const ( // SchemeReserved (0x00) MUST NOT appear in valid caps. Verifiers // reject it so a zero-filled / never-initialised footer is caught. SchemeReserved Scheme = 0x00 // SchemeSecp256k1 is 65-byte secp256k1 ECDSA (R||S||v). SchemeSecp256k1 Scheme = 0x01 // SchemeEd25519 is 64-byte Ed25519 (RFC 8032). SchemeEd25519 Scheme = 0x02 // SchemeMLDSA65 is the 3309-byte FIPS 204 Level-3 ML-DSA-65 signature. SchemeMLDSA65 Scheme = 0x03 // SchemeHybrid is concatenated Ed25519 || ML-DSA-65 (64 + 3309 = 3373 B). SchemeHybrid Scheme = 0x04 )
type Signer ¶
type Signer interface {
// Sign returns a fixed-size signature over the supplied payload.
// The signature MUST verify under the Public() key on the verifier
// side. Implementations SHOULD be deterministic per call for
// replay-debugging and MUST NOT leak the secret key. The final
// byte (sig[AlgTagOffset]) MUST carry the algorithm tag.
Sign(payload []byte) ([SigSize]byte, error)
// Public returns the canonical 32-byte hash of the signer's public
// key. This must match the cap's Issuer field for Verify to accept
// the signature.
Public() [32]byte
}
Signer abstracts the issuer's signing key. The v1.1 wire format's signature footer (SigSize bytes, see cap.go) is wide enough for any supported primitive: secp256k1 ECDSA (65 B), Ed25519 (64 B), or ML-DSA-65 (3309 B per FIPS 204 §5.2). Implementations write their scheme tag at sig[AlgTagOffset] before signing so verifiers can dispatch on it.
This package only requires the interface; concrete signers live in the runtime's consumers (e.g. github.com/hanzoai/iam/capauth) where the appropriate crypto dependency is wired in. The Ed25519Signer stub below is provided for tests and bootstrap.
type Verifier ¶
type Verifier struct {
IsRevoked func(capID [32]byte) bool
IssuerKey func(issuerHash [32]byte) ([]byte, error)
SchemeVerify func(scheme Scheme, pub []byte, payload []byte, sig [SigSize]byte) error
}
Verifier holds the policy dependencies cap validation needs:
IsRevoked is a side-channel lookup against the revocation list. Return true to reject the cap regardless of signature or expiry. A nil func is treated as "nothing revoked".
IssuerKey resolves an issuer's 32-byte hash back to its raw public key bytes (ed25519 raw pubkey or ML-DSA-65 FIPS 204 encoding). Must return ErrIssuerUnknown for unknown issuers.
SchemeVerify dispatches on the algorithm-tag byte at sig[AlgTagOffset] to validate the signature under the right primitive. A nil func defaults to ed25519-only (the bootstrap scheme); consumers that want ML-DSA-65 or hybrid wire a dispatch table whose default falls back to the package-private ed25519 path so callers do not need to special-case the bootstrap.
func (Verifier) Verify ¶
Verify validates a single cap independent of chain context. Checks:
- signature is valid for the cap's Issuer (signed payload = the full ZAP buffer with the Sig field zeroed)
- not expired at the supplied now (unix seconds)
- not revoked
- caveat list parses cleanly
Returns nil if the cap is acceptable. Note that Verify does NOT walk the parent chain — use VerifyChain for that. A cap that passes Verify is structurally sound but may still be useless if its parent is revoked or its permissions don't include the op being attempted.
func (Verifier) VerifyChain ¶
func (v Verifier) VerifyChain(leaf Cap, chain []Cap, op uint64, target [32]byte, holder [32]byte, now int64) error
VerifyChain validates a cap proof end-to-end:
- leaf has not expired, is not revoked, has a valid signature
- leaf grants op (bit set in Permissions)
- leaf grants target (matches the supplied target)
- leaf grants holder (matches the supplied holder)
- chain links each parent ID to the next cap in chain
- every link verifies (signature, expiry, revocation)
- every link's permissions are a superset of the child's
- the root link (chain[len-1]) has Parent == zero
- caveats are walked at each level
Pass chain as parents nearest-to-leaf first: chain[0] is the leaf's parent, chain[len-1] is the root. An empty chain means leaf is a root (and the function only checks the leaf itself).