← Back to projects
CipherVault

CipherVault

Client-side AES-256-GCM encrypted storage for sensitive data

CompleteTypeScriptWeb Crypto APIReactFastAPISQLite
View Source

CipherVault

Client-side encrypted file storage with a server-blind architecture.

CipherVault is a file-storage application I built around a simple question:

What if the server never had access to the user's plaintext files or encryption password?

Instead of uploading plaintext files and relying on the backend to protect them, CipherVault encrypts files inside the browser before they are uploaded.

The backend receives and stores only encrypted data.

The idea

The important part is that encryption and decryption happen in the browser, not on the server.

The server stores the encrypted blob and metadata, but does not need the user's encryption password.

Why client-side encryption?

A traditional storage application can look like:

Browser → Server → Storage
             │
             └── Plaintext may be accessible

CipherVault instead uses:

Browser → Encrypt → Server → Encrypted Storage

This creates a clear security boundary between the storage system and the contents of the files.

The backend can authenticate sessions, manage metadata, and store encrypted blobs without needing to understand the files themselves.

Cryptography

CipherVault uses the browser's native Web Crypto API rather than implementing cryptographic primitives manually.

Password-based key derivation

The user's password is converted into an AES-256 key using:

The salt is stored with the encrypted payload. It is not secret and allows the same password to derive the same key during decryption.

File encryption

Files are encrypted using:

AES-256-GCM

GCM provides authenticated encryption, meaning that modified or corrupted ciphertext should fail authentication during decryption rather than silently producing altered plaintext.

Each encryption operation also uses a fresh random 96-bit nonce.

Filename encryption

The original filename is encrypted as well.

This means the server does not need to store filenames such as:

tax-return.pdf
personal-notes.txt

The original filename is recovered only after successful client-side decryption.

Data flow

During an upload:

File + Password
      │
      ▼
   Browser
      │
      ├── Generate random salt
      ├── Derive AES-256 key
      ├── Encrypt file
      └── Encrypt filename
      │
      ▼
Encrypted Payload
      │
      ▼
FastAPI
      │
      ├── SQLite → Metadata
      └── Storage → Encrypted Blob

During a download, the process is reversed:

Encrypted Blob
      │
      ▼
   Browser
      │
      ├── Extract salt + nonce + ciphertext
      ├── Derive key from password
      ├── Authenticate + decrypt file
      └── Decrypt filename
      │
      ▼
Original File

A wrong password causes AES-GCM authentication to fail because the derived key cannot authenticate the ciphertext.

Backend architecture

The backend is built with FastAPI.

It handles:

The database stores metadata such as:

file_id
owner_id
file_size
upload_timestamp
blob_path

The encrypted file itself is stored separately from the database.

This keeps the database focused on metadata while the storage layer handles encrypted blobs.

Server-blind storage

One of the simplest ways I tested the architecture was opening a stored encrypted .bin file directly from the server's storage directory.

Instead of the original document, the server only contains ciphertext.

The important distinction is:

The server stores the ciphertext, not the plaintext.

This does not make the application invulnerable, but it means that obtaining the stored blob alone should not reveal the original file without the user's password.

What I learned

CipherVault taught me that secure storage is less about choosing an encryption algorithm and more about designing the entire data flow around the security property.

The project gave me practical experience with:

The biggest lesson was:

Security properties have to be designed into the architecture from the beginning.

Encrypting a file at one point in the application is not enough if plaintext or encryption keys are later sent to the server.

Current status

Complete

CipherVault currently supports:

The implementation is intentionally a learning-focused project rather than a production-grade password manager or enterprise storage system.

Future improvements

If I continued the project, the main areas would be:

Repository

GitHub — CipherVault