mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[PM-6211] Create key generation service (#7939)
* create key generation service * replace old key generation service and add references * use key generation service in key connector service * use key generation service in send service * user key generation service in access service * use key generation service in device trust service * fix tests * fix browser * add createKeyFromMaterial and tests * create ephemeral key * fix tests * rename method and add returns docs * ignore material in destructure * modify test * specify material as key material * pull out magic strings to properties * make salt optional and generate if not provided * fix test * fix parameters * update docs to include link to HKDF rfc
This commit is contained in:
@@ -9,6 +9,7 @@ import { AccountService } from "../../auth/abstractions/account.service";
|
||||
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
|
||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { CsprngArray } from "../../types/csprng";
|
||||
import { OrganizationId, ProviderId, UserId } from "../../types/guid";
|
||||
import {
|
||||
OrgKey,
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service";
|
||||
import { EncryptService } from "../abstractions/encrypt.service";
|
||||
import { KeyGenerationService } from "../abstractions/key-generation.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platform-utils.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
@@ -80,6 +82,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
readonly everHadUserKey$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
protected keyGenerationService: KeyGenerationService,
|
||||
protected cryptoFunctionService: CryptoFunctionService,
|
||||
protected encryptService: EncryptService,
|
||||
protected platformUtilService: PlatformUtilsService,
|
||||
@@ -219,8 +222,8 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
throw new Error("No Master Key found.");
|
||||
}
|
||||
|
||||
const newUserKey = await this.cryptoFunctionService.aesGenerateKey(512);
|
||||
return this.buildProtectedSymmetricKey(masterKey, newUserKey);
|
||||
const newUserKey = await this.keyGenerationService.createKey(512);
|
||||
return this.buildProtectedSymmetricKey(masterKey, newUserKey.key);
|
||||
}
|
||||
|
||||
async clearUserKey(clearStoredKeys = true, userId?: UserId): Promise<void> {
|
||||
@@ -294,7 +297,12 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
kdf: KdfType,
|
||||
KdfConfig: KdfConfig,
|
||||
): Promise<MasterKey> {
|
||||
return (await this.makeKey(password, email, kdf, KdfConfig)) as MasterKey;
|
||||
return (await this.keyGenerationService.deriveKeyFromPassword(
|
||||
password,
|
||||
email,
|
||||
kdf,
|
||||
KdfConfig,
|
||||
)) as MasterKey;
|
||||
}
|
||||
|
||||
async clearMasterKey(userId?: UserId): Promise<void> {
|
||||
@@ -452,8 +460,8 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
throw new Error("No key provided");
|
||||
}
|
||||
|
||||
const newSymKey = await this.cryptoFunctionService.aesGenerateKey(512);
|
||||
return this.buildProtectedSymmetricKey(key, newSymKey);
|
||||
const newSymKey = await this.keyGenerationService.createKey(512);
|
||||
return this.buildProtectedSymmetricKey(key, newSymKey.key);
|
||||
}
|
||||
|
||||
async clearOrgKeys(memoryOnly?: boolean, userId?: UserId): Promise<void> {
|
||||
@@ -522,10 +530,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async makeOrgKey<T extends OrgKey | ProviderKey>(): Promise<[EncString, T]> {
|
||||
const shareKey = await this.cryptoFunctionService.aesGenerateKey(512);
|
||||
const shareKey = await this.keyGenerationService.createKey(512);
|
||||
const publicKey = await this.getPublicKey();
|
||||
const encShareKey = await this.rsaEncrypt(shareKey, publicKey);
|
||||
return [encShareKey, new SymmetricCryptoKey(shareKey) as T];
|
||||
const encShareKey = await this.rsaEncrypt(shareKey.key, publicKey);
|
||||
return [encShareKey, shareKey as T];
|
||||
}
|
||||
|
||||
async setPrivateKey(encPrivateKey: EncryptedString): Promise<void> {
|
||||
@@ -588,7 +596,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async makePinKey(pin: string, salt: string, kdf: KdfType, kdfConfig: KdfConfig): Promise<PinKey> {
|
||||
const pinKey = await this.makeKey(pin, salt, kdf, kdfConfig);
|
||||
const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdf, kdfConfig);
|
||||
return (await this.stretchKey(pinKey)) as PinKey;
|
||||
}
|
||||
|
||||
@@ -636,20 +644,16 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return new SymmetricCryptoKey(masterKey) as MasterKey;
|
||||
}
|
||||
|
||||
async makeSendKey(keyMaterial: Uint8Array): Promise<SymmetricCryptoKey> {
|
||||
const sendKey = await this.cryptoFunctionService.hkdf(
|
||||
async makeSendKey(keyMaterial: CsprngArray): Promise<SymmetricCryptoKey> {
|
||||
return await this.keyGenerationService.deriveKeyFromMaterial(
|
||||
keyMaterial,
|
||||
"bitwarden-send",
|
||||
"send",
|
||||
64,
|
||||
"sha256",
|
||||
);
|
||||
return new SymmetricCryptoKey(sendKey);
|
||||
}
|
||||
|
||||
async makeCipherKey(): Promise<CipherKey> {
|
||||
const randomBytes = await this.cryptoFunctionService.aesGenerateKey(512);
|
||||
return new SymmetricCryptoKey(randomBytes) as CipherKey;
|
||||
return (await this.keyGenerationService.createKey(512)) as CipherKey;
|
||||
}
|
||||
|
||||
async clearKeys(userId?: UserId): Promise<any> {
|
||||
@@ -802,8 +806,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
publicKey: string;
|
||||
privateKey: EncString;
|
||||
}> {
|
||||
const rawKey = await this.cryptoFunctionService.aesGenerateKey(512);
|
||||
const userKey = new SymmetricCryptoKey(rawKey) as UserKey;
|
||||
const userKey = (await this.keyGenerationService.createKey(512)) as UserKey;
|
||||
const [publicKey, privateKey] = await this.makeKeyPair(userKey);
|
||||
await this.setUserKey(userKey);
|
||||
await this.activeUserEncryptedPrivateKeyState.update(() => privateKey.encryptedString);
|
||||
@@ -986,46 +989,6 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return [new SymmetricCryptoKey(newSymKey) as T, protectedSymKey];
|
||||
}
|
||||
|
||||
private async makeKey(
|
||||
password: string,
|
||||
salt: string,
|
||||
kdf: KdfType,
|
||||
kdfConfig: KdfConfig,
|
||||
): Promise<SymmetricCryptoKey> {
|
||||
let key: Uint8Array = null;
|
||||
if (kdf == null || kdf === KdfType.PBKDF2_SHA256) {
|
||||
if (kdfConfig.iterations == null) {
|
||||
kdfConfig.iterations = PBKDF2_ITERATIONS.defaultValue;
|
||||
}
|
||||
|
||||
key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfConfig.iterations);
|
||||
} else if (kdf == KdfType.Argon2id) {
|
||||
if (kdfConfig.iterations == null) {
|
||||
kdfConfig.iterations = ARGON2_ITERATIONS.defaultValue;
|
||||
}
|
||||
|
||||
if (kdfConfig.memory == null) {
|
||||
kdfConfig.memory = ARGON2_MEMORY.defaultValue;
|
||||
}
|
||||
|
||||
if (kdfConfig.parallelism == null) {
|
||||
kdfConfig.parallelism = ARGON2_PARALLELISM.defaultValue;
|
||||
}
|
||||
|
||||
const saltHash = await this.cryptoFunctionService.hash(salt, "sha256");
|
||||
key = await this.cryptoFunctionService.argon2(
|
||||
password,
|
||||
saltHash,
|
||||
kdfConfig.iterations,
|
||||
kdfConfig.memory * 1024, // convert to KiB from MiB
|
||||
kdfConfig.parallelism,
|
||||
);
|
||||
} else {
|
||||
throw new Error("Unknown Kdf.");
|
||||
}
|
||||
return new SymmetricCryptoKey(key);
|
||||
}
|
||||
|
||||
// --LEGACY METHODS--
|
||||
// We previously used the master key for additional keys, but now we use the user key.
|
||||
// These methods support migrating the old keys to the new ones.
|
||||
|
||||
Reference in New Issue
Block a user