Encryption & Security¶
Your files are encrypted before they reach cloud storage — not just in transit.
Most cloud storage services encrypt the connection (HTTPS) but store your files in plaintext on their servers. Lenzeye goes further: files are encrypted at rest, with a unique encryption key per user, before they are written to cloud storage.
Encryption at a Glance¶
| Property | Value |
|---|---|
| Algorithm | AES-256-CTR (Counter Mode) |
| Key size | 256-bit (32 bytes) per user |
| IV (Initialization Vector) | 128-bit, randomly generated per file |
| Integrity | HMAC-SHA256 (Encrypt-then-MAC) |
| Key storage | Per-user key encrypted with AES-256-GCM master key |
| Master key location | Environment variable — never in code or database |
How Encryption Works¶
flowchart TD
A[File chunk sent\nto server] --> B[AES-256-CTR\nencryption applied]
B --> C[Encrypted chunk\nuploaded to Wasabi S3]
C --> D[HMAC tag\naccumulated]
D --> E[HMAC stored\nin S3 metadata]
E --> F[File fully encrypted\nat rest on cloud]
- Each file gets a unique IV — a 128-bit random value generated at upload start. This ensures two identical files produce different ciphertext.
- AES-256-CTR encrypts each chunk — the stream cipher processes each 10 MB chunk at its correct byte offset, independently.
- HMAC-SHA256 is computed — an integrity tag is accumulated chunk-by-chunk during upload, then stored in S3 object metadata.
- On download, HMAC is verified first — if the file was tampered with in storage, the HMAC check fails and the download is rejected before a single decrypted byte is returned.
Why AES-256-CTR?¶
AES-CTR (Counter Mode) is chosen specifically because it is seekable:
- Any byte position in the encrypted file can be decrypted independently
- This allows each upload chunk to be encrypted at its correct offset without sequential dependency
- Decryption can be streamed — the entire file never needs to be in memory at once
This is the same approach used by TLS, SSH, and most modern encrypted file systems.
Key Management¶
Lenzeye uses a two-tier key architecture:
flowchart LR
A[Master Key\nenv variable] --> B[Wraps per-user key\nwith AES-256-GCM]
B --> C[Encrypted user key\nstored in database]
C --> D[Decrypted at upload/download\ntime only, in memory]
- Master key — A single 256-bit application key stored as an environment variable on the server. Never in code, never in the database.
- Per-user key — Each user gets a unique 256-bit key, which is itself encrypted with the master key before being stored in the database.
- Key versioning — Old key versions are retained so previously encrypted files remain decryptable even after a key rotation.
This means: even if someone gained read access to the database, they would only find encrypted user keys — which are useless without the master key.
Integrity Verification (HMAC-SHA256)¶
Lenzeye uses Encrypt-then-MAC — the industry standard approach:
- Data is encrypted first
- HMAC is computed over the ciphertext (not plaintext)
- HMAC is stored in S3 metadata
- On download, HMAC is verified before decryption begins
- If verification fails →
InvalidSignatureexception → download rejected
This detects any tampering with the stored ciphertext — whether accidental (storage corruption) or deliberate (an attacker modifying bytes in S3).
What Is NOT Encrypted¶
For transparency:
- File names — stored as S3 object keys (e.g.,
user@email.com/folder/filename.jpg). File names are visible in S3 metadata. - Upload guidelines — plain text, no encryption needed.
- User account data — stored in PostgreSQL, protected by database-level access controls.
Security in Transit¶
All communication between browser, Lenzeye server, and Wasabi S3 uses HTTPS/TLS. Presigned URLs are time-limited (expiry enforced by S3) and single-use in practice.