mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
[PM-17667] Move key-generation service to KM ownership (#16015)
* Move key-generation service * Update comment * Add deprecation comments * Fix firefox build * Update comment * Update DI import * Update module imports
This commit is contained in:
2
libs/common/src/key-management/crypto/index.ts
Normal file
2
libs/common/src/key-management/crypto/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { KeyGenerationService } from "./key-generation/key-generation.service";
|
||||
export { DefaultKeyGenerationService } from "./key-generation/default-key-generation.service";
|
||||
@@ -0,0 +1,94 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KdfConfig } from "@bitwarden/key-management";
|
||||
import { PureCrypto } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { SdkLoadService } from "../../../platform/abstractions/sdk/sdk-load.service";
|
||||
import { EncryptionType } from "../../../platform/enums";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { CsprngArray } from "../../../types/csprng";
|
||||
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
|
||||
|
||||
import { KeyGenerationService } from "./key-generation.service";
|
||||
|
||||
export class DefaultKeyGenerationService implements KeyGenerationService {
|
||||
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,
|
||||
kdfConfig: KdfConfig,
|
||||
): Promise<SymmetricCryptoKey> {
|
||||
if (typeof password === "string") {
|
||||
password = new TextEncoder().encode(password);
|
||||
}
|
||||
if (typeof salt === "string") {
|
||||
salt = new TextEncoder().encode(salt);
|
||||
}
|
||||
|
||||
await SdkLoadService.Ready;
|
||||
return new SymmetricCryptoKey(
|
||||
PureCrypto.derive_kdf_material(password, salt, kdfConfig.toSdkConfig()),
|
||||
);
|
||||
}
|
||||
|
||||
async stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {
|
||||
// The key to be stretched is actually usually the output of a KDF, and not actually meant for AesCbc256_B64 encryption,
|
||||
// but has the same key length. Only 256-bit key materials should be stretched.
|
||||
if (key.inner().type != EncryptionType.AesCbc256_B64) {
|
||||
throw new Error("Key passed into stretchKey is not a 256-bit key.");
|
||||
}
|
||||
|
||||
const newKey = new Uint8Array(64);
|
||||
// Master key and pin key are always 32 bytes
|
||||
const encKey = await this.cryptoFunctionService.hkdfExpand(
|
||||
key.inner().encryptionKey,
|
||||
"enc",
|
||||
32,
|
||||
"sha256",
|
||||
);
|
||||
const macKey = await this.cryptoFunctionService.hkdfExpand(
|
||||
key.inner().encryptionKey,
|
||||
"mac",
|
||||
32,
|
||||
"sha256",
|
||||
);
|
||||
|
||||
newKey.set(new Uint8Array(encKey));
|
||||
newKey.set(new Uint8Array(macKey), 32);
|
||||
|
||||
return new SymmetricCryptoKey(newKey);
|
||||
}
|
||||
}
|
||||
@@ -4,21 +4,21 @@ import { mock } from "jest-mock-extended";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { PBKDF2KdfConfig, Argon2KdfConfig } from "@bitwarden/key-management";
|
||||
|
||||
import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service";
|
||||
import { CsprngArray } from "../../types/csprng";
|
||||
import { SdkLoadService } from "../abstractions/sdk/sdk-load.service";
|
||||
import { EncryptionType } from "../enums";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
import { SdkLoadService } from "../../../platform/abstractions/sdk/sdk-load.service";
|
||||
import { EncryptionType } from "../../../platform/enums";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { CsprngArray } from "../../../types/csprng";
|
||||
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
|
||||
|
||||
import { KeyGenerationService } from "./key-generation.service";
|
||||
import { DefaultKeyGenerationService } from "./default-key-generation.service";
|
||||
|
||||
describe("KeyGenerationService", () => {
|
||||
let sut: KeyGenerationService;
|
||||
let sut: DefaultKeyGenerationService;
|
||||
|
||||
const cryptoFunctionService = mock<CryptoFunctionService>();
|
||||
|
||||
beforeEach(() => {
|
||||
sut = new KeyGenerationService(cryptoFunctionService);
|
||||
sut = new DefaultKeyGenerationService(cryptoFunctionService);
|
||||
});
|
||||
|
||||
describe("createKey", () => {
|
||||
@@ -0,0 +1,90 @@
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KdfConfig } from "@bitwarden/key-management";
|
||||
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { CsprngArray } from "../../../types/csprng";
|
||||
|
||||
/**
|
||||
* @deprecated This is a low-level cryptographic service. New functionality should not be built
|
||||
* on top of it, and instead should be built in the sdk.
|
||||
*/
|
||||
export abstract class KeyGenerationService {
|
||||
/**
|
||||
* Generates a key of the given length suitable for use in AES encryption
|
||||
*
|
||||
* @deprecated WARNING: DO NOT USE THIS FOR NEW CODE. Direct generation and handling of keys should only be done in the SDK,
|
||||
* as memory safety cannot be ensured in a JS context.
|
||||
*
|
||||
* @param bitLength Length of key.
|
||||
* 256 bits = 32 bytes
|
||||
* 512 bits = 64 bytes
|
||||
* @returns Generated key.
|
||||
*/
|
||||
abstract createKey(bitLength: 256 | 512): Promise<SymmetricCryptoKey>;
|
||||
/**
|
||||
* Generates key material from CSPRNG and derives a 64 byte key from it.
|
||||
* Uses HKDF, see {@link https://datatracker.ietf.org/doc/html/rfc5869 RFC 5869}
|
||||
* for details.
|
||||
*
|
||||
* @deprecated HAZMAT WARNING: DO NOT USE THIS FOR NEW CODE. This is a low-level cryptographic function.
|
||||
* New functionality should not be built on top of it, and instead should be built in the sdk.
|
||||
*
|
||||
* @param bitLength Length of key material.
|
||||
* @param purpose Purpose for the key derivation function.
|
||||
* Different purposes results in different keys, even with the same material.
|
||||
* @param salt Optional. If not provided will be generated from CSPRNG.
|
||||
* @returns An object containing the salt, key material, and derived key.
|
||||
*/
|
||||
abstract createKeyWithPurpose(
|
||||
bitLength: 128 | 192 | 256 | 512,
|
||||
purpose: string,
|
||||
salt?: string,
|
||||
): Promise<{ salt: string; material: CsprngArray; derivedKey: SymmetricCryptoKey }>;
|
||||
/**
|
||||
* Derives a 64 byte key from key material.
|
||||
*
|
||||
* @deprecated HAZMAT WARNING: DO NOT USE THIS FOR NEW CODE. This is a low-level cryptographic function.
|
||||
* New functionality should not be built on top of it, and instead should be built in the sdk.
|
||||
*
|
||||
* @remark The key material should be generated from {@link createKey}, or {@link createKeyWithPurpose}.
|
||||
* Uses HKDF, see {@link https://datatracker.ietf.org/doc/html/rfc5869 RFC 5869} for details.
|
||||
* @param material key material.
|
||||
* @param salt Salt for the key derivation function.
|
||||
* @param purpose Purpose for the key derivation function.
|
||||
* Different purposes results in different keys, even with the same material.
|
||||
* @returns 64 byte derived key.
|
||||
*/
|
||||
abstract deriveKeyFromMaterial(
|
||||
material: CsprngArray,
|
||||
salt: string,
|
||||
purpose: string,
|
||||
): Promise<SymmetricCryptoKey>;
|
||||
/**
|
||||
* Derives a 32 byte key from a password using a key derivation function.
|
||||
*
|
||||
* @deprecated HAZMAT WARNING: DO NOT USE THIS FOR NEW CODE. This is a low-level cryptographic function.
|
||||
* New functionality should not be built on top of it, and instead should be built in the sdk.
|
||||
*
|
||||
* @param password Password to derive the key from.
|
||||
* @param salt Salt for the key derivation function.
|
||||
* @param kdfConfig Configuration for the key derivation function.
|
||||
* @returns 32 byte derived key.
|
||||
*/
|
||||
abstract deriveKeyFromPassword(
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
kdfConfig: KdfConfig,
|
||||
): Promise<SymmetricCryptoKey>;
|
||||
|
||||
/**
|
||||
* Derives a 64 byte key from a 32 byte key using a key derivation function.
|
||||
*
|
||||
* @deprecated HAZMAT WARNING: DO NOT USE THIS FOR NEW CODE. This is a low-level cryptographic function.
|
||||
* New functionality should not be built on top of it, and instead should be built in the sdk.
|
||||
*
|
||||
* @param key 32 byte key.
|
||||
* @returns 64 byte derived key.
|
||||
*/
|
||||
abstract stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey>;
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
import { AppIdService } from "../../../platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "../../../platform/abstractions/config/config.service";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
|
||||
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
|
||||
@@ -30,6 +29,7 @@ import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-cr
|
||||
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { UserKey, DeviceKey } from "../../../types/key";
|
||||
import { KeyGenerationService } from "../../crypto";
|
||||
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "../../crypto/models/enc-string";
|
||||
|
||||
@@ -25,7 +25,6 @@ import { DeviceType } from "../../../enums";
|
||||
import { AppIdService } from "../../../platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "../../../platform/abstractions/config/config.service";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
|
||||
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
|
||||
@@ -37,6 +36,7 @@ import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-cr
|
||||
import { CsprngArray } from "../../../types/csprng";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { DeviceKey, UserKey } from "../../../types/key";
|
||||
import { KeyGenerationService } from "../../crypto";
|
||||
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "../../crypto/models/enc-string";
|
||||
|
||||
@@ -18,9 +18,9 @@ import { TokenService } from "../../../auth/services/token.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { KeyGenerationService } from "../../../platform/services/key-generation.service";
|
||||
import { OrganizationId, UserId } from "../../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../../types/key";
|
||||
import { KeyGenerationService } from "../../crypto";
|
||||
import { EncString } from "../../crypto/models/enc-string";
|
||||
import { FakeMasterPasswordService } from "../../master-password/services/fake-master-password.service";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/key-connector-user-key.request";
|
||||
|
||||
@@ -23,13 +23,13 @@ import { Organization } from "../../../admin-console/models/domain/organization"
|
||||
import { TokenService } from "../../../auth/abstractions/token.service";
|
||||
import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response";
|
||||
import { KeysRequest } from "../../../models/request/keys.request";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { KEY_CONNECTOR_DISK, StateProvider, UserKeyDefinition } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey } from "../../../types/key";
|
||||
import { KeyGenerationService } from "../../crypto";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../master-password/abstractions/master-password.service.abstraction";
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/key-connector.service";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/key-connector-user-key.request";
|
||||
|
||||
@@ -13,13 +13,13 @@ import {
|
||||
mockAccountServiceWith,
|
||||
} from "../../../../spec";
|
||||
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { StateService } from "../../../platform/abstractions/state.service";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { StateProvider } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../../types/key";
|
||||
import { KeyGenerationService } from "../../crypto";
|
||||
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "../../crypto/models/enc-string";
|
||||
|
||||
@@ -11,7 +11,6 @@ import { KdfConfig } from "@bitwarden/key-management";
|
||||
import { PureCrypto } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { StateService } from "../../../platform/abstractions/state.service";
|
||||
import { EncryptionType } from "../../../platform/enums";
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
} from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../../types/key";
|
||||
import { KeyGenerationService } from "../../crypto";
|
||||
import { CryptoFunctionService } from "../../crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
import { EncryptedString, EncString } from "../../crypto/models/enc-string";
|
||||
|
||||
@@ -9,11 +9,11 @@ import { AccountService } from "../../auth/abstractions/account.service";
|
||||
import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { EncString, EncryptedString } from "../../key-management/crypto/models/enc-string";
|
||||
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { PIN_DISK, PIN_MEMORY, StateProvider, UserKeyDefinition } from "../../platform/state";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { PinKey, UserKey } from "../../types/key";
|
||||
import { KeyGenerationService } from "../crypto";
|
||||
|
||||
import { PinServiceAbstraction } from "./pin.service.abstraction";
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import { mock } from "jest-mock-extended";
|
||||
import { DEFAULT_KDF_CONFIG, KdfConfigService } from "@bitwarden/key-management";
|
||||
|
||||
import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec";
|
||||
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { PinKey, UserKey } from "../../types/key";
|
||||
import { KeyGenerationService } from "../crypto";
|
||||
import { CryptoFunctionService } from "../crypto/abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../crypto/abstractions/encrypt.service";
|
||||
import { EncString } from "../crypto/models/enc-string";
|
||||
|
||||
@@ -1,66 +1,2 @@
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KdfConfig } from "@bitwarden/key-management";
|
||||
|
||||
import { CsprngArray } from "../../types/csprng";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
|
||||
|
||||
export abstract class KeyGenerationService {
|
||||
/**
|
||||
* Generates a key of the given length suitable for use in AES encryption
|
||||
* @param bitLength Length of key.
|
||||
* 256 bits = 32 bytes
|
||||
* 512 bits = 64 bytes
|
||||
* @returns Generated key.
|
||||
*/
|
||||
abstract createKey(bitLength: 256 | 512): Promise<SymmetricCryptoKey>;
|
||||
/**
|
||||
* Generates key material from CSPRNG and derives a 64 byte key from it.
|
||||
* Uses HKDF, see {@link https://datatracker.ietf.org/doc/html/rfc5869 RFC 5869}
|
||||
* for details.
|
||||
* @param bitLength Length of key material.
|
||||
* @param purpose Purpose for the key derivation function.
|
||||
* Different purposes results in different keys, even with the same material.
|
||||
* @param salt Optional. If not provided will be generated from CSPRNG.
|
||||
* @returns An object containing the salt, key material, and derived key.
|
||||
*/
|
||||
abstract createKeyWithPurpose(
|
||||
bitLength: 128 | 192 | 256 | 512,
|
||||
purpose: string,
|
||||
salt?: string,
|
||||
): Promise<{ salt: string; material: CsprngArray; derivedKey: SymmetricCryptoKey }>;
|
||||
/**
|
||||
* Derives a 64 byte key from key material.
|
||||
* @remark The key material should be generated from {@link createKey}, or {@link createKeyWithPurpose}.
|
||||
* Uses HKDF, see {@link https://datatracker.ietf.org/doc/html/rfc5869 RFC 5869} for details.
|
||||
* @param material key material.
|
||||
* @param salt Salt for the key derivation function.
|
||||
* @param purpose Purpose for the key derivation function.
|
||||
* Different purposes results in different keys, even with the same material.
|
||||
* @returns 64 byte derived key.
|
||||
*/
|
||||
abstract deriveKeyFromMaterial(
|
||||
material: CsprngArray,
|
||||
salt: string,
|
||||
purpose: string,
|
||||
): Promise<SymmetricCryptoKey>;
|
||||
/**
|
||||
* Derives a 32 byte key from a password using a key derivation function.
|
||||
* @param password Password to derive the key from.
|
||||
* @param salt Salt for the key derivation function.
|
||||
* @param kdfConfig Configuration for the key derivation function.
|
||||
* @returns 32 byte derived key.
|
||||
*/
|
||||
abstract deriveKeyFromPassword(
|
||||
password: string | Uint8Array,
|
||||
salt: string | Uint8Array,
|
||||
kdfConfig: KdfConfig,
|
||||
): Promise<SymmetricCryptoKey>;
|
||||
|
||||
/**
|
||||
* Derives a 64 byte key from a 32 byte key using a key derivation function.
|
||||
* @param key 32 byte key.
|
||||
* @returns 64 byte derived key.
|
||||
*/
|
||||
abstract stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey>;
|
||||
}
|
||||
/** Temporary re-export. This should not be used for new imports */
|
||||
export { KeyGenerationService } from "../../key-management/crypto/key-generation/key-generation.service";
|
||||
|
||||
@@ -1,92 +1,2 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KdfConfig } from "@bitwarden/key-management";
|
||||
import { PureCrypto } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { CryptoFunctionService } from "../../key-management/crypto/abstractions/crypto-function.service";
|
||||
import { CsprngArray } from "../../types/csprng";
|
||||
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "../abstractions/key-generation.service";
|
||||
import { SdkLoadService } from "../abstractions/sdk/sdk-load.service";
|
||||
import { EncryptionType } 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,
|
||||
kdfConfig: KdfConfig,
|
||||
): Promise<SymmetricCryptoKey> {
|
||||
if (typeof password === "string") {
|
||||
password = new TextEncoder().encode(password);
|
||||
}
|
||||
if (typeof salt === "string") {
|
||||
salt = new TextEncoder().encode(salt);
|
||||
}
|
||||
|
||||
await SdkLoadService.Ready;
|
||||
return new SymmetricCryptoKey(
|
||||
PureCrypto.derive_kdf_material(password, salt, kdfConfig.toSdkConfig()),
|
||||
);
|
||||
}
|
||||
|
||||
async stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {
|
||||
// The key to be stretched is actually usually the output of a KDF, and not actually meant for AesCbc256_B64 encryption,
|
||||
// but has the same key length. Only 256-bit key materials should be stretched.
|
||||
if (key.inner().type != EncryptionType.AesCbc256_B64) {
|
||||
throw new Error("Key passed into stretchKey is not a 256-bit key.");
|
||||
}
|
||||
|
||||
const newKey = new Uint8Array(64);
|
||||
// Master key and pin key are always 32 bytes
|
||||
const encKey = await this.cryptoFunctionService.hkdfExpand(
|
||||
key.inner().encryptionKey,
|
||||
"enc",
|
||||
32,
|
||||
"sha256",
|
||||
);
|
||||
const macKey = await this.cryptoFunctionService.hkdfExpand(
|
||||
key.inner().encryptionKey,
|
||||
"mac",
|
||||
32,
|
||||
"sha256",
|
||||
);
|
||||
|
||||
newKey.set(new Uint8Array(encKey));
|
||||
newKey.set(new Uint8Array(macKey), 32);
|
||||
|
||||
return new SymmetricCryptoKey(newKey);
|
||||
}
|
||||
}
|
||||
/** Temporary re-export. This should not be used for new imports */
|
||||
export { DefaultKeyGenerationService as KeyGenerationService } from "../../key-management/crypto/key-generation/default-key-generation.service";
|
||||
|
||||
Reference in New Issue
Block a user