This document outlines the security architecture, threat model, cryptographic specifications, and operational policies for Veil.
Veil relies strictly on mature, audited libraries (such as the RustCrypto and dalek ecosystems) for all cryptographic operations. Custom cryptography is not permitted.
| Primitive / Algorithm | Purpose | Specification |
|---|---|---|
| Argon2id | Server-side password & recovery key hashing | RFC 9106 default profile: - Memory: 65,536 KB (64 MiB) - Iterations (m_cost): 3 - Parallelism (p_cost): 4 |
| X25519 | Diffie-Hellman Key Exchange | Curve25519-based ECDH (32-byte keys) |
| Ed25519 | Digital Signatures | Ed25519 (EdDSA) signing & verification (32-byte keys, 64-byte signatures) |
| ChaCha20-Poly1305 | Symmetric Encryption | Authenticated Encryption with Associated Data (AEAD), random 12-byte nonces |
| HKDF-SHA256 | Key Derivation | Key-based HKDF extract & expand for ratchet key updates |
| HMAC-SHA256 | Token hashing & integrity verification | Keyed-hash message authentication code |
| BIP-39 Mnemonic | Account Recovery Key | 24-word mnemonic phrase (256-bit entropy) generated client-side |
All cryptographic keys, salts, database IDs, session identifiers, nonces, and tokens must be generated using a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) seeded by the host Operating System's entropy source.
- In Rust: Using
rand::rngs::OsRngorrand::thread_rng(). - In Dart/Flutter: Using
cryptosecure random generators or platform-native cryptographically secure random APIs.
- Access Tokens: Opaque 256-bit cryptographically random strings (represented as Hex or Base64).
- Expiry: 15 minutes.
- Storage: In the database, only the SHA-256 hash of the access token is stored (
sessions.access_token_hash).
- Refresh Tokens: Opaque 256-bit cryptographically random strings.
- Expiry: 30 days.
- Storage: Stored hashed using
HMAC-SHA256with a server-held secret key. - Rotation: A refresh token is strictly single-use. Every request to rotate a token results in the old token being revoked and a new refresh token issued.
Authentication uses Bearer tokens transmitted via the Authorization header (Authorization: Bearer <token>) rather than ambient browser cookies. Therefore, standard Cross-Site Request Forgery (CSRF) protection is not required for the API endpoints as the browser does not attach Bearer tokens automatically.
- Login attempts are logged in the
login_attemptstable, trackingusername,ip_hash,user_agent, andsuccess/failurestate. - Rate-Limiting: Max 5 failed login attempts in 10 minutes per username/IP. If exceeded, a 15-minute cooldown lock is applied.
- Recovery Rate-Limiting: Recovery key submission is locked for 1 hour after 3 failed attempts to protect against brute-forcing the BIP-39 recovery phrase.
- Compromised Server Database: In the event of a full database disclosure:
- Passwords and recovery keys remain protected by salted Argon2id hashes.
- Active session refresh tokens are protected by HMAC-SHA256 under a separate server configuration secret key, preventing attackers from hijacking active user sessions.
- Message payloads are E2E encrypted and cannot be decrypted by the server or an attacker.
- Device Loss / Compromise:
- Each device maintains its own key pair and identity.
- A compromised device can be selectively revoked from any other active verified device, restricting access without rotating master keys.
- Account recovery via the 24-word mnemonic rotates all keys, revokes all sessions/devices, and forces re-approval.