mirror of
https://github.com/zap-proto/zap-spec.git
synced 2026-10-30 15:02:53 +00:00
Widen the Sig footer from sig96 (96 B) to sig3408 (3408 B) so a full FIPS 204 ML-DSA-65 signature (3309 B) fits inline. Capability struct grows from 260 B to 3572 B; Revocation from 136 B to 3448 B. The field offset of Sig itself (164) is unchanged — only the field width. 3408 is the smallest 16-byte-aligned size that fits ML-DSA-65 with 99 B headroom. Schemes shorter than 3408 (secp256k1 65 B, Ed25519 64 B) are zero-padded on the right; the algorithm tag at Sig[3407] tells the verifier which primitive to dispatch on. Wire-incompatible with v1.0. Per the project's "no backwards compatibility, only forwards perfection" rule, every encoder/decoder updates in lockstep at the v1.1 tag — no compat layer, no union, no version negotiation. Cross-references: - capabilities_kinds.md gains the "Wire schemes" tag table. - SPEC.md §6 documents the v1.0 → v1.1 wire transition.
105 lines
5.5 KiB
Plaintext
105 lines
5.5 KiB
Plaintext
# ZAP capability schema v1.1
|
|
#
|
|
# Object-capability auth across the Lux/Liquidity stack. Replaces bearer JWTs:
|
|
# every authority is a typed, transferable, attenuable, revocable Capability
|
|
# signed by the issuer and held by a named principal. Capnp-level-4 sturdy refs
|
|
# in spirit, zcap-ld in compatibility.
|
|
#
|
|
# Cross-references:
|
|
# - capabilities_kinds.md - CapKind, CaveatKind, Permission bit assignments
|
|
# - SPEC.md - Issue/Attenuate/Verify/Revoke semantics, threat model
|
|
#
|
|
# Wire stability: this is the v1.1 wire format. v1.1 widens the signature
|
|
# footer from 96 bytes (v1.0) to 3408 bytes so a full ML-DSA-65 signature
|
|
# (FIPS 204 §5.2, 3309 bytes) fits inline. v1.0 buffers do NOT round-trip
|
|
# through v1.1 parsers and vice versa - the field-width change shifts the
|
|
# struct size from 260 bytes to 3572 bytes. There is no compat layer; per
|
|
# the project's "no backwards compatibility, only forwards perfection"
|
|
# rule, every encoder/decoder is updated in lockstep at the v1.1 tag.
|
|
#
|
|
# Future shape changes still require a new struct name + new ShapeKind.
|
|
# All offsets are in bytes from the start of the fixed header.
|
|
|
|
package cap
|
|
|
|
# 32-byte object identity hash (BLAKE3 over canonical bytes). Used for object
|
|
# targets, principal pubkey hashes, parent-cap hashes, asset IDs, etc.
|
|
type id32 = bytes_fixed[32]
|
|
|
|
# 3408-byte signature footer. Sized to fit any of:
|
|
# - secp256k1 ECDSA (65 bytes, padded right with zeros)
|
|
# - Ed25519 (64 bytes, padded right with zeros)
|
|
# - ML-DSA-65 (3309 bytes per FIPS 204 §5.2, padded right with zeros)
|
|
# - hybrid Ed25519+ML-DSA-65 (3309 + 64 + 16-byte separator, padded)
|
|
# 3408 is 16-byte aligned (3408 = 213 * 16) for cache-friendly access and
|
|
# leaves 99 bytes of headroom above the FIPS 204 figure.
|
|
#
|
|
# Algorithm dispatch: encoders write the algorithm tag in the final byte
|
|
# of the footer (0x01 secp256k1 ECDSA, 0x02 Ed25519, 0x03 ML-DSA-65, 0x04
|
|
# hybrid Ed25519+ML-DSA-65). Decoders read the tag byte to know which
|
|
# verifier to invoke; the tag byte is INSIDE the signed payload (the
|
|
# signer sets it before computing the signature) so a tag flip changes
|
|
# the signature and is detected by verifier mismatch.
|
|
type sig3408 = bytes_fixed[3408]
|
|
|
|
|
|
# Capability - a typed authority over a target object.
|
|
#
|
|
# Replaces bearer JWTs across the stack. Issued by a principal, held by another
|
|
# principal, exercised against an object. Layout is at v1.1.
|
|
#
|
|
# Signature scope: bytes [0..164) of this struct ('s fixed header up to and
|
|
# including Caveats list pointer), concatenated with the canonical encoding
|
|
# of every Caveat in Caveats (kind-byte then value bytes, in list order).
|
|
# See SPEC.md "Signature chain" for the exact rule.
|
|
struct Capability {
|
|
Kind u32 @0 # CapKind: authority profile (see capabilities_kinds.md)
|
|
Target id32 @4 # 32B BLAKE3 hash of the object this cap grants over
|
|
Holder id32 @36 # 32B hash of the wielder's public key (binds to keypair, not bearer)
|
|
Issuer id32 @68 # 32B hash of the issuer's public key (verifier resolves to pubkey via registry)
|
|
Permissions u64 @100 # bitmask: which ops on Target are permitted (must subset of Parent's)
|
|
Parent id32 @108 # hash of parent Capability bytes (all-zero = root cap; revoke parent -> chain dies)
|
|
IssuedAt u64 @140 # unix seconds, issuer's clock at signing time
|
|
ExpiresAt u64 @148 # unix seconds, 0 = never (caveat CaveatKindExpiresAt may tighten further)
|
|
Caveats list<Caveat> @156 # macaroon-style restrictions, AND-composed; each child cap MAY add caveats
|
|
Sig sig3408 @164 # issuer signature over Capability[0..164) || Caveats canonical bytes
|
|
}
|
|
|
|
|
|
# Caveat - composable restriction on a Capability.
|
|
#
|
|
# Caveats are AND-composed: every caveat in the leaf cap AND every caveat
|
|
# in every ancestor must be satisfied for the cap to authorize an action.
|
|
# A child cap MAY add caveats but MUST NOT remove or weaken parent caveats.
|
|
struct Caveat {
|
|
Kind u32 @0 # CaveatKind: the restriction class (see capabilities_kinds.md)
|
|
Value bytes @4 # kind-specific binary payload (length defined per-kind in capabilities_kinds.md)
|
|
}
|
|
|
|
|
|
# Revocation - kill-entry for a Capability or its descendants.
|
|
#
|
|
# Published on-chain (LP-XXX revocation log) or fetched from IAM's revocation
|
|
# endpoint. Listing a CapID kills that capability AND every descendant whose
|
|
# Parent chain transitively references it. Only the original Issuer may
|
|
# revoke - the RevokerSig must verify against the Issuer pubkey of the
|
|
# capability identified by CapID.
|
|
struct Revocation {
|
|
CapID id32 @0 # 32B BLAKE3 hash of the revoked Capability's canonical bytes
|
|
RevokedAt u64 @32 # unix seconds when the revocation was issued
|
|
RevokerSig sig3408 @40 # signature by the original Issuer over CapID || RevokedAt
|
|
}
|
|
|
|
|
|
# CapProof - presentation bundle for verification without network fetch.
|
|
#
|
|
# The wielder presents (Leaf, Chain) to the verifier. Chain holds the parent
|
|
# capabilities from the leaf's Parent up to the root (root has Parent=zero).
|
|
# Verifier walks Chain, checks each link's signature, each child's Issuer ==
|
|
# parent's Holder, the Permissions subset rule, the Caveat-add-only rule, and
|
|
# consults the revocation list at each step. See SPEC.md "Verify".
|
|
struct CapProof {
|
|
Leaf Capability @0 # the cap being exercised (4B nested-struct pointer)
|
|
Chain list<Capability> @4 # parents from Leaf.Parent up to root, in order
|
|
}
|