diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 18399faa726..0b55ebb7357 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -69,6 +69,10 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { + DefaultKeyGenerationService, + KeyGenerationService, +} from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; @@ -97,7 +101,6 @@ import { Fido2ClientService as Fido2ClientServiceAbstraction } from "@bitwarden/ import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction"; import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; @@ -135,7 +138,6 @@ import { Fido2ActiveRequestManager } from "@bitwarden/common/platform/services/f import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service"; import { Fido2ClientService } from "@bitwarden/common/platform/services/fido2/fido2-client.service"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; -import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; @@ -311,7 +313,7 @@ export default class MainBackground { i18nService: I18nServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction; logService: LogServiceAbstraction; - keyGenerationService: KeyGenerationServiceAbstraction; + keyGenerationService: KeyGenerationService; keyService: KeyServiceAbstraction; cryptoFunctionService: CryptoFunctionServiceAbstraction; masterPasswordService: InternalMasterPasswordServiceAbstraction; @@ -472,7 +474,7 @@ export default class MainBackground { const isDev = process.env.ENV === "development"; this.logService = new ConsoleLogService(isDev); this.cryptoFunctionService = new WebCryptoFunctionService(self); - this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService); + this.keyGenerationService = new DefaultKeyGenerationService(this.cryptoFunctionService); this.storageService = new BrowserLocalStorageService(this.logService); this.intraprocessMessagingSubject = new Subject>>(); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 7c67e672889..5950bdc214c 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -62,6 +62,7 @@ import { } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service"; @@ -79,7 +80,6 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 0ec24768b79..e98481ee0fe 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -60,6 +60,10 @@ import { import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { ClientType } from "@bitwarden/common/enums"; +import { + DefaultKeyGenerationService, + KeyGenerationService, +} from "@bitwarden/common/key-management/crypto"; import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation"; @@ -81,7 +85,6 @@ import { EnvironmentService, RegionConfig, } from "@bitwarden/common/platform/abstractions/environment.service"; -import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums"; @@ -99,7 +102,6 @@ import { DefaultConfigService } from "@bitwarden/common/platform/services/config import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; -import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; @@ -239,7 +241,7 @@ export class ServiceContainer { individualExportService: IndividualVaultExportServiceAbstraction; organizationExportService: OrganizationVaultExportServiceAbstraction; searchService: SearchService; - keyGenerationService: KeyGenerationServiceAbstraction; + keyGenerationService: KeyGenerationService; cryptoFunctionService: NodeCryptoFunctionService; encryptService: EncryptServiceImplementation; authService: AuthService; @@ -397,7 +399,7 @@ export class ServiceContainer { process.env.ADDITIONAL_REGIONS as unknown as RegionConfig[], ); - this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService); + this.keyGenerationService = new DefaultKeyGenerationService(this.cryptoFunctionService); this.tokenService = new TokenService( this.singleUserStateProvider, diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 95d1f4643fa..3d65094ba60 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -51,6 +51,7 @@ import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { ClientType } from "@bitwarden/common/enums"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service"; @@ -67,7 +68,6 @@ import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } fro import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/platform/abstractions/fido2/fido2-user-interface.service.abstraction"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService, LogService as LogServiceAbstraction, @@ -304,7 +304,7 @@ const safeProviders: SafeProvider[] = [ deps: [ PinServiceAbstraction, InternalMasterPasswordServiceAbstraction, - KeyGenerationServiceAbstraction, + KeyGenerationService, CryptoFunctionServiceAbstraction, EncryptService, PlatformUtilsServiceAbstraction, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 6a145fb3210..11948a04047 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -149,6 +149,10 @@ import { OrganizationBillingApiService } from "@bitwarden/common/billing/service import { OrganizationSponsorshipApiService } from "@bitwarden/common/billing/services/organization/organization-sponsorship-api.service"; import { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service"; import { TaxService } from "@bitwarden/common/billing/services/tax.service"; +import { + DefaultKeyGenerationService, + KeyGenerationService, +} from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; @@ -184,7 +188,6 @@ import { } from "@bitwarden/common/platform/abstractions/environment.service"; import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -222,7 +225,6 @@ import { DefaultBroadcasterService } from "@bitwarden/common/platform/services/d import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service"; import { DefaultServerSettingsService } from "@bitwarden/common/platform/services/default-server-settings.service"; import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service"; -import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; @@ -660,15 +662,15 @@ const safeProviders: SafeProvider[] = [ GlobalStateProvider, SUPPORTS_SECURE_STORAGE, SECURE_STORAGE, - KeyGenerationServiceAbstraction, + KeyGenerationService, EncryptService, LogService, LOGOUT_CALLBACK, ], }), safeProvider({ - provide: KeyGenerationServiceAbstraction, - useClass: KeyGenerationService, + provide: KeyGenerationService, + useClass: DefaultKeyGenerationService, deps: [CryptoFunctionServiceAbstraction], }), safeProvider({ @@ -677,7 +679,7 @@ const safeProviders: SafeProvider[] = [ deps: [ PinServiceAbstraction, InternalMasterPasswordServiceAbstraction, - KeyGenerationServiceAbstraction, + KeyGenerationService, CryptoFunctionServiceAbstraction, EncryptService, PlatformUtilsServiceAbstraction, @@ -767,7 +769,7 @@ const safeProviders: SafeProvider[] = [ deps: [ KeyService, I18nServiceAbstraction, - KeyGenerationServiceAbstraction, + KeyGenerationService, SendStateProviderAbstraction, EncryptService, ], @@ -1013,7 +1015,7 @@ const safeProviders: SafeProvider[] = [ deps: [ StateProvider, StateServiceAbstraction, - KeyGenerationServiceAbstraction, + KeyGenerationService, EncryptService, LogService, CryptoFunctionServiceAbstraction, @@ -1035,7 +1037,7 @@ const safeProviders: SafeProvider[] = [ TokenServiceAbstraction, LogService, OrganizationServiceAbstraction, - KeyGenerationServiceAbstraction, + KeyGenerationService, LOGOUT_CALLBACK, StateProvider, ], @@ -1194,7 +1196,7 @@ const safeProviders: SafeProvider[] = [ provide: DeviceTrustServiceAbstraction, useClass: DeviceTrustService, deps: [ - KeyGenerationServiceAbstraction, + KeyGenerationService, CryptoFunctionServiceAbstraction, KeyService, EncryptService, @@ -1230,7 +1232,7 @@ const safeProviders: SafeProvider[] = [ CryptoFunctionServiceAbstraction, EncryptService, KdfConfigService, - KeyGenerationServiceAbstraction, + KeyGenerationService, LogService, StateProvider, ], diff --git a/libs/common/src/key-management/crypto/index.ts b/libs/common/src/key-management/crypto/index.ts new file mode 100644 index 00000000000..8c0dc5a0732 --- /dev/null +++ b/libs/common/src/key-management/crypto/index.ts @@ -0,0 +1,2 @@ +export { KeyGenerationService } from "./key-generation/key-generation.service"; +export { DefaultKeyGenerationService } from "./key-generation/default-key-generation.service"; diff --git a/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts b/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts new file mode 100644 index 00000000000..8e8d2de1ce4 --- /dev/null +++ b/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts @@ -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 { + 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 { + 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 { + 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 { + // 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); + } +} diff --git a/libs/common/src/platform/services/key-generation.service.spec.ts b/libs/common/src/key-management/crypto/key-generation/key-generation.service.spec.ts similarity index 88% rename from libs/common/src/platform/services/key-generation.service.spec.ts rename to libs/common/src/key-management/crypto/key-generation/key-generation.service.spec.ts index fb6c0a459b3..b8408cbb4cf 100644 --- a/libs/common/src/platform/services/key-generation.service.spec.ts +++ b/libs/common/src/key-management/crypto/key-generation/key-generation.service.spec.ts @@ -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(); beforeEach(() => { - sut = new KeyGenerationService(cryptoFunctionService); + sut = new DefaultKeyGenerationService(cryptoFunctionService); }); describe("createKey", () => { diff --git a/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts b/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts new file mode 100644 index 00000000000..d6be436384e --- /dev/null +++ b/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts @@ -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; + /** + * 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; + /** + * 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; + + /** + * 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; +} diff --git a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts index 372b3282a72..58a2c680afa 100644 --- a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts +++ b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts @@ -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"; diff --git a/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts b/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts index 50a6b0efa21..7ed28518012 100644 --- a/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts +++ b/libs/common/src/key-management/device-trust/services/device-trust.service.spec.ts @@ -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"; diff --git a/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts b/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts index 3db2a7ecd79..67961616034 100644 --- a/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts +++ b/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts @@ -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"; diff --git a/libs/common/src/key-management/key-connector/services/key-connector.service.ts b/libs/common/src/key-management/key-connector/services/key-connector.service.ts index 0c4f4090e61..a6207ab92e2 100644 --- a/libs/common/src/key-management/key-connector/services/key-connector.service.ts +++ b/libs/common/src/key-management/key-connector/services/key-connector.service.ts @@ -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"; diff --git a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts index a09de9008d1..693a4fb39a6 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts @@ -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"; diff --git a/libs/common/src/key-management/master-password/services/master-password.service.ts b/libs/common/src/key-management/master-password/services/master-password.service.ts index 75e5032e004..41cfb268f44 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.ts @@ -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"; diff --git a/libs/common/src/key-management/pin/pin.service.implementation.ts b/libs/common/src/key-management/pin/pin.service.implementation.ts index f926f4a4af2..da46cd3bc76 100644 --- a/libs/common/src/key-management/pin/pin.service.implementation.ts +++ b/libs/common/src/key-management/pin/pin.service.implementation.ts @@ -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"; diff --git a/libs/common/src/key-management/pin/pin.service.spec.ts b/libs/common/src/key-management/pin/pin.service.spec.ts index 3d7dbaa4718..b014c26c7dc 100644 --- a/libs/common/src/key-management/pin/pin.service.spec.ts +++ b/libs/common/src/key-management/pin/pin.service.spec.ts @@ -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"; diff --git a/libs/common/src/platform/abstractions/key-generation.service.ts b/libs/common/src/platform/abstractions/key-generation.service.ts index 91c630ed638..8a230fb5b86 100644 --- a/libs/common/src/platform/abstractions/key-generation.service.ts +++ b/libs/common/src/platform/abstractions/key-generation.service.ts @@ -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; - /** - * 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; - /** - * 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; - - /** - * 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; -} +/** Temporary re-export. This should not be used for new imports */ +export { KeyGenerationService } from "../../key-management/crypto/key-generation/key-generation.service"; diff --git a/libs/common/src/platform/services/key-generation.service.ts b/libs/common/src/platform/services/key-generation.service.ts index d25be087b06..55d1f96e7df 100644 --- a/libs/common/src/platform/services/key-generation.service.ts +++ b/libs/common/src/platform/services/key-generation.service.ts @@ -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 { - 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 { - 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 { - 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 { - // 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"; diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index 04cf6289879..4d872e2cd5c 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -2,6 +2,7 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, bufferCount, firstValueFrom, lastValueFrom, of, take } from "rxjs"; import { EncryptedOrganizationKeyData } from "@bitwarden/common/admin-console/models/data/encrypted-organization-key.data"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { @@ -12,7 +13,6 @@ import { FakeMasterPasswordService } from "@bitwarden/common/key-management/mast import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { VaultTimeoutStringType } from "@bitwarden/common/key-management/vault-timeout"; import { VAULT_TIMEOUT } from "@bitwarden/common/key-management/vault-timeout/services/vault-timeout-settings.state"; -import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index f55a8211dbe..4942279e436 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -19,6 +19,7 @@ import { ProfileOrganizationResponse } from "@bitwarden/common/admin-console/mod import { ProfileProviderOrganizationResponse } from "@bitwarden/common/admin-console/models/response/profile-provider-organization.response"; import { ProfileProviderResponse } from "@bitwarden/common/admin-console/models/response/profile-provider.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { @@ -29,7 +30,6 @@ import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key- import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { VaultTimeoutStringType } from "@bitwarden/common/key-management/vault-timeout"; import { VAULT_TIMEOUT } from "@bitwarden/common/key-management/vault-timeout/services/vault-timeout-settings.state"; -import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";