Overview
The KeyService manages all cryptographic keys in Bitwarden, including user keys, organization keys, provider keys, and key pairs. It handles key generation, derivation, storage, and rotation.
Location
libs/key-management/src/abstractions/key.service.ts
Interface
User Key Management
userKey$()
Returns an observable stream of the user’s encryption key.
abstract userKey$(userId: UserId): Observable<UserKey | null>;
Parameters:
Returns: Observable<UserKey | null> - Stream of user keys (null if user is locked/logged out)
Example:
const userKey$ = keyService.userKey$(userId);
userKey$.subscribe(key => {
if (key) {
// User is unlocked
} else {
// User is locked or logged out
}
});
getUserKey()
Retrieves the user’s encryption key.
abstract getUserKey(userId?: string): Promise<UserKey | null>;
Parameters:
userId - Optional user ID
Returns: Promise<UserKey | null> - The user key or null
Deprecated. Use userKey$() with a required UserId instead.
setUserKey()
Sets the user key and stores any additional versions (auto, biometrics, pin).
abstract setUserKey(key: UserKey, userId: UserId): Promise<void>;
Parameters:
key - The user key to set
userId - The user ID
Throws: Error when key or userId is null. Lock the account to clear a key.
hasUserKey()
Checks if a user key is available in memory.
abstract hasUserKey(userId: UserId): Promise<boolean>;
Parameters:
Returns: Promise<boolean> - True if user key is available
clearStoredUserKey()
Clears the stored user key from storage.
abstract clearStoredUserKey(userId: string): Promise<void>;
Parameters:
Throws: Error when userId is null or undefined
Key Generation
makeUserKey()
Generates a new user key and encrypts it with the master key.
abstract makeUserKey(masterKey: MasterKey): Promise<[UserKey, EncString]>;
Parameters:
masterKey - The user’s master key
Returns: Promise<[UserKey, EncString]> - Tuple of new user key and encrypted version
Throws: Error when master key is null or undefined
Deprecated. Interacting with the master key directly is prohibited. For new features, use SDK methods for user cryptography initialization or contact the KM team.
makeMasterKey()
Derives a master key from password using KDF.
abstract makeMasterKey(
password: string,
email: string,
kdfConfig: KdfConfig
): Promise<MasterKey>;
Parameters:
password - The user’s master password
email - The user’s email address
kdfConfig - Key derivation function configuration
Returns: Promise<MasterKey> - Derived master key
Deprecated. Interacting with the master key directly is prohibited.
makeCipherKey()
Generates a new cipher encryption key.
abstract makeCipherKey(): Promise<CipherKey>;
Returns: Promise<CipherKey> - A new cipher key
Example:
const cipherKey = await keyService.makeCipherKey();
makeDataEncKey()
Generates a new data encryption key and wraps it with the provided key.
abstract makeDataEncKey<T extends UserKey | OrgKey>(
key: T,
): Promise<[SymmetricCryptoKey, EncString]>;
Parameters:
key - User key or organization key to wrap with
Returns: Promise<[SymmetricCryptoKey, EncString]> - New key and wrapped version
Deprecated. Do not use this for new code / new cryptographic designs.
makeSendKey()
Creates a Send encryption key from key material.
abstract makeSendKey(keyMaterial: Uint8Array): Promise<SymmetricCryptoKey>;
Parameters:
keyMaterial - Key material to derive from
Returns: Promise<SymmetricCryptoKey> - Send key
Asymmetric Key Operations
makeKeyPair()
Generates a new RSA key pair.
abstract makeKeyPair(key: SymmetricCryptoKey): Promise<[string, EncString]>;
Parameters:
key - Symmetric key to wrap the private key with
Returns: Promise<[string, EncString]> - Tuple of [publicKey, wrappedPrivateKey]
Throws: Error if the provided key is null
Deprecated. New use-cases should be done in the SDK. Contact the Key Management team.
makeOrgKey()
Creates a new organization key encrypted with the user’s public key.
abstract makeOrgKey<T extends OrgKey | ProviderKey>(
userId: UserId
): Promise<[EncString, T]>;
Parameters:
userId - User ID for public key lookup
Returns: Promise<[EncString, T]> - Encrypted org/provider key and decrypted key
Throws:
Error when userId is null or undefined
Error when no public key is found for the user
userPrivateKey$()
Returns an observable of the user’s decrypted private key.
abstract userPrivateKey$(userId: UserId): Observable<UserPrivateKey | null>;
Parameters:
Returns: Observable<UserPrivateKey | null> - Stream of private keys
userPublicKey$()
Returns an observable of the user’s public key.
abstract userPublicKey$(userId: UserId): Observable<Uint8Array | null>;
Parameters:
Returns: Observable<Uint8Array | null> - Stream of public keys
userEncryptionKeyPair$()
Returns an observable of the user’s key pair (guaranteed to be consistent).
abstract userEncryptionKeyPair$(
userId: UserId,
): Observable<{ privateKey: UserPrivateKey; publicKey: UserPublicKey } | null>;
Parameters:
Returns: Observable<{privateKey, publicKey} | null> - Key pair or null
getFingerprint()
Generates a fingerprint phrase for a public key.
abstract getFingerprint(
fingerprintMaterial: string,
publicKey: Uint8Array
): Promise<string[]>;
Parameters:
fingerprintMaterial - Material to include in fingerprint
publicKey - The public key
Returns: Promise<string[]> - Array of fingerprint words
Throws: Error when publicKey is null or undefined
Master Key Operations
getOrDeriveMasterKey()
Retrieves or derives the master key from a password.
abstract getOrDeriveMasterKey(
password: string,
userId: UserId
): Promise<MasterKey>;
Parameters:
password - The master password
userId - The user ID
Returns: Promise<MasterKey> - The master key
Throws:
Error when userId is null/undefined
Error when email or KDF config cannot be found
Deprecated. Use a high-level function from MasterPasswordService instead.
encryptUserKeyWithMasterKey()
Encrypts the user key with the master key.
abstract encryptUserKeyWithMasterKey(
masterKey: MasterKey,
userKey: UserKey,
): Promise<[UserKey, EncString]>;
Parameters:
masterKey - The master key
userKey - The user key to encrypt
Returns: Promise<[UserKey, EncString]> - User key and encrypted version
Throws: Error when userKey or masterKey is null/undefined
Deprecated. Use a high-level function from MasterPasswordService instead.
hashMasterKey()
Creates a master password hash for authentication.
abstract hashMasterKey(
password: string,
key: MasterKey,
hashPurpose?: HashPurpose,
): Promise<string>;
Parameters:
password - The master password
key - The master key
hashPurpose - Hash purpose (defaults to HashPurpose.ServerAuthorization)
Returns: Promise<string> - Password hash
Hash Purposes:
enum HashPurpose {
ServerAuthorization = 1, // For server authentication
LocalAuthorization = 2, // For local authentication
}
Deprecated. Use a high-level function from MasterPasswordService instead.
Organization Key Management
setOrgKeys()
Stores encrypted organization keys.
abstract setOrgKeys(
orgs: ProfileOrganizationResponse[],
providerOrgs: ProfileProviderOrganizationResponse[],
userId: UserId,
): Promise<void>;
Parameters:
orgs - Organization data
providerOrgs - Provider organization data
userId - The user ID
getOrgKey()
Retrieves an organization’s symmetric key.
abstract getOrgKey(orgId: string): Promise<OrgKey | null>;
Parameters:
orgId - The organization ID
Returns: Promise<OrgKey | null> - The organization key
Throws: Error when not active user
Deprecated. Use the observable userOrgKeys$ and map to the desired OrgKey instead.
orgKeys$()
Returns an observable of all organization keys for a user.
abstract orgKeys$(
userId: UserId
): Observable<Record<OrganizationId, OrgKey> | null>;
Parameters:
Returns: Observable<Record<OrganizationId, OrgKey> | null> - Map of org IDs to keys
Throws: Error if an invalid user ID is passed
Provider Key Management
setProviderKeys()
Stores provider keys for a user.
abstract setProviderKeys(
providers: ProfileProviderResponse[],
userId: UserId
): Promise<void>;
Parameters:
providers - Provider organization data
userId - The user ID
providerKeys$()
Returns an observable of provider keys.
abstract providerKeys$(
userId: UserId
): Observable<Record<ProviderId, ProviderKey> | null>;
Parameters:
Returns: Observable<Record<ProviderId, ProviderKey> | null> - Map of provider IDs to keys
Throws: Error if an invalid user ID is passed
Cipher Decryption Keys
cipherDecryptionKeys$()
Returns all keys needed for decrypting ciphers.
abstract cipherDecryptionKeys$(
userId: UserId,
legacySupport?: boolean,
): Observable<CipherDecryptionKeys | null>;
Parameters:
userId - The user ID
legacySupport - Support legacy key format (default: false)
Returns: Observable<CipherDecryptionKeys | null> - Decryption keys
Types:
type CipherDecryptionKeys = {
userKey: UserKey;
orgKeys: Record<OrganizationId, OrgKey> | null;
};
Throws: Error if an invalid user ID is passed
Account Initialization
initAccount()
Initializes all necessary crypto keys for a new account.
abstract initAccount(userId: UserId): Promise<{
userKey: UserKey;
publicKey: string;
privateKey: EncString;
}>;
Parameters:
Returns: Promise<{userKey, publicKey, privateKey}> - Newly created keys
Throws:
Error if userId is null or undefined
Error if user already has a user key
Deprecated. New cryptography initialization should be done in the SDK. See PM-21771.
Key Validation
validateUserKey()
Validates that a user key is correct for a given user.
abstract validateUserKey(key: UserKey, userId: UserId): Promise<boolean>;
Parameters:
key - The key to validate
userId - The user ID
Returns: Promise<boolean> - True if key is valid
Cleanup
clearKeys()
Clears all of the user’s keys from storage.
abstract clearKeys(userId: UserId): Promise<void>;
Parameters:
Throws: Error when userId is null or undefined
Key Types
All key types are branded types built on top of SymmetricCryptoKey or Uint8Array:
// Symmetric keys
type UserKey = Opaque<SymmetricCryptoKey, "UserKey">;
type MasterKey = Opaque<SymmetricCryptoKey, "MasterKey">;
type OrgKey = Opaque<SymmetricCryptoKey, "OrgKey">;
type ProviderKey = Opaque<SymmetricCryptoKey, "ProviderKey">;
type CipherKey = Opaque<SymmetricCryptoKey, "CipherKey">;
type DeviceKey = Opaque<SymmetricCryptoKey, "DeviceKey">;
type PrfKey = Opaque<SymmetricCryptoKey, "PrfKey">;
// Asymmetric keys
type UserPrivateKey = Opaque<Uint8Array, "UserPrivateKey">;
type UserPublicKey = Opaque<UnsignedPublicKey, "UserPublicKey">;
Location: libs/common/src/types/key.ts
KDF Configuration
Key Derivation Function (KDF) configurations control how master keys are derived from passwords.
KdfConfig
Union type for KDF configurations:
type KdfConfig = PBKDF2KdfConfig | Argon2KdfConfig;
PBKDF2KdfConfig
PBKDF2-SHA256 configuration:
class PBKDF2KdfConfig {
kdfType: KdfType.PBKDF2_SHA256;
iterations: number; // 600,000 - 2,000,000 (default: 600,000)
}
Example:
const kdfConfig = new PBKDF2KdfConfig(600_000);
Argon2KdfConfig
Argon2id configuration:
class Argon2KdfConfig {
kdfType: KdfType.Argon2id;
iterations: number; // 2-10 (default: 3)
memory: number; // 16-1024 MB (default: 64)
parallelism: number; // 1-16 (default: 4)
}
Example:
const kdfConfig = new Argon2KdfConfig(3, 64, 4);
KdfType
enum KdfType {
PBKDF2_SHA256 = 0,
Argon2id = 1,
}
Location: libs/key-management/src/models/kdf-config.ts
Usage Examples
Generating and Setting User Key
import { KeyService } from '@bitwarden/key-management/abstractions/key.service';
// Generate new user key with master key
const [userKey, encryptedUserKey] = await keyService.makeUserKey(masterKey);
// Set the user key
await keyService.setUserKey(userKey, userId);
Accessing User Keys
// Using observable (recommended)
keyService.userKey$(userId).subscribe(async userKey => {
if (userKey) {
// User is unlocked, can decrypt data
const data = await encryptService.decryptString(encrypted, userKey);
}
});
// Using promise (legacy)
const userKey = await keyService.getUserKey(userId);
Deriving Master Key
import { PBKDF2KdfConfig } from '@bitwarden/key-management/models/kdf-config';
const kdfConfig = new PBKDF2KdfConfig(600_000);
const masterKey = await keyService.makeMasterKey(
'user-password',
'[email protected]',
kdfConfig
);
Creating Organization Key
// Generate new org key
const [encryptedOrgKey, orgKey] = await keyService.makeOrgKey(userId);
// The orgKey can be used for encryption
// The encryptedOrgKey is stored/shared with members
Getting Cipher Decryption Keys
const keys$ = keyService.cipherDecryptionKeys$(userId);
keys$.subscribe(keys => {
if (keys) {
const { userKey, orgKeys } = keys;
// Decrypt personal ciphers with userKey
// Decrypt org ciphers with orgKeys[orgId]
}
});
Generating Fingerprint
const publicKey = await keyService.userPublicKey$(userId).toPromise();
const fingerprint = await keyService.getFingerprint(
'[email protected]',
publicKey
);
console.log('Fingerprint:', fingerprint.join('-'));
// Example output: "alligator-transfer-laziness-macaroni-blue"
Security Considerations
-
Master Key Deprecation: Direct interaction with master keys is deprecated. Use
MasterPasswordService for high-level operations.
-
Key Storage: User keys should be cleared from memory when the user locks their vault.
-
KDF Configuration: Use appropriate KDF parameters:
- PBKDF2: Minimum 600,000 iterations
- Argon2id: Minimum 2 iterations, 16 MB memory, 1 parallelism
-
Key Validation: Always validate keys before use with
validateUserKey().
-
Observable Cleanup: Subscribe to key observables carefully to avoid memory leaks.
-
SDK Migration: New cryptographic features should use SDK methods instead of these low-level functions.
-
Error Handling: Many methods throw errors. Always wrap in try-catch blocks.
-
User Context: Most operations require a
UserId. Never use keys from one user for another user’s data.