Files
threshold/protocols/mldsa-tee/envelope.go
zeekay 70c77d1818 protocols/mldsa-tee: institutional-custody FIPS 204 via TEE-gated seed reconstruction
Sibling of protocols/slhdsa-tee for the FIPS 204 (ML-DSA / pulsar)
primitive. Output is byte-identical to single-party
pulsar.SignDeterministic on the same seed-derived sk.

Same composition pattern:
  Signer { gate kms.ReleaseGate; hsmP hsm.Provider;
           appr approval.ApprovalProvider; cfg Config }

Permissionless paths (pulsar.OrchestrateV03Sign v0.3 AlgebraicAggregate)
are UNAFFECTED. This is the opt-in institutional-custody extension.

Use cases: IAM signing-as-a-service with attested release, bridge
oracles whose signing key is governed by executive approval per signature.

9 tests + 6 Config sub-tests; t.Parallel; ML-DSA is much faster than
SLH-DSA so the whole package suite runs in well under a second.
2026-06-01 12:17:52 -07:00

96 lines
3.0 KiB
Go

// SPDX-License-Identifier: BSD-3-Clause
package mldsatee
import (
"context"
"crypto/sha256"
"crypto/subtle"
"errors"
"fmt"
"github.com/luxfi/mpc/cc/attest"
"github.com/luxfi/mpc/pkg/kms"
)
// Envelope is the minimal kms.CompositeAttestation implementation
// consumed by this package. Mirrors slhdsa-tee.Envelope in shape so
// embedders that compose both primitives can share the same plumbing.
type Envelope struct {
Kind attest.Kind
EvidenceBytes []byte
ExpectedNonce [32]byte
RIM [32]byte
Hardware [32]byte
TEEPub [32]byte
VerifyOpts []attest.Option
}
var _ kms.CompositeAttestation = (*Envelope)(nil)
// Verify implements kms.CompositeAttestation.Verify.
func (e *Envelope) Verify(expectedNonce [32]byte) (bool, error) {
if subtle.ConstantTimeCompare(e.ExpectedNonce[:], expectedNonce[:]) != 1 {
return false, nil
}
return true, nil
}
// VerifyEvidence implements kms.CompositeAttestation.VerifyEvidence.
//
// Dispatches the single evidence blob to cc/attest, returns the
// verified report on success, surfaces any chain / signature /
// policy failure verbatim. Additionally pins the operator-asserted
// RIM to sha256(measurement) of the verified report.
func (e *Envelope) VerifyEvidence(ctx context.Context, opts ...attest.Option) ([]*attest.VerifiedReport, error) {
if len(e.EvidenceBytes) == 0 {
return nil, fmt.Errorf("%w: empty evidence", attest.ErrInvalidEvidence)
}
allOpts := append([]attest.Option{}, e.VerifyOpts...)
allOpts = append(allOpts, opts...)
rep, err := attest.Dispatch(ctx, e.Kind, e.EvidenceBytes, allOpts...)
if err != nil {
return nil, err
}
if err := defaultRIMCheck(rep, e.RIM); err != nil {
return nil, err
}
return []*attest.VerifiedReport{rep}, nil
}
// RIMDigest implements kms.CompositeAttestation.RIMDigest.
func (e *Envelope) RIMDigest() [32]byte { return e.RIM }
// HardwareFingerprint implements kms.CompositeAttestation.HardwareFingerprint.
func (e *Envelope) HardwareFingerprint() [32]byte { return e.Hardware }
// TEEPublicKey implements kms.CompositeAttestation.TEEPublicKey.
func (e *Envelope) TEEPublicKey() [32]byte { return e.TEEPub }
// EvidenceIssuers implements kms.CompositeAttestation.EvidenceIssuers.
func (e *Envelope) EvidenceIssuers() []string {
switch e.Kind {
case attest.KindSEVSNP:
return []string{kms.IssuerSEVSNP}
case attest.KindTDX:
return []string{kms.IssuerTDX}
case attest.KindNRAS:
return []string{kms.IssuerNVNRAS}
default:
return nil
}
}
// defaultRIMCheck verifies the cc/attest verified-report Measurement
// matches the operator-asserted RIM digest under sha256-folding.
func defaultRIMCheck(rep *attest.VerifiedReport, expected [32]byte) error {
if rep == nil {
return errors.New("mldsa-tee: defaultRIMCheck: nil verified report")
}
got := sha256.Sum256(rep.Measurement)
if subtle.ConstantTimeCompare(got[:], expected[:]) != 1 {
return fmt.Errorf("%w: report measurement does not fold to operator-asserted RIM", attest.ErrPolicy)
}
return nil
}