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
- func ComputeBorshParsedTransactionPayloadHash(signablePayload, inputDigest, metadataDigest string, intermediateOutput []byte) (string, error)
- type APIClient
- type AppAttestation
- type AttestationVerifier
- type Formatter
- func (f *Formatter) FormatApprovals(approvals []manifest.Approval) []map[string]interface{}
- func (f *Formatter) FormatApprovalsJSONV2(approvals []manifest.ApprovalJSON) []map[string]interface{}
- func (f *Formatter) FormatManifest(m *manifest.Manifest) string
- func (f *Formatter) FormatManifestEnvelopeJSON(env *manifest.ManifestEnvelope) map[string]interface{}
- func (f *Formatter) FormatManifestEnvelopeJSONV2(env *manifest.ManifestEnvelopeJSONV2) map[string]interface{}
- func (f *Formatter) FormatManifestJSON(m *manifest.Manifest) map[string]interface{}
- func (f *Formatter) FormatManifestJSONV2(m *manifest.ManifestJSONV2) map[string]interface{}
- func (f *Formatter) FormatMembers(members []manifest.QuorumMember) []map[string]string
- func (f *Formatter) FormatMembersJSONV2(members []manifest.QuorumMemberJSON) []map[string]string
- func (f *Formatter) FormatPCRValidationResults(results []PCRValidationResult, indent string) string
- func (f *Formatter) FormatPCRValues(pcrs map[uint][]byte, title string, indent string) string
- func (f *Formatter) FormatPatchMembers(members []manifest.MemberPubKey) []map[string]string
- func (f *Formatter) FormatVerificationResult(result *VerifyResult) map[string]interface{}
- type ManifestSerializationResult
- type PCRValidationResult
- type ParseResult
- type RegisteredSource
- type Service
- type SolTransfer
- type SolanaAccount
- type SolanaAddressTableLookup
- type SolanaIdlDataOrAccountsError
- type SolanaIdlParseError
- type SolanaIntermediateInstruction
- type SolanaIntermediateOutput
- type SolanaParsedInstructionDataIo
- type SolanaRpcParsedInstructionDataIo
- type SolanaSimulatedInstruction
- type SolanaSimulationError
- type SolanaSingleAddressTableLookup
- type SplTransfer
- type VerifyRequest
- type VerifyResponseRequest
- type VerifyResult
Constants ¶
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 (*Formatter) FormatApprovals ¶
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 ¶
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 ¶
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 ¶
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
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
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