Documentation
¶
Overview ¶
Package gpuattest implements a device attestation crypto suite for Helix Cluster OS GPU nodes. It is pure-Go, depends only on the Go standard library crypto primitives, and is fully deterministic in its accept/reject outcomes.
It covers four cohesive capabilities over a shared device Descriptor model:
(A) HXC-1568 — device-info challenge/response + stable fingerprint.
A Verifier issues a nonce-bearing Challenge; the device signs
(nonce || fingerprint || tick) with its ed25519 key. Verify accepts a
genuine response and rejects expired, replayed, tampered, wrong-key and
forged-descriptor responses, each with a distinct typed error.
(B) HXC-1569 — seeded matmul proof-of-GPU-work (PoVW). From a seed two NxN
integer matrices are generated by a self-contained xorshift PRNG (no
math/rand global, no clock). C = A*B is computed and a SHA-256 chain is
folded over the rows of C. VerifyProof recomputes every link and reports
the first broken link index on mismatch.
(C) HXC-1570 — O(1) spot-check verification. Each word of the proof carries a
committed leaf hash inside a Merkle tree. SpotCheck verifies only the
requested k indices in O(k log N) using Merkle inclusion proofs — never
re-touching the other words — and names the failing index on tamper.
(D) HXC-1571 — device-sealed AES-256-GCM. SealForDevice derives a key from a
device secret via HKDF-SHA256 and seals a payload under a fresh random
nonce. OpenFromDevice round-trips for the sealing device and is rejected
(GCM authentication failure) for any other device secret.
CLAUDE-2 note: this package is platform-agnostic pure Go (no /proc, no cgroup, no DRM, no cgo). It runs identically on every supported OS, so it needs no per-OS equivalents.
Index ¶
- Variables
- func Fingerprint(d Descriptor) [32]byte
- func OpenFromDevice(ciphertext, deviceSecret []byte) ([]byte, error)
- func SealForDevice(payload, deviceSecret []byte) ([]byte, error)
- func SpotCheck(trustedRoot [32]byte, n int, opened []OpenWord) error
- func VerifyNode(v *Verifier, proofs []DeviceProof, nowTick int64) error
- func VerifyProof(p Proof) (ok bool, firstBrokenLink int, err error)
- type Challenge
- type Descriptor
- type Device
- type DeviceProof
- type NodeDescriptor
- type OpenWord
- type Proof
- type Response
- type Verifier
- type WordResponse
Constants ¶
This section is empty.
Variables ¶
var ( // ErrExpired is returned when the challenge's expiry tick has passed at the // time of verification. ErrExpired = errors.New("gpuattest: challenge expired") // ErrReplay is returned when the challenge nonce has already been consumed // by this Verifier (single-use nonce set). ErrReplay = errors.New("gpuattest: nonce replayed") // ErrTampered is returned when the signature does not match the // reconstructed signed message (any signed field altered). ErrTampered = errors.New("gpuattest: signed payload tampered") // ErrWrongKey is returned when the response was signed by a key that does // not match the descriptor's public key. ErrWrongKey = errors.New("gpuattest: signature key mismatch") // ErrForgedDescriptor is returned when the descriptor presented at verify // time does not hash to the fingerprint that was signed. ErrForgedDescriptor = errors.New("gpuattest: descriptor fingerprint mismatch") )
Typed errors. Each attack variant in Verify maps to a DISTINCT sentinel so a caller (and the closure tests) can prove exactly which check rejected the response, rather than seeing one opaque failure.
var ( // ErrDuplicateGPUUUID is returned when two devices in a NodeDescriptor share // the same descriptor UUID. Per-device proofs are keyed by UUID, so a // duplicate would make a proof ambiguous; enumeration rejects the node // outright rather than silently collapsing the pair. ErrDuplicateGPUUUID = errors.New("gpuattest: duplicate GPU UUID in node") // ErrNoDevices is returned when a NodeDescriptor carries zero devices. A node // with nothing to attest is a caller error, not an empty-but-valid result. ErrNoDevices = errors.New("gpuattest: node has no devices") )
var ErrProofBroken = errors.New("gpuattest: proof chain link mismatch")
ErrProofBroken is returned by VerifyProof when a chain link does not match.
var ErrSealOpen = errors.New("gpuattest: device seal open failed (auth)")
ErrSealOpen is returned by OpenFromDevice when the ciphertext fails GCM authentication under the supplied device secret — i.e. the wrong device (or tampered ciphertext).
var ErrSpotCheckFailed = errors.New("gpuattest: spot-check word mismatch")
ErrSpotCheckFailed is wrapped by SpotCheck with the offending index.
Functions ¶
func Fingerprint ¶
func Fingerprint(d Descriptor) [32]byte
Fingerprint is the stable SHA-256 over the descriptor's canonical encoding.
func OpenFromDevice ¶
OpenFromDevice reverses SealForDevice. It derives the key from deviceSecret, splits off the nonce prefix, and authenticates+decrypts. A wrong device secret derives a different key, so GCM authentication fails and ErrSealOpen is returned (no plaintext leaks).
func SealForDevice ¶
SealForDevice encrypts payload under a key derived from deviceSecret using AES-256-GCM. A fresh 12-byte random nonce is generated per call and PREPENDED to the ciphertext, so the same payload+secret produces different output each time yet always decrypts. Output layout: nonce(12) || gcmCiphertext.
func SpotCheck ¶
SpotCheck verifies ONLY the supplied opened words against the trusted root, using each word's Merkle inclusion proof. Total work is O(k log N), independent of the other N-k words. It returns nil if every opened word is genuine; on the first failure it returns an error naming that exact index (wrapping ErrSpotCheckFailed). n is the committed word count (tree width).
A check at index i fails precisely when that index's transmitted word no longer hashes — through its genuine proof path — into the trusted root, i.e. exactly when the word at i was tampered. Indices whose words are untouched always pass, regardless of tampering elsewhere.
func VerifyNode ¶
func VerifyNode(v *Verifier, proofs []DeviceProof, nowTick int64) error
VerifyNode independently verifies every DeviceProof at nowTick. Each proof is checked in isolation against its own challenge, response and descriptor; any single failure makes VerifyNode return that proof's verification error. A nil return means every per-device proof verified genuinely.
func VerifyProof ¶
VerifyProof independently re-derives A,B,C from the proof's seed/size and recomputes the whole chain, comparing every link. It returns:
- (true, -1, nil) when every link and the root match.
- (false, i, ErrProofBroken) when link i is the FIRST that disagrees with the recomputed value (or, if all links agree but the claimed Root does not, i == N to flag the root).
Types ¶
type Challenge ¶
Challenge is issued by a Verifier. The device must echo the nonce and sign a message bound to the issuing tick and the device fingerprint.
type Descriptor ¶
type Descriptor struct {
Vendor string
Model string
UUID string
VRAM uint64 // bytes
PubKey ed25519.PublicKey
}
Descriptor is the immutable, signable identity of a GPU device. PubKey is the device's ed25519 public key; the matching private key never leaves the device.
type Device ¶
type Device struct {
Descriptor Descriptor
// contains filtered or unexported fields
}
Device holds a key pair and its descriptor. NewDevice generates a fresh ed25519 key (crypto/rand is fine here — tests drive accept/reject via fixed challenges and key reuse, not by predicting the key bytes).
type DeviceProof ¶
type DeviceProof struct {
GPUUUID string
Challenge Challenge
Response Response
Descriptor Descriptor
}
DeviceProof is the per-device attestation work product. It is keyed by GPUUUID (the attested descriptor's UUID) and bundles everything needed to independently verify this single device: the issued Challenge, the device's signed Response, and the Descriptor that was attested. VerifyNode verifies each DeviceProof on its own.
func EnumerateNode ¶
func EnumerateNode(v *Verifier, node NodeDescriptor, issueTick, expiryTick, respondTick int64) ([]DeviceProof, error)
EnumerateNode produces one DeviceProof per device in the node. For each device it issues a fresh challenge from v, has THAT device Respond at respondTick, and records the proof keyed by the device's descriptor UUID.
It rejects an empty device list (ErrNoDevices) and any duplicate device UUID (ErrDuplicateGPUUUID) BEFORE issuing any challenge, so a malformed node never consumes verifier nonces. The returned slice has exactly len(node.Devices) proofs, in device order, each with GPUUUID equal to that device's UUID.
type NodeDescriptor ¶
NodeDescriptor names a node and the set of GPU devices physically present on it. Devices each carry their own key pair and descriptor (see Device).
type OpenWord ¶
OpenWord is what a prover transmits for ONE spot-checked index: the claimed word plus the Merkle inclusion proof (sibling path) for that leaf position. The verifier holds only the trusted root and recomputes inclusion in O(log N); it never receives or touches the other N-1 words.
type Proof ¶
Proof is a deterministic proof of having computed C = A*B for the matrices derived from Seed at size N. Chain[i] = H(Chain[i-1] || row_i(C)), with Chain[-1] taken as the zero hash. Root == Chain[N-1].
type Response ¶
type Response struct {
Nonce [32]byte
Fingerprint [32]byte
Tick int64
SignerPub ed25519.PublicKey
Sig []byte
}
Response is the device's signed answer. Fingerprint is the fingerprint the device claims (and signed); Tick is the device-asserted production tick. SignerPub is the public key the device claims signed this response; Verify binds it to the descriptor's key (mismatch => wrong-key) AND verifies the signature under it (failure under a matching key => tampered field).
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier issues challenges and verifies responses. It owns the single-use nonce set used to reject replays.
func NewVerifier ¶
func NewVerifier() *Verifier
NewVerifier returns a Verifier with an empty consumed-nonce set.
func (*Verifier) Issue ¶
Issue creates a Challenge with a fresh random nonce, bound to the issue and expiry ticks supplied by the caller's clock.
func (*Verifier) Verify ¶
Verify checks a response against the challenge and the presented descriptor at the verifier's current tick. Order of checks matters: expiry and replay are cheap pre-conditions; fingerprint binding, key match and signature are the cryptographic core. On success the nonce is consumed (single-use).
Returns nil for a genuine response, or one of the distinct sentinel errors.
type WordResponse ¶
type WordResponse struct {
Root [32]byte
// contains filtered or unexported fields
}
WordResponse is a PoVW-style response made of many words, each committed under a Merkle root. The verifier holds only Root (32 bytes); to check word i it is given the word plus a logarithmic inclusion proof — never the whole set.
func NewWordResponse ¶
func NewWordResponse(words [][]byte) *WordResponse
NewWordResponse commits to the given words and builds the Merkle tree. The words slice is copied defensively so later external mutation cannot silently change committed state.
func (*WordResponse) Len ¶
func (w *WordResponse) Len() int
Len reports how many words were committed.
func (*WordResponse) Open ¶
func (w *WordResponse) Open(i int) (OpenWord, bool)
Open produces the OpenWord a prover would send for index i, drawn from THIS (committed) response's genuine tree. A dishonest prover may later substitute OpenWord.Word with a tampered value; the proof path still comes from the genuine tree, so substitution at index i only ever fails the check at i.