Goals
- Persistent storage that survives process restarts
- Encryption at rest for all persisted keying material
- Compatibility with OpenMLS and Proteus storage traits
- Cross-platform: native (Linux/macOS/Windows), iOS, Android, and WebAssembly
Architecture overview
Core types
DatabaseKey
DatabaseKey is a 32-byte AES encryption key. It is Zeroize + ZeroizeOnDrop, meaning
the memory is zeroed when the value is dropped:
Debug implementation prints only the first 10 bytes of the SHA-256 hash to prevent
accidental key leakage in logs.
Database
Database is the connection handle. It is cheap to clone (all state is behind Arc):
Semaphore).
All mutating operations require an active transaction — calling save or remove
without one returns CryptoKeystoreError::MutatingOperationWithoutTransaction.
ConnectionType
Opening a database
Platform implementations
- Native (Linux / macOS / Windows / iOS / Android)
- WASM (Browser / Electron)
The native backend delegates entirely to SQLCipher,
an encrypted fork of SQLite.Encryption details:
- Backing store: encrypted SQLite file
- Page size: 4096 bytes (SQLCipher default)
- Cipher: AES-256-CBC, applied per-page
- Per-page IV: freshly generated on every page write, appended to the page
- Page authentication: HMAC-SHA-512 tag, appended to the page
- Key derivation: PBKDF2-HMAC-SHA512 from the provided passphrase
- The 16-byte KDF salt is stored in the first 16 bytes of the database file
- Crypto provider: OpenSSL (default), configurable to NSS, LibTomCrypt, or
Security.frameworkon Apple platforms
Key derivation from a passphrase
When using the SQLCipher backend, theDatabaseKey bytes are passed directly as the
SQLCipher key material. SQLCipher internally derives the AES-256 page-encryption key
via PBKDF2-HMAC-SHA512 and stores the 16-byte salt in the first 16 bytes of the file.
If your application derives the DatabaseKey from a user-supplied passphrase, do so
before calling Database::open:
Key rotation
PRAGMA rekey which re-encrypts the entire database in-place.
Migrating from passphrase-string keys to DatabaseKey
Early versions of CoreCrypto accepted raw passphrase strings directly. The
migrate_database_key_type_to_bytes helper migrates an existing database opened with a
string passphrase to the current DatabaseKey (byte-array) format:
DatabaseKey.
Blob size limits
The keystore enforces a maximum blob length of 1 GB (1_000_000_000 bytes) for all
platforms. On non-WASM platforms an additional SQLite limit of i32::MAX bytes applies
per value. In practice, individual cryptographic objects are orders of magnitude smaller.
Transactions
The keystore has its own transaction layer that CoreCrypto’sTransactionContext builds
on top of. All writes are buffered in a KeystoreTransaction and flushed atomically
on commit_transaction():
Semaphore). Starting
a new transaction blocks until the previous one completes or is rolled back.