verify

package
v0.169.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package verify provides end-to-end verification of transactions executed in AWS Nitro enclaves.

The verification process validates:

  • AWS Nitro attestation document authenticity
  • ECDSA signature correctness
  • QoS manifest integrity via hash comparison
  • PCR (Platform Configuration Register) values

Verification Flow

Call Verify with an attestation document and transaction details:

result, err := verifyService.Verify(ctx, &verify.VerifyRequest{
	UnsignedPayload: "base64-payload",
	QosManifestHex:  "expected-manifest-hash",
})
if err != nil {
	log.Fatal(err)
}
if !result.Valid {
	log.Printf("Verification failed: %s", result.Message)
}

Detailed Results

VerifyResult includes detailed information about each step:

  • Attestation verification status and PCR values
  • Signature verification with public key extraction
  • Manifest decoding and hash comparison
  • Comprehensive error messages explaining failures

Customization

Use VerifyRequest fields to customize verification:

  • QosManifestHex: Compare manifest hash (optional)
  • PivotBinaryHashHex: Verify binary hash (optional)
  • SaveManifestPath: Save manifest to file (optional)

Index

Constants

View Source
const SolanaIntermediateSchemaVersion uint16 = 2

SolanaIntermediateSchemaVersion is the schema_version this client understands. It matches SOLANA_INTERMEDIATE_SCHEMA_VERSION in the parser's intermediate.rs.

Variables

This section is empty.

Functions

func ComputeBorshParsedTransactionPayloadHash added in v0.110.0

func ComputeBorshParsedTransactionPayloadHash(signablePayload, inputDigest, metadataDigest string, intermediateOutput []byte) (string, error)

ComputeBorshParsedTransactionPayloadHash recomputes the SHA-256 of the Borsh-encoded ParsedTransactionPayload — what the enclave signs as AppAttestation.Message. Compare the result against the reported AppAttestation.Message to bind that signed digest to the response's (signablePayload, inputPayloadDigest, metadataDigest); without this binding, an attacker who controls the transport could substitute signablePayload while keeping a valid signature over an unrelated message.

intermediateOutput is the raw (already base64-decoded) bytes of the parser's optional machine-readable intermediate output. The proto field carrying it is #[borsh(skip)], so the four-field derived encoding is unchanged; when the field is non-empty the parser appends its Borsh Vec<u8> encoding (u32-LE length prefix followed by the raw bytes) to the signed bytes. We reproduce that here so the message binding stays valid when intermediate output is present. Pass nil/empty to get the byte-for-byte pre-feature digest — the signing path is out-of-band (no tag byte), so an empty value matches the legacy four-field encoding exactly.

Types

type APIClient

type APIClient interface {
	CreateSignablePayload(ctx context.Context, req *api.CreateSignablePayloadRequest) (*api.SignablePayloadResponse, error)
}

APIClient interface for making API calls

type AppAttestation

type AppAttestation struct {
	Message   string `json:"message"`
	PublicKey string `json:"publicKey"`
	Scheme    string `json:"scheme"`
	Signature string `json:"signature"`
}

AppAttestation represents the parsed app attestation structure

type AttestationVerifier

type AttestationVerifier interface {
	Validate(attestationDocument []byte) (*nitroverifier.ValidationResult, error)
}

AttestationVerifier interface for verifying attestations

type Formatter

type Formatter struct{}

Formatter formats verification and manifest data for display

func NewFormatter

func NewFormatter() *Formatter

NewFormatter creates a new formatter

func (*Formatter) FormatApprovals

func (f *Formatter) FormatApprovals(approvals []manifest.Approval) []map[string]interface{}

FormatApprovals formats Approval array for output

func (*Formatter) FormatApprovalsJSONV2 added in v0.169.0

func (f *Formatter) FormatApprovalsJSONV2(approvals []manifest.ApprovalJSON) []map[string]interface{}

FormatApprovalsJSONV2 formats a []manifest.ApprovalJSON for output.

func (*Formatter) FormatManifest

func (f *Formatter) FormatManifest(m *manifest.Manifest) string

FormatManifest formats manifest details for display

func (*Formatter) FormatManifestEnvelopeJSON

func (f *Formatter) FormatManifestEnvelopeJSON(env *manifest.ManifestEnvelope) map[string]interface{}

FormatManifestEnvelopeJSON formats manifest envelope for JSON output

func (*Formatter) FormatManifestEnvelopeJSONV2 added in v0.169.0

func (f *Formatter) FormatManifestEnvelopeJSONV2(env *manifest.ManifestEnvelopeJSONV2) map[string]interface{}

FormatManifestEnvelopeJSONV2 formats a QOS JSON (v2) manifest envelope for JSON output.

func (*Formatter) FormatManifestJSON

func (f *Formatter) FormatManifestJSON(m *manifest.Manifest) map[string]interface{}

FormatManifestJSON formats manifest for JSON output

func (*Formatter) FormatManifestJSONV2 added in v0.169.0

func (f *Formatter) FormatManifestJSONV2(m *manifest.ManifestJSONV2) map[string]interface{}

FormatManifestJSONV2 formats a QOS JSON (v2) manifest for JSON output, including the dns and pivot.env fields that have no home on the older Borsh-oriented Manifest type formatted by FormatManifestJSON.

func (*Formatter) FormatMembers

func (f *Formatter) FormatMembers(members []manifest.QuorumMember) []map[string]string

FormatMembers formats QuorumMember array for output

func (*Formatter) FormatMembersJSONV2 added in v0.169.0

func (f *Formatter) FormatMembersJSONV2(members []manifest.QuorumMemberJSON) []map[string]string

FormatMembersJSONV2 formats a []manifest.QuorumMemberJSON for output.

func (*Formatter) FormatPCRValidationResults

func (f *Formatter) FormatPCRValidationResults(results []PCRValidationResult, indent string) string

FormatPCRValidationResults formats PCR validation results for display

func (*Formatter) FormatPCRValues

func (f *Formatter) FormatPCRValues(pcrs map[uint][]byte, title string, indent string) string

FormatPCRValues formats PCR values with descriptive labels and proper formatting

func (*Formatter) FormatPatchMembers

func (f *Formatter) FormatPatchMembers(members []manifest.MemberPubKey) []map[string]string

FormatPatchMembers formats MemberPubKey array for output

func (*Formatter) FormatVerificationResult

func (f *Formatter) FormatVerificationResult(result *VerifyResult) map[string]interface{}

FormatVerificationResult formats a verification result for display

type ManifestSerializationResult

type ManifestSerializationResult struct {
	RawManifestHash          string
	ReserializedManifestHash string
	EnvelopeHash             string
	UserDataHash             string
	RawManifestB64           string // Base64-encoded manifest for debugging
	EnvelopeB64              string // Base64-encoded envelope for debugging
	Matches                  bool
	// MatchedVia names which hash actually satisfied the UserData binding
	// when Matches is true: "raw" (RawManifestHash), "reserialized" (a
	// Borsh ReserializedManifestHash), "canonical" (a JSON envelope's
	// canonical-JSON ReserializedManifestHash), or "envelope" (EnvelopeHash).
	// Formatters must not assume "raw" unconditionally: a JSON envelope
	// never matches via RawManifestHash (see the isJSONEnvelope gating in
	// processManifest), so reporting a raw-hash match for it would be
	// false.
	MatchedVia            string
	ReserializationNeeded bool
	Error                 string
}

ManifestSerializationResult tracks manifest hash verification

type PCRValidationResult

type PCRValidationResult struct {
	Index    uint   `json:"index"`
	Expected string `json:"expected"`
	Actual   string `json:"actual"`
	Valid    bool   `json:"valid"`
}

PCRValidationResult represents the result of validating a single PCR

type ParseResult

type ParseResult struct {
	SignablePayload                  string            `json:"signablePayload"`
	TurnkeySerializedSignablePayload string            `json:"turnkeySerializedSignablePayload"`
	Attestations                     map[string]string `json:"attestations"`
	QosManifestB64                   string            `json:"qosManifestB64,omitempty"`
	QosManifestEnvelopeB64           string            `json:"qosManifestEnvelopeB64,omitempty"`
}

ParseResult represents the result of parsing transaction

type RegisteredSource added in v0.153.0

type RegisteredSource borsh.Enum

RegisteredSource mirrors intermediate.rs RegisteredSource: where a program ID was registered

const (
	RegisteredSourceNative RegisteredSource = iota
	RegisteredSourcePreset
	RegisteredSourceThirdParty
	RegisteredSourceCallerSupplied
	RegisteredSourceUnregistered
)

func (RegisteredSource) MarshalJSON added in v0.153.0

func (s RegisteredSource) MarshalJSON() ([]byte, error)

func (RegisteredSource) String added in v0.153.0

func (s RegisteredSource) String() string

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service handles verification logic

func NewService

func NewService(apiClient APIClient, attestationVerifier AttestationVerifier) *Service

NewService creates a new verification service

func (*Service) Verify

func (s *Service) Verify(ctx context.Context, req *VerifyRequest) (*VerifyResult, error)

Verify performs end-to-end verification of a transaction in an AWS Nitro enclave: it calls the configured APIClient to fetch a SignablePayloadResponse and then runs the post-fetch verification chain via VerifyResponse.

func (*Service) VerifyResponse added in v0.110.0

func (s *Service) VerifyResponse(_ context.Context, response *api.SignablePayloadResponse, req *VerifyResponseRequest) (*VerifyResult, error)

VerifyResponse runs the verification chain on a pre-fetched SignablePayloadResponse — used when the response was obtained out-of-band (e.g., a backend integration that received the Turnkey response via another channel and never makes the API call directly). Equivalent to Steps 2-5 of Verify; APIClient is not invoked.

type SolTransfer added in v0.136.0

type SolTransfer struct {
	From   string `json:"from"`
	To     string `json:"to"`
	Amount string `json:"amount"`
}

SolTransfer mirrors intermediate.rs SolTransfer.

type SolanaAccount added in v0.136.0

type SolanaAccount struct {
	AccountKey string `json:"accountKey"`
	Signer     bool   `json:"signer"`
	Writable   bool   `json:"writable"`
}

SolanaAccount mirrors intermediate.rs SolanaAccount.

type SolanaAddressTableLookup added in v0.136.0

type SolanaAddressTableLookup struct {
	AddressTableKey string  `json:"addressTableKey"`
	WritableIndexes []int32 `json:"writableIndexes"`
	ReadonlyIndexes []int32 `json:"readonlyIndexes"`
}

SolanaAddressTableLookup mirrors intermediate.rs SolanaAddressTableLookup.

type SolanaIdlDataOrAccountsError added in v0.153.0

type SolanaIdlDataOrAccountsError struct {
	InstructionName string `json:"instructionName"`
	Error           string `json:"error"`
}

SolanaIdlDataOrAccountsError mirrors the payload shape shared by intermediate.rs SolanaIdlParseError's DataParseError and AccountsMapError struct variants.

type SolanaIdlParseError added in v0.153.0

type SolanaIdlParseError struct {
	Enum                  borsh.Enum `borsh_enum:"true"`
	DataParseError        SolanaIdlDataOrAccountsError
	AccountsMapError      SolanaIdlDataOrAccountsError
	DiscriminatorNotFound string
	IdlResolutionError    string
}

SolanaIdlParseError mirrors intermediate.rs SolanaIdlParseError: why IDL decode failed for an instruction, when it was attempted at all. Complex Borsh enum -- exactly one of the fields below is populated, selected by Enum. Field order after Enum must match the Rust variant declaration order exactly (DataParseError, AccountsMapError, DiscriminatorNotFound, IdlResolutionError).

func (*SolanaIdlParseError) MarshalJSON added in v0.153.0

func (e *SolanaIdlParseError) MarshalJSON() ([]byte, error)

type SolanaIntermediateInstruction added in v0.136.0

type SolanaIntermediateInstruction struct {
	ProgramKey            string                           `json:"programKey"`
	Accounts              []SolanaAccount                  `json:"accounts"`
	InstructionDataHex    string                           `json:"instructionDataHex"`
	AddressTableLookups   []SolanaSingleAddressTableLookup `json:"addressTableLookups"`
	ParsedInstructionData *SolanaParsedInstructionDataIo   `json:"parsedInstructionData,omitempty"`
	IdlParseError         *SolanaIdlParseError             `json:"idlParseError,omitempty"`
	RegisteredSource      RegisteredSource                 `json:"registeredSource"`
}

SolanaIntermediateInstruction mirrors intermediate.rs SolanaIntermediateInstruction. Field order matches the Rust struct exactly (Borsh is positional): IdlParseError and RegisteredSource are appended after ParsedInstructionData because they postdate the schema shipped on main. Borsh has no field-skipping, so this is a breaking layout change -- a consumer built against the prior shape hard-fails to decode rather than truncating cleanly. SolanaIntermediateSchemaVersion is what actually carries compatibility here: any field change must bump it.

type SolanaIntermediateOutput added in v0.136.0

type SolanaIntermediateOutput struct {
	SchemaVersion       uint16                          `json:"schemaVersion"`
	AccountKeys         []string                        `json:"accountKeys"`
	ProgramKeys         []string                        `json:"programKeys"`
	Instructions        []SolanaIntermediateInstruction `json:"instructions"`
	Transfers           []SolTransfer                   `json:"transfers"`
	SplTransfers        []SplTransfer                   `json:"splTransfers"`
	RecentBlockhash     string                          `json:"recentBlockhash"`
	AddressTableLookups []SolanaAddressTableLookup      `json:"addressTableLookups"`
	// SimulatedInstructions is a flat list of every call (top-level and
	// inner/CPI alike) a caller-supplied transaction simulation observed.
	// Independent of Instructions (static decode): no positional correlation,
	// no index, no nesting.
	SimulatedInstructions []SolanaSimulatedInstruction `json:"simulatedInstructions,omitempty"`
	// SimulationError says why a supplied simulation could not be read. Nil
	// means it was read or none was sent, so SimulatedInstructions is
	// authoritative: empty there means the simulation had no inner
	// instructions, not that we failed to read it.
	SimulationError *SolanaSimulationError `json:"simulationError,omitempty"`
}

SolanaIntermediateOutput mirrors intermediate.rs SolanaIntermediateOutput. schema_version is the first field so decoders can gate on it before trusting the remaining layout.

func DecodeSolanaIntermediateOutput added in v0.136.0

func DecodeSolanaIntermediateOutput(b []byte) (*SolanaIntermediateOutput, error)

DecodeSolanaIntermediateOutput decodes the raw Borsh bytes of the parser's Solana intermediate output. It rejects any schema_version other than the one this client mirrors, so a parser-side layout change surfaces as an explicit error rather than a silently misdecoded struct.

type SolanaParsedInstructionDataIo added in v0.136.0

type SolanaParsedInstructionDataIo struct {
	InstructionName     string            `json:"instructionName"`
	Discriminator       string            `json:"discriminator"`
	NamedAccounts       map[string]string `json:"namedAccounts"`
	ProgramCallArgsJSON string            `json:"programCallArgsJson"`
	IdlSource           string            `json:"idlSource"`
	IdlHash             string            `json:"idlHash"`
}

SolanaParsedInstructionDataIo mirrors intermediate.rs SolanaParsedInstructionDataIo. NamedAccounts is a BTreeMap<String,String> on the Rust side; Borsh encodes it as a length-prefixed, key-sorted map, which decodes cleanly into a Go map (iteration order is irrelevant on decode).

type SolanaRpcParsedInstructionDataIo added in v0.153.0

type SolanaRpcParsedInstructionDataIo struct {
	Program    string `json:"program"`
	ParsedJSON string `json:"parsedJson"`
}

SolanaRpcParsedInstructionDataIo mirrors intermediate.rs SolanaRpcParsedInstructionDataIo: the RPC's own jsonParsed decode of a simulated instruction, distinct from the parser's own IDL-decoded SolanaParsedInstructionDataIo.

type SolanaSimulatedInstruction added in v0.153.0

type SolanaSimulatedInstruction struct {
	Index                 uint32                            `json:"index"`
	StackHeight           uint32                            `json:"stackHeight"`
	ProgramKey            string                            `json:"programKey"`
	Accounts              []string                          `json:"accounts"`
	InstructionDataHex    string                            `json:"instructionDataHex"`
	RegisteredSource      RegisteredSource                  `json:"registeredSource"`
	ParsedInstructionData *SolanaParsedInstructionDataIo    `json:"parsedInstructionData,omitempty"`
	SolanaRpcParsedData   *SolanaRpcParsedInstructionDataIo `json:"solanaRpcParsedData,omitempty"`
	IdlParseError         *SolanaIdlParseError              `json:"idlParseError,omitempty"`
}

SolanaSimulatedInstruction mirrors intermediate.rs SolanaSimulatedInstruction.

type SolanaSimulationError added in v0.153.0

type SolanaSimulationError borsh.Enum

SolanaSimulationError mirrors intermediate.rs SolanaSimulationError: why a caller-supplied simulateTransaction result could not be read. Unit variants; the detail is logged parser-side rather than encoded.

const (
	// Discriminants start at 1, matching the Rust enum's explicit values
	// (#[borsh(use_discriminant = true)]). 0 is not a variant, so the zero value
	// borsh-go produces for Option::None cannot be mistaken for a real error.
	SolanaSimulationErrorInvalidBase64 SolanaSimulationError = iota + 1
	SolanaSimulationErrorInvalidJSON
	SolanaSimulationErrorSimulationFailed
	SolanaSimulationErrorCallerIdlRecordsUnusable
	SolanaSimulationErrorCompiledInstruction
	SolanaSimulationErrorInvalidInstructionData
)

func (SolanaSimulationError) MarshalJSON added in v0.153.0

func (e SolanaSimulationError) MarshalJSON() ([]byte, error)

func (SolanaSimulationError) String added in v0.153.0

func (e SolanaSimulationError) String() string

type SolanaSingleAddressTableLookup added in v0.136.0

type SolanaSingleAddressTableLookup struct {
	AddressTableKey string `json:"addressTableKey"`
	Index           int32  `json:"index"`
	Writable        bool   `json:"writable"`
}

SolanaSingleAddressTableLookup mirrors intermediate.rs SolanaSingleAddressTableLookup.

type SplTransfer added in v0.136.0

type SplTransfer struct {
	From      string   `json:"from"`
	To        string   `json:"to"`
	Amount    string   `json:"amount"`
	Owner     string   `json:"owner"`
	Signers   []string `json:"signers"`
	TokenMint *string  `json:"tokenMint,omitempty"`
	Decimals  *string  `json:"decimals,omitempty"`
	Fee       *string  `json:"fee,omitempty"`
}

SplTransfer mirrors intermediate.rs SplTransfer.

type VerifyRequest

type VerifyRequest struct {
	UnsignedPayload    string
	QosManifestHex     string
	PivotBinaryHashHex string
	SaveManifestPath   string
	Chain              string
	// ChainMetadata is forwarded to the Turnkey parse API when non-nil.
	// When set, Verify locally recomputes metadataDigest via Borsh encoding and
	// compares it against the backend-reported value.
	ChainMetadata *api.RequestChainMetadata
	// IncludeIntermediateOutput requests the parser's machine-readable,
	// Borsh-encoded intermediate output. When the backend returns it, Verify
	// decodes it into VerifyResult.IntermediateOutput and folds its bytes into
	// the signed-message binding.
	IncludeIntermediateOutput bool
}

VerifyRequest represents the parameters for verification

type VerifyResponseRequest added in v0.110.0

type VerifyResponseRequest struct {
	UnsignedPayload    string
	QosManifestHex     string
	PivotBinaryHashHex string
	SaveManifestPath   string
	// ChainMetadata, when non-nil, is used to locally recompute the expected
	// metadataDigest via Borsh encoding and compare against the value reported
	// by the backend in SignablePayloadResponse.
	ChainMetadata *api.RequestChainMetadata
}

VerifyResponseRequest carries the fields VerifyResponse needs that aren't already in the SignablePayloadResponse. Use VerifyResponse when the response was obtained out-of-band (e.g., a backend integration that received a Turnkey response via another channel) and an APIClient call is not desired.

type VerifyResult

type VerifyResult struct {
	Valid                   bool                        `json:"valid"`
	AttestationValid        bool                        `json:"attestationValid"`
	SignatureValid          bool                        `json:"signatureValid"`
	ModuleID                string                      `json:"moduleId"`
	PublicKeyHex            string                      `json:"publicKey"`
	SignablePayload         string                      `json:"signablePayload"`
	InputPayloadDigest      string                      `json:"inputPayloadDigest,omitempty"`
	MetadataDigest          string                      `json:"metadataDigest,omitempty"`
	MessageHex              string                      `json:"message"`
	SignatureHex            string                      `json:"signature"`
	QosManifestHash         string                      `json:"qosManifest,omitempty"`
	PivotBinaryHash         string                      `json:"pivotBinaryHash,omitempty"`
	PCR4                    string                      `json:"pcr4,omitempty"`
	UserData                []byte                      `json:"-"`
	PCRs                    map[uint][]byte             `json:"-"`
	PCRValidationResults    []PCRValidationResult       `json:"-"`
	PublicKey               *ecdsa.PublicKey            `json:"-"`
	Manifest                *manifest.Manifest          `json:"-"`
	AttestationDocument     interface{}                 `json:"-"`
	ManifestReserialization ManifestSerializationResult `json:"-"`
	// IntermediateOutput is the decoded Solana intermediate output, populated
	// only when the backend returned a non-empty intermediateOutput. The
	// formatter surfaces it under the "intermediateOutput" JSON key.
	IntermediateOutput *SolanaIntermediateOutput `json:"-"`
}

VerifyResult represents the result of verification

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL