mirror of
https://github.com/luxfi/pulsar.git
synced 2026-09-24 00:25:52 +00:00
pulsar: revert TEE coupling — TEE belongs to the consumer, not the protocol
Reverts the TEE attestation surface added in v1.0.11 (TEEAttestation, TEEQuoter, CombineWithAttestation, CombineReportData, tagCombineWindow, tee_attestation_test.go). Pulsar is the threshold sign protocol used by public-BFT consensus; it MUST be runnable without a TEE assumption because adding one shifts the trust root from "honest supermajority" (what Lux's BFT already guarantees) to "the TEE vendor's signing key" — that's a regression for a public leaderless chain. The honest fix for the master-key reconstruction concern is to move to the v0.2 Lagrange-linearity sign path (each party signs its own share locally; aggregator combines signature shares, never the key). That target was already on the Pulsar roadmap before this rip; the v0.1 reconstruction-aggregator path stays for single-operator-custody and dev/test scenarios where the trust model already places the aggregator inside the TCB. Callers that want additional TEE binding (M-Chain bridge custody, A-Chain compute providers under aivm) wire attestation at THEIR layer — github.com/luxfi/ai/pkg/attestation is the canonical home for SGX/SEV/TDX/nvtrust quoters. Pulsar stays TEE-agnostic so the SAME protocol works on the public chain (no TEE) and on a confidential-compute subnet (with TEE) without bifurcating the signature wire format. types.go Signature docstring is rewritten to reflect this layering: v0.1 is dev/single-operator only; v0.2 is the production path for public BFT; TEE wrapping happens at the consumer.
This commit is contained in:
@@ -1,239 +0,0 @@
|
||||
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package pulsar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// fakeQuoter is a software-only TEEQuoter used to pin the binding
|
||||
// between the aggregator's claimed report-data and the embedded
|
||||
// signature event. Production deployments wire in a real SGX/SEV/TDX
|
||||
// quoter; this test surface exists only so we can pin the function's
|
||||
// contract end-to-end.
|
||||
type fakeQuoter struct {
|
||||
platform string
|
||||
measurement []byte
|
||||
failWith error
|
||||
}
|
||||
|
||||
func (f *fakeQuoter) Platform() string { return f.platform }
|
||||
func (f *fakeQuoter) EnclaveMeasurement() []byte { return f.measurement }
|
||||
func (f *fakeQuoter) Quote(reportData []byte) ([]byte, error) {
|
||||
if f.failWith != nil {
|
||||
return nil, f.failWith
|
||||
}
|
||||
// A real quote envelope is platform-specific. For test purposes we
|
||||
// build a synthetic envelope whose internal layout is:
|
||||
// measurement (32 B) || reportData (64 B) || sha3-256(quote prefix)
|
||||
// so the verifier can recover both fields without a vendor parser.
|
||||
envelope := make([]byte, 0, len(f.measurement)+len(reportData)+32)
|
||||
envelope = append(envelope, f.measurement...)
|
||||
envelope = append(envelope, reportData...)
|
||||
tail := sha3.Sum256(envelope)
|
||||
envelope = append(envelope, tail[:]...)
|
||||
return envelope, nil
|
||||
}
|
||||
|
||||
func TestCombineWithAttestation_RoundTrip(t *testing.T) {
|
||||
params := MustParamsFor(ModeP65)
|
||||
pub, shares, _, ident := runDKGWithIdentities(t, 5, 3, ModeP65)
|
||||
|
||||
msg := []byte("threshold sign with TEE attestation")
|
||||
var sid [16]byte
|
||||
copy(sid[:], "pulsar-tee-01")
|
||||
attempt := uint32(1)
|
||||
quorum := []NodeID{shares[0].NodeID, shares[1].NodeID, shares[2].NodeID}
|
||||
sessionKeys := ident.quorumSessionKeys(t, quorum, sid, msg)
|
||||
|
||||
signers := make([]*ThresholdSigner, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
s, err := NewThresholdSigner(params, sid, attempt, quorum, shares[i], sessionKeys[shares[i].NodeID], msg, deterministicReader([]byte{byte(i), 0xEE}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
signers[i] = s
|
||||
}
|
||||
|
||||
r1 := make([]*Round1Message, 3)
|
||||
for i, s := range signers {
|
||||
m, err := s.Round1(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("Round1 %d: %v", i, err)
|
||||
}
|
||||
r1[i] = m
|
||||
}
|
||||
r2 := make([]*Round2Message, 3)
|
||||
for i, s := range signers {
|
||||
m, _, err := s.Round2(r1)
|
||||
if err != nil {
|
||||
t.Fatalf("Round2 %d: %v", i, err)
|
||||
}
|
||||
r2[i] = m
|
||||
}
|
||||
|
||||
measurement := bytes.Repeat([]byte{0x4D}, 32)
|
||||
q := &fakeQuoter{platform: "sgx-dcap", measurement: measurement}
|
||||
|
||||
sig, err := CombineWithAttestation(params, pub, msg, nil, false, sid, attempt, quorum, 3, r1, r2, shares, q)
|
||||
if err != nil {
|
||||
t.Fatalf("CombineWithAttestation: %v", err)
|
||||
}
|
||||
|
||||
// The signature must verify under stock FIPS 204 — the TEE
|
||||
// attestation is an ADDITIONAL layer, never a replacement for
|
||||
// signature verification.
|
||||
if err := Verify(params, pub, msg, sig); err != nil {
|
||||
t.Fatalf("FIPS 204 Verify rejected the threshold-produced sig: %v", err)
|
||||
}
|
||||
|
||||
// The attestation must be present and well-formed.
|
||||
if sig.Attestation == nil {
|
||||
t.Fatal("Attestation is nil — quoter was wired but produced no attestation")
|
||||
}
|
||||
if sig.Attestation.Platform != "sgx-dcap" {
|
||||
t.Errorf("Platform = %q want sgx-dcap", sig.Attestation.Platform)
|
||||
}
|
||||
if !bytes.Equal(sig.Attestation.EnclaveMeasurement, measurement) {
|
||||
t.Errorf("EnclaveMeasurement mismatch")
|
||||
}
|
||||
if len(sig.Attestation.ReportData) != 64 {
|
||||
t.Fatalf("ReportData len = %d want 64 (SGX REPORT_DATA slot width)", len(sig.Attestation.ReportData))
|
||||
}
|
||||
|
||||
// Report-data must match the canonical binding.
|
||||
wantReport := CombineReportData(sid, attempt, pub, msg)
|
||||
if !bytes.Equal(sig.Attestation.ReportData, wantReport) {
|
||||
t.Error("ReportData does not match CombineReportData(sid,attempt,pub,msg)")
|
||||
}
|
||||
|
||||
// Quote must embed both measurement and report-data verbatim.
|
||||
embeddedMeasurement := sig.Attestation.Quote[:32]
|
||||
embeddedReportData := sig.Attestation.Quote[32:96]
|
||||
if !bytes.Equal(embeddedMeasurement, measurement) {
|
||||
t.Error("Quote did not embed measurement at the expected offset")
|
||||
}
|
||||
if !bytes.Equal(embeddedReportData, sig.Attestation.ReportData) {
|
||||
t.Error("Quote did not embed report-data at the expected offset")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCombineWithAttestation_NoQuoter_ProducesPlainSignature(t *testing.T) {
|
||||
params := MustParamsFor(ModeP65)
|
||||
pub, shares, _, ident := runDKGWithIdentities(t, 5, 3, ModeP65)
|
||||
|
||||
msg := []byte("no quoter wired — bare Combine")
|
||||
var sid [16]byte
|
||||
copy(sid[:], "pulsar-tee-02")
|
||||
attempt := uint32(1)
|
||||
quorum := []NodeID{shares[0].NodeID, shares[1].NodeID, shares[2].NodeID}
|
||||
sessionKeys := ident.quorumSessionKeys(t, quorum, sid, msg)
|
||||
|
||||
signers := make([]*ThresholdSigner, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
s, err := NewThresholdSigner(params, sid, attempt, quorum, shares[i], sessionKeys[shares[i].NodeID], msg, deterministicReader([]byte{byte(i), 0xEE}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
signers[i] = s
|
||||
}
|
||||
|
||||
r1 := make([]*Round1Message, 3)
|
||||
for i, s := range signers {
|
||||
m, _ := s.Round1(msg)
|
||||
r1[i] = m
|
||||
}
|
||||
r2 := make([]*Round2Message, 3)
|
||||
for i, s := range signers {
|
||||
m, _, _ := s.Round2(r1)
|
||||
r2[i] = m
|
||||
}
|
||||
|
||||
sig, err := CombineWithAttestation(params, pub, msg, nil, false, sid, attempt, quorum, 3, r1, r2, shares, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("CombineWithAttestation(nil quoter): %v", err)
|
||||
}
|
||||
if sig.Attestation != nil {
|
||||
t.Error("Attestation is non-nil despite nil quoter")
|
||||
}
|
||||
if err := Verify(params, pub, msg, sig); err != nil {
|
||||
t.Fatalf("Verify: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCombineWithAttestation_QuoterError_DiscardsSignature(t *testing.T) {
|
||||
params := MustParamsFor(ModeP65)
|
||||
pub, shares, _, ident := runDKGWithIdentities(t, 5, 3, ModeP65)
|
||||
|
||||
msg := []byte("quoter fails — no signature should be returned")
|
||||
var sid [16]byte
|
||||
copy(sid[:], "pulsar-tee-03")
|
||||
attempt := uint32(1)
|
||||
quorum := []NodeID{shares[0].NodeID, shares[1].NodeID, shares[2].NodeID}
|
||||
sessionKeys := ident.quorumSessionKeys(t, quorum, sid, msg)
|
||||
|
||||
signers := make([]*ThresholdSigner, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
s, err := NewThresholdSigner(params, sid, attempt, quorum, shares[i], sessionKeys[shares[i].NodeID], msg, deterministicReader([]byte{byte(i), 0xEE}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
signers[i] = s
|
||||
}
|
||||
|
||||
r1 := make([]*Round1Message, 3)
|
||||
for i, s := range signers {
|
||||
m, _ := s.Round1(msg)
|
||||
r1[i] = m
|
||||
}
|
||||
r2 := make([]*Round2Message, 3)
|
||||
for i, s := range signers {
|
||||
m, _, _ := s.Round2(r1)
|
||||
r2[i] = m
|
||||
}
|
||||
|
||||
wantErr := errors.New("tee: enclave attestation service unreachable")
|
||||
q := &fakeQuoter{platform: "sgx-dcap", measurement: bytes.Repeat([]byte{0x4D}, 32), failWith: wantErr}
|
||||
|
||||
sig, err := CombineWithAttestation(params, pub, msg, nil, false, sid, attempt, quorum, 3, r1, r2, shares, q)
|
||||
if !errors.Is(err, wantErr) {
|
||||
t.Fatalf("err = %v want %v (quoter failure must propagate)", err, wantErr)
|
||||
}
|
||||
if sig != nil {
|
||||
t.Error("sig is non-nil despite quoter error — caller must NOT receive a signature without attestation when one was requested")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCombineReportData_Stable(t *testing.T) {
|
||||
// Pure-function property: same inputs ⇒ same 64-byte output.
|
||||
var sid [16]byte
|
||||
copy(sid[:], "stable-report-01")
|
||||
pub := &PublicKey{Mode: ModeP65, Bytes: bytes.Repeat([]byte{0xAB}, 32)}
|
||||
msg := []byte("hello pulsar")
|
||||
|
||||
a := CombineReportData(sid, 7, pub, msg)
|
||||
b := CombineReportData(sid, 7, pub, msg)
|
||||
if !bytes.Equal(a, b) {
|
||||
t.Fatal("CombineReportData is not deterministic")
|
||||
}
|
||||
if len(a) != 64 {
|
||||
t.Fatalf("CombineReportData length = %d want 64", len(a))
|
||||
}
|
||||
|
||||
// Different attempt ⇒ different report-data.
|
||||
c := CombineReportData(sid, 8, pub, msg)
|
||||
if bytes.Equal(a, c) {
|
||||
t.Error("ReportData collides across distinct attempts")
|
||||
}
|
||||
|
||||
// Different message ⇒ different report-data.
|
||||
d := CombineReportData(sid, 7, pub, []byte("hello pulsar!"))
|
||||
if bytes.Equal(a, d) {
|
||||
t.Error("ReportData collides across distinct messages")
|
||||
}
|
||||
}
|
||||
@@ -479,111 +479,6 @@ func Combine(params *Params, groupPubkey *PublicKey, message []byte, ctx []byte,
|
||||
return &Signature{Mode: params.Mode, Bytes: sigBytes}, nil
|
||||
}
|
||||
|
||||
// TEEQuoter is the runtime hook the aggregator uses to obtain a
|
||||
// hardware-enclave attestation quote covering the Combine()
|
||||
// reconstruction window. The aggregator wires a Quoter at startup
|
||||
// based on which platform the binary is running on:
|
||||
//
|
||||
// Intel SGX (DCAP) — sgxquoter.New() in pulsar/tee/sgx
|
||||
// AMD SEV-SNP — snpguest binding in pulsar/tee/sev
|
||||
// Intel TDX — tdx-quote in pulsar/tee/tdx
|
||||
// ARM CCA — cca-token in pulsar/tee/cca
|
||||
// AWS Nitro Enclaves — nsm-driver in pulsar/tee/nitro
|
||||
// Software-only (test) — TestQuoter (never accept in production)
|
||||
//
|
||||
// Quote MUST embed reportData verbatim in the platform-specific
|
||||
// REPORT_DATA / USER_DATA slot. Platform implementations that
|
||||
// cannot bind the full 64 bytes MUST hash reportData to fit
|
||||
// (SGX has a 64-byte slot, SEV-SNP has a 64-byte slot, TDX has a
|
||||
// 64-byte slot — none of them need truncation for this caller).
|
||||
type TEEQuoter interface {
|
||||
Platform() string
|
||||
EnclaveMeasurement() []byte
|
||||
Quote(reportData []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// CombineReportData builds the 64-byte report-data binding that
|
||||
// goes into the TEE quote. It is a pure function of the public
|
||||
// signing-event inputs — no secret material participates.
|
||||
//
|
||||
// Domain separation: the "pulsar-combine-window-v1" suffix scopes
|
||||
// this binding to the threshold-Combine reconstruction window, so
|
||||
// the same enclave attesting to (say) a DKG transcript or a TEE-
|
||||
// guarded key-resharing cannot have its quote replayed as proof of
|
||||
// a Combine reconstruction.
|
||||
//
|
||||
// The output is exactly 64 bytes so it drops into the SGX/TDX/SEV
|
||||
// 64-byte REPORT_DATA slot without truncation or hashing.
|
||||
func CombineReportData(sessionID [16]byte, attempt uint32, groupPubkey *PublicKey, message []byte) []byte {
|
||||
parts := [][]byte{
|
||||
sessionID[:],
|
||||
{byte(attempt >> 24), byte(attempt >> 16), byte(attempt >> 8), byte(attempt)},
|
||||
}
|
||||
if groupPubkey != nil {
|
||||
parts = append(parts, groupPubkey.Bytes)
|
||||
}
|
||||
parts = append(parts, message)
|
||||
parts = append(parts, []byte("pulsar-combine-window-v1"))
|
||||
var buf []byte
|
||||
for _, p := range parts {
|
||||
buf = append(buf, p...)
|
||||
}
|
||||
return cshake256(buf, 64, tagCombineWindow)
|
||||
}
|
||||
|
||||
// CombineWithAttestation runs Combine() and additionally requests
|
||||
// a TEE attestation quote covering the reconstruction window. The
|
||||
// returned Signature carries Attestation populated with the
|
||||
// platform's evidence so relying parties that distrust the
|
||||
// aggregator host can verify that reconstruction happened inside
|
||||
// a measured enclave.
|
||||
//
|
||||
// When quoter is nil the function falls through to plain Combine();
|
||||
// the returned Signature has Attestation == nil and the caller is
|
||||
// responsible for documenting that the deployment's trust model
|
||||
// includes the aggregator host.
|
||||
//
|
||||
// The quoter is invoked AFTER the FIPS 204 sign call succeeds and
|
||||
// BEFORE the secret-material zeroisation in Combine(). Any error
|
||||
// from the quoter discards the just-computed signature and returns
|
||||
// the quoter error verbatim — we never emit a signature without
|
||||
// attestation when the operator wired one in.
|
||||
func CombineWithAttestation(
|
||||
params *Params,
|
||||
groupPubkey *PublicKey,
|
||||
message []byte,
|
||||
ctx []byte,
|
||||
randomized bool,
|
||||
sessionID [16]byte,
|
||||
attempt uint32,
|
||||
quorum []NodeID,
|
||||
threshold int,
|
||||
round1 []*Round1Message,
|
||||
round2 []*Round2Message,
|
||||
allShares []*KeyShare,
|
||||
quoter TEEQuoter,
|
||||
) (*Signature, error) {
|
||||
sig, err := Combine(params, groupPubkey, message, ctx, randomized, sessionID, attempt, quorum, threshold, round1, round2, allShares)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if quoter == nil {
|
||||
return sig, nil
|
||||
}
|
||||
reportData := CombineReportData(sessionID, attempt, groupPubkey, message)
|
||||
quote, qerr := quoter.Quote(reportData)
|
||||
if qerr != nil {
|
||||
return nil, qerr
|
||||
}
|
||||
sig.Attestation = &TEEAttestation{
|
||||
Platform: quoter.Platform(),
|
||||
Quote: quote,
|
||||
ReportData: reportData,
|
||||
EnclaveMeasurement: quoter.EnclaveMeasurement(),
|
||||
}
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// transcriptTau1 builds the Round-1 transcript tau_1 = (sid, kappa,
|
||||
// T, i, pk, mu). tau_1 is bound into every commit and MAC so that a
|
||||
// cross-session replay of the commit-and-reveal pair becomes a
|
||||
|
||||
@@ -60,14 +60,6 @@ const (
|
||||
tagExpandB = "PULSAR-EXPANDB-V1"
|
||||
tagComplaint = "PULSAR-COMPLAINT-V1"
|
||||
tagSeedShare = "PULSAR-SEED-SHARE-V1"
|
||||
// tagCombineWindow: scopes the 64-byte report-data that the
|
||||
// aggregator binds into a TEE attestation quote covering the
|
||||
// Combine() reconstruction window. Domain-separated from every
|
||||
// other Pulsar transcript tag so a quote attesting to (say) a
|
||||
// DKG ceremony or a key resharing cannot be replayed as proof
|
||||
// of a Combine reconstruction with a chosen (sid, attempt,
|
||||
// groupPubkey, message). See threshold.go CombineReportData.
|
||||
tagCombineWindow = "PULSAR-COMBINE-WINDOW-V1"
|
||||
)
|
||||
|
||||
// functionName is the SP 800-185 cSHAKE function-name parameter.
|
||||
|
||||
+26
-87
@@ -133,96 +133,35 @@ type KeyShare struct {
|
||||
// applied. A relying party that can verify ML-DSA can verify a
|
||||
// Pulsar Signature with no code change.
|
||||
//
|
||||
// Trust-model note for the threshold path: Combine() briefly
|
||||
// reconstructs the master ML-DSA secret key in the aggregator's
|
||||
// memory before calling FIPS 204 sign. This is byte-equal to a
|
||||
// non-threshold ML-DSA-65 signature by construction (that is the
|
||||
// whole point — relying parties verify with stock FIPS 204), but
|
||||
// the aggregator MUST run inside a hardware enclave for the
|
||||
// post-quantum guarantee to hold against the aggregator host.
|
||||
// Production deployments populate Attestation with the SGX/SEV/TDX
|
||||
// quote covering the reconstruction window; relying parties that
|
||||
// distrust the aggregator host MUST check Attestation in addition
|
||||
// to ML-DSA Verify. The signature bytes alone are not evidence
|
||||
// that the reconstruction was enclave-confined.
|
||||
// Trust-model note for the threshold path: the v0.1 reconstruction-
|
||||
// aggregator instantiation of Combine() briefly reconstructs the
|
||||
// master ML-DSA secret key in the aggregator's memory before
|
||||
// calling FIPS 204 sign. This is byte-equal to a non-threshold
|
||||
// ML-DSA-65 signature by construction (that is the point — relying
|
||||
// parties verify with stock FIPS 204), but reconstruction-at-the-
|
||||
// aggregator is NOT a public-BFT-safe trust model: a compromised
|
||||
// or coresident-snooping aggregator can exfiltrate the master sk.
|
||||
//
|
||||
// v0.1 is therefore a development / single-operator-custody path,
|
||||
// NOT the public-chain production path. The production path is the
|
||||
// v0.2 Lagrange-linearity sign (see threshold.go header) where each
|
||||
// party signs its own ML-DSA share and the aggregator combines
|
||||
// signature shares — the master key is never reconstructed in any
|
||||
// party's memory, so the security model reduces to "honest
|
||||
// supermajority", which is exactly what Lux's BFT consensus already
|
||||
// assumes. No TEE assumption is required and no TEE vendor enters
|
||||
// the trust root.
|
||||
//
|
||||
// Callers that wish to additionally TEE-bind the aggregator (e.g.
|
||||
// M-Chain bridge custody where a single operator runs the
|
||||
// aggregator, or A-Chain compute providers under aivm) wire TEE
|
||||
// attestation at THEIR layer using github.com/luxfi/ai/pkg/attestation
|
||||
// — Pulsar itself stays TEE-agnostic so the same protocol works on
|
||||
// the public chain (no TEE) and on a confidential-compute subnet
|
||||
// (with TEE), without bifurcating the wire format.
|
||||
type Signature struct {
|
||||
Mode Mode
|
||||
Bytes []byte
|
||||
|
||||
// Attestation is the optional TEE evidence covering the
|
||||
// aggregator's Combine() reconstruction. nil when the aggregator
|
||||
// ran outside a TEE (single-tenant operator, dev/test, or a
|
||||
// deployment where the trust model places the aggregator inside
|
||||
// the TCB by policy). When non-nil this is a vendor quote
|
||||
// (Intel SGX DCAP, AMD SEV-SNP attestation report, ARM CCA token,
|
||||
// or a TDX quote) over a transcript hash that binds:
|
||||
// sid || attempt || quorum || groupPubkey || message ||
|
||||
// SHA3-256(reconstructed_sk_lifetime_marker)
|
||||
// The exact attestation envelope is platform-specific; relying
|
||||
// parties verify the quote against the platform CA and then
|
||||
// match the report-data binding.
|
||||
Attestation *TEEAttestation
|
||||
}
|
||||
|
||||
// TEEAttestation carries a hardware-enclave attestation quote that
|
||||
// covers the aggregator's Combine() reconstruction window.
|
||||
//
|
||||
// The signature bytes themselves are stock FIPS 204 ML-DSA — a
|
||||
// relying party can verify them with any conformant implementation.
|
||||
// The TEE attestation is an INDEPENDENT layer that lets a relying
|
||||
// party who does not trust the aggregator host (only the TEE
|
||||
// vendor) confirm that the brief master-key reconstruction inside
|
||||
// Combine() happened in an enclave with measured code, sealed
|
||||
// memory, and no key egress.
|
||||
//
|
||||
// Without this attestation the threshold's post-quantum guarantee
|
||||
// degrades to "trust the aggregator host" for the duration of the
|
||||
// reconstruction call — typically <1 ms on the master path, but
|
||||
// long enough for a memory-snooping coresident attacker to exfil
|
||||
// the ML-DSA sk if the aggregator host is compromised. The TEE
|
||||
// closes that window.
|
||||
type TEEAttestation struct {
|
||||
// Platform is the TEE family that produced Quote.
|
||||
// "sgx-dcap" — Intel SGX Data Center Attestation Primitives
|
||||
// "sev-snp" — AMD SEV-SNP attestation report
|
||||
// "tdx" — Intel TDX quote
|
||||
// "cca" — ARM Confidential Compute Architecture token
|
||||
// "nitro" — AWS Nitro Enclaves attestation document
|
||||
// Relying parties that do not recognise Platform MUST refuse the
|
||||
// signature (fail-closed); never silently downgrade to "no TEE".
|
||||
Platform string
|
||||
|
||||
// Quote is the raw vendor-formatted attestation evidence. Its
|
||||
// internal layout is platform-specific; relying parties parse it
|
||||
// with the platform's verification library (e.g. Intel TGX/QVL,
|
||||
// AMD's snpguest, AWS nitro-cli). The relying party MUST verify
|
||||
// the quote against the platform's trust anchor (root CA) before
|
||||
// trusting any field inside it.
|
||||
Quote []byte
|
||||
|
||||
// ReportData is the 64-byte (SGX/TDX) or 32-byte (SEV-SNP) field
|
||||
// inside Quote that this aggregator bound to. It is computed as
|
||||
// SHA3-256 (truncated/padded to the platform's slot width) of:
|
||||
// sid || attempt || groupPubkey || message ||
|
||||
// sha3-256("pulsar-combine-window-v1")
|
||||
// Relying parties recompute ReportData from the signature
|
||||
// envelope and refuse the signature if it does not match the
|
||||
// value inside Quote — this is what binds the attestation to
|
||||
// THIS signing event rather than some other event the same
|
||||
// enclave attested to earlier.
|
||||
ReportData []byte
|
||||
|
||||
// EnclaveMeasurement is the platform's code-identity hash for
|
||||
// the aggregator binary. Relying parties pin the expected
|
||||
// measurement out-of-band (governance vote, signed allowlist,
|
||||
// hardware-rooted manifest); the attestation only proves the
|
||||
// reconstruction was inside SOME enclave at that measurement.
|
||||
// Whether that measurement is acceptable is the relying party's
|
||||
// policy.
|
||||
// SGX: MRENCLAVE (32 bytes)
|
||||
// SEV-SNP: MEASUREMENT (48 bytes)
|
||||
// TDX: MRTD (48 bytes)
|
||||
EnclaveMeasurement []byte
|
||||
}
|
||||
|
||||
// Round1Message is the broadcast emitted by ThresholdSigner.Round1.
|
||||
|
||||
Reference in New Issue
Block a user