mirror of
https://github.com/zap-proto/go.git
synced 2026-10-30 15:05:00 +00:00
zapgen — interface (RPC) support: - Parse `interface` decls (brace + whitespace forms, one parser) with 1-based auto-assigned method ordinals; methods take ≤1 struct request and (after `returns`) ≤1 struct response, matching the spec README grammar. - desugar: `interface` opens a whitespace block; method lines pass through verbatim (no @N). Block headers now honored only at file scope, so a struct field NAMED `interface`/`struct` stays a field (H4 preserved). - emit: per interface a 1-based ordinal table, a typed Client over a Channel seam, and an abstract Handler + Dispatch<Iface> ordinal router, all over the new runtime call envelope. Method names exported. - New `rpc` package: the ZAP call envelope (BuildRequest/ParseRequest, BuildResponse/ParseResponse, Call, Response, status codes), byte-compatible with the other runtimes' transport envelopes (Version2 header + router msgType flags). Adds zap.NewBuilderV2. - examples/echo: generated service + end-to-end client↔dispatch↔handler test covering request/response, void, no-request, bare, handler-error, and unknown-ordinal paths. Golden fixtures for the interface codegen. cap — reconcile to SPEC v1.1: - B3 signature scope: sign SPEC §3 canonical bytes (Capability[0..164) || canonical(Caveats), Kind:u32||len:u32||Value), via Cap.CanonicalBytes — one definition shared by signer and verifier; excludes Sig and heap indirection so caps verify cross-language. CapID = Hash32(Canonical||Sig). - B2 delegation gate: define cross-cutting Permission bits (PermAttenuate 1<<32, PermAudit, PermRoot); enforce SPEC §2.3 step 3d in VerifyChain and at mint time in Attenuate (refuse a cap the verifier would reject). - B4/B6: VerifyRevocation is scheme-aware (dispatches on the tag byte); verifySig fails closed on SchemeReserved/unknown instead of falling back to ed25519. Verifier.VerifyRevocation added for PQ revocations. - spec_test.go covers all four. Rebrand finish + docs: - Zero capnp/Cap'n Proto and zero hanzo/avalabs/capnproto.org tokens in zap-native source (zap.go doc neutralized; cap doc-comment brand refs generalized). LLM.md refreshed: zapgen shipped (not "forthcoming"), rpc package, interface DSL, cap signature scope. Ignore the agents example binary. go build ./... and go test ./... -count=1 pass; race-clean on changed pkgs.
112 lines
3.8 KiB
Go
112 lines
3.8 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package cap
|
|
|
|
import (
|
|
"encoding/binary"
|
|
)
|
|
|
|
// 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.
|
|
type Revocation struct {
|
|
CapID [32]byte
|
|
RevokedAt int64
|
|
RevokerSig [SigSize]byte
|
|
}
|
|
|
|
// revocationPayload serializes the bytes that get signed.
|
|
func revocationPayload(capID [32]byte, revokedAt int64) []byte {
|
|
out := make([]byte, 40)
|
|
copy(out[:32], capID[:])
|
|
binary.LittleEndian.PutUint64(out[32:], uint64(revokedAt))
|
|
return out
|
|
}
|
|
|
|
// Revoke produces a Revocation record signed by signer. The signer must
|
|
// be the cap's original issuer — only the issuer (or a delegated
|
|
// revocation authority, out of scope here) can revoke a cap.
|
|
func Revoke(c Cap, now int64, signer Signer) (Revocation, error) {
|
|
if signer == nil {
|
|
return Revocation{}, errMissingSigner
|
|
}
|
|
if signer.Public() != c.Issuer() {
|
|
return Revocation{}, ErrChainBroken
|
|
}
|
|
id := c.ID()
|
|
sig, err := signer.Sign(revocationPayload(id, now))
|
|
if err != nil {
|
|
return Revocation{}, err
|
|
}
|
|
return Revocation{
|
|
CapID: id,
|
|
RevokedAt: now,
|
|
RevokerSig: sig,
|
|
}, nil
|
|
}
|
|
|
|
// VerifyRevocation checks that r is a valid revocation under issuerPub
|
|
// using the bootstrap scheme dispatch (ed25519 mandatory-to-implement,
|
|
// fail-closed on unknown/reserved tags). 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.
|
|
//
|
|
// For ML-DSA-65 / hybrid / secp256k1 revocations, use
|
|
// Verifier.VerifyRevocation with a SchemeVerify hook wired — the package
|
|
// function only carries the built-in ed25519 path.
|
|
func VerifyRevocation(r Revocation, issuerPub []byte) error {
|
|
return Verifier{}.VerifyRevocation(r, issuerPub)
|
|
}
|
|
|
|
// VerifyRevocation checks that r is a valid revocation under issuerPub,
|
|
// dispatching on the algorithm tag in r.RevokerSig[AlgTagOffset] exactly
|
|
// as cap signatures do (B4: scheme-aware, not hardcoded ed25519). The
|
|
// dispatch is fail-closed (SPEC §2.3 step 3c): a tag the verifier does
|
|
// not implement, or SchemeReserved (0x00), is rejected. Wire a
|
|
// SchemeVerify hook on the Verifier to accept ML-DSA-65 / hybrid /
|
|
// secp256k1 revocations.
|
|
func (v Verifier) VerifyRevocation(r Revocation, issuerPub []byte) error {
|
|
if len(issuerPub) == 0 {
|
|
return ErrIssuerUnknown
|
|
}
|
|
return v.verifySig(issuerPub, revocationPayload(r.CapID, r.RevokedAt), r.RevokerSig)
|
|
}
|
|
|
|
// EncodeRevocation marshals a Revocation into canonical ZAP wire bytes
|
|
// via the generated RevocationView builder. Symmetric to DecodeRevocation.
|
|
func EncodeRevocation(r Revocation) []byte {
|
|
return NewRevocationView(RevocationViewInput{
|
|
CapID: r.CapID,
|
|
RevokedAt: uint64(r.RevokedAt),
|
|
RevokerSig: r.RevokerSig,
|
|
})
|
|
}
|
|
|
|
// DecodeRevocation parses a ZAP-framed Revocation buffer back into the
|
|
// idiomatic Go struct. Returns ErrBadMagic / ErrTooShort on framing
|
|
// errors. Symmetric to EncodeRevocation.
|
|
func DecodeRevocation(b []byte) (Revocation, error) {
|
|
v, err := WrapRevocationView(b)
|
|
if err != nil {
|
|
return Revocation{}, err
|
|
}
|
|
return Revocation{
|
|
CapID: v.CapID(),
|
|
RevokedAt: int64(v.RevokedAt()),
|
|
RevokerSig: v.RevokerSig(),
|
|
}, nil
|
|
}
|