Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions protocol/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ const (
ProofZKReplay ProofTier = "zk-replay"
)

type VerificationStatus string

const (
VerificationUnknown VerificationStatus = ""
VerificationPending VerificationStatus = "pending"
VerificationAccepted VerificationStatus = "accepted"
VerificationRejected VerificationStatus = "rejected"
VerificationConflicted VerificationStatus = "conflicted"
)

type RuntimeAdapterKind string

const (
Expand Down Expand Up @@ -247,6 +257,233 @@ func (e ExecutorRef) RequiresAttestation() bool {
}
}

type SignatureEnvelope struct {
Algorithm string `json:"algorithm,omitempty"`
KeyID string `json:"key_id,omitempty"`
Value string `json:"value,omitempty"`
Verified bool `json:"verified,omitempty"`
}

type VerifierResult struct {
Provider string `json:"provider"`
Status VerificationStatus `json:"status"`
Message string `json:"message,omitempty"`
Attestation AttestationDecision `json:"attestation,omitzero"`
KeyRelease KeyReleaseDecision `json:"key_release,omitzero"`
}

type AttestationDecision struct {
Provider string `json:"provider,omitempty"`
VerifierID string `json:"verifier_id,omitempty"`
DecisionID string `json:"decision_id,omitempty"`
HardwareClass string `json:"hardware_class,omitempty"`
ExecutorImageDigest string `json:"executor_image_digest,omitempty"`
ExecutorRootFSDigest string `json:"executor_rootfs_digest,omitempty"`
PolicyID string `json:"policy_id,omitempty"`
Nonce string `json:"nonce,omitempty"`
IssuedAt time.Time `json:"issued_at,omitzero"`
ExpiresAt time.Time `json:"expires_at,omitzero"`
SignatureVerified bool `json:"signature_verified,omitempty"`
ConfidentialGPU bool `json:"confidential_gpu,omitempty"`
Signature SignatureEnvelope `json:"signature,omitzero"`
}

func (a AttestationDecision) BindingDigest() string {
a.Signature = SignatureEnvelope{}
a.SignatureVerified = false
data, err := json.Marshal(a)
if err != nil {
panic(err)
}
sum := sha256.Sum256(data)
return "sha256:" + hex.EncodeToString(sum[:])
}

type KeyReleaseDecision struct {
Provider string `json:"provider,omitempty"`
DecisionID string `json:"decision_id,omitempty"`
AttestationDecisionID string `json:"attestation_decision_id,omitempty"`
AttestationDigest string `json:"attestation_digest,omitempty"`
AttestationProvider string `json:"attestation_provider,omitempty"`
AttestationVerifierID string `json:"attestation_verifier_id,omitempty"`
AttestationKeyID string `json:"attestation_key_id,omitempty"`
PolicyID string `json:"policy_id,omitempty"`
TaskID string `json:"task_id,omitempty"`
TaskHash string `json:"task_hash,omitempty"`
InputHash string `json:"input_hash,omitempty"`
DependencyClosureHash string `json:"dependency_closure_hash,omitempty"`
WorkerID string `json:"worker_id,omitempty"`
PoolID string `json:"pool_id,omitempty"`
KeyRefHash string `json:"key_ref_hash,omitempty"`
Released bool `json:"released,omitempty"`
ExpiresAt time.Time `json:"expires_at,omitzero"`
Signature SignatureEnvelope `json:"signature,omitzero"`
}

type AttestedProofBinding struct {
Executor ExecutorRef `json:"executor"`
PolicyID string `json:"policy_id"`
TaskID string `json:"task_id"`
TaskHash string `json:"task_hash"`
InputHash string `json:"input_hash"`
DependencyClosureHash string `json:"dependency_closure_hash"`
WorkerID string `json:"worker_id"`
PoolID string `json:"pool_id"`
StartedAt time.Time `json:"started_at"`
FinishedAt time.Time `json:"finished_at"`
Verifier VerifierResult `json:"verifier"`
}

func ValidateAttestedProofBinding(binding AttestedProofBinding) error {
var errs []error
require := func(name, value string) {
if value == "" {
errs = append(errs, fmt.Errorf("%s is required", name))
}
}

if err := binding.Executor.ValidateForProof(); err != nil {
errs = append(errs, err)
}
require("verifier.provider", binding.Verifier.Provider)
if binding.Verifier.Status != VerificationAccepted {
errs = append(errs, fmt.Errorf("verifier.status must be %q", VerificationAccepted))
}

attestation := binding.Verifier.Attestation
require("verifier.attestation.provider", attestation.Provider)
require("verifier.attestation.verifier_id", attestation.VerifierID)
require("verifier.attestation.decision_id", attestation.DecisionID)
require("verifier.attestation.hardware_class", attestation.HardwareClass)
require("verifier.attestation.executor_image_digest", attestation.ExecutorImageDigest)
require("verifier.attestation.executor_rootfs_digest", attestation.ExecutorRootFSDigest)
require("verifier.attestation.policy_id", attestation.PolicyID)
require("verifier.attestation.nonce", attestation.Nonce)
if err := validateVerifiedSignature("verifier.attestation.signature", attestation.Signature); err != nil {
errs = append(errs, err)
}
if !attestation.SignatureVerified {
errs = append(errs, errors.New("verifier.attestation.signature_verified is required"))
}
if attestation.IssuedAt.IsZero() {
errs = append(errs, errors.New("verifier.attestation.issued_at is required"))
}
if attestation.ExpiresAt.IsZero() {
errs = append(errs, errors.New("verifier.attestation.expires_at is required"))
}
if !attestation.IssuedAt.IsZero() && !attestation.ExpiresAt.IsZero() && !attestation.ExpiresAt.After(attestation.IssuedAt) {
errs = append(errs, errors.New("verifier.attestation.expires_at must be after issued_at"))
}
if attestation.PolicyID != "" && attestation.PolicyID != binding.PolicyID {
errs = append(errs, errors.New("verifier.attestation.policy_id must match receipt policy_id"))
}
if attestation.HardwareClass != "" && attestation.HardwareClass != string(binding.Executor.ExecutionSecurityTier) {
errs = append(errs, errors.New("verifier.attestation.hardware_class must match executor.execution_security_tier"))
}
if attestation.ExecutorImageDigest != "" && attestation.ExecutorImageDigest != binding.Executor.ImageDigest {
errs = append(errs, errors.New("verifier.attestation.executor_image_digest must match executor.image_digest"))
}
if attestation.ExecutorRootFSDigest != "" && attestation.ExecutorRootFSDigest != binding.Executor.RootFSDigest {
errs = append(errs, errors.New("verifier.attestation.executor_rootfs_digest must match executor.rootfs_digest"))
}
if binding.Executor.ExecutionSecurityTier == ExecutionConfidentialGPU && !attestation.ConfidentialGPU {
errs = append(errs, errors.New("verifier.attestation.confidential_gpu is required for confidential GPU execution"))
}

keyRelease := binding.Verifier.KeyRelease
require("verifier.key_release.provider", keyRelease.Provider)
require("verifier.key_release.decision_id", keyRelease.DecisionID)
require("verifier.key_release.attestation_decision_id", keyRelease.AttestationDecisionID)
require("verifier.key_release.attestation_digest", keyRelease.AttestationDigest)
require("verifier.key_release.attestation_provider", keyRelease.AttestationProvider)
require("verifier.key_release.attestation_verifier_id", keyRelease.AttestationVerifierID)
require("verifier.key_release.attestation_key_id", keyRelease.AttestationKeyID)
require("verifier.key_release.policy_id", keyRelease.PolicyID)
require("verifier.key_release.task_id", keyRelease.TaskID)
require("verifier.key_release.task_hash", keyRelease.TaskHash)
require("verifier.key_release.input_hash", keyRelease.InputHash)
require("verifier.key_release.dependency_closure_hash", keyRelease.DependencyClosureHash)
require("verifier.key_release.worker_id", keyRelease.WorkerID)
require("verifier.key_release.pool_id", keyRelease.PoolID)
require("verifier.key_release.key_ref_hash", keyRelease.KeyRefHash)
if err := validateVerifiedSignature("verifier.key_release.signature", keyRelease.Signature); err != nil {
errs = append(errs, err)
}
if !keyRelease.Released {
errs = append(errs, errors.New("verifier.key_release.released is required"))
}
if keyRelease.ExpiresAt.IsZero() {
errs = append(errs, errors.New("verifier.key_release.expires_at is required"))
}
if keyRelease.AttestationDecisionID != "" && attestation.DecisionID != "" && keyRelease.AttestationDecisionID != attestation.DecisionID {
errs = append(errs, errors.New("verifier.key_release.attestation_decision_id must match verifier.attestation.decision_id"))
}
if keyRelease.AttestationDigest != "" && keyRelease.AttestationDigest != attestation.BindingDigest() {
errs = append(errs, errors.New("verifier.key_release.attestation_digest must match verifier.attestation digest"))
}
if keyRelease.AttestationProvider != "" && keyRelease.AttestationProvider != attestation.Provider {
errs = append(errs, errors.New("verifier.key_release.attestation_provider must match verifier.attestation.provider"))
}
if keyRelease.AttestationVerifierID != "" && keyRelease.AttestationVerifierID != attestation.VerifierID {
errs = append(errs, errors.New("verifier.key_release.attestation_verifier_id must match verifier.attestation.verifier_id"))
}
if keyRelease.AttestationKeyID != "" && keyRelease.AttestationKeyID != attestation.Signature.KeyID {
errs = append(errs, errors.New("verifier.key_release.attestation_key_id must match verifier.attestation.signature.key_id"))
}
if keyRelease.PolicyID != "" && keyRelease.PolicyID != binding.PolicyID {
errs = append(errs, errors.New("verifier.key_release.policy_id must match receipt policy_id"))
}
if keyRelease.TaskID != "" && keyRelease.TaskID != binding.TaskID {
errs = append(errs, errors.New("verifier.key_release.task_id must match receipt task_id"))
}
if keyRelease.TaskHash != "" && keyRelease.TaskHash != binding.TaskHash {
errs = append(errs, errors.New("verifier.key_release.task_hash must match receipt task_hash"))
}
if keyRelease.InputHash != "" && keyRelease.InputHash != binding.InputHash {
errs = append(errs, errors.New("verifier.key_release.input_hash must match receipt input_hash"))
}
if keyRelease.DependencyClosureHash != "" && keyRelease.DependencyClosureHash != binding.DependencyClosureHash {
errs = append(errs, errors.New("verifier.key_release.dependency_closure_hash must match receipt dependency_closure_hash"))
}
if keyRelease.WorkerID != "" && keyRelease.WorkerID != binding.WorkerID {
errs = append(errs, errors.New("verifier.key_release.worker_id must match receipt worker_id"))
}
if keyRelease.PoolID != "" && keyRelease.PoolID != binding.PoolID {
errs = append(errs, errors.New("verifier.key_release.pool_id must match receipt pool_id"))
}
if !keyRelease.ExpiresAt.IsZero() && !attestation.ExpiresAt.IsZero() && keyRelease.ExpiresAt.After(attestation.ExpiresAt) {
errs = append(errs, errors.New("verifier.key_release.expires_at must not exceed verifier.attestation.expires_at"))
}
if !binding.StartedAt.IsZero() && !attestation.IssuedAt.IsZero() && binding.StartedAt.Before(attestation.IssuedAt) {
errs = append(errs, errors.New("started_at must not precede verifier.attestation.issued_at"))
}
if !binding.FinishedAt.IsZero() && !attestation.ExpiresAt.IsZero() && binding.FinishedAt.After(attestation.ExpiresAt) {
errs = append(errs, errors.New("finished_at must not exceed verifier.attestation.expires_at"))
}
if !binding.FinishedAt.IsZero() && !keyRelease.ExpiresAt.IsZero() && binding.FinishedAt.After(keyRelease.ExpiresAt) {
errs = append(errs, errors.New("finished_at must not exceed verifier.key_release.expires_at"))
}

return errors.Join(errs...)
}

func validateVerifiedSignature(prefix string, sig SignatureEnvelope) error {
var errs []error
if sig.Algorithm == "" {
errs = append(errs, fmt.Errorf("%s.algorithm is required", prefix))
}
if sig.KeyID == "" {
errs = append(errs, fmt.Errorf("%s.key_id is required", prefix))
}
if sig.Value == "" {
errs = append(errs, fmt.Errorf("%s.value is required", prefix))
}
if !sig.Verified {
errs = append(errs, fmt.Errorf("%s.verified is required", prefix))
}
return errors.Join(errs...)
}

func ExecutorMatchesPlacementRequirements(executor ExecutorRef, req PlacementRequirements) bool {
if req.ExecutorProvider != "" && executor.Provider != req.ExecutorProvider {
return false
Expand Down
Loading
Loading