mirror of
https://github.com/luxfi/mpc.git
synced 2026-09-24 00:26:24 +00:00
150 lines
5.3 KiB
Go
150 lines
5.3 KiB
Go
// GPU evidence-report parser. The NVIDIA Confidential Compute stack
|
|
// produces a JSON-encoded "evidence" envelope that the host forwards
|
|
// to the NVIDIA Remote Attestation Service (NRAS). The envelope wraps
|
|
// a binary attestation report signed by the GPU's confidential-compute
|
|
// firmware plus the GPU's certificate chain.
|
|
//
|
|
// This parser is a clean-room implementation of the publicly
|
|
// documented schema (see nvTrust, NVIDIA Apache-2 release). We do NOT
|
|
// vendor NVIDIA proprietary code. Field names mirror the public spec
|
|
// so downstream code reads naturally against the documentation.
|
|
package nvidia
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Errors returned by the parser.
|
|
var (
|
|
ErrReportEmpty = errors.New("nvidia/report: empty input")
|
|
ErrReportBadJSON = errors.New("nvidia/report: bad JSON envelope")
|
|
ErrReportBadVersion = errors.New("nvidia/report: unsupported evidence_version")
|
|
ErrReportMissingNonce = errors.New("nvidia/report: missing nonce")
|
|
ErrReportBadNonce = errors.New("nvidia/report: nonce must be 32 bytes hex")
|
|
ErrReportMissingArch = errors.New("nvidia/report: missing architecture")
|
|
ErrReportUnknownArch = errors.New("nvidia/report: unknown architecture")
|
|
ErrReportNoMeasurements = errors.New("nvidia/report: no measurements present")
|
|
)
|
|
|
|
// SupportedEvidenceVersions enumerates the report-envelope versions
|
|
// this parser understands. Bumped when NVIDIA ships a new schema.
|
|
var SupportedEvidenceVersions = map[string]bool{
|
|
"2.0": true, // Hopper H100 GA
|
|
"2.1": true, // Hopper post-launch fixes
|
|
"3.0": true, // Blackwell B100/B200
|
|
}
|
|
|
|
// SupportedArchitectures enumerates the GPU architectures we recognise.
|
|
var SupportedArchitectures = map[string]bool{
|
|
"Hopper": true,
|
|
"Blackwell": true,
|
|
}
|
|
|
|
// Measurement is one entry in the GPU's measurement list. PCRIndex
|
|
// follows the spec's natural numbering. Value is the raw measurement
|
|
// bytes (typically a 48-byte SHA-384 digest).
|
|
type Measurement struct {
|
|
PCRIndex uint8 `json:"pcr_index"`
|
|
Name string `json:"name"`
|
|
Value []byte `json:"-"` // decoded from ValueHex
|
|
ValueHex string `json:"value"`
|
|
}
|
|
|
|
// rawReport is the on-the-wire envelope. Field names are the
|
|
// canonical lower-snake spelling used by the NVIDIA evidence schema.
|
|
type rawReport struct {
|
|
EvidenceVersion string `json:"evidence_version"`
|
|
GPUUUID string `json:"gpu_uuid"`
|
|
Architecture string `json:"architecture"`
|
|
DriverVersion string `json:"driver_version"`
|
|
VBIOSVersion string `json:"vbios_version"`
|
|
NonceHex string `json:"nonce"`
|
|
Measurements []Measurement `json:"measurements"`
|
|
CertChain []string `json:"cert_chain"` // PEM blocks
|
|
Quote string `json:"attestation_quote"` // base64 binary
|
|
NVSwitchPresent bool `json:"nvswitch_present"`
|
|
}
|
|
|
|
// GPUReport is the parsed, validated GPU evidence report — every field
|
|
// downstream verification logic needs, with no JSON-specific shape.
|
|
type GPUReport struct {
|
|
EvidenceVersion string
|
|
UUID string
|
|
Architecture string
|
|
DriverVersion string
|
|
VBIOSVersion string
|
|
Nonce [32]byte
|
|
Measurements []Measurement
|
|
CertChain []string // PEM blocks (verified at NRAS, kept for audit)
|
|
QuoteB64 string
|
|
NVSwitchPresent bool
|
|
}
|
|
|
|
// ParseGPUReport reads the JSON envelope and returns a validated
|
|
// GPUReport. All structural problems are caught here so downstream
|
|
// consumers (NRAS client, RIM matcher, composite) deal only with
|
|
// well-formed input.
|
|
func ParseGPUReport(data []byte) (*GPUReport, error) {
|
|
if len(data) == 0 {
|
|
return nil, ErrReportEmpty
|
|
}
|
|
var r rawReport
|
|
if err := json.Unmarshal(data, &r); err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrReportBadJSON, err)
|
|
}
|
|
if !SupportedEvidenceVersions[r.EvidenceVersion] {
|
|
return nil, fmt.Errorf("%w: %q", ErrReportBadVersion, r.EvidenceVersion)
|
|
}
|
|
if r.Architecture == "" {
|
|
return nil, ErrReportMissingArch
|
|
}
|
|
if !SupportedArchitectures[r.Architecture] {
|
|
return nil, fmt.Errorf("%w: %q", ErrReportUnknownArch, r.Architecture)
|
|
}
|
|
if r.NonceHex == "" {
|
|
return nil, ErrReportMissingNonce
|
|
}
|
|
nonceBytes, err := hex.DecodeString(strings.TrimPrefix(r.NonceHex, "0x"))
|
|
if err != nil || len(nonceBytes) != 32 {
|
|
return nil, ErrReportBadNonce
|
|
}
|
|
if len(r.Measurements) == 0 {
|
|
return nil, ErrReportNoMeasurements
|
|
}
|
|
for i := range r.Measurements {
|
|
raw, err := hex.DecodeString(strings.TrimPrefix(r.Measurements[i].ValueHex, "0x"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("nvidia/report: bad measurement[%d] hex: %v", i, err)
|
|
}
|
|
r.Measurements[i].Value = raw
|
|
}
|
|
|
|
rep := &GPUReport{
|
|
EvidenceVersion: r.EvidenceVersion,
|
|
UUID: r.GPUUUID,
|
|
Architecture: r.Architecture,
|
|
DriverVersion: r.DriverVersion,
|
|
VBIOSVersion: r.VBIOSVersion,
|
|
Measurements: r.Measurements,
|
|
CertChain: r.CertChain,
|
|
QuoteB64: r.Quote,
|
|
NVSwitchPresent: r.NVSwitchPresent,
|
|
}
|
|
copy(rep.Nonce[:], nonceBytes)
|
|
return rep, nil
|
|
}
|
|
|
|
// MeasurementMap returns measurements indexed by Name for fast lookup
|
|
// during RIM matching. Names are case-sensitive per spec.
|
|
func (r *GPUReport) MeasurementMap() map[string]Measurement {
|
|
out := make(map[string]Measurement, len(r.Measurements))
|
|
for _, m := range r.Measurements {
|
|
out[m.Name] = m
|
|
}
|
|
return out
|
|
}
|