Files
mpc/pkg/attestation/nvidia/hwattest_test.go
zeekay 3f1f30de35 v0.61+v0.62: scheduler trust gate + NVIDIA attestation primitives
v0.61 — wire the scheduler workload-policy gate into the MPC SignGate.
SignGate gains optional Scheduler + Worker fields; AuthorizeSign now
runs scheduler.Eligible(worker, ci.PolicyID) BEFORE the policy
verifier and short-circuits on insufficient trust posture. Existing
callers that pass nil Scheduler are unaffected.

  pkg/scheduler/policy_gate.go     — TrustMode/IOLevel enums,
                                     WorkloadPolicy.Eligible(Worker)
  pkg/scheduler/service_drain.go   — AuthorizeDrain + AuthorizeSession
  pkg/mpc/sign_orchestrator.go     — Scheduler/Worker fields,
                                     WithScheduler(...), two-stage gate

v0.62 — NVIDIA Confidential-Compute attestation primitives. Clean-room
parsers + verifiers against the publicly documented nvTrust schema
(Apache-2 derived); no NVIDIA proprietary code vendored.

  pkg/attestation/nvidia/gpu_report.go   — GPU evidence-report parser
  pkg/attestation/nvidia/nras_client.go  — NRAS HTTP client + JWS verify
  pkg/attestation/nvidia/rim.go          — RIM signature + value match
  pkg/attestation/nvidia/composite.go    — VerifiedBundle (CPU+GPU bind)

Tests (57 passing, -race clean):
  pkg/scheduler                        — 11 (truth-table, drain,
                                          session, orchestrator
                                          integration)
  pkg/attestation/nvidia               — 46 (parser, RIM Ed25519/ECDSA/
                                          RSA-PSS, NRAS happy/error/
                                          signature paths, ECDSA JOSE)
  pkg/attestation/nvidia (hwattest)    — 3, env-skipped without real GPU

Sibling v0.60 reconciliation: pkg/registry + pkg/attestation already
land WorkerCapability + CompositeAttestation envelope using lattice
trust types. nvidia.VerifiedBundle is renamed (was Composite-) to
avoid collision with pkg/attestation.CompositeAttestation; the
canonical envelope wraps the verified blobs this package emits.

Residual: lattice/v7/types is not yet published, so pkg/registry +
pkg/attestation cannot compile against luxfi/[email protected]; the
sibling v0.60 work tracks that. pkg/policy still has a pre-existing
unresolved import of luxfi/fhe/threshold (should be /pkg/threshold);
sign_orchestrator_scheduler_test.go is ready to run as soon as that
upstream issue is fixed by its owner.
2025-12-28 16:53:46 -08:00

142 lines
3.8 KiB
Go

//go:build hwattest
// Real-hardware attestation tests. Build with `-tags hwattest` on a
// host with a Hopper (H100) or Blackwell (B100/B200) GPU plus the
// NVIDIA confidential-compute stack. The default build excludes this
// file so CI does not fault on hardware that is not present.
//
// Set environment:
//
// NRAS_ENDPOINT — full URL to NRAS (default: production NRAS)
// NRAS_TRUST_ROOT_PEM — path to a PEM file containing the NRAS
// signing public key
// GPU_REPORT_PATH — path to a JSON evidence report produced by
// the local nvidia-cc-host binary
// RIM_PATH — path to a signed RIM JSON for this driver
// and VBIOS combination
// RIM_TRUST_ROOT_PEM — path to a PEM file containing the RIM
// signing public key (may equal NRAS root)
//
// All tests skip when their inputs are missing — running with the
// build tag on a host that lacks a configured GPU is harmless.
package nvidia
import (
"context"
"crypto/x509"
"encoding/pem"
"os"
"testing"
"time"
)
func loadPubKeyPEM(t *testing.T, path string) interface{} {
t.Helper()
if path == "" {
t.Skipf("PEM path not set")
}
raw, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read PEM %s: %v", path, err)
}
block, _ := pem.Decode(raw)
if block == nil {
t.Fatalf("no PEM block in %s", path)
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
t.Fatalf("parse PKIX %s: %v", path, err)
}
return pub
}
func TestHardware_ParseLocalGPUReport(t *testing.T) {
path := os.Getenv("GPU_REPORT_PATH")
if path == "" {
t.Skip("GPU_REPORT_PATH not set")
}
raw, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read evidence: %v", err)
}
rep, err := ParseGPUReport(raw)
if err != nil {
t.Fatalf("parse evidence: %v", err)
}
if rep.Architecture != "Hopper" && rep.Architecture != "Blackwell" {
t.Fatalf("unexpected arch: %q", rep.Architecture)
}
t.Logf("parsed report: arch=%s driver=%s vbios=%s",
rep.Architecture, rep.DriverVersion, rep.VBIOSVersion)
}
func TestHardware_VerifyAgainstRealNRAS(t *testing.T) {
endpoint := os.Getenv("NRAS_ENDPOINT")
if endpoint == "" {
t.Skip("NRAS_ENDPOINT not set")
}
reportPath := os.Getenv("GPU_REPORT_PATH")
if reportPath == "" {
t.Skip("GPU_REPORT_PATH not set")
}
rootPath := os.Getenv("NRAS_TRUST_ROOT_PEM")
if rootPath == "" {
t.Skip("NRAS_TRUST_ROOT_PEM not set")
}
raw, err := os.ReadFile(reportPath)
if err != nil {
t.Fatalf("read evidence: %v", err)
}
rep, err := ParseGPUReport(raw)
if err != nil {
t.Fatalf("parse evidence: %v", err)
}
pub := loadPubKeyPEM(t, rootPath)
c := NewNRASClient(endpoint, []TrustRoot{{KeyID: "nras", Public: pub}})
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
res, err := c.Verify(ctx, raw, rep.Nonce, VerifyOptions{})
if err != nil {
t.Fatalf("nras verify: %v", err)
}
if res.Token == "" {
t.Fatal("empty token")
}
}
func TestHardware_RIMMatchAgainstReport(t *testing.T) {
reportPath := os.Getenv("GPU_REPORT_PATH")
rimPath := os.Getenv("RIM_PATH")
rimRoot := os.Getenv("RIM_TRUST_ROOT_PEM")
if reportPath == "" || rimPath == "" || rimRoot == "" {
t.Skip("GPU_REPORT_PATH / RIM_PATH / RIM_TRUST_ROOT_PEM not all set")
}
rep, err := readReport(reportPath)
if err != nil {
t.Fatalf("read report: %v", err)
}
rimRaw, err := os.ReadFile(rimPath)
if err != nil {
t.Fatalf("read rim: %v", err)
}
pub := loadPubKeyPEM(t, rimRoot)
rim, err := ParseAndVerifyRIM(rimRaw, []TrustRoot{{KeyID: "rim", Public: pub}})
if err != nil {
t.Fatalf("rim parse+verify: %v", err)
}
if err := rim.Match(rep); err != nil {
t.Fatalf("rim match: %v", err)
}
}
func readReport(path string) (*GPUReport, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return ParseGPUReport(raw)
}