One message. One chance. No second chances.
1LIFE is a browser-based encryption tool built around the ODE (One-Decrypt Encryption) protocol — a novel cryptographic scheme where each ciphertext can only be decrypted exactly once. After the first successful decryption, the key material is permanently and irrevocably destroyed through three independent enforcement layers. No servers. No cloud. Pure mathematics.
- Overview
- Encryption Modes
- ODE Protocol — How It Works
- Three-Layer Destruction System
- Blob File Format
- Cryptographic Algorithms
- Known Loopholes & Limitations
- Security Properties
- Usage Guide
- Technical Stack
- Browser Compatibility
- Developer Notes
- Credits
1LIFE implements the ODE v1 standard alongside four additional industry-grade cipher modes. The key innovation of ODE is that decryption is not just a read operation — it is a destructive, irreversible state transition. Once a blob is decrypted:
- The key derivation components (salt + nonce) are overwritten with zeros
- A SHA-256 fingerprint of the blob is saved to
localStorage - The hash chain is broken permanently
- The blob file is mutated on download
All three layers must be independently bypassed to decrypt twice — which is cryptographically and architecturally prevented.
| Mode | Algorithm | Key Type | Decryption Limit | Use Case |
|---|---|---|---|---|
| ODE · 1-Life | AES-256-GCM + SHA3-512 hash chain | Auto-derived | Once only | Self-destruct secrets |
| AES-256-GCM | AES-GCM + PBKDF2 (200k iterations) | Password | Unlimited | Secure standard messaging |
| ChaCha20-Poly1305 | ChaCha20 stream + PBKDF2 (100k iterations) | Password | Unlimited | Mobile / low-power devices |
| AES-256-CBC | AES-CBC + HMAC-SHA256 + PBKDF2 (150k iterations) | Password | Unlimited | Legacy compatibility |
| Hybrid RSA+AES | RSA-2048 + AES-256-GCM session key | Auto keypair | Unlimited | Asymmetric use, no password |
salt = CSPRNG(32 bytes)
nonce = CSPRNG(16 bytes)
K = SHA3-256("ODE-KEY-DERIVATION-v1" || salt || nonce)
H = SHA3-512("ODE-CHAIN-v1" || K || nonce || salt)
C, tag = AES-256-GCM(plaintext, K, nonce)
blob = "ODE1" || salt || nonce || H || tag || C
The key K is never stored — only its derivation inputs (salt, nonce) are embedded. The hash chain H binds all three components. Destroying either salt or nonce makes key derivation impossible.
1. Compute fingerprint = SHA-256(blob[0:128])
If fingerprint in localStorage → BLOCK (Layer 1)
2. If blob[4:36] == 0x00 * 32 → BLOCK (Layer 2)
If blob[36:52] == 0x00 * 16 → BLOCK (Layer 2)
3. Recover K = SHA3-256("ODE-KEY-DERIVATION-v1" || salt || nonce)
expected = SHA3-512("ODE-CHAIN-v1" || K || nonce || salt)
If H != expected → TAMPERED
4. plaintext = AES-256-GCM-Decrypt(C, K, nonce, tag)
5. DESTROY:
blob[4 : 4+32+16+64] = 0x00 (zero salt + nonce + chain)
localStorage.add(fingerprint)
6. Return plaintext
Each layer is independently sufficient to block re-decryption. An attacker must bypass all three simultaneously.
- A SHA-256 digest of the first 128 bytes of the original blob is computed before destruction
- Stored in
localStorageunder key1life_v2_fpsas a JSON array - Survives: page refresh, tab close, browser restart, re-upload of the original untouched file
- Does not survive: clearing browser storage, incognito mode, different browser/device
- Bytes
[4 : 116]of the blob (salt + nonce + chain) are overwritten with0x00 - This is written to the downloaded destroyed blob file
- Any re-upload of the destroyed file fails immediately — before any cryptographic operation
- Survives: as long as the user downloads and uses the destroyed file
- SHA3-512 chain binds key + nonce + salt with domain-separated prefix
- Zeroing salt/nonce (Layer 2) also breaks the chain — double-enforcement
- Tampering any byte in the header produces a different chain and fails verification
- AES-256-GCM authentication tag provides additional integrity over the ciphertext
Offset Size Field
──────────────────────────────────────────────
0 4 Magic: "ODE1" (0x4F 0x44 0x45 0x31)
4 32 Salt (random, CSPRNG)
36 16 Nonce (random, CSPRNG)
52 64 Hash Chain (SHA3-512)
116 16 AES-GCM Auth Tag
132 N Ciphertext (N = plaintext length)
Total: 132 + N bytes
Offset Size Field
──────────────────────────────────────────────
0 4 Magic: "ODE1" (unchanged)
4 112 ZEROED (salt + nonce + chain = all 0x00)
116 16 Auth Tag (unchanged)
132 N Ciphertext (unchanged but undecryptable)
| Format | Magic | Description |
|---|---|---|
.enc |
AESGCM |
AES-256-GCM password-based |
.cc20 |
CC20P1 |
ChaCha20-Poly1305 |
.cbc |
AESCBC |
AES-256-CBC + HMAC |
.hyb |
HYBRID |
RSA-2048 + AES-256-GCM |
1LIFE implements SHA3-256 and SHA3-512 in pure JavaScript (no external library) for use in the browser. The implementation uses the standard Keccak permutation with:
- Rate: 1088 bits (SHA3-256) / 576 bits (SHA3-512)
- Capacity: 512 bits / 1024 bits
- Padding:
0x06(SHA-3 domain separation, not original Keccak0x01)
Uses the Web Crypto API (crypto.subtle) for hardware-accelerated AES-GCM:
- 256-bit key, 128-bit authentication tag
- 96-bit IV (12 bytes) for standard GCM operation
- Authenticated encryption — any modification to ciphertext or header is detected
Password-based key derivation for non-ODE modes:
| Mode | Iterations | Hash |
|---|---|---|
| AES-256-GCM | 200,000 | SHA-256 |
| ChaCha20 | 100,000 | SHA-256 |
| AES-256-CBC | 150,000 | SHA-256 |
- 2048-bit modulus, public exponent
65537 - Hash: SHA-256
- One-time keypair generated per encryption session
- Private key exported as JWK and embedded in the blob (no separate key management)
This section documents undisclosed vulnerabilities in the 1LIFE/ODE scheme. These are not hypothetical — they are real architectural gaps that a sufficiently motivated adversary can exploit.
Severity: HIGH
The fingerprint database lives in localStorage — which is browser-local and non-persistent under certain conditions.
Exploitation paths:
- Open the original
.odefile in a different browser (Chrome → Firefox) — fingerprint database is not shared - Open in an incognito / private window — localStorage is isolated and cleared on close
- Open on a different device entirely — Layer 1 does not exist
- Clear browser storage (
localStorage.clear()) — all fingerprints erased - Use a browser automation tool (Puppeteer, Playwright) with a fresh profile
Impact: A recipient who knows about this can decrypt the same ODE blob multiple times by simply rotating through browser contexts.
Mitigation (not implemented): A server-side key revocation oracle would close this gap — but defeats the "no servers" design goal.
Severity: HIGH
The ODE destruction only modifies the in-browser memory buffer and the downloaded destroyed blob. The original file on disk is never touched by the browser (browsers cannot overwrite arbitrary files on the filesystem by design).
Exploitation path:
- Receive a
.odefile - Make a copy:
cp secret.ode secret_backup.ode - Decrypt
secret.odevia 1LIFE (blob is destroyed in memory, destroyed version downloaded) - Ignore the destroyed blob — never replace the original
- Clear
localStorage - Decrypt
secret_backup.ode— succeeds
Impact: Any recipient who copies the file before decrypting (or simply doesn't download the destroyed blob) can decrypt unlimited times. The "destruction" only works if the recipient cooperates.
Root cause: Browsers have no authority over the host filesystem. The ODE scheme requires trust in the recipient's compliance — which is not a security property.
Severity: MEDIUM
At the moment of decryption, the plaintext exists in JavaScript memory as a Uint8Array. This is readable via:
- Browser DevTools → Memory tab → heap snapshot
- JavaScript injection (if the page is not served over HTTPS)
- OS-level process memory dump of the browser process
The plaintext is also rendered directly into the DOM (outTxt.textContent = display) — it exists as a string in the DOM tree, accessible via document.getElementById('dec-output-text').textContent from any script with DOM access.
Impact: The plaintext can be extracted at display time regardless of the cryptographic strength of the outer layer.
Severity: LOW–MEDIUM
The SHA-3 implementation is written in pure JavaScript and does not use constant-time operations for the Keccak permutation. While this is unlikely to be exploitable in a browser context (no direct timing channel), it means:
- No protection against timing side-channel attacks in a controlled measurement environment
- Performance is significantly slower than native implementations — making brute-force detection of the key derivation harder to notice
Note: The Web Crypto API operations (AES-GCM, PBKDF2, RSA-OAEP) are constant-time as they use the browser's native crypto implementation.
Severity: MEDIUM
In Hybrid (RSA+AES) mode, the RSA private key is exported as JWK and embedded directly in the blob. This means:
- Anyone who receives the blob has the private key
- There is no asymmetric access control — encryption and decryption capability are identical
- The "asymmetric" label is misleading for the current implementation
Proper asymmetric encryption would require the sender to have the recipient's public key and never embed the private key. The current implementation is effectively symmetric — just with RSA-wrapped key material.
Severity: LOW
ODE derives the key deterministically from salt + nonce. If an attacker:
- Captures the blob before decryption (network interception, file access)
- Obtains the salt and nonce (which are in the blob in plaintext)
They can recompute the key and decrypt without triggering any of the three destruction layers — because destruction only runs during a decryption attempt through the 1LIFE interface.
The key material embedded in the unmodified blob is permanently sufficient to reconstruct K. An attacker with direct access to the blob file never needs to go through the 1LIFE interface.
Severity: LOW
The fingerprint is SHA-256 of the first 128 bytes. Two crafted blobs with the same first 128 bytes would have the same fingerprint. This is a second-preimage resistance assumption on SHA-256 — considered safe today but worth noting.
Additionally, if an attacker can write to localStorage (XSS vulnerability in any page on the same origin), they could either:
- Pre-populate a fingerprint to block legitimate decryption
- Clear the fingerprint database to re-enable decryption
| # | Loophole | Severity | Bypasses Layers | Fixable Without Server? |
|---|---|---|---|---|
| 1 | Cross-device / incognito localStorage | HIGH | Layer 1 | No |
| 2 | Pre-decryption file copy | HIGH | All 3 | No |
| 3 | Memory / DOM snapshotting | MEDIUM | N/A (post-decrypt) | Partial |
| 4 | Pure JS SHA-3 timing | LOW | None | Yes (use SubtleCrypto) |
| 5 | Hybrid mode private key embedding | MEDIUM | N/A (design flaw) | Yes (proper PKI) |
| 6 | Direct blob key recovery | LOW | All 3 | No |
| 7 | localStorage fingerprint collision/poisoning | LOW | Layer 1 | Partial |
ODE enforces one-decrypt only against a cooperative recipient using the same browser. It is not a security guarantee against an adversary with direct file access or alternate browser contexts. The scheme is best understood as a social/UX enforcement mechanism with cryptographic underpinning — not a hardware-enforced or server-backed guarantee.
For true one-time decryption with adversarial enforcement, consider:
- Hardware Security Modules (HSM) with key deletion policies
- Server-side key escrow with audit log and one-time key release
- Quantum key distribution (QKD) for physical one-time use
| Property | Status | Notes |
|---|---|---|
| Confidentiality | ✅ Strong | AES-256-GCM is quantum-resistant to classical attacks |
| Integrity | ✅ Strong | AES-GCM auth tag + SHA3-512 chain |
| Authenticity | No digital signature — cannot verify sender identity | |
| One-decrypt (cooperative) | ✅ Yes | Three layers enforce this when recipient uses 1LIFE |
| One-decrypt (adversarial) | ❌ No | File copy + alternate browser defeats all layers |
| Forward secrecy | ❌ No | Key derivable from blob indefinitely |
| Post-quantum resistance | AES-256 is safe; SHA-3 is safe; RSA-2048 is not PQ-safe | |
| Zero-knowledge | ❌ No | Plaintext visible in DOM during decryption |
| Server-free operation | ✅ Yes | Entirely client-side |
- Open
1life.htmlin any modern browser - Select ODE · 1-Life as the encryption mode
- Type or paste your message
- Click Encrypt (or press
Ctrl+Enter) - Download the
.odefile — this is your sealed blob - Share the
.odefile with the intended recipient
No password required for ODE mode. The key is auto-derived and self-contained.
- Open
1life.htmlin a browser - Switch to the Decrypt tab
- Upload the
.odefile - The inspect panel shows blob status — verify it reads
INTACT - Click Decrypt
- Download the destroyed blob if you need to prove the key was used
- The original file's fingerprint is now saved — it cannot be decrypted again on this device
- Select the desired cipher mode
- Enter your message and a strong password
- Encrypt and share the file + password separately (never together)
- Recipient uploads the file, enters the password, decrypts
- Select Hybrid RSA+AES
- No password required — a one-time RSA-2048 keypair is generated automatically
- Encrypt and download the
.hybfile - Recipient uploads the file and decrypts without a password (private key is embedded)
- Language: Vanilla JavaScript (ES2020+), no dependencies, no build step
- Crypto: Web Crypto API (
crypto.subtle) for AES-GCM, PBKDF2, RSA-OAEP - SHA-3: Custom pure-JS Keccak-f[1600] implementation (SHA3-256 and SHA3-512)
- Storage:
localStoragefor fingerprint blacklist only - UI: Pure HTML/CSS — single self-contained
.htmlfile - Fonts: Bebas Neue (display), DM Mono (code/UI), Instrument Serif (italic body)
- Server requirement: None — file can be opened directly from disk
| Browser | Support | Notes |
|---|---|---|
| Chrome 90+ | ✅ Full | Recommended |
| Firefox 90+ | ✅ Full | |
| Safari 15+ | ✅ Full | |
| Edge 90+ | ✅ Full | Chromium-based |
| Opera 76+ | ✅ Full | |
| IE 11 | ❌ None | No Web Crypto API |
| Chrome (file://) | Web Crypto requires secure context (HTTPS or localhost) |
Note: For local use (
file://protocol), some browsers restrictcrypto.subtle. Serve vialocalhostfor full functionality:python3 -m http.server 8080
# Simple HTTP server (Python)
python3 -m http.server 8080
# Or with Node.js
npx serve .
# Open in browser
open http://localhost:8080/1life.htmlEach mode follows a consistent pattern:
// 1. Define magic bytes (6 bytes recommended)
const MY_MAGIC = new Uint8Array([...]);
// 2. Implement encrypt
async function myEnc(plain, pass) {
// ... derive key, encrypt, return concat(MY_MAGIC, ...)
}
// 3. Implement decrypt
async function myDec(blob, pass) {
if (!ctEq(blob.slice(0,6), MY_MAGIC)) throw new Error("Wrong format");
// ... parse, decrypt, return plaintext
}
// 4. Register in detectMode()
if (ctEq(b.slice(0,6), MY_MAGIC)) return 'my-mode';
// 5. Add mode card in HTML, handle in doEncrypt()/doDecrypt() switch// Key: "1life_v2_fps"
// Value: JSON array of SHA-256 hex strings
["a3f9b2c1...", "d7e4a891...", "..."]Clearing used fingerprints (for testing):
localStorage.removeItem('1life_v2_fps');╔══════════════════════════════════════╗
║ DEVELOPED BY ║
║ AdiscLabs ║
║ @Aditya Bhosale ║
╠══════════════════════════════════════╣
║ 1LIFE v2.0 ║
║ ODE Protocol — One-Decrypt ║
║ Encryption Standard ║
╚══════════════════════════════════════╝
ODE Protocol — designed and implemented by AdiscLabs
Cryptographic primitives:
- AES-256-GCM via Web Crypto API (W3C standard)
- SHA3-256 / SHA3-512 — custom Keccak-f[1600] implementation
- PBKDF2-SHA256 via Web Crypto API
- RSA-OAEP-2048 via Web Crypto API
This project is released for educational and research purposes. The ODE protocol specification is open. The implementation may be freely used, modified, and distributed with attribution to AdiscLabs / @Aditya Bhosale.
Use in production systems handling sensitive data is not recommended without a formal security audit — particularly in light of the known loopholes documented above.
"The only truly secure system is one that is powered off, cast in a block of concrete and sealed in a lead-lined room with armed guards." — Gene Spafford