# Zero-Knowledge Encryption Implementation Guide ## Table of Contents 1. [Proton-Style Encryption for MongoDB](#proton-style-encryption-for-mongodb) 2. [Shareable Links with Embedded Passwords](#shareable-links-with-embedded-passwords) 3. [Security Best Practices](#security-best-practices) 4. [Advanced Features](#advanced-features) --- ## Proton-Style Encryption for MongoDB ### Architecture Overview ``` Application Layer (Client Side) ├── Encryption/Decryption happens HERE ├── Queries constructed with encrypted searchable fields └── Data never leaves application unencrypted MongoDB (Server Side) └── Stores only encrypted data ``` ### Implementation Approaches #### 1. Application-Level Encryption (Recommended) Encrypt sensitive fields before they reach MongoDB: ```javascript // Using Node.js with crypto const crypto = require('crypto'); class EncryptedMongo { constructor(encryptionKey) { this.algorithm = 'aes-256-gcm'; this.key = encryptionKey; } encrypt(text) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(this.algorithm, this.key, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return { data: encrypted, iv: iv.toString('hex'), authTag: authTag.toString('hex') }; } decrypt(encryptedObj) { const decipher = crypto.createDecipheriv( this.algorithm, this.key, Buffer.from(encryptedObj.iv, 'hex') ); decipher.setAuthTag(Buffer.from(encryptedObj.authTag, 'hex')); let decrypted = decipher.update(encryptedObj.data, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } } // Example usage const encryptedMongo = new EncryptedMongo(userEncryptionKey); // Storing encrypted data await db.collection('users').insertOne({ _id: userId, email: encryptedMongo.encrypt('user@example.com'), ssn: encryptedMongo.encrypt('123-45-6789'), // Non-sensitive fields can remain plaintext for queries username: 'johndoe', // plaintext for searching createdAt: new Date() }); // Retrieving and decrypting const user = await db.collection('users').findOne({ _id: userId }); const decryptedEmail = encryptedMongo.decrypt(user.email); ``` #### 2. Deterministic Encryption for Searchable Fields For fields you need to query (like email), use deterministic encryption: ```javascript const crypto = require('crypto'); function deterministicEncrypt(text, key) { const hmac = crypto.createHmac('sha256', key); const iv = hmac.update(text).digest(); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); return encrypted; } // Same input always produces same output const encryptedEmail1 = deterministicEncrypt('user@example.com', key); const encryptedEmail2 = deterministicEncrypt('user@example.com', key); console.log(encryptedEmail1 === encryptedEmail2); // true // Now you can query by encrypted email await db.collection('users').findOne({ emailSearchable: deterministicEncrypt('user@example.com', userKey) }); ``` #### 3. Field-Level Encryption Strategy ```javascript // Document structure example { _id: ObjectId("..."), // Public/indexable fields (plaintext) username: "johndoe", userId: "unique-id-123", createdAt: ISODate("2026-01-07"), // Deterministically encrypted (searchable but not readable) email_searchable: "a1b2c3d4...", // for equality queries username_searchable: "e5f6g7h8...", // Randomly encrypted (not searchable, most secure) sensitiveData: { encrypted: true, data: "x9y8z7...", iv: "1a2b3c...", authTag: "4d5e6f..." } } ``` ### Key Management Strategy ```javascript // Never store the master key in MongoDB! class KeyManager { constructor() { this.masterKey = process.env.MASTER_ENCRYPTION_KEY; // Environment variable } // Derive unique key per user/document deriveUserKey(userId) { return crypto .createHash('sha256') .update(`${this.masterKey}:${userId}`) .digest(); } // For additional security, use KDF (scrypt/argon2) async deriveKeyWithScrypt(userId) { return new Promise((resolve, reject) => { crypto.scrypt( this.masterKey, `salt:${userId}`, 32, (err, derivedKey) => { if (err) reject(err); else resolve(derivedKey); } ); }); } } ``` ### MongoDB Schema Example with Mongoose ```javascript const mongoose = require('mongoose'); const encryptedFieldSchema = new mongoose.Schema({ data: { type: String, required: true }, iv: { type: String, required: true }, authTag: { type: String, required: true } }); const userSchema = new mongoose.Schema({ // Public fields username: { type: String, index: true }, userId: { type: String, unique: true }, // Searchable encrypted fields emailEncrypted: { type: String, index: true }, // Fully encrypted data profile: encryptedFieldSchema, financialData: encryptedFieldSchema, createdAt: { type: Date, default: Date.now } }); // Middleware to encrypt before save userSchema.pre('save', async function(next) { if (this.isModified('profile')) { const encrypted = encryptionService.encrypt(this.profile); this.profile = encrypted; } next(); }); ``` ### MongoDB Field Level Encryption (Official Alternative) MongoDB offers official client-side field level encryption: ```bash npm install mongodb-client-encryption ``` ```javascript const { MongoClient } = require('mongodb'); const { ClientEncryption } = require('mongodb-client-encryption'); const encryption = new ClientEncryption( client, { keyVaultNamespace: 'encryption.__keyVault', kmsProviders: { local: { key: masterKey } } } ); // Auto-encrypt specific fields const encryptedValue = await encryption.encrypt( 'sensitive-data', 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' ); ``` --- ## Shareable Links with Embedded Passwords ### Architecture Overview ``` 1. User has encrypted data in MongoDB 2. Creates a public share with a password 3. Generates a shareable link with encrypted password 4. External user clicks link → password extracted → data decrypted ``` ### Implementation #### 1. Share Link Structure ``` https://yourapp.com/share/{shareId}?key={encryptedShareKey} ``` Components: - **shareId**: References the shared data in MongoDB - **key**: Encrypted version of the decryption password (self-contained in URL) #### 2. MongoDB Schema for Shared Data ```javascript const mongoose = require('mongoose'); const shareSchema = new mongoose.Schema({ shareId: { type: String, unique: true, required: true }, // Reference to original encrypted data userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, documentId: { type: mongoose.Schema.Types.ObjectId }, collectionName: { type: String }, // The encrypted data (encrypted with share-specific password) encryptedData: { data: { type: String, required: true }, iv: { type: String, required: true }, authTag: { type: String, required: true } }, // Metadata createdAt: { type: Date, default: Date.now }, expiresAt: { type: Date }, accessCount: { type: Number, default: 0 }, maxAccessCount: { type: Number }, // Optional: limit access // Optional: Additional security allowedEmails: [String], // Restrict to specific emails requireEmailVerification: { type: Boolean, default: false }, isRevoked: { type: Boolean, default: false }, revokedAt: Date }); const Share = mongoose.model('Share', shareSchema); ``` #### 3. Creating a Shareable Link ```javascript const crypto = require('crypto'); class ShareService { constructor() { this.algorithm = 'aes-256-gcm'; } // Generate a random share password generateSharePassword() { return crypto.randomBytes(32).toString('hex'); } // Generate unique share ID generateShareId() { return crypto.randomBytes(16).toString('hex'); } // Encrypt data with share password encryptData(data, password) { const salt = crypto.randomBytes(32); const key = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256'); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(this.algorithm, key, iv); let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return { data: encrypted, iv: iv.toString('hex'), salt: salt.toString('hex'), authTag: authTag.toString('hex') }; } // Create shareable link async createShare(userId, documentData, options = {}) { // Generate a unique share password const sharePassword = this.generateSharePassword(); // Encrypt the data with this password const encryptedData = this.encryptData(documentData, sharePassword); // Create share record const shareId = this.generateShareId(); const share = await Share.create({ shareId, userId, encryptedData, expiresAt: options.expiresAt, maxAccessCount: options.maxAccessCount }); // Generate the shareable link with embedded password const shareLink = this.generateShareLink(shareId, sharePassword); return { shareId, shareLink, expiresAt: share.expiresAt }; } // Generate share link with encrypted password generateShareLink(shareId, password) { // Encrypt the password with a server-side master key // This way the password is in the URL but not readable const masterKey = process.env.SHARE_LINK_MASTER_KEY; const encryptedPassword = this.encryptPasswordInUrl(password, masterKey); return `https://yourapp.com/share/${shareId}?key=${encodeURIComponent(encryptedPassword)}`; } encryptPasswordInUrl(password, masterKey) { // Use URL-safe base64 encoding const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(this.algorithm, masterKey, iv); let encrypted = cipher.update(password, 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); // URL-safe format: iv:authTag:encrypted return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`; } } ``` #### 4. Accessing Shared Data (Backend) ```javascript class ShareAccessService { constructor() { this.algorithm = 'aes-256-gcm'; this.masterKey = process.env.SHARE_LINK_MASTER_KEY; } // Decrypt password from URL decryptPasswordFromUrl(encryptedPassword) { const parts = encryptedPassword.split(':'); const iv = Buffer.from(parts[0], 'hex'); const authTag = Buffer.from(parts[1], 'hex'); const encrypted = parts[2]; const decipher = crypto.createDecipheriv(this.algorithm, this.masterKey, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } // Decrypt share data decryptShareData(encryptedData, password) { const salt = Buffer.from(encryptedData.salt, 'hex'); const key = crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256'); const iv = Buffer.from(encryptedData.iv, 'hex'); const authTag = Buffer.from(encryptedData.authTag, 'hex'); const decipher = crypto.createDecipheriv(this.algorithm, key, iv); decipher.setAuthTag(authTag); let decrypted = decipher.update(encryptedData.data, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return JSON.parse(decrypted); } // Access shared data async accessShare(shareId, encryptedPassword) { // Find share record const share = await Share.findOne({ shareId }); if (!share) { throw new Error('Share not found'); } // Check if revoked if (share.isRevoked) { throw new Error('Share has been revoked'); } // Check if expired if (share.expiresAt && new Date() > share.expiresAt) { throw new Error('Share has expired'); } // Check access limit if (share.maxAccessCount && share.accessCount >= share.maxAccessCount) { throw new Error('Maximum access count reached'); } // Decrypt password from URL const password = this.decryptPasswordFromUrl(encryptedPassword); // Decrypt data const decryptedData = this.decryptShareData(share.encryptedData, password); // Increment access count share.accessCount += 1; await share.save(); return { data: decryptedData, expiresAt: share.expiresAt }; } } ``` #### 5. Express.js API Endpoint ```javascript const express = require('express'); const router = express.Router(); // POST /api/share - Create a new share router.post('/share', async (req, res) => { try { const { userId, data, options } = req.body; const shareService = new ShareService(); const result = await shareService.createShare(userId, data, options); res.json(result); } catch (error) { res.status(500).json({ error: error.message }); } }); // GET /api/share/:shareId - Access shared data router.get('/share/:shareId', async (req, res) => { try { const { shareId } = req.params; const { key } = req.query; // Encrypted password from URL if (!key) { return res.status(400).json({ error: 'Missing decryption key' }); } const accessService = new ShareAccessService(); const result = await accessService.accessShare(shareId, key); res.json(result); } catch (error) { res.status(404).json({ error: error.message }); } }); module.exports = router; ``` #### 6. Frontend: Share Access Page (React) ```javascript import React, { useEffect, useState } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; function SharedDataView() { const { shareId } = useParams(); const [searchParams] = useSearchParams(); const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchData() { try { const key = searchParams.get('key'); if (!key) { throw new Error('Invalid share link'); } const response = await fetch(`/api/share/${shareId}?key=${encodeURIComponent(key)}`); if (!response.ok) { throw new Error('Failed to access shared data'); } const result = await response.json(); setData(result.data); } catch (err) { setError(err.message); } finally { setLoading(false); } } fetchData(); }, [shareId, searchParams]); if (loading) return
Loading...
; if (error) return
Error: {error}
; return (

Shared Data

{JSON.stringify(data, null, 2)}
); } export default SharedDataView; ``` #### 7. Creating a Share (Frontend) ```javascript async function createShare() { const dataToShare = { name: 'John Doe', email: 'john@example.com', // ... other fields }; const response = await fetch('/api/share', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId: 'current-user-id', data: dataToShare, options: { expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days maxAccessCount: 10 } }) }); const result = await response.json(); // result.shareLink is ready to share! // Example: https://yourapp.com/share/a1b2c3d4...?key=e5f6g7h8... return result.shareLink; } ``` --- ## Security Best Practices ### Key Management 1. **Never store encryption keys in MongoDB** - Use environment variables - Consider key management services (AWS KMS, HashiCorp Vault) - Hardware Security Modules (HSMs) for production 2. **Use different encryption keys per:** - User - Document type - Environment (dev/staging/prod) 3. **Implement key rotation:** ```javascript async function rotateKey(oldKey, newKey, collection) { const documents = await collection.find().toArray(); for (const doc of documents) { const decrypted = decrypt(doc.encryptedField, oldKey); const reencrypted = encrypt(decrypted, newKey); await collection.updateOne( { _id: doc._id }, { $set: { encryptedField: reencrypted } } ); } } ``` ### Environment Variables ```bash # .env MASTER_ENCRYPTION_KEY= SHARE_LINK_MASTER_KEY= MONGODB_URI=mongodb://localhost:27017/yourdb ``` Generate master keys: ```bash node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" ``` ### Backup Strategy - **Backup encrypted data with separate key backups** - Store keys in different physical locations - Test restore procedures regularly ### Limitations of Encrypted Database - **No range queries** on encrypted numeric fields - **No regex/full-text search** on encrypted text - **Indexing only works** with deterministic encryption (less secure) - **Performance overhead** from encryption/decryption --- ## Advanced Features ### 1. Password Expiration ```javascript // Set expiration when creating share const share = await createShare(userId, data, { expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours }); ``` ### 2. Access Limits ```javascript const share = await createShare(userId, data, { maxAccessCount: 5 // Only 5 people can access }); ``` ### 3. Additional Password Protection ```javascript // Require a separate password (not in URL) const shareSchema = new mongoose.Schema({ // ... other fields passwordRequired: { type: Boolean, default: false }, passwordHash: String }); // User must enter password on page load // Password is verified before decrypting data ``` ### 4. Email Verification ```javascript // Restrict access to specific emails const share = await createShare(userId, data, { allowedEmails: ['user@example.com', 'trusted@domain.com'], requireEmailVerification: true }); ``` ### 5. Revocable Shares ```javascript // Middleware to check revocation shareSchema.pre('save', function(next) { if (this.isModified('isRevoked') && this.isRevoked) { this.revokedAt = new Date(); } next(); }); // Revoke a share async function revokeShare(shareId) { await Share.findOneAndUpdate( { shareId }, { isRevoked: true } ); } ``` --- ## Summary This implementation provides: ✅ **Zero-knowledge**: MongoDB never stores unencrypted shared data ✅ **Self-contained links**: Password embedded in URL ✅ **Access controls**: Expiration, access limits, email restrictions ✅ **Revocability**: Users can revoke shares anytime ✅ **Security**: AES-256-GCM encryption with PBKDF2 key derivation ✅ **User experience**: One-click access for recipients ✅ **Per-user encryption**: Each user's data encrypted with unique keys ✅ **Searchable encryption**: Deterministic encryption for queryable fields ## Dependencies ```json { "dependencies": { "mongoose": "^8.0.0", "express": "^4.18.0", "crypto": "built-in", "mongodb-client-encryption": "^6.0.0" } } ``` ## Password Recovery in Zero-Knowledge Systems ### The Core Problem In a zero-knowledge system: - **User's password** (or derived key) encrypts their data - **Server never sees the password** in plaintext - **If password is lost**, the data is mathematically unrecoverable This is actually a **feature**, not a bug - it's what makes the system truly zero-knowledge! --- ### Solution Approaches (Ranked by Security) #### 1. Secure Recovery Phrase (Recommended) ⭐ Give users a recovery phrase during signup that they must save securely. ```javascript class UserRegistration { async registerUser(email, password) { // Generate a random recovery key const recoveryKey = crypto.randomBytes(32).toString('hex'); // Encrypt recovery key with user's password const encryptedRecoveryKey = this.encryptWithPassword( recoveryKey, password ); // Store encrypted recovery key in database await User.create({ email, passwordHash: await hashPassword(password), // For authentication only encryptedRecoveryKey, dataEncryptionKey: this.deriveKeyFromPassword(password) }); // Display recovery key ONCE - never show again! return { userId: user.id, recoveryKey: recoveryKey, // Show this to user warning: "Save this key securely. You'll need it to recover your account." }; } } // Recovery process async function recoverAccount(email, recoveryKey, newPassword) { const user = await User.findOne({ email }); // Decrypt the stored encrypted recovery key // This is encrypted with the OLD password, but we have the recovery key // So we can re-encrypt it with the NEW password const dataEncryptionKey = this.decryptDataEncryptionKey( user.encryptedRecoveryKey, recoveryKey ); // Re-encrypt data encryption key with new password user.encryptedRecoveryKey = this.encryptWithPassword( recoveryKey, newPassword ); user.dataEncryptionKey = this.deriveKeyFromPassword(newPassword); await user.save(); return { success: true, message: "Account recovered successfully" }; } ``` **Pros:** - ✅ Maintains zero-knowledge property - ✅ User has full control - ✅ No backdoors for attackers **Cons:** - ❌ Users might lose the recovery key - ❌ Requires user education --- #### 2. Multi-Share Secret Splitting (Shamir's Secret Sharing) 🔐 Split the recovery key into multiple parts. User needs a threshold of parts to recover. ```javascript const secrets = require('secrets.js'); // npm install secrets.js class ShamirRecovery { async createUserWithShamirBackup(email, password) { // Generate master encryption key const masterKey = crypto.randomBytes(32); // Split into shares (e.g., 5 shares, need 3 to recover) const shares = secrets.share( masterKey.toString('hex'), 3, // threshold 5 // total shares ); // Store shares in different locations const recoverySetup = { userShare: shares[0], // Given to user emailShare: shares[1], // Emailed to user trustedContactShare: shares[2], // Sent to trusted contact backupShare1: shares[3], // Stored in secure backup backupShare2: shares[4] // Stored in separate location }; // Encrypt master key with user's password const encryptedMasterKey = this.encryptWithPassword( masterKey, password ); await User.create({ email, encryptedMasterKey, shamirShares: { userShare: recoverySetup.userShare, // Other shares stored elsewhere } }); return { recoveryShare: recoverySetup.userShare, emailShare: recoverySetup.emailShare, instructions: "Keep at least 3 of these 5 shares safe" }; } async recoverAccount(email, providedShares, newPassword) { const user = await User.findOne({ email }); // User provides 3+ shares if (providedShares.length < 3) { throw new Error('Need at least 3 shares to recover'); } // Combine shares to recover master key const masterKeyHex = secrets.combine(providedShares); const masterKey = Buffer.from(masterKeyHex, 'hex'); // Re-encrypt with new password user.encryptedMasterKey = this.encryptWithPassword( masterKey, newPassword ); await user.save(); return { success: true }; } } ``` **Pros:** - ✅ No single point of failure - ✅ Flexible recovery options - ✅ Still maintains zero-knowledge **Cons:** - ❌ More complex to implement - ❌ Users might find it confusing --- #### 3. Trusted Contact Recovery 👥 Allow trusted contacts to help recover access. ```javascript class TrustedContactRecovery { async setupTrustedContact(userId, contactEmail, userPassword) { // Generate a recovery key for this contact const recoveryKey = crypto.randomBytes(32); // Encrypt recovery key with contact's public key // (Contact would need to have an account too) const encryptedRecoveryKey = crypto.publicEncrypt( contactPublicKey, recoveryKey ); // Store the encrypted recovery share await TrustedContact.create({ userId, contactEmail, encryptedRecoveryKey, createdAt: new Date() }); } async initiateRecovery(userId) { // Notify all trusted contacts const contacts = await TrustedContact.find({ userId }); for (const contact of contacts) { await sendEmail({ to: contact.contactEmail, subject: 'Account Recovery Request', body: 'Click to approve account recovery', approvalLink: `/approve-recovery?token=${generateToken()}` }); } } async recoverWithContactApproval(recoveryToken, newPassword) { // Once enough contacts approve (e.g., 2 of 3) const approvals = await RecoveryApproval.find({ token: recoveryToken }); if (approvals.length >= 2) { // Combine encrypted shares and decrypt with contact private keys // Then re-encrypt with new password const masterKey = this.combineContactShares(approvals); await this.resetPassword(userId, masterKey, newPassword); return { success: true }; } } } ``` **Pros:** - ✅ Social recovery mechanism - ✅ No need to remember complex keys - ✅ Still technically zero-knowledge **Cons:** - ❌ Requires trusted contacts to also use the service - ❌ Social engineering risks --- #### 4. Time-Locked Recovery ⏰ Encrypt recovery keys with a future decryption (using blockchain or time-lock crypto). ```javascript class TimeLockedRecovery { async createTimeLockedBackup(userId, password, lockDays) { // Generate recovery key const recoveryKey = crypto.randomBytes(32); // Encrypt recovery key with user's password const encryptedRecoveryKey = this.encryptWithPassword( recoveryKey, password ); // Create a time-locked backup // In production, use something like Drand or blockchain timelock const timeLock = { recoveryKey, unlockDate: new Date(Date.now() + lockDays * 24 * 60 * 60 * 1000), encryptedBackup: this.encryptForFuture(recoveryKey, lockDays) }; await TimeLockBackup.create({ userId, encryptedRecoveryKey, timeLock, createdAt: new Date() }); } async recoverWithTimeLock(userId) { const backup = await TimeLockBackup.findOne({ userId }); if (new Date() < backup.timeLock.unlockDate) { throw new Error('Backup not yet available'); } // Decrypt using time-lock release const recoveryKey = await this.decryptFromFuture( backup.timeLock.encryptedBackup ); return recoveryKey; } } ``` **Pros:** - ✅ Automatic recovery after time period - ✅ Prevents impulsive data loss **Cons:** - ❌ User must wait for lock period - ❌ Complex implementation --- ### What NOT to Do ❌ #### ❌ Store Passwords in Plaintext ```javascript // NEVER DO THIS await User.create({ email, password: password // ❌ Defeats the whole purpose }); ``` #### ❌ Store Encryption Keys on Server ```javascript // NEVER DO THIS await User.create({ email, encryptedData: data, encryptionKey: key // ❌ Not zero-knowledge }); ``` #### ❌ Backdoor Encryption ```javascript // NEVER DO THIS const key = deriveKey(password); const backdoorKey = process.env.BACKDOOR_KEY; // ❌ Huge security risk ``` --- ### Hybrid Approach (Practical Recommendation) Combine multiple methods for different scenarios: ```javascript class HybridRecoverySystem { async setupUserRecovery(email, password) { const userId = await this.createUser(email, password); // 1. Generate recovery phrase (primary method) const recoveryPhrase = this.generateRecoveryPhrase(); // 2. Setup 2 trusted contacts (secondary method) await this.setupTrustedContact(userId, 'contact1@email.com', password); await this.setupTrustedContact(userId, 'contact2@email.com', password); // 3. Create time-locked backup (last resort) await this.createTimeLockedBackup(userId, password, 30); // 30 days return { userId, recoveryPhrase, trustedContacts: ['contact1@email.com', 'contact2@email.com'], timeLockDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) }; } async recoverAccount(email, method, ...args) { switch (method) { case 'phrase': return this.recoverWithPhrase(email, ...args); case 'contacts': return this.recoverWithContacts(email, ...args); case 'timelock': return this.recoverWithTimeLock(email, ...args); default: throw new Error('Invalid recovery method'); } } } ``` --- ### User Education is Key 📚 ```javascript // During registration const registrationInfo = { title: 'Account Recovery Setup', message: ` IMPORTANT: Save your recovery phrase securely! Your recovery phrase: ${recoveryPhrase} Store it in: - A password manager - A safe deposit box - Written down and stored securely Without this phrase, your data cannot be recovered if you forget your password. We also recommend setting up trusted contacts as a backup recovery method. `, checkboxRequired: true, checkboxLabel: 'I understand that without my recovery phrase, my data cannot be recovered' }; ``` --- ### UI/UX Example (React) ```javascript function RecoverySetup({ onComplete }) { const [recoveryPhrase, setRecoveryPhrase] = useState(''); const [confirmed, setConfirmed] = useState(false); useEffect(() => { // Generate and show recovery phrase const phrase = generateRecoveryPhrase(); setRecoveryPhrase(phrase); }, []); return (

Account Recovery Setup

Save this recovery phrase securely. You won't be able to see it again!
setConfirmed(e.target.checked)} /> } label="I have securely stored my recovery phrase" />
); } ``` --- ### Recovery Methods Comparison | Method | Security | UX | Complexity | Zero-Knowledge? | |--------|----------|-----|------------|-----------------| | Recovery Phrase | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ✅ Yes | | Shamir's Secret Sharing | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ✅ Yes | | Trusted Contacts | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ✅ Yes | | Time-Locked | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ Yes | | Server Key Storage | ⭐ | ⭐⭐⭐⭐⭐ | ⭐ | ❌ No | --- ### Bottom Line **There is no way to recover data without some form of backup.** The question is: which backup method aligns with your security requirements and user experience goals? For most applications, I recommend: 1. **Primary**: Recovery phrase (most secure) 2. **Secondary**: Trusted contacts (user-friendly) 3. **Fallback**: Time-locked backup (last resort) --- ## Related Concepts - **Proton Mail/Drive**: Zero-knowledge encryption services - **End-to-end encryption**: Data encrypted at rest and in transit - **PBKDF2**: Password-based key derivation function - **AES-256-GCM**: Advanced Encryption Standard with authentication - **Deterministic encryption**: Same plaintext produces same ciphertext - **Key rotation**: Periodic replacement of encryption keys - **Shamir's Secret Sharing**: Cryptographic method for splitting secrets - **Social recovery**: Using trusted contacts for account recovery