1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 02:03:39 +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:
Jake Fink
2024-02-23 08:48:15 -05:00
committed by GitHub
parent 071959317c
commit 19a373d87e
27 changed files with 401 additions and 149 deletions

View File

@@ -0,0 +1,85 @@
import { KdfConfig } from "../../auth/models/domain/kdf-config";
import { CsprngArray } from "../../types/csprng";
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "../abstractions/key-generation.service";
import {
ARGON2_ITERATIONS,
ARGON2_MEMORY,
ARGON2_PARALLELISM,
KdfType,
PBKDF2_ITERATIONS,
} from "../enums";
import { Utils } from "../misc/utils";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
export class KeyGenerationService implements KeyGenerationServiceAbstraction {
constructor(private cryptoFunctionService: CryptoFunctionService) {}
async createKey(bitLength: 256 | 512): Promise<SymmetricCryptoKey> {
const key = await this.cryptoFunctionService.aesGenerateKey(bitLength);
return new SymmetricCryptoKey(key);
}
async createKeyWithPurpose(
bitLength: 128 | 192 | 256 | 512,
purpose: string,
salt?: string,
): Promise<{ salt: string; material: CsprngArray; derivedKey: SymmetricCryptoKey }> {
if (salt == null) {
const bytes = await this.cryptoFunctionService.randomBytes(32);
salt = Utils.fromBufferToUtf8(bytes);
}
const material = await this.cryptoFunctionService.aesGenerateKey(bitLength);
const key = await this.cryptoFunctionService.hkdf(material, salt, purpose, 64, "sha256");
return { salt, material, derivedKey: new SymmetricCryptoKey(key) };
}
async deriveKeyFromMaterial(
material: CsprngArray,
salt: string,
purpose: string,
): Promise<SymmetricCryptoKey> {
const key = await this.cryptoFunctionService.hkdf(material, salt, purpose, 64, "sha256");
return new SymmetricCryptoKey(key);
}
async deriveKeyFromPassword(
password: string | Uint8Array,
salt: string | Uint8Array,
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);
}
}