enclave
Hardware-backed signing and encryption for Rust — macOS Secure Enclave, Windows TPM 2.0, Linux TPM 2.0 / keyring.
Private keys never leave the hardware. Touch ID and Windows Hello are built in.
What you can build with this
- SSH key management — hardware-backed P-256 keys, signed with Touch ID,
compatible with
ssh-agentandsk-ecdsa-sha2-nistp256@openssh.com - Credential caching — encrypt API tokens and passwords under the TPM; they survive process restart but cannot be stolen without hardware access
- Code signing / git commit signing — hardware-enforced key storage with optional biometric gate per operation
- Secret storage with integrity checks — tamper-evident config files backed by HMAC in the platform secure store (Keychain / DPAPI / Secret Service)
- In-process memory protection — guard-paged, mlock'd buffers and AES-256-GCM in-memory sealed secrets for long-lived processes that handle sensitive material
Platform support
| macOS | Windows | Linux | WSL2 | |
|---|---|---|---|---|
| Signing (ECDSA P-256) | Secure Enclave | TPM 2.0 | TPM 2.0 / keyring | Bridge → Windows TPM |
| Encryption (ECIES P-256) | Secure Enclave | TPM 2.0 | TPM 2.0 / keyring | Bridge → Windows TPM |
| FIDO2 hardware security keys | — | ✅ WebAuthn | — | Bridge → Windows |
| Touch ID / Windows Hello | ✅ | ✅ | — | — |
| Tamper-evident files | ✅ | ✅ | ✅ | ✅ |
| Guard-page / mlock'd buffers | ✅ | ✅ | ✅ | ✅ |
Signing
Generate a P-256 key in the hardware, sign data, get a DER-encoded ECDSA signature back. Works identically on macOS (Secure Enclave), Windows (TPM), and Linux (TPM or keyring).
use ;
let config = new;
let signer = create_signer?;
// Creates the key the first time; opens it on subsequent calls.
signer.generate_key?;
// Returns a DER-encoded ECDSA P-256 signature.
let sig: = signer.sign?;
With user presence (Touch ID / Windows Hello)
use PresenceOptions;
// Strict: prompt on every call. Cached: reuse within TTL (macOS only).
let sig = signer.sign_with_presence?;
Key lifecycle
let pubkey: = signer.public_key?; // uncompressed SEC1
let keys: = signer.list_keys?;
signer.rename_key?;
signer.delete_key?;
Encryption
ECIES P-256 encryption under a hardware-backed key. Encrypted data survives process restart; it can only be decrypted on the same machine by the same user.
use ;
let config = new;
let enc = create_encryptor?;
enc.generate_key?;
let ciphertext: = enc.encrypt?;
// Returns Zeroizing<Vec<u8>> — wiped from memory on drop.
let plaintext = enc.decrypt?;
The ECIES wire format is:
[0x01 version][65B ephemeral pubkey][12B nonce][ciphertext][16B GCM tag]
FIDO2 hardware security keys (Windows / WSL2)
Generate TPM-bound FIDO2 credentials and produce sk-ecdsa-sha2-nistp256@openssh.com
SSH signatures with hardware-enforced Windows Hello confirmation.
use ;
let sk = create_security_key;
if sk.is_available
Only available on Windows native and WSL2 (via bridge to Windows TPM).
Returns Err(NotAvailable) on macOS and native Linux.
Tamper-evident files
HMAC-SHA-256 protection for config files, key metadata, and any file where you
need to detect undetected modification. The HMAC key lives in the platform
secure store; files on disk remain plaintext with a .hmac sidecar.
use ;
let handle = create_tamper_evident?;
handle.write?;
match handle.verify?
// read() verifies and returns content in one step.
let content: = handle.read?;
For high-security files, .with_trust_anchor() stores the HMAC in the platform
secure store — deleting the sidecar cannot bypass verification.
For tests and CI (no Keychain/DPAPI access, no prompts):
use create_tamper_evident_ephemeral;
let handle = create_tamper_evident_ephemeral;
User presence
Acquire a standalone presence confirmation decoupled from any specific key operation, or evict the cached presence token to force re-authentication.
use create_auth;
let auth = create_auth?;
let caps = auth.capabilities;
// caps.biometric_available, caps.presence_caching, caps.authenticator_name
auth.request_presence?;
auth.evict_presence_cache; // force re-auth on the next operation
In-process memory protection
Protect secret material that lives in the process for extended periods — session tokens, decrypted keys, cached credentials. Ported from asherah-ffi.
Guard-paged buffers
Pages flanking the secret region are set to PROT_NONE; overflows trigger
SIGSEGV. Random canaries are verified on destroy(). The region is mlock'd
(no swap) and zeroized before unmapping.
use SecureBuffer;
let mut buf = new?;
buf.bytes.copy_from_slice;
buf.freeze?; // PROT_READ — no accidental mutation
// ... use buf.as_slice() ...
buf.destroy?; // zeroizes, verifies canaries, unmaps
AES-256-GCM in-memory sealed secrets
Secrets live as ciphertext on the heap and are only decrypted briefly when you
call open(). The plaintext is returned in a guard-paged, mlock'd slot — not
the regular heap — and is zeroed when the slot drops.
use MemoryEnclave;
let sealed = seal?;
let slot = sealed.open?;
// use slot.as_slice() — plaintext is here
// slot drops → plaintext zeroed immediately
Thread-safe shared buffers
use ;
let buf = from_bytes?;
let copy = buf.bytes_zeroizing; // Zeroizing<Vec<u8>>
// At process shutdown, zero all registered buffers:
zeroize_all_registered_at_shutdown;
Configuration
use ;
let config = new;
// -unsigned is appended to app_name automatically for unsigned binaries,
// preventing dev key collisions with production keys.
macOS: Touch ID gate on the wrapping key (requires entitlement):
use ;
use Duration;
MacOs
Signed vs unsigned binaries
Development builds (cargo build) are unsigned. enclave appends -unsigned
to your app name automatically, preventing dev keys from ever touching
production key storage. Production signed builds get full Keychain ACL binding
(macOS), meaning only your binary can access its own keys.
Security properties
| Property | Mechanism |
|---|---|
| Keys never leave hardware | SE / TPM only performs private-key operations |
| No swap exposure | mlock + MADV_DONTDUMP on all secret pages |
| Buffer overflow detection | PROT_NONE guard pages + random canaries |
| Zeroization | All secret-bearing types wipe memory on drop |
| Ciphertext integrity | AES-256-GCM authentication (MemoryEnclave) |
| Metadata integrity | HMAC-SHA-256 verified against platform secure store |
| Dev/prod isolation | -unsigned suffix on unsigned binary app names |
See THREAT_MODEL.md for the full threat model, limitations, and residual risks.
Building applications that wrap third-party tools
If you're building an application that wraps a CLI tool and needs to inject secrets into it (via environment variables, temp files, or an agent protocol), see DELIVERY_TIERS.md for the four integration patterns and guidance on when to use each.
Examples
# No hardware required — always works
# Software mock (CI-safe, no prompts, no hardware)
ENCLAVE_MOCK=1
ENCLAVE_MOCK=1
# Real hardware (prompts Touch ID / Windows Hello)
# Run all CI-safe examples via cargo test
ENCLAVE_MOCK=1
License
MIT — Copyright 2026 Jay Gowdy