Backend wallet security
Openfort backend wallets use a Trusted Execution Environment (TEE) hosted on GCP Confidential Space for all cryptographic operations. This isolated compute environment provides hardware-enforced security guarantees that protect private keys from unauthorized access, including access from system operators.
Architecture overview
┌─────────────────────────────────────────────────────────────────────┐
│ API SERVICE │
│ (Request validation, rate limiting, audit logging) │
└────────────────────────────────┬────────────────────────────────────┘
│ gRPC + JWT
▼
┌─────────────────────────────────────────────────────────────────────┐
│ GCP CONFIDENTIAL SPACE (TEE) │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ Wallet Service │ │
│ │ │ │
│ │ • JWT signature verification (ES256) │ │
│ │ • Key generation (secp256k1, ed25519) │ │
│ │ • Transaction signing │ │
│ │ • Key import/export (RSA E2E encryption) │ │
│ │ │ │
│ │ Private keys never leave TEE memory in plaintext │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ Attestation token │
└────────────────────────────────┼────────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Cloud KMS │ │ Database │ │ STS │
│ (KEK) │ │ (Encrypted │ │ (Identity) │
│ │ │ Storage) │ │ │
└─────────────┘ └─────────────┘ └─────────────┘All requests flow through the API service for validation before being routed to the TEE. The TEE is the only place where private key operations happen.
Trusted execution environment (TEE)
What is a TEE?
A Trusted execution environment provides hardware-enforced isolation:
- Memory encryption: Protects code and data from the host operating system using AMD SEV-SNP memory encryption.
- No persistent storage: The TEE doesn't have persistent storage, interactive access, or external networking.
- Remote attestation: Cryptographic proofs verify exactly what code is running inside the enclave.
- Operator isolation: Even cloud provider administrators can't access TEE memory.
Security properties
| Property | How TEE provides it |
|---|---|
| Confidentiality | AMD SEV-SNP encrypts memory; keys are never exposed outside the TEE. |
| Integrity | Attestation proves the code hasn't been modified. |
| Authenticity | Attestation tokens prove the TEE identity to KMS. |
| Operator isolation | Even with root access, operators can't read TEE memory. |
Trust boundaries
Inside the TEE (Trusted):- Application code running wallet operations.
- Private key generation and signing.
- JWT verification.
- API service layer.
- Database (stores only encrypted data).
- Network infrastructure.
- Cloud infrastructure and operators.
Key management
Envelope encryption
Openfort protects private keys with two-layer envelope encryption:
┌─────────────────────────────────────────────────────────────────────┐
│ KEY HIERARCHY │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ KEK (Key Encryption Key) │ │
│ │ │ │
│ │ Location: Cloud KMS (HSM-backed) │ │
│ │ Type: AES-256-GCM │ │
│ │ Access: Attested Confidential Space workloads only │ │
│ │ Rotation: KMS-managed (automatic) │ │
│ │ Export: Never (hardware-protected) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ encrypts │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ DEK (Data Encryption Key) │ │
│ │ │ │
│ │ Location: Database (encrypted) │ │
│ │ Type: AES-256-GCM (32 bytes) │ │
│ │ Scope: One per wallet │ │
│ │ Encrypted with: KEK via Cloud KMS │ │
│ │ AAD: Wallet address (prevents DEK swapping) │ │
│ │ │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ encrypts │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Wallet Private Key │ │
│ │ │ │
│ │ Location: Database (encrypted) │ │
│ │ Type: secp256k1 (32 bytes) or ed25519 (64 bytes) │ │
│ │ Encrypted with: DEK using AES-256-GCM │ │
│ │ Plaintext: Only exists in TEE memory during operations │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘How encryption works
Wallet creation:- Generate a key pair inside the TEE (secp256k1 or ed25519) based on the chain type.
- Generate a unique DEK (32 random bytes) inside the TEE.
- Encrypt the private key with the DEK using AES-256-GCM.
- Encrypt the DEK with the KEK via Cloud KMS (with the wallet address as AAD).
- Zero sensitive memory inside the TEE.
- Store encrypted data in the database.
- Retrieve encrypted wallet data from the database.
- Decrypt the DEK via KMS (requires TEE attestation).
- Decrypt the private key with the DEK inside the TEE.
- Sign the transaction inside the TEE.
- Zero sensitive memory.
- Return the signature.
Attack vector analysis
| Attack vector | Protection |
|---|---|
| Stolen database | DEKs are useless without KMS access and attestation. |
| Compromised API server | Can't decrypt the DEK (no attestation). |
| Malicious operator | Can't decrypt the DEK (no TEE access). |
| KMS compromise | Requires also compromising the database. |
| DEK swapping | AAD binding prevents cross-wallet attacks. |
Authentication
Backend wallet operations use JWT-based authentication with ECDSA P-256 (ES256) signatures. This provides strong authentication and doesn't require secrets to be stored in the TEE.
Wallet secrets
Wallet secrets are asymmetric ECDSA private keys conforming to the secp256r1 (P-256) elliptic curve standard. You must sign each request with the private key, and the server verifies it using the stored public key.
Authentication flow
- Client: Creates the request body and computes
reqHash = SHA256(sorted JSON body). - Client: Builds a JWT with the
reqHash, timestamps, and request identifiers. - Client: Signs the JWT with the wallet secret (ES256).
- API Service: Validates the secret key and checks for replay attacks.
- TEE: Verifies the ES256 signature and validates all claims.
- TEE: Executes the operation if authentication succeeds.
JWT claim validation
| Claim | Validation | Purpose |
|---|---|---|
alg | Must be "ES256" | Only P-256 ECDSA allowed |
kid | Must exist; lookup in DB | Identifies the public key |
uris | At least one; must match request | Scopes request to specific endpoints |
reqHash | SHA256 of sorted JSON body | Request integrity |
iat | Not in the future and not older than two minutes | Token freshness |
nbf | Not in the future (30-second skew allowed) | Prevents pre-activated tokens |
jti | Unique (checked by API Service) | Replay protection |
Security properties
| Property | How achieved |
|---|---|
| Authentication | ES256 signature verification. |
| Integrity | reqHash binds the signature to the request body. |
| Freshness | iat max age (two minutes) and nbf validation. |
| Replay protection | jti tracking (API Service) and iat freshness (TEE). |
| Non-repudiation | Asymmetric signature (only client has the private key). |
Secret rotation and recovery
- You can delete compromised wallet secrets and generate new ones through the Openfort Dashboard without risking fund loss.
- Rotation immediately invalidates the old secret.
- A project can have multiple active secrets for seamless rotation.
Key import and export
Transfer private keys securely using RSA-4096 OAEP SHA-256 end-to-end encryption:
| Operation | Encryption key | Decryption key |
|---|---|---|
| Import | Static server public key (embedded in SDK) | KMS HSM private key (non-extractable) |
| Export | Ephemeral client public key (per-request) | Client private key (SDK-local) |
Import flow
- SDK encrypts the private key with the static RSA public key of the server.
- Server decrypts via KMS
AsymmetricDecrypt(requires TEE attestation). - Server stores the key using envelope encryption.
Export flow
- SDK generates an ephemeral RSA-4096 key pair.
- SDK sends the public key with the export request.
- Server encrypts the private key with the client public key.
- SDK decrypts locally and discards ephemeral keys.
The import key is HSM-backed, so operators can't extract it and only attested workloads can perform decryption operations.
Best practices
- Store wallet secrets securely using your platform's secret management (for example, environment variables or secret managers).
- Never expose wallet secrets in client-side code.
- Rotate secrets periodically and immediately if you suspect a compromise.