github.com held commits this host lacked, which refuses the mirror push
("fetch first") and stops github receiving anything at all. Merging rather
than overwriting, so the mirror is a fast-forward again and neither side's
work is dropped.
Adds .hanzo/workflows/cicd.yml calling the canonical pipeline and drops the
GitHub-hosted workflows it replaces. Lands here because this is where the repo
is written — git.hanzo.ai is upstream and github.com is its push mirror, so the
same change made on github.com never reaches the runner fleet.
ARM CCA Realm attestation: a CBOR/COSE CCA token collection (platform token
signed by the operator-pinned CPAK/IAK + realm token signed by the RAK, with
the H(RAK) platform-realm binding enforced). Vendor arm.cca, fail-closed zero
value, real COSE_Sign1 verification.
armcca branched before the registry landed and carried its own parallel copy
(extraVerifiers map + a duplicate registerVerifier + a second VerifierFor
lookup). Converged to the one registry: deleted that block so armcca.go's
init() registers through the canonical registerVerifier in verifier.go, and
repointed its self-registration test to RegisteredVerifier. There is one
registry, one lookup, one dispatch path — no second way.
go.mod: unioned the require blocks (cbor/v2, go-sev-guest, go-tdx-guest,
veraison/ccatoken, go-cose); go mod tidy clean; ccatoken is v1.4.0 and its
transitive jwx/v2, secp256k1/v4 carry their major in the import path — no dep
bumped to a new major. doc.go + verifier.go: ARM CCA is now a real 6th Kind,
not pending. verifier_test.go: TestRegistry_RoutesEveryKind covers all six.
go build/vet/test + go test -race ./attest/... green; gofmt clean.
Fold Dispatch's hard Kind switch into the init() registry SGX introduced.
The registry (registry map, registerVerifier, RegisteredVerifier) moves out
of sgx.go into verifier.go as framework; Dispatch becomes one registry
lookup, fail-closed (ErrUnsupportedKind) for an unregistered Kind. The
switch — including Nitro's added case — is deleted: there is now exactly one
routing path.
Each Kind file is self-contained: its string const, its Verifier impl, its
per-call options, and its 'func init() { registerVerifier(...) }' live
together. SEVSNP/TDX/NVTrust/Nitro consts move out of verifier.go beside
their verifiers (SGX already did this); NVTrust's per-call WithNVTrustRIM /
WithNVTrustTrustRoots move to nvtrust.go for symmetry with WithNitro*. The
shared config keeps the per-Kind per-call policy fields (kdsGetter is shared
by SEV+TDX; nvtrust RIM/roots; nitro roots/PCRs/maxAge); NVTrust's
deployment trust anchors stay on the struct via NewNVTrust. NVTrust
registers its fail-closed zero value, matching prior Dispatch semantics.
registerVerifier panics on a duplicate Kind — a duplicate registration is a
programmer error that must fail loud at startup, never silently shadow a
verifier on the trust path.
doc.go: drop the now-false TDX/NVTrust STUB labels (both real), document all
five shipped Kinds + the registry + the pending ARMCCA slot, and the TDX
MRTD+RTMR measurement fold. verifier_test.go: retire the stub tests (done by
the TDX merge) and add TestRegistry_RoutesEveryKind asserting registry
routing for all five Kinds and fail-closed for the unregistered armcca slot.
go build/vet/test + go test -race ./attest/... all green; gofmt clean.
COSE_Sign1/CBOR attestation document verifier (ES384, pinned AWS Nitro
Enclaves Root G1). go.mod conflict with the TDX require-block resolved by
unioning all four direct deps (cbor/v2, go-sev-guest, go-tdx-guest, go-cose);
go mod tidy clean, no version bumped.
The switch+registry coexistence introduced here is transient — the next
commit folds Dispatch into the single init() registry.
Adds the ARM Confidential Compute Architecture (CCA / RME Realms) TEE
attestation Kind to the one-verifier seam, alongside SEV-SNP/TDX/NVTrust.
Orthogonal and self-contained, in its own files (armcca.go, armcca_test.go).
Verification follows the IETF RATS model + the ARM CCA token format and is
built on exactly the two named primitives — CBOR (fxamacker/cbor/v2) and
COSE (veraison/go-cose) — plus stdlib:
- parse the CBOR collection envelope (CMW tag 907 or legacy EAT tag 399)
into the platform + realm COSE_Sign1 tokens
- verify the platform token COSE_Sign1 against an operator-pinned CPAK
(ARMCCA.TrustAnchors; empty => refuse, no insecure mode)
- extract the RAK from the realm claims (raw P-384 point or COSE_Key, per
realm-profile presence) and verify the realm token COSE_Sign1 under it
- enforce the platform<->realm binding: platform nonce == H_alg(RAK)
- map RIM -> Measurement, realm challenge -> ReportData (freshness),
REM/config/lifecycle/profiles -> Extra; CompositeHash over verified fields
- refusal taxonomy: bad COSE sig -> ErrSignatureInvalid; broken binding ->
ErrChainInvalid; malformed -> ErrInvalidEvidence; stale nonce / wrong
measurement / missing anchor -> ErrPolicy
KindARMCCA self-registers via init() into a per-file registry (VerifierFor);
it edits no shared file. Wiring Dispatch's switch to consult the registry is a
one-line maintainer follow-up, kept out of this change by file scope.
Tested against real ARM CCA token vectors from the Veraison / Trusted-Firmware
RMM corpus (CMW/tag-907 P-384 CPAK + EAT/tag-399 P-256 IAK), cross-checked
against the canonical veraison/ccatoken reference (a test-only dependency),
with tamper / broken-binding / wrong-key / stale-nonce / malformed refusal
coverage. ccatoken/psatoken/jwx stay out of the production import graph.
The prior KindNVTrust local path was cryptographically hollow: it matched
PLAINTEXT JSON measurements against the (public) RIM and never parsed or
verified the GPU-signed SPDM report or the device certificate chain — a
host could echo golden values and pass. This makes it production-real.
ModeLocal (cloud-free, full hardware verify), in order, refusing on any
failure:
1. chain the GPU device leaf cert to an operator-pinned NVIDIA device-
identity root (no insecure mode; empty root => refuse);
2. verify the SPDM MEASUREMENTS (0x60) response signature over the
DSP0274 transcript (request || response[:-sig]) with the leaf key,
SPDM 1.1 and 1.2, ECDSA P-384/P-256 + RSA-PSS, with a strict
algo<->key-type binding (substitution defense);
3. extract measurements from the SIGNED record and match them BY INDEX
to the NVIDIA-signed RIM (completeness enforced — closes the drop-a-
bad-measurement attack);
4. bind the SIGNED requester nonce to the caller's freshness challenge.
ModeNRAS (remote): verify an NVIDIA NRAS EAT/JWT — JWS signature against
pinned signer keys (or x5c chain to a pinned NRAS root), exp/nbf freshness,
eat_nonce binding, positive overall-result. No network in Verify (the POST
stays in NRASClient).
GPU measurement -> VerifiedReport.Measurement (RIM root / EAT root); trust-
score/tier stays caller policy on top. ChipID is the trusted leaf serial,
not the untrusted envelope UUID. Trust anchors are operator-pinned via
NewNVTrust options, never from evidence; the zero value (Dispatch) is fail-
closed. No new dependencies (stdlib crypto only).
New: attest/nvidia/{spdm,devicechain,eat}.go. Extended: gpu_report.go
(spdm_request/response/asym_algo), rim.go (index-keyed MatchSPDM). 30 new
tests: self-consistent SPDM+chain+RIM vectors (1.1 and 1.2), NRAS EAT
vectors, and tamper rejection (bad SPDM sig, tampered signed record, wrong
device root, RIM mismatch, RIM sig tamper, nonce mismatch, missing SPDM,
bad/expired/negative EAT). go build + go test ./... green (race-clean).
Adds AWS Nitro Enclaves as an orthogonal hardware Kind behind the existing
one-Verifier seam, mirroring the SEV-SNP/NVTrust pattern (struct + Verify +
compile-time guard; wired into Dispatch).
Nitro.Verify performs real, end-to-end verification of a Nitro attestation
document (CBOR-encoded COSE_Sign1, RFC 9052):
1. Parse the COSE_Sign1 envelope, tagged (CBOR tag 18) or untagged.
2. CBOR-decode the attestation-document payload with a hardened decoder
(duplicate map keys rejected) and validate its shape per the AWS CDDL
(module_id, digest=="SHA384", timestamp, pcrs 0..31 of 48 bytes,
certificate, cabundle).
3. Require the leaf certificate's key to be ECDSA P-384.
4. Build the X.509 chain leaf -> cabundle intermediates -> the pinned AWS
Nitro Enclaves Root G1, anchored ONLY at the embedded root (never the
cabundle's own root), ExtKeyUsageAny (Nitro leaves carry no EKU), CRL
disabled per AWS.
5. Verify the COSE_Sign1 signature against the leaf P-384 key, binding the
protected-header algorithm to ES384 (kills algorithm substitution).
6. Map PCR0 -> Measurement, nonce -> ReportData, user_data -> HostData; all
PCRs into Extra. Enforce caller policy: WithNitroPCRs, the generic
WithExpectedMeasurement (PCR0) / WithExpectedReportData (nonce), and
WithNitroMaxAge freshness.
The trust anchor is pinned, not taken from the evidence: the embedded AWS
root PEM is self-checked at parse time against the AWS-published SHA-256
fingerprint 641a0321...79bb5b. No insecure mode — a document that does not
chain to the pinned AWS root is refused. WithNitroRoots overrides the anchor
for deterministic tests only (mirrors the SEV WithKDSGetter test override).
Tests (white-box, hermetic, no network): a self-consistent vector built with
real cryptography (P-384 root/intermediate/leaf chain, real COSE_Sign1/ES384
via go-cose, real CBOR via fxamacker), the genuine pinned-root authenticity
check (fingerprint/CN/curve/self-signature/validity), and a full tamper matrix
(bad signature, tampered payload, wrong/unrelated root, expired/broken chain,
wrong/absent PCR, measurement/nonce mismatch, stale/future timestamp, wrong
digest, bad PCR length, non-P384 leaf, algorithm substitution, duplicate CBOR
key, bad CBOR). CompositeHash determinism + content-binding asserted.
Deps: github.com/fxamacker/cbor/v2 v2.9.2, github.com/veraison/go-cose v1.3.0.
TDX was a fail-loud stub (Verify returned ErrNotImplemented, parsing nothing).
It now performs the complete Intel TDX DCAP attestation flow via the canonical
Google verifier github.com/google/go-tdx-guest (v0.3.1, sibling of the
go-sev-guest used by KindSEVSNP), matching the SEV path's rigor:
- Parse the DCAP v4 quote (QuoteV4 only) via go-tdx-guest/abi.
- Verify the PCK certificate chain embedded in the quote's signed
certification data up to the Intel SGX Root CA pinned inside go-tdx-guest
(verify/trusted_root.pem) — the trust anchor is never taken from the quote.
- Verify the Quoting Enclave (QE) report signature and the attestation-key
binding (QE report data == SHA-256(att key || QE auth data)).
- Verify the TD report (TDQuoteBody) ECDSA-P256 signature.
- Fetch Intel PCS collateral (TCB Info + QE Identity + PCK/Root CRLs) and
enforce TCB status (UpToDate) and revocation. GetCollateral and
CheckRevocations are ON; DefaultOptions leaves them off, so we set them.
- Fold MRTD + the four RTMRs into a 48-byte SHA-384 TD measurement root
(Measurement); raw registers + MRSEAM/TD_ATTRIBUTES/XFAM/etc. in Extra.
- Enforce caller policy (expected measurement / report-data nonce binding)
in constant time, and bind CompositeHash to the canonical TD fields.
(nil, err) always means refuse — no partial report, no best-effort fallback.
go-tdx-guest error strings are classified onto the package refusal taxonomy
(ErrInvalidEvidence / ErrSignatureInvalid / ErrChainInvalid / ErrPolicy).
Tests (attest/tdx_test.go) run offline against a REAL Intel Sapphire Rapids
TDX quote + Intel collateral committed verbatim to testdata/ (from the
go-tdx-guest corpus; the go-tdx-guest `testing` getter is linux-gated, so a
small replay getter is used here):
- real SPR quote passes the full DCAP cryptographic chain;
- full production path refuses the fixture on TCB status (upstream
deliberately bumped the sample TCB SVN above the quote) -> ErrPolicy;
- tamper rejection: flipped MRTD breaks the TD report signature, flipped
ECDSA sig rejected, bad version / truncation / garbage -> ErrInvalidEvidence;
- measurement fold + composite hash determinism + policy binding;
- no-fallback when Intel PCS is unreachable; stale-clock refusal; Dispatch
routing; context cancellation.
verifier_test.go: retired TestStubsReturnNotImplemented /
TestDispatch_StubKindsReturnNotImplemented (TDX was the last stub kind they
asserted) now that TDX is real — mirrors the earlier NVTrust promotion which
already edited this file. go.mod/go.sum: add go-tdx-guest v0.3.1 (latest, < v1).
GOWORK=off go build/vet/test/-race ./... all green.
Add KindSGX as a new orthogonal hardware Kind, mirroring the SEV-SNP /
NVTrust shape exactly: a zero-value SGX{} Verifier, canonical
VerifiedReport projection, domain-separated CompositeHash, and the
errors.Is refusal taxonomy (nil report on any error).
Pure-Go DCAP verify (no maintained Google go-sgx-guest exists; go-sev-guest
is AMD, go-tdx-guest is TDX and parses neither the SGX v3 quote nor the
enclave report). From crypto/{ecdsa,ecdh,x509,sha256} + encoding/{asn1,
binary,pem} only — zero new dependencies, no cgo, no network on the verify
path. Offline chain of trust, all from the quote bytes + the pinned Intel
SGX Root CA:
1. structure: version 3, ECDSA-P256 att key, Intel QE vendor ID, every
length bounded (ErrInvalidEvidence)
2. PCK chain leaf -> Intel SGX CA -> pinned Intel SGX Root CA, within
validity at the verification clock (ErrChainInvalid; the root is
pinned in source, never read from the quote)
3. QE report ECDSA-signed by the PCK leaf (ErrSignatureInvalid)
4. attestation-key binding: SHA-256(att_pubkey||qe_auth) == QE
report_data[0:32], [32:64]==0 (ErrChainInvalid) — the DCAP hinge
5. ISV enclave report (with header) ECDSA-signed by the bound att key,
point validated on P-256 first / invalid-curve defense
(ErrSignatureInvalid)
6. MRENCLAVE || MRSIGNER -> Measurement (+ individually in Extra);
pin via WithExpectedMeasurement
7. freshness: ISV report_data nonce via WithExpectedReportData (ErrPolicy)
Honest scope: TCB-level status + revocation need Intel PCS collateral
(signed TCB Info + QE Identity + CRLs, keyed by FMSPC). Not faked — FMSPC,
CPUSVN, PCESVN, ISV SVN are extracted and surfaced in Extra for a
TCB-evaluating layer; certificate-validity staleness IS enforced here.
Self-registers via init() into a package registry (RegisteredVerifier
lookup) — the open driver-pattern extension point. The legacy Dispatch
switch in verifier.go is unchanged (not edited); folding its default case
into RegisteredVerifier unifies the two routing paths (follow-up).
Tests build a byte-exact, self-consistent SGX ECDSA v3 quote over a fresh
PCK chain (with a real Intel-SGX FMSPC extension) — same pattern as
nvtrust_test.go. Covers happy path, deterministic CompositeHash across
keys, FMSPC + SubjectKeyId fallback, self-registration, pinned-root refusal
of the test chain, and tamper rejection: ISV sig, QE sig, measurement body,
broken att-key binding, wrong measurement/report-data policy, stale chain,
untrusted root, malformed framing.
Extract the canonical vendor-neutral attestation verifier out of luxfi/mpc
into its own orthogonal leaf module github.com/luxfi/cc so the MPC custody
system, the TEE custody extension, and the confidential-AI / proof-of-AI
system can all depend on ONE verifier and the verifier depends on none of
their domains.
- attest: Verifier interface, VerifiedReport, Option, Dispatch.
KindSEVSNP (PROD, go-sev-guest + AMD KDS), KindTDX (fail-loud stub),
KindNVTrust (NEW, PROD local-RIM mode).
- attest/nvidia: GPU evidence parser, signed-RIM verifier + measurement
matcher, NRAS remote JWS client (moved verbatim from mpc/pkg/attestation/
nvidia — clean stdlib-only leaf).
- KindNVTrust replaces the former KindNRAS stub with a real, cloud-free
verify: parse GPU evidence, verify the NVIDIA-signed RIM against an
operator-pinned key, match every measurement to the signed golden value.
No insecure mode; empty RIM/roots is refused.
Leaf builds + tests green standalone (GOWORK=off).