Files
zeekay a1bf2bfa1e feat: RPC/interfaces codegen + cap spec-conformance (SPEC §2.3/§3) + rebrand finish
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.
2026-06-20 00:02:22 -07:00

97 lines
3.5 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package cap
import (
"crypto/ed25519"
"crypto/rand"
"errors"
)
// 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 a
// consumer's auth layer where the appropriate crypto dependency is wired
// in. The Ed25519Signer stub below is provided for tests and bootstrap.
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
}
// ---- ed25519 test stub ----------------------------------------------------
// 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 a consumer's auth layer.
type Ed25519Signer struct {
priv ed25519.PrivateKey
pub [32]byte
}
// NewEd25519Signer generates a fresh keypair.
func NewEd25519Signer() (*Ed25519Signer, error) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
s := &Ed25519Signer{priv: priv}
s.pub = Hash32(pub)
return s, nil
}
// Sign produces a 64-byte ed25519 signature placed at the leading bytes
// of the SigSize footer and tagged with SchemeEd25519 at AlgTagOffset.
func (s *Ed25519Signer) Sign(payload []byte) ([SigSize]byte, error) {
var out [SigSize]byte
sig := ed25519.Sign(s.priv, payload)
if len(sig) != ed25519.SignatureSize {
return out, errors.New("cap: ed25519 sign produced wrong size")
}
copy(out[:ed25519.SignatureSize], sig)
out[AlgTagOffset] = byte(SchemeEd25519)
return out, nil
}
// Public returns the 32-byte hash of the ed25519 public key.
func (s *Ed25519Signer) Public() [32]byte { return s.pub }
// PublicKey returns the raw 32-byte ed25519 public key. Used in tests to
// register the signer with a Verifier's IssuerKey lookup.
func (s *Ed25519Signer) PublicKey() ed25519.PublicKey {
return s.priv.Public().(ed25519.PublicKey)
}
// verifyEd25519 checks a padded ed25519 signature against a raw pubkey.
// The signature occupies sig[:ed25519.SignatureSize]; the bytes between
// it and sig[AlgTagOffset] are zero pad ignored by this verifier.
func verifyEd25519(pub []byte, payload []byte, sig [SigSize]byte) error {
if len(pub) != ed25519.PublicKeySize {
return errors.New("cap: ed25519 pubkey wrong size")
}
if !ed25519.Verify(ed25519.PublicKey(pub), payload, sig[:ed25519.SignatureSize]) {
return ErrSigMismatch
}
return nil
}