1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 01:03:35 +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

@@ -5,11 +5,11 @@ import { CryptoFunctionService } from "../../platform/abstractions/crypto-functi
import { CryptoService } from "../../platform/abstractions/crypto.service";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { StateService } from "../../platform/abstractions/state.service";
import { EncString } from "../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "../../types/csprng";
import { UserKey, DeviceKey } from "../../types/key";
import { DeviceTrustCryptoServiceAbstraction } from "../abstractions/device-trust-crypto.service.abstraction";
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
@@ -22,6 +22,7 @@ import {
export class DeviceTrustCryptoService implements DeviceTrustCryptoServiceAbstraction {
constructor(
private keyGenerationService: KeyGenerationService,
private cryptoFunctionService: CryptoFunctionService,
private cryptoService: CryptoService,
private encryptService: EncryptService,
@@ -165,10 +166,7 @@ export class DeviceTrustCryptoService implements DeviceTrustCryptoServiceAbstrac
private async makeDeviceKey(): Promise<DeviceKey> {
// Create 512-bit device key
const randomBytes: CsprngArray = await this.cryptoFunctionService.aesGenerateKey(512);
const deviceKey = new SymmetricCryptoKey(randomBytes) as DeviceKey;
return deviceKey;
return (await this.keyGenerationService.createKey(512)) as DeviceKey;
}
async decryptUserKeyWithDeviceKey(

View File

@@ -7,6 +7,7 @@ import { CryptoFunctionService } from "../../platform/abstractions/crypto-functi
import { CryptoService } from "../../platform/abstractions/crypto.service";
import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { StateService } from "../../platform/abstractions/state.service";
import { EncryptionType } from "../../platform/enums/encryption-type.enum";
@@ -24,6 +25,7 @@ import { DeviceTrustCryptoService } from "./device-trust-crypto.service.implemen
describe("deviceTrustCryptoService", () => {
let deviceTrustCryptoService: DeviceTrustCryptoService;
const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>();
const cryptoService = mock<CryptoService>();
const encryptService = mock<EncryptService>();
@@ -37,6 +39,7 @@ describe("deviceTrustCryptoService", () => {
jest.clearAllMocks();
deviceTrustCryptoService = new DeviceTrustCryptoService(
keyGenerationService,
cryptoFunctionService,
cryptoService,
encryptService,
@@ -166,17 +169,18 @@ describe("deviceTrustCryptoService", () => {
describe("makeDeviceKey", () => {
it("creates a new non-null 64 byte device key, securely stores it, and returns it", async () => {
const mockRandomBytes = new Uint8Array(deviceKeyBytesLength) as CsprngArray;
const mockDeviceKey = new SymmetricCryptoKey(mockRandomBytes) as DeviceKey;
const cryptoFuncSvcGenerateKeySpy = jest
.spyOn(cryptoFunctionService, "aesGenerateKey")
.mockResolvedValue(mockRandomBytes);
const keyGenSvcGenerateKeySpy = jest
.spyOn(keyGenerationService, "createKey")
.mockResolvedValue(mockDeviceKey);
// TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests
const deviceKey = await (deviceTrustCryptoService as any).makeDeviceKey();
expect(cryptoFuncSvcGenerateKeySpy).toHaveBeenCalledTimes(1);
expect(cryptoFuncSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8);
expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledTimes(1);
expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8);
expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey);

View File

@@ -2,8 +2,8 @@ import { ApiService } from "../../abstractions/api.service";
import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationUserType } from "../../admin-console/enums";
import { KeysRequest } from "../../models/request/keys.request";
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
import { CryptoService } from "../../platform/abstractions/crypto.service";
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
import { LogService } from "../../platform/abstractions/log.service";
import { StateService } from "../../platform/abstractions/state.service";
import { Utils } from "../../platform/misc/utils";
@@ -24,7 +24,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
private tokenService: TokenService,
private logService: LogService,
private organizationService: OrganizationService,
private cryptoFunctionService: CryptoFunctionService,
private keyGenerationService: KeyGenerationService,
private logoutCallback: (expired: boolean, userId?: string) => Promise<void>,
) {}
@@ -94,11 +94,11 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
keyConnectorUrl: legacyKeyConnectorUrl,
userDecryptionOptions,
} = tokenResponse;
const password = await this.cryptoFunctionService.aesGenerateKey(512);
const password = await this.keyGenerationService.createKey(512);
const kdfConfig = new KdfConfig(kdfIterations, kdfMemory, kdfParallelism);
const masterKey = await this.cryptoService.makeMasterKey(
Utils.fromBufferToB64(password),
password.keyB64,
await this.tokenService.getEmail(),
kdf,
kdfConfig,