mirror of
https://github.com/luxfi/mpc.git
synced 2026-09-24 00:26:24 +00:00
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.
116 lines
3.7 KiB
Go
116 lines
3.7 KiB
Go
package nvidia
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func goodReportJSON() []byte {
|
|
return []byte(`{
|
|
"evidence_version": "2.1",
|
|
"gpu_uuid": "GPU-1234-5678",
|
|
"architecture": "Hopper",
|
|
"driver_version": "535.104.05",
|
|
"vbios_version": "96.00.74.00.01",
|
|
"nonce": "` + hex.EncodeToString(make([]byte, 32)) + `",
|
|
"measurements": [
|
|
{"pcr_index": 0, "name": "FW_RUNTIME", "value": "deadbeef00112233"},
|
|
{"pcr_index": 1, "name": "VBIOS_RT", "value": "cafebabe44556677"}
|
|
],
|
|
"cert_chain": ["-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----"],
|
|
"attestation_quote": "AAAA",
|
|
"nvswitch_present": false
|
|
}`)
|
|
}
|
|
|
|
func TestParseGPUReport_HappyPath(t *testing.T) {
|
|
r, err := ParseGPUReport(goodReportJSON())
|
|
if err != nil {
|
|
t.Fatalf("parse: %v", err)
|
|
}
|
|
if r.UUID != "GPU-1234-5678" {
|
|
t.Fatalf("uuid: %s", r.UUID)
|
|
}
|
|
if r.Architecture != "Hopper" {
|
|
t.Fatalf("arch: %s", r.Architecture)
|
|
}
|
|
if len(r.Measurements) != 2 {
|
|
t.Fatalf("measurements: %d", len(r.Measurements))
|
|
}
|
|
if r.Measurements[0].Name != "FW_RUNTIME" {
|
|
t.Fatalf("name: %s", r.Measurements[0].Name)
|
|
}
|
|
if hex.EncodeToString(r.Measurements[0].Value) != "deadbeef00112233" {
|
|
t.Fatalf("value decode: %x", r.Measurements[0].Value)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsEmpty(t *testing.T) {
|
|
if _, err := ParseGPUReport(nil); !errors.Is(err, ErrReportEmpty) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsBadJSON(t *testing.T) {
|
|
if _, err := ParseGPUReport([]byte("{not json")); !errors.Is(err, ErrReportBadJSON) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsBadVersion(t *testing.T) {
|
|
in := []byte(`{"evidence_version":"0.9","gpu_uuid":"x","architecture":"Hopper","nonce":"` + hex.EncodeToString(make([]byte, 32)) + `","measurements":[{"name":"a","value":"00"}]}`)
|
|
if _, err := ParseGPUReport(in); !errors.Is(err, ErrReportBadVersion) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsUnknownArch(t *testing.T) {
|
|
in := []byte(`{"evidence_version":"2.1","gpu_uuid":"x","architecture":"Pascal","nonce":"` + hex.EncodeToString(make([]byte, 32)) + `","measurements":[{"name":"a","value":"00"}]}`)
|
|
if _, err := ParseGPUReport(in); !errors.Is(err, ErrReportUnknownArch) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsMissingNonce(t *testing.T) {
|
|
in := []byte(`{"evidence_version":"2.1","gpu_uuid":"x","architecture":"Hopper","measurements":[{"name":"a","value":"00"}]}`)
|
|
if _, err := ParseGPUReport(in); !errors.Is(err, ErrReportMissingNonce) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsBadNonce(t *testing.T) {
|
|
in := []byte(`{"evidence_version":"2.1","gpu_uuid":"x","architecture":"Hopper","nonce":"deadbeef","measurements":[{"name":"a","value":"00"}]}`)
|
|
if _, err := ParseGPUReport(in); !errors.Is(err, ErrReportBadNonce) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsNoMeasurements(t *testing.T) {
|
|
in := []byte(`{"evidence_version":"2.1","gpu_uuid":"x","architecture":"Hopper","nonce":"` + hex.EncodeToString(make([]byte, 32)) + `","measurements":[]}`)
|
|
if _, err := ParseGPUReport(in); !errors.Is(err, ErrReportNoMeasurements) {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestParseGPUReport_RejectsBadMeasurementHex(t *testing.T) {
|
|
in := []byte(`{"evidence_version":"2.1","gpu_uuid":"x","architecture":"Hopper","nonce":"` + hex.EncodeToString(make([]byte, 32)) + `","measurements":[{"name":"a","value":"GG"}]}`)
|
|
if _, err := ParseGPUReport(in); err == nil {
|
|
t.Fatal("expected error on bad hex")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMap(t *testing.T) {
|
|
r, err := ParseGPUReport(goodReportJSON())
|
|
if err != nil {
|
|
t.Fatalf("parse: %v", err)
|
|
}
|
|
m := r.MeasurementMap()
|
|
if len(m) != 2 {
|
|
t.Fatalf("map size %d", len(m))
|
|
}
|
|
if _, ok := m["FW_RUNTIME"]; !ok {
|
|
t.Fatal("missing FW_RUNTIME")
|
|
}
|
|
}
|