Skip to main content

Seller Automated Release

TEE

Seller Automated Release (SAR) is an enclave-resident verification flow. Seller credential material is encrypted to an upload key that is generated inside the AWS Nitro Enclave. The enclave validates that credential material against the payment platform, converts it into an encrypted bundle, and later uses the bundle to verify payments and sign a V3 PaymentAttestation.

The important primitive is the TEE boundary: plaintext seller credentials are only available inside the measured enclave process.

Enclave Responsibilities

ResponsibilityDetail
Generate upload keyThe enclave generates an RSA seller-upload key at startup. The private key stays in enclave memory.
Prove the upload keyThe public upload key is embedded in the Nitro attestation document so clients can verify it before encryption.
Decrypt uploadsSeller credential uploads are compact JWE payloads encrypted to the attested upload key.
Validate credentialsThe enclave uses the uploaded material to prove live access to the claimed payment account.
Encrypt bundlesValidated credentials are encrypted with a per-bundle AES-256-GCM key wrapped by AWS KMS.
Verify paymentsThe enclave decrypts the bundle in memory, queries the platform, normalizes the payment, and runs verifier checks.
Sign attestationsThe enclave signs the final EIP-712 PaymentAttestation for on-chain settlement.

Nitro Verification Package

Clients should use @zkp2p/zkp2p-attestation to verify the Nitro attestation document before encrypting seller credentials.

npm install @zkp2p/zkp2p-attestation

The package verifies nonce binding, freshness, the AWS Nitro certificate chain, the COSE signature, and the trusted PCR8 pin. It then extracts the enclave's attested seller-upload public key.

import { createNitroAttestationClient } from "@zkp2p/zkp2p-attestation";

const nitro = createNitroAttestationClient({
environment: "staging", // or "production"
});

const verified = await nitro.fetchAndVerifyAttestation();

console.log(verified.attestedSellerUploadKey.publicKeySha256Hex);
console.log(verified.payload.pcr8Hex);

For the normal upload path, the package can verify the enclave and create the compact JWE in one call:

const encryptedUpload = await nitro.createEncryptedSellerCredentialUpload({
payeeId: "1130030979",
sessionMaterial: {
apiToken: "<wise-api-token>",
profileId: "41246868",
balanceId: "61249083",
currency: "EUR",
},
});

Advanced callers can split verification from encryption within a single session:

const verified = await nitro.fetchAndVerifyAttestation();

const encryptedUpload = await nitro.encryptSellerCredentialUpload({
key: verified.attestedSellerUploadKey,
payeeId: "1130030979",
sessionMaterial: {
apiToken: "<wise-api-token>",
profileId: "41246868",
balanceId: "61249083",
currency: "EUR",
},
});

Do not cache the attested upload key across enclave restarts. A restart generates a new upload key, and ciphertext sealed to the old key fails closed.

Upload Sealing

Seller credential material is sealed as compact JWE with:

LayerValue
Key managementRSA-OAEP-256
Content encryptionA256GCM
Recipient keySeller-upload public key from the verified Nitro document

The encrypted plaintext includes:

FieldPurpose
payeeIdPlatform-native seller recipient identifier.
sessionMaterialPlatform-specific credential material.
boundPubKeySha256SHA-256 of the attested DER SPKI upload key.
issuedAtMsUpload freshness timestamp.
jweIdUnique replay-rejection identifier.

The enclave rejects uploads that are stale, replayed, or bound to a different upload key.

Credential Bundle Encryption

After live validation, the enclave encrypts the credential into a reusable bundle.

Bundle layerMechanism
Data encryption keyFresh AES-256 key from AWS KMS GenerateDataKey.
Credential ciphertextAES-256-GCM over { payeeId, credential }.
Wrapped keyKMS ciphertext blob for later enclave decrypt.
Bundle signatureEIP-712 signature over the encrypted bundle fields.
Validation timestampcredentialValidatedAt, sampled immediately after platform validation succeeds.

The plaintext data encryption key is zeroed after use. Later verification unwraps the KMS-wrapped key, decrypts the bundle in memory, and discards plaintext after the platform lookup.

In-Enclave Verification

During payment verification, the enclave performs:

1. KMS unwrap bundle key.
2. AES-GCM decrypt credential bundle.
3. Check keccak256(decryptedPayeeId) against the intent payee hash.
4. Query the payment platform with the seller credential material.
5. Normalize the payment into method, payee, amount, currency, timestamp, and payment id.
6. Run UnifiedPaymentVerifier checks.
7. Sign the EIP-712 PaymentAttestation.

The key binding is:

keccak256(decryptedPayeeId) == intent.payeeDetails

This binds the decrypted credential to the seller identity in the intent.

Verifier Checks

CheckRequirement
Payment methodObserved platform hash matches intent.paymentMethod.
Fiat currencyObserved currency hash matches intent.fiatCurrency.
PayeeDecrypted payee hash matches intent.payeeDetails.
TimestampPayment timestamp plus policy buffer is greater than or equal to intent timestamp.
Intent hashPayment evidence is bound to the requested intent hash.
Conversion rateIntent conversion rate is positive.
AmountObserved payment amount is positive.

Release amount:

min(paymentAmount * 10^18 * 10^4 / conversionRate, intentAmount)

paymentAmount is in fiat minor units. The 10^4 factor adjusts fiat cents into 6-decimal USDC units.

Supported Credential Types

PlatformCredential materialPayee identifier
venmoSession cookie bundle and request headers.Venmo account id.
cashappSession cookie bundle, headers, and request payload hints.Cashtag without leading $.
wisePersonal API token, profile id, balance id, and currency.Wise multi-currency-account recipient id.
paypalGmail OAuth mailbox evidence for PayPal receiver emails.Seller PayPal email address.

Failure Semantics

FailureMeaning
Nitro verification failsThe client must not encrypt credentials to that enclave.
Upload decrypt failsThe ciphertext was not sealed to the current enclave upload key or failed authentication.
Upload binding failsThe payload is stale, replayed, or bound to a different key.
Credential validation failsThe credential does not prove live access to the claimed payment account.
Bundle decrypt failsThe encrypted bundle has been corrupted, tampered with, or wrapped for incompatible KMS context.
Payee hash check failsThe decrypted credential does not belong to the intent payee.
Platform lookup failsThe enclave cannot prove the payment from the available seller-side data.
Verifier check failsThe observed payment does not satisfy the intent.