AES-256-GCM
Authenticated encryption detects tampering immediately. A single changed bit makes decryption fail instead of emitting garbage.
ZERO-KNOWLEDGE FILE ENCRYPTION
Everything you need to run, understand, and deploy AegisCrypt — a browser-only AES-256-GCM vault that never uploads your files.
$ git clone https://github.com/Bangkahdev/AegisCrypt-Web.git
$ cd AegisCrypt-Web
$ npm install
$ npm start
VITE v8 ready
Local: http://localhost:5173/
Uploads: 0 files
Status: all crypto runs locally
AegisCrypt is a client-side encryption suite that seals files into
.aegis containers using the Web Crypto API.
Passwords, key material, and plaintext stay inside browser memory.
Authenticated encryption detects tampering immediately. A single changed bit makes decryption fail instead of emitting garbage.
HMAC-SHA-256 with 100,000 iterations and a fresh 32-byte salt per file. Built to slow down brute-force and rainbow-table attacks.
Combine a password with any file. Key material becomes password + SHA-256(keyfile), so the exact file is required to decrypt.
Encryption streams files in 1 MB blocks, each with its own IV, so multi-gigabyte files do not need one giant heap allocation.
There is no backend. The app is a static bundle; every cryptographic operation runs in your browser tab.
GCM authentication tags and strict chunk-length validation reject corrupted or modified containers.
AegisCrypt does not operate a server that could see your data. The
browser performs key derivation and encryption locally, and the
resulting .aegis file is downloaded
directly to your device.
Node.js 18 or newer, npm, and a modern browser with the Web Crypto API (Chrome, Edge, Firefox, or Safari).
# 1. Clone the repository
git clone https://github.com/Bangkahdev/AegisCrypt-Web.git
# 2. Enter the project
cd AegisCrypt-Web
# 3. Install dependencies
npm install
# 4. Start the dev server
npm start
The app opens at http://localhost:5173. For a
production build and a local preview:
npm run build # typecheck + production bundle
npm run preview # serve the production build
npm run test:unit # Vitest security suite
npm run test:e2e # Playwright browser tests
| Script | What it does |
|---|---|
npm start | Vite dev server with HMR |
npm run build | TypeScript check, then Vite production build |
npm run test:unit | Runs the Vitest crypto suite once |
npm run test:e2e | Runs Playwright end-to-end browser tests |
Every operation follows the same pipeline. Nothing is sent over the network, and every chunk gets its own initialization vector.
The password bytes are combined with the SHA-256 hash of the optional keyfile. This keeps mixed input sizes consistent and makes the keyfile part of the derived key.
PBKDF2-HMAC-SHA-256 runs 100,000 iterations over a fresh 32-byte random salt, producing a 256-bit AES key.
Each 1 MB plaintext chunk is encrypted with AES-256-GCM and a unique 12-byte IV. The IV and ciphertext are packed with a 4-byte length.
The header plus encrypted chunks are assembled as a Blob and
downloaded as filename.ext.aegis.
If you lose the password or the exact keyfile, the data cannot be decrypted. AegisCrypt has no server, reset flow, or backdoor.
.aegis file into the queue.The drop zone accepts multiple files at once, including files selected from a folder picker. Every file is processed sequentially and logged in the operation console.
The .aegis v2 container is a simple binary
format: a fixed 38-byte header followed by length-prefixed chunks.
| Offset | Size | Field | Value |
|---|---|---|---|
| 0 | 5 | Magic | ASCII AEGIS |
| 5 | 1 | Version | 0x02 |
| 6 | 32 | Salt | CSPRNG, per file |
| Size | Field | Notes |
|---|---|---|
| 4 | Length | Little-endian; IV + ciphertext + tag |
| 12 | IV | Fresh CSPRNG value for every chunk |
| n | Ciphertext | AES-256-GCM output |
| 16 | Auth tag | Appended by Web Crypto |
Version 2 containers are interoperable with the desktop version. A different version byte is rejected instead of being processed with the wrong layout.
The crypto engine lives in
src/services/cryptoService.ts and is fully
browser-based.
| Export | Signature | Purpose |
|---|---|---|
processFileEncrypt |
(file, password, keyFileBuffer, signal, onProgress) => Promise<Blob> |
Streams a file into a sealed container |
processFileDecrypt |
(fileBuffer, password, keyFileBuffer, signal, onProgress) => Promise<Blob> |
Validates the header and restores the original |
downloadBlob |
(data, filename) => void |
Triggers a browser download |
getEncryptedFilename |
(name) => string |
Appends .aegis |
getDecryptedFilename |
(name) => string |
Removes .aegis or adds a prefix |
prepareKeyMaterial |
(password, keyFileBuffer) => Promise<Uint8Array> |
Combines password and keyfile hash |
deriveKey |
(material, salt, usage) => Promise<CryptoKey> |
PBKDF2 key derivation |
import {
processFileEncrypt,
processFileDecrypt,
downloadBlob,
} from "./services/cryptoService";
const controller = new AbortController();
const sealed = await processFileEncrypt(
file,
password,
keyFileBuffer,
controller.signal,
(bytes) => console.log(`${bytes} bytes processed`)
);
downloadBlob(sealed, `${file.name}.aegis`);
const restored = await processFileDecrypt(
await sealed.arrayBuffer(),
password,
keyFileBuffer,
controller.signal,
() => {}
);
downloadBlob(restored, file.name);
| Constant | Value | Meaning |
|---|---|---|
PBKDF2_ITERATIONS | 100,000 | Key stretching rounds |
SALT_LENGTH | 32 bytes | Per-file random salt |
IV_LENGTH | 12 bytes | GCM initialization vector |
TAG_LENGTH_BITS | 128 | GCM authentication tag |
KEY_LENGTH | 256 | AES key size |
CHUNK_SIZE | 1 MiB | Streaming block size |
AegisCrypt-Web/
├── public/
│ └── favicon.svg
├── src/
│ ├── components/
│ │ ├── ui/ # shadcn-style primitives
│ │ ├── FileList.tsx # queue UI
│ │ ├── LogBox.tsx # operation log
│ │ ├── LoginScreen.tsx # session entry
│ │ └── PasswordInput.tsx # strength meter
│ ├── lib/
│ │ └── utils.ts # cn() helper
│ ├── services/
│ │ └── cryptoService.ts # AES-GCM engine
│ ├── App.tsx # orchestration
│ ├── constants.ts # crypto + format constants
│ ├── index.css # theme tokens
│ ├── main.tsx # entry point
│ └── types.ts # shared interfaces
├── docs-site/ # this neo-brutalist docs site
├── tests/
│ ├── cryptoService.test.ts
│ └── e2e/app.spec.ts
├── cloudflare.config.ts # Workers deployment
└── vite.config.ts
Use a long, unique password, store the keyfile separately from the encrypted file, and keep an offline backup. Because there is no backend, recovery is entirely your responsibility.
Both the app and this documentation site run as static assets on
Cloudflare Workers, deployed with the cf CLI.
The zero-knowledge encryption workspace.
DOCSThis documentation site.
# Deploy the main app (from the repository root)
npm run build
cf deploy
# Deploy the documentation site
cd docs-site
npm install
npm run build
cf deploy
cloudflare.config.ts declares each Worker's
custom domain. Cloudflare creates the DNS record and certificate when
the Worker is deployed.
No. If a keyfile was used during encryption, the exact same file is required. The key material includes its SHA-256 hash.
No. The deployed app is a static bundle. Encryption, decryption, and key derivation happen in the browser.
The keyfile probably differs, or the container was modified. GCM authentication rejects both cases.
Encryption streams in 1 MB chunks and avoids a single giant allocation. Very large containers can still be limited by browser memory during decryption.
Any modern browser with the Web Crypto API, including Chrome, Edge, Firefox, and Safari.