Key hierarchy
Passwork's cryptographic model is built on a key hierarchy, where each level protects the next.
Hierarchy overview
Master password
↓ (PBKDF2)
User master key
↓ (AES-256-CBC)
Private RSA key
↓ (RSA-OAEP)
Vault key
↓ (AES-256-CBC)
Record key
↓ (AES-256-CBC)
├── Record data
└── Attachment keys
↓ (AES-256-CBC)
File contents
Complete key scheme
Level 0: User login
User enters the master password. Salt (20 characters) is retrieved from the server. PBKDF2 is executed on the client (300,000 iterations, SHA-256).
Level 1: User master key
PBKDF2 result is the master key (512 bits). It is used for two purposes:
- Computing SHA-256 hash to send to the server (password verification)
- Decrypting the user's private RSA key (AES-256-CBC)
Level 2: RSA keys
Private RSA key (2048 bits) is stored on the server in encrypted form. Public RSA key is stored openly. The private key is used to decrypt vault keys (RSA-OAEP).
Level 3: Vault key
Vault key (256 bits) is stored as a separate copy for each user with access. Each copy is encrypted with the corresponding user's public RSA key.
Level 4: Record key
Record key (256 bits) is encrypted with the vault key. It is used to encrypt: password field, custom fields, TOTP secret, attachment keys.
Level 5: Attachment keys
Each file has its own attachment key (256 bits). The attachment key is encrypted with the record key. File contents are encrypted with the attachment key.
Alternative branch (Inbox section)
When a record is transferred directly to a user (bypassing the vault), the record key is encrypted with the recipient's public RSA key directly.
Detailed key descriptions
Level 0: Master password
The master password is a secret known only to the user.
| Parameter | Value |
|---|---|
| Minimum length | 12 characters |
| Storage | Never stored |
| Transmission | Never transmitted to server |
| Usage | Entered at each login (or restored from localStorage) |
Level 1: User master key
The master key is derived from the master password using PBKDF2.
| Parameter | Value |
|---|---|
| Algorithm | PBKDF2 |
| Hash function | SHA-256 |
| Iterations | 300,000 |
| Key length | 512 bits |
| Salt | 20 characters, unique per user |
| Salt alphabet | A-Z, a-z, 0-9, @, ! (64 characters) |
| Library | pbkdf2 (npm) |
Formula:
Master key = PBKDF2(master password, salt, 300000, 64, SHA-256)
Master key usage:
- Decrypting user's private RSA key (AES-256-CBC)
- Computing hash for server verification (SHA-256)
Level 2: User RSA keys
Asymmetric key pair for secure symmetric key exchange.
| Parameter | Value |
|---|---|
| Algorithm | RSA-OAEP |
| Modulus length | 2048 bits |
| Public exponent | 65537 (0x010001) |
| Hash function | SHA-256 |
| Storage format | PEM (PKCS#8 for private, SPKI for public) |
| Library | WebCrypto API (crypto.subtle) |
Public key:
- Stored on server in plaintext
- Used to encrypt vault keys when granting access
Private key:
- Stored on server in encrypted form
- Encrypted with user's master key (AES-256-CBC)
- Decrypted locally after master password entry
RSA key generation: WebCrypto API generates the key pair. The public key is immediately sent to the server. The private key is encrypted with the master key via AES-256-CBC and sent to the server in encrypted form.
Level 3: Vault key
The vault key is a symmetric key protecting all records inside the vault.
| Parameter | Value |
|---|---|
| Type | Symmetric (AES-256-CBC) |
| Length | 100 characters |
| Alphabet | A-Z, a-z, 0-9, @, ! (64 characters) |
| Input entropy | ~596 bits |
| Effective key | 256 bits (for AES-256) |
| Generation | On client when creating vault |
| Generator | crypto.getRandomValues() (browser CSPRNG) |
Key transformation: The 100-character string is converted to a 256-bit AES key via a key derivation function (KDF). During encryption, a random 64-bit salt is generated, which together with the original key is used to derive the final 256-bit key. The salt is saved with the ciphertext.
Storage: A separate copy of the vault key is created for each user with access. Each copy is encrypted with the corresponding user's public RSA key (RSA-OAEP). All copies are stored on the server. Each user can only decrypt their own copy with their private RSA key.
Level 4: Record key
The record key is a unique key for encrypting the contents of one record. Generated individually for each record.
| Parameter | Value |
|---|---|
| Type | Symmetric |
| Length | 100 characters |
| Alphabet | A-Z, a-z, 0-9, @, ! (64 characters) |
| Input entropy | ~596 bits |
| Effective key | 256 bits (for AES-256) |
| Generation | On client when creating record |
| Generator | crypto.getRandomValues() (browser CSPRNG) |
| Storage | Encrypted with vault key (AES-256-CBC) |
Key transformation: The 100-character string is converted to a 256-bit AES key via a key derivation function (KDF). During encryption, a random 64-bit salt is generated, which together with the original key is used to derive the final 256-bit key. The salt is saved with the ciphertext.
- Unification — single format for all symmetric keys (vault, record, attachment)
- Redundant entropy — 596 input bits ensure maximum strength of the 256-bit output key
- Future-proofing — switching to longer keys won't require changing generators
- Collision protection — probability of key collision is practically zero
What is encrypted with the record key (AES-256-CBC):
- Password field
- All custom fields (name, value, type)
- TOTP secret
- Attachment keys
- Record revisions (previous values)
Initialization Vector (IV): A unique 128-bit IV is automatically generated for each encryption operation. The IV is included in the encryption result.
Alternative path (Inbox section): When a record is transferred directly to a user (without vault access), the record key is encrypted with the recipient's public RSA key.
Level 5: Attachment key
The attachment key is a unique key for encrypting one file.
| Parameter | Value |
|---|---|
| Type | Symmetric |
| Length | 100 characters |
| Alphabet | A-Z, a-z, 0-9, @, ! (64 characters) |
| Input entropy | ~596 bits |
| Effective key | 256 bits (for AES-256) |
| Generation | On client when uploading file |
| Generator | crypto.getRandomValues() (browser CSPRNG) |
| Storage | Encrypted with record key (AES-256-CBC) |
Key transformation: Like vault and record keys, the 100-character string is converted to a 256-bit AES key via KDF with a 64-bit salt.
File encryption process:
- Random attachment key (256 bits) is generated
- File is encrypted with this key (AES-256-CBC)
- Attachment key is encrypted with the record key
- Both encrypted objects are sent to the server
Decryption chain
Decryption sequence when accessing a record:
- User enters master password
- Client requests salt and PBKDF2 parameters from server
- Client computes master key:
PBKDF2(password, salt, 300000, 64, SHA-256) - Client computes hash:
SHA-256(master key) - Client sends hash to server for verification
- Server verifies hash and returns encrypted private RSA key
- Client decrypts RSA key with master key (AES-256-CBC)
- Client requests vault data
- Server returns encrypted vault key and records
- Client decrypts vault key with private RSA key (WebCrypto RSA-OAEP)
- Client decrypts record key with vault key (AES-256-CBC)
- Client decrypts record fields with record key (AES-256-CBC)
- (If needed) Client decrypts attachment key → decrypts file
Key characteristics table
| Key | Algorithm | Length | Entropy | Where generated | Where stored |
|---|---|---|---|---|---|
| Master password | — | ≥12 characters | Depends on user | User | Nowhere |
| PBKDF2 salt | — | 20 characters | ~120 bits | Server | Server (open) |
| Master key | PBKDF2-SHA256 | 512 bits | 512 bits | Client | Nowhere (in memory) |
| RSA public | RSA-OAEP | 2048 bits | — | Client | Server (open) |
| RSA private | RSA-OAEP | 2048 bits | — | Client | Server (encrypted) |
| Vault key | AES-256-CBC | 100 chars → 256 bits | ~596 bits input | Client | Server (RSA encrypted) |
| Record key | AES-256-CBC | 100 chars → 256 bits | ~596 bits input | Client | Server (encrypted) |
| Attachment key | AES-256-CBC | 100 chars → 256 bits | ~596 bits input | Client | Server (encrypted) |
| Server key | AES-256-CFB | 256 bits | 256 bits | Server | File on server |
Hierarchy security principles
Level isolation
Compromising a key at one level does not reveal keys at other levels:
- Leaking a record key does not reveal other records
- Leaking a vault key does not reveal records in other vaults
- Leaking a private RSA key requires knowing the master key
Minimized storage
- Master password and master key are not stored permanently
- Private RSA key is stored only in encrypted form
- Record and attachment keys are stored only in encrypted form
Key uniqueness
- Each user has a unique RSA key pair
- Each vault has a unique key
- Each record has a unique key
- Each attachment has a unique key
Zero-Knowledge
The server stores only:
- Encrypted data
- Encrypted keys
- Public keys
- Hashes for verification (that don't allow recovering the secret)
The server cannot decrypt user data.