SecureVault: A Zero-Knowledge Password Manager Design Exercise
A systems-design exercise in zero-knowledge architecture — what it takes for a server to store secrets it can never read, with the threat model written before the data model.
Systems Design Exercise. This is a design study, not a deployed product. Any figures are illustrative targets used to reason about the system — not production results.
SecureVault: A Zero-Knowledge Password Manager Design Exercise
Executive Summary
SecureVault is a systems-design exercise — a design study, not a shipped product — exploring zero-knowledge architecture for a password manager: a design where even the service provider can never read user secrets. It works through client-side encryption, a key hierarchy with envelope encryption, and a threat model written before any schema. References to users or organizations are illustrative scenarios used to reason about the design, not a real customer base.
The Challenge
Building a password manager requires the highest security standards:
Security Requirements
- Zero-knowledge architecture (server never sees plaintext)
- End-to-end encryption for all data
- Secure key exchange for team sharing
- Browser extension security (prevent XSS/CSRF)
- SOC 2 Type II compliance
Technical Requirements
- Support 10,000+ users
- Team vaults with granular permissions
- Browser extensions (Chrome, Firefox, Safari)
- Desktop applications (Windows, macOS, Linux)
- Mobile apps (iOS, Android)
- <100ms encryption/decryption time
Architecture Overview
Zero-Knowledge Architecture
The architecture diagram above captures the central constraint: all encryption and decryption happen client-side, so the API and database only ever handle ciphertext. The server is designed to be unable to read the data it stores.
Core Components
1. Client-Side Encryption
- AES-256-GCM: Symmetric encryption for vault data
- RSA-4096: Asymmetric encryption for key exchange
- Argon2: Key derivation from master password
- PBKDF2: Additional key stretching
2. Zero-Knowledge Architecture
- All encryption/decryption on client
- Server only stores encrypted blobs
- Master password never transmitted
- Keys derived client-side only
3. Team Sharing
- Public-key cryptography for secure sharing
- Granular permissions (read, write, admin)
- Secure key exchange protocol
- Audit logging for all shares
4. Browser Extension Security
- Content Security Policy (CSP)
- XSS prevention measures
- CSRF protection
- Secure communication with API
Technical Implementation
Client-Side Encryption
All encryption happens in the browser/desktop app:
// Encryption service (client-side only)
import * as crypto from 'crypto'
class EncryptionService {
private readonly ALGORITHM = 'aes-256-gcm'
private readonly KEY_LENGTH = 32
private readonly IV_LENGTH = 16
private readonly TAG_LENGTH = 16
async deriveKey(masterPassword: string, salt: Buffer): Promise<Buffer> {
// Argon2 key derivation
const argon2 = require('argon2')
return await argon2.hash(masterPassword, {
type: argon2.argon2id,
salt,
memoryCost: 65536, // 64 MB
timeCost: 3,
parallelism: 4,
})
}
async encrypt(data: string, key: Buffer): Promise<EncryptedData> {
const iv = crypto.randomBytes(this.IV_LENGTH)
const cipher = crypto.createCipheriv(this.ALGORITHM, key, iv)
let encrypted = cipher.update(data, 'utf8', 'base64')
encrypted += cipher.final('base64')
const tag = cipher.getAuthTag()
return {
encrypted,
iv: iv.toString('base64'),
tag: tag.toString('base64'),
}
}
async decrypt(encryptedData: EncryptedData, key: Buffer): Promise<string> {
const iv = Buffer.from(encryptedData.iv, 'base64')
const tag = Buffer.from(encryptedData.tag, 'base64')
const decipher = crypto.createDecipheriv(
this.ALGORITHM,
key,
iv
)
decipher.setAuthTag(tag)
let decrypted = decipher.update(encryptedData.encrypted, 'base64', 'utf8')
decrypted += decipher.final('utf8')
return decrypted
}
}
Secure Team Sharing
Public-key cryptography for team vaults:
// Team sharing with public-key cryptography
import { generateKeyPairSync, publicEncrypt, privateDecrypt } from 'crypto'
class TeamSharingService {
async shareVaultItem(
itemId: string,
recipientUserId: string,
permissions: Permissions
) {
// Get recipient's public key
const recipient = await this.getUser(recipientUserId)
const recipientPublicKey = this.parsePublicKey(recipient.publicKey)
// Get vault item encryption key
const itemKey = await this.getItemKey(itemId)
// Encrypt item key with recipient's public key
const encryptedKey = publicEncrypt(
recipientPublicKey,
Buffer.from(itemKey)
)
// Store share record (encrypted key + permissions)
await db.shares.create({
itemId,
recipientUserId,
encryptedKey: encryptedKey.toString('base64'),
permissions,
createdAt: new Date(),
})
// Log share action
await this.auditLogger.logShare(itemId, recipientUserId, permissions)
}
async accessSharedItem(userId: string, shareId: string): Promise<VaultItem> {
// Get share record
const share = await db.shares.findOne({ where: { id: shareId } })
// Verify user has access
if (share.recipientUserId !== userId) {
throw new Error('Unauthorized')
}
// Get user's private key
const user = await this.getUser(userId)
const privateKey = await this.decryptPrivateKey(
user.encryptedPrivateKey,
user.masterPasswordHash
)
// Decrypt item key
const itemKey = privateDecrypt(
privateKey,
Buffer.from(share.encryptedKey, 'base64')
)
// Get encrypted item
const encryptedItem = await db.vaultItems.findOne({
where: { id: share.itemId },
})
// Decrypt item (client-side)
return await this.encryptionService.decrypt(encryptedItem, itemKey)
}
}
Browser Extension Security
Secure browser extension implementation:
// Browser extension content script (secure)
class SecureVaultExtension {
private apiClient: SecureAPIClient
async fillPassword(domain: string): Promise<void> {
// Get encrypted vault from server
const encryptedVault = await this.apiClient.getVault()
// Decrypt client-side (user enters master password)
const masterPassword = await this.promptMasterPassword()
const vault = await this.encryptionService.decryptVault(
encryptedVault,
masterPassword
)
// Find matching password
const item = vault.items.find((item) =>
item.domain === domain
)
if (!item) return
// Fill password securely (no plaintext in DOM)
await this.fillFormField(item.password, { secure: true })
// Clear master password from memory
this.clearMemory(masterPassword)
}
// Content Security Policy
private getCSP(): string {
return `
default-src 'self';
script-src 'self' 'unsafe-inline';
connect-src https://api.securevault.io;
style-src 'self' 'unsafe-inline';
`
}
}
Master Password Verification
Secure password verification without transmitting password:
// Zero-knowledge password verification
class AuthenticationService {
async verifyMasterPassword(
username: string,
masterPassword: string
): Promise<boolean> {
// Get user's salt and verification hash
const user = await db.users.findOne({ where: { username } })
// Derive key from master password (client-side)
const derivedKey = await this.encryptionService.deriveKey(
masterPassword,
Buffer.from(user.salt, 'base64')
)
// Create verification hash (client-side)
const verificationHash = await this.createVerificationHash(derivedKey)
// Compare with stored hash (constant-time comparison)
return this.constantTimeCompare(
verificationHash,
user.verificationHash
)
}
private constantTimeCompare(a: string, b: string): boolean {
if (a.length !== b.length) return false
let result = 0
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i)
}
return result === 0
}
}
Security Measures
1. Encryption
- AES-256-GCM for symmetric encryption
- RSA-4096 for asymmetric encryption
- Argon2 for key derivation
- All encryption client-side only
2. Zero-Knowledge Architecture
- Server never sees plaintext passwords
- Master password never transmitted
- Keys derived client-side only
- Encrypted blobs stored on server
3. Access Control
- Multi-factor authentication (2FA/TOTP)
- Biometric authentication on mobile
- Session management with secure tokens
- Device management and revocation
4. Compliance
- SOC 2 Type II certified
- Regular security audits
- Penetration testing
- Bug bounty program
Design Targets
Goals the design is built around — not production results:
- Zero-knowledge guarantee: the server stores only ciphertext and can never derive plaintext
- Recoverability vs. secrecy: an honest stance that "we can't read your data" also means "we can't recover it for you"
- Responsiveness: client-side crypto stays fast even on large vaults
- Secure sharing: team sharing without ever exposing plaintext to the server
Failure Modes
For a zero-knowledge system, the failure modes are mostly about what the design deliberately gives up:
- Master password loss = data loss. By design, there's no backdoor. Mitigation: recovery keys / account recovery that preserve the zero-knowledge property, plus clear user communication.
- Weak master passwords. The whole model rests on this secret. Mitigation: strong KDF (high-cost Argon2/PBKDF2), strength enforcement, and optional hardware-key wrapping.
- Metadata leakage. Even if contents are encrypted, item counts, sizes, and timing can leak. Mitigation: padding, batching, and minimizing server-visible structure.
- Client-side crypto cost on large vaults. Decrypting everything upfront stalls the UI. Mitigation: lazy, per-item decryption and a key hierarchy so only needed items are unlocked.
- Sync conflicts across devices. Encrypted blobs are hard to merge. Mitigation: per-item versioning with explicit, user-visible conflict resolution.
Key Learnings
1. Security Must Be Built-In
Zero-knowledge architecture required security to be fundamental to the design, not added later.
2. Client-Side Encryption is Complex
Managing encryption keys and ensuring security across multiple platforms was challenging.
3. User Experience Matters
Even with strict security, the product must be easy to use or users will find alternatives.
4. Compliance is Ongoing
SOC 2 Type II requires continuous monitoring and regular audits.
5. Trust is Earned
Building trust in a password manager takes time and requires transparency about security practices.
Future Improvements
- Hardware Security Keys: FIDO2/WebAuthn support
- Biometric Authentication: Enhanced mobile security
- Password Health Scoring: AI-powered password strength analysis
- Dark Web Monitoring: Breach detection and alerts
Conclusion
SecureVault is a design study in zero-knowledge architecture — building a server that's useful without ever being able to read user data. The interesting tension is deliberate: a system that genuinely can't read your data also can't recover it for you, and every design decision flows from owning that tradeoff honestly. The threat model drives the data model.
Technologies Used: TypeScript, React, Electron, Node.js, NestJS, PostgreSQL, AWS (KMS, S3), Web Extensions API, Crypto APIs, Docker
Format: Reference architecture / systems design study
Status: Conceptual design — figures are illustrative targets, not production results