1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-28 04:08:47 +02: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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 401 additions and 149 deletions

View File

@ -30,6 +30,10 @@ import {
I18nServiceInitOptions, I18nServiceInitOptions,
i18nServiceFactory, i18nServiceFactory,
} from "../../../platform/background/service-factories/i18n-service.factory"; } from "../../../platform/background/service-factories/i18n-service.factory";
import {
KeyGenerationServiceInitOptions,
keyGenerationServiceFactory,
} from "../../../platform/background/service-factories/key-generation-service.factory";
import { import {
PlatformUtilsServiceInitOptions, PlatformUtilsServiceInitOptions,
platformUtilsServiceFactory, platformUtilsServiceFactory,
@ -42,6 +46,7 @@ import {
type DeviceTrustCryptoServiceFactoryOptions = FactoryOptions; type DeviceTrustCryptoServiceFactoryOptions = FactoryOptions;
export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactoryOptions & export type DeviceTrustCryptoServiceInitOptions = DeviceTrustCryptoServiceFactoryOptions &
KeyGenerationServiceInitOptions &
CryptoFunctionServiceInitOptions & CryptoFunctionServiceInitOptions &
CryptoServiceInitOptions & CryptoServiceInitOptions &
EncryptServiceInitOptions & EncryptServiceInitOptions &
@ -61,6 +66,7 @@ export function deviceTrustCryptoServiceFactory(
opts, opts,
async () => async () =>
new DeviceTrustCryptoService( new DeviceTrustCryptoService(
await keyGenerationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts), await cryptoFunctionServiceFactory(cache, opts),
await cryptoServiceFactory(cache, opts), await cryptoServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts), await encryptServiceFactory(cache, opts),

View File

@ -9,10 +9,6 @@ import {
apiServiceFactory, apiServiceFactory,
ApiServiceInitOptions, ApiServiceInitOptions,
} from "../../../platform/background/service-factories/api-service.factory"; } from "../../../platform/background/service-factories/api-service.factory";
import {
CryptoFunctionServiceInitOptions,
cryptoFunctionServiceFactory,
} from "../../../platform/background/service-factories/crypto-function-service.factory";
import { import {
CryptoServiceInitOptions, CryptoServiceInitOptions,
cryptoServiceFactory, cryptoServiceFactory,
@ -22,6 +18,10 @@ import {
CachedServices, CachedServices,
factory, factory,
} from "../../../platform/background/service-factories/factory-options"; } from "../../../platform/background/service-factories/factory-options";
import {
KeyGenerationServiceInitOptions,
keyGenerationServiceFactory,
} from "../../../platform/background/service-factories/key-generation-service.factory";
import { import {
logServiceFactory, logServiceFactory,
LogServiceInitOptions, LogServiceInitOptions,
@ -46,7 +46,7 @@ export type KeyConnectorServiceInitOptions = KeyConnectorServiceFactoryOptions &
TokenServiceInitOptions & TokenServiceInitOptions &
LogServiceInitOptions & LogServiceInitOptions &
OrganizationServiceInitOptions & OrganizationServiceInitOptions &
CryptoFunctionServiceInitOptions; KeyGenerationServiceInitOptions;
export function keyConnectorServiceFactory( export function keyConnectorServiceFactory(
cache: { keyConnectorService?: AbstractKeyConnectorService } & CachedServices, cache: { keyConnectorService?: AbstractKeyConnectorService } & CachedServices,
@ -64,7 +64,7 @@ export function keyConnectorServiceFactory(
await tokenServiceFactory(cache, opts), await tokenServiceFactory(cache, opts),
await logServiceFactory(cache, opts), await logServiceFactory(cache, opts),
await organizationServiceFactory(cache, opts), await organizationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts), await keyGenerationServiceFactory(cache, opts),
opts.keyConnectorServiceOptions.logoutCallback, opts.keyConnectorServiceOptions.logoutCallback,
), ),
); );

View File

@ -54,6 +54,7 @@ import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/pla
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.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 { 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 { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -72,6 +73,7 @@ import { ContainerService } from "@bitwarden/common/platform/services/container.
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.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 { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
@ -180,7 +182,6 @@ import BrowserMessagingPrivateModeBackgroundService from "../platform/services/b
import BrowserMessagingService from "../platform/services/browser-messaging.service"; import BrowserMessagingService from "../platform/services/browser-messaging.service";
import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service"; import BrowserPlatformUtilsService from "../platform/services/browser-platform-utils.service";
import { BrowserStateService } from "../platform/services/browser-state.service"; import { BrowserStateService } from "../platform/services/browser-state.service";
import { KeyGenerationService } from "../platform/services/key-generation.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service"; import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider"; import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service"; import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
@ -207,6 +208,7 @@ export default class MainBackground {
i18nService: I18nServiceAbstraction; i18nService: I18nServiceAbstraction;
platformUtilsService: PlatformUtilsServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction;
logService: LogServiceAbstraction; logService: LogServiceAbstraction;
keyGenerationService: KeyGenerationServiceAbstraction;
cryptoService: CryptoServiceAbstraction; cryptoService: CryptoServiceAbstraction;
cryptoFunctionService: CryptoFunctionServiceAbstraction; cryptoFunctionService: CryptoFunctionServiceAbstraction;
tokenService: TokenServiceAbstraction; tokenService: TokenServiceAbstraction;
@ -326,6 +328,7 @@ export default class MainBackground {
? new BrowserMessagingPrivateModeBackgroundService() ? new BrowserMessagingPrivateModeBackgroundService()
: new BrowserMessagingService(); : new BrowserMessagingService();
this.logService = new ConsoleLogService(false); this.logService = new ConsoleLogService(false);
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
this.cryptoFunctionService = new WebCryptoFunctionService(self); this.cryptoFunctionService = new WebCryptoFunctionService(self);
this.storageService = new BrowserLocalStorageService(); this.storageService = new BrowserLocalStorageService();
this.secureStorageService = new BrowserLocalStorageService(); this.secureStorageService = new BrowserLocalStorageService();
@ -333,14 +336,14 @@ export default class MainBackground {
BrowserApi.manifestVersion === 3 BrowserApi.manifestVersion === 3
? new LocalBackedSessionStorageService( ? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false), new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
new KeyGenerationService(this.cryptoFunctionService), this.keyGenerationService,
) )
: new MemoryStorageService(); : new MemoryStorageService();
this.memoryStorageForStateProviders = this.memoryStorageForStateProviders =
BrowserApi.manifestVersion === 3 BrowserApi.manifestVersion === 3
? new LocalBackedSessionStorageService( ? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false), new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
new KeyGenerationService(this.cryptoFunctionService), this.keyGenerationService,
) )
: new BackgroundMemoryStorageService(); : new BackgroundMemoryStorageService();
this.globalStateProvider = new DefaultGlobalStateProvider( this.globalStateProvider = new DefaultGlobalStateProvider(
@ -426,6 +429,7 @@ export default class MainBackground {
); );
this.i18nService = new BrowserI18nService(BrowserApi.getUILanguage(), this.stateService); this.i18nService = new BrowserI18nService(BrowserApi.getUILanguage(), this.stateService);
this.cryptoService = new BrowserCryptoService( this.cryptoService = new BrowserCryptoService(
this.keyGenerationService,
this.cryptoFunctionService, this.cryptoFunctionService,
this.encryptService, this.encryptService,
this.platformUtilsService, this.platformUtilsService,
@ -478,7 +482,7 @@ export default class MainBackground {
this.tokenService, this.tokenService,
this.logService, this.logService,
this.organizationService, this.organizationService,
this.cryptoFunctionService, this.keyGenerationService,
logoutCallback, logoutCallback,
); );
@ -506,6 +510,7 @@ export default class MainBackground {
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
this.deviceTrustCryptoService = new DeviceTrustCryptoService( this.deviceTrustCryptoService = new DeviceTrustCryptoService(
this.keyGenerationService,
this.cryptoFunctionService, this.cryptoFunctionService,
this.cryptoService, this.cryptoService,
this.encryptService, this.encryptService,
@ -636,7 +641,7 @@ export default class MainBackground {
this.sendService = new BrowserSendService( this.sendService = new BrowserSendService(
this.cryptoService, this.cryptoService,
this.i18nService, this.i18nService,
this.cryptoFunctionService, this.keyGenerationService,
this.stateService, this.stateService,
); );
this.sendApiService = new SendApiService( this.sendApiService = new SendApiService(

View File

@ -1,6 +1,5 @@
import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { InternalSendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { cryptoFunctionServiceFactory } from "../../platform/background/service-factories/crypto-function-service.factory";
import { import {
CryptoServiceInitOptions, CryptoServiceInitOptions,
cryptoServiceFactory, cryptoServiceFactory,
@ -14,6 +13,10 @@ import {
i18nServiceFactory, i18nServiceFactory,
I18nServiceInitOptions, I18nServiceInitOptions,
} from "../../platform/background/service-factories/i18n-service.factory"; } from "../../platform/background/service-factories/i18n-service.factory";
import {
KeyGenerationServiceInitOptions,
keyGenerationServiceFactory,
} from "../../platform/background/service-factories/key-generation-service.factory";
import { import {
stateServiceFactory, stateServiceFactory,
StateServiceInitOptions, StateServiceInitOptions,
@ -25,6 +28,7 @@ type SendServiceFactoryOptions = FactoryOptions;
export type SendServiceInitOptions = SendServiceFactoryOptions & export type SendServiceInitOptions = SendServiceFactoryOptions &
CryptoServiceInitOptions & CryptoServiceInitOptions &
I18nServiceInitOptions & I18nServiceInitOptions &
KeyGenerationServiceInitOptions &
StateServiceInitOptions; StateServiceInitOptions;
export function sendServiceFactory( export function sendServiceFactory(
@ -39,7 +43,7 @@ export function sendServiceFactory(
new BrowserSendService( new BrowserSendService(
await cryptoServiceFactory(cache, opts), await cryptoServiceFactory(cache, opts),
await i18nServiceFactory(cache, opts), await i18nServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts), await keyGenerationServiceFactory(cache, opts),
await stateServiceFactory(cache, opts), await stateServiceFactory(cache, opts),
), ),
); );

View File

@ -20,6 +20,10 @@ import {
} from "./crypto-function-service.factory"; } from "./crypto-function-service.factory";
import { encryptServiceFactory, EncryptServiceInitOptions } from "./encrypt-service.factory"; import { encryptServiceFactory, EncryptServiceInitOptions } from "./encrypt-service.factory";
import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { FactoryOptions, CachedServices, factory } from "./factory-options";
import {
KeyGenerationServiceInitOptions,
keyGenerationServiceFactory,
} from "./key-generation-service.factory";
import { import {
PlatformUtilsServiceInitOptions, PlatformUtilsServiceInitOptions,
platformUtilsServiceFactory, platformUtilsServiceFactory,
@ -29,6 +33,7 @@ import { StateProviderInitOptions, stateProviderFactory } from "./state-provider
type CryptoServiceFactoryOptions = FactoryOptions; type CryptoServiceFactoryOptions = FactoryOptions;
export type CryptoServiceInitOptions = CryptoServiceFactoryOptions & export type CryptoServiceInitOptions = CryptoServiceFactoryOptions &
KeyGenerationServiceInitOptions &
CryptoFunctionServiceInitOptions & CryptoFunctionServiceInitOptions &
EncryptServiceInitOptions & EncryptServiceInitOptions &
PlatformUtilsServiceInitOptions & PlatformUtilsServiceInitOptions &
@ -47,6 +52,7 @@ export function cryptoServiceFactory(
opts, opts,
async () => async () =>
new BrowserCryptoService( new BrowserCryptoService(
await keyGenerationServiceFactory(cache, opts),
await cryptoFunctionServiceFactory(cache, opts), await cryptoFunctionServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts), await encryptServiceFactory(cache, opts),
await platformUtilsServiceFactory(cache, opts), await platformUtilsServiceFactory(cache, opts),

View File

@ -1,4 +1,5 @@
import { KeyGenerationService } from "../../services/key-generation.service"; import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
import { import {
cryptoFunctionServiceFactory, cryptoFunctionServiceFactory,
@ -12,9 +13,9 @@ export type KeyGenerationServiceInitOptions = KeyGenerationServiceFactoryOptions
CryptoFunctionServiceInitOptions; CryptoFunctionServiceInitOptions;
export function keyGenerationServiceFactory( export function keyGenerationServiceFactory(
cache: { keyGenerationService?: KeyGenerationService } & CachedServices, cache: { keyGenerationService?: KeyGenerationServiceAbstraction } & CachedServices,
opts: KeyGenerationServiceInitOptions, opts: KeyGenerationServiceInitOptions,
): Promise<KeyGenerationService> { ): Promise<KeyGenerationServiceAbstraction> {
return factory( return factory(
cache, cache,
"keyGenerationService", "keyGenerationService",

View File

@ -1,5 +0,0 @@
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
export interface AbstractKeyGenerationService {
makeEphemeralKey(numBytes?: number): Promise<SymmetricCryptoKey>;
}

View File

@ -1,20 +0,0 @@
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { AbstractKeyGenerationService } from "./abstractions/abstract-key-generation.service";
export class KeyGenerationService implements AbstractKeyGenerationService {
constructor(private cryptoFunctionService: CryptoFunctionService) {}
async makeEphemeralKey(numBytes = 16): Promise<SymmetricCryptoKey> {
const keyMaterial = await this.cryptoFunctionService.randomBytes(numBytes);
const key = await this.cryptoFunctionService.hkdf(
keyMaterial,
"bitwarden-ephemeral",
"ephemeral",
64,
"sha256",
);
return new SymmetricCryptoKey(key);
}
}

View File

@ -1,13 +1,13 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import BrowserLocalStorageService from "./browser-local-storage.service"; import BrowserLocalStorageService from "./browser-local-storage.service";
import BrowserMemoryStorageService from "./browser-memory-storage.service"; import BrowserMemoryStorageService from "./browser-memory-storage.service";
import { KeyGenerationService } from "./key-generation.service";
import { LocalBackedSessionStorageService } from "./local-backed-session-storage.service"; import { LocalBackedSessionStorageService } from "./local-backed-session-storage.service";
describe("Browser Session Storage Service", () => { describe("Browser Session Storage Service", () => {
@ -206,7 +206,11 @@ describe("Browser Session Storage Service", () => {
describe("new key creation", () => { describe("new key creation", () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(sessionStorage, "get").mockResolvedValue(null); jest.spyOn(sessionStorage, "get").mockResolvedValue(null);
keyGenerationService.makeEphemeralKey.mockResolvedValue(key); keyGenerationService.createKeyWithPurpose.mockResolvedValue({
salt: "salt",
material: null,
derivedKey: key,
});
jest.spyOn(sut, "setSessionEncKey").mockResolvedValue(); jest.spyOn(sut, "setSessionEncKey").mockResolvedValue();
}); });
@ -214,7 +218,7 @@ describe("Browser Session Storage Service", () => {
const result = await sut.getSessionEncKey(); const result = await sut.getSessionEncKey();
expect(result).toStrictEqual(key); expect(result).toStrictEqual(key);
expect(keyGenerationService.makeEphemeralKey).toBeCalledTimes(1); expect(keyGenerationService.createKeyWithPurpose).toBeCalledTimes(1);
}); });
it("should store a symmetric crypto key if it makes one", async () => { it("should store a symmetric crypto key if it makes one", async () => {

View File

@ -2,6 +2,7 @@ import { Subject } from "rxjs";
import { Jsonify } from "type-fest"; import { Jsonify } from "type-fest";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { import {
AbstractMemoryStorageService, AbstractMemoryStorageService,
StorageUpdate, StorageUpdate,
@ -13,7 +14,6 @@ import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/sym
import { devFlag } from "../decorators/dev-flag.decorator"; import { devFlag } from "../decorators/dev-flag.decorator";
import { devFlagEnabled } from "../flags"; import { devFlagEnabled } from "../flags";
import { AbstractKeyGenerationService } from "./abstractions/abstract-key-generation.service";
import BrowserLocalStorageService from "./browser-local-storage.service"; import BrowserLocalStorageService from "./browser-local-storage.service";
import BrowserMemoryStorageService from "./browser-memory-storage.service"; import BrowserMemoryStorageService from "./browser-memory-storage.service";
@ -31,7 +31,7 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
constructor( constructor(
private encryptService: EncryptService, private encryptService: EncryptService,
private keyGenerationService: AbstractKeyGenerationService, private keyGenerationService: KeyGenerationService,
) { ) {
super(); super();
this.updates$ = this.updatesSubject.asObservable(); this.updates$ = this.updatesSubject.asObservable();
@ -138,10 +138,17 @@ export class LocalBackedSessionStorageService extends AbstractMemoryStorageServi
async getSessionEncKey(): Promise<SymmetricCryptoKey> { async getSessionEncKey(): Promise<SymmetricCryptoKey> {
let storedKey = await this.sessionStorage.get<SymmetricCryptoKey>(keys.encKey); let storedKey = await this.sessionStorage.get<SymmetricCryptoKey>(keys.encKey);
if (storedKey == null || Object.keys(storedKey).length == 0) { if (storedKey == null || Object.keys(storedKey).length == 0) {
storedKey = await this.keyGenerationService.makeEphemeralKey(); const generatedKey = await this.keyGenerationService.createKeyWithPurpose(
128,
"ephemeral",
"bitwarden-ephemeral",
);
storedKey = generatedKey.derivedKey;
await this.setSessionEncKey(storedKey); await this.setSessionEncKey(storedKey);
return storedKey;
} else {
return SymmetricCryptoKey.fromJSON(storedKey);
} }
return SymmetricCryptoKey.fromJSON(storedKey);
} }
async setSessionEncKey(input: SymmetricCryptoKey): Promise<void> { async setSessionEncKey(input: SymmetricCryptoKey): Promise<void> {

View File

@ -53,6 +53,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { FileUploadService } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service"; import { FileUploadService } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { import {
LogService, LogService,
LogService as LogServiceAbstraction, LogService as LogServiceAbstraction,
@ -358,13 +359,13 @@ function getBgService<T>(service: keyof MainBackground) {
useFactory: ( useFactory: (
cryptoService: CryptoService, cryptoService: CryptoService,
i18nService: I18nServiceAbstraction, i18nService: I18nServiceAbstraction,
cryptoFunctionService: CryptoFunctionService, keyGenerationService: KeyGenerationService,
stateServiceAbstraction: StateServiceAbstraction, stateServiceAbstraction: StateServiceAbstraction,
) => { ) => {
return new BrowserSendService( return new BrowserSendService(
cryptoService, cryptoService,
i18nService, i18nService,
cryptoFunctionService, keyGenerationService,
stateServiceAbstraction, stateServiceAbstraction,
); );
}, },

View File

@ -38,6 +38,7 @@ import { UserVerificationService } from "@bitwarden/common/auth/services/user-ve
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { ClientType } from "@bitwarden/common/enums"; import { ClientType } from "@bitwarden/common/enums";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction"; import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums"; import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { Account } from "@bitwarden/common/platform/models/domain/account"; import { Account } from "@bitwarden/common/platform/models/domain/account";
@ -50,6 +51,7 @@ import { CryptoService } from "@bitwarden/common/platform/services/crypto.servic
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation"; import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.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 { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
@ -164,6 +166,7 @@ export class Main {
individualExportService: IndividualVaultExportServiceAbstraction; individualExportService: IndividualVaultExportServiceAbstraction;
organizationExportService: OrganizationVaultExportServiceAbstraction; organizationExportService: OrganizationVaultExportServiceAbstraction;
searchService: SearchService; searchService: SearchService;
keyGenerationService: KeyGenerationServiceAbstraction;
cryptoFunctionService: NodeCryptoFunctionService; cryptoFunctionService: NodeCryptoFunctionService;
encryptService: EncryptServiceImplementation; encryptService: EncryptServiceImplementation;
authService: AuthService; authService: AuthService;
@ -296,7 +299,10 @@ export class Main {
migrationRunner, migrationRunner,
); );
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
this.cryptoService = new CryptoService( this.cryptoService = new CryptoService(
this.keyGenerationService,
this.cryptoFunctionService, this.cryptoFunctionService,
this.encryptService, this.encryptService,
this.platformUtilsService, this.platformUtilsService,
@ -337,7 +343,7 @@ export class Main {
this.sendService = new SendService( this.sendService = new SendService(
this.cryptoService, this.cryptoService,
this.i18nService, this.i18nService,
this.cryptoFunctionService, this.keyGenerationService,
this.stateService, this.stateService,
); );
@ -383,7 +389,7 @@ export class Main {
this.tokenService, this.tokenService,
this.logService, this.logService,
this.organizationService, this.organizationService,
this.cryptoFunctionService, this.keyGenerationService,
async (expired: boolean) => await this.logout(), async (expired: boolean) => await this.logout(),
); );
@ -399,6 +405,7 @@ export class Main {
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService); this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
this.deviceTrustCryptoService = new DeviceTrustCryptoService( this.deviceTrustCryptoService = new DeviceTrustCryptoService(
this.keyGenerationService,
this.cryptoFunctionService, this.cryptoFunctionService,
this.cryptoService, this.cryptoService,
this.encryptService, this.encryptService,

View File

@ -25,6 +25,7 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.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 { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { import {
LogService, LogService,
LogService as LogServiceAbstraction, LogService as LogServiceAbstraction,
@ -183,6 +184,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
provide: CryptoServiceAbstraction, provide: CryptoServiceAbstraction,
useClass: ElectronCryptoService, useClass: ElectronCryptoService,
deps: [ deps: [
KeyGenerationServiceAbstraction,
CryptoFunctionServiceAbstraction, CryptoFunctionServiceAbstraction,
EncryptService, EncryptService,
PlatformUtilsServiceAbstraction, PlatformUtilsServiceAbstraction,

View File

@ -3,6 +3,7 @@ import { mock } from "jest-mock-extended";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
@ -23,6 +24,7 @@ import { ElectronStateService } from "./electron-state.service.abstraction";
describe("electronCryptoService", () => { describe("electronCryptoService", () => {
let sut: ElectronCryptoService; let sut: ElectronCryptoService;
const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>(); const cryptoFunctionService = mock<CryptoFunctionService>();
const encryptService = mock<EncryptService>(); const encryptService = mock<EncryptService>();
const platformUtilService = mock<PlatformUtilsService>(); const platformUtilService = mock<PlatformUtilsService>();
@ -39,6 +41,7 @@ describe("electronCryptoService", () => {
stateProvider = new FakeStateProvider(accountService); stateProvider = new FakeStateProvider(accountService);
sut = new ElectronCryptoService( sut = new ElectronCryptoService(
keyGenerationService,
cryptoFunctionService, cryptoFunctionService,
encryptService, encryptService,
platformUtilService, platformUtilService,

View File

@ -1,6 +1,7 @@
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service"; import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
@ -18,6 +19,7 @@ import { ElectronStateService } from "./electron-state.service.abstraction";
export class ElectronCryptoService extends CryptoService { export class ElectronCryptoService extends CryptoService {
constructor( constructor(
keyGenerationService: KeyGenerationService,
cryptoFunctionService: CryptoFunctionService, cryptoFunctionService: CryptoFunctionService,
encryptService: EncryptService, encryptService: EncryptService,
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
@ -28,6 +30,7 @@ export class ElectronCryptoService extends CryptoService {
private biometricStateService: BiometricStateService, private biometricStateService: BiometricStateService,
) { ) {
super( super(
keyGenerationService,
cryptoFunctionService, cryptoFunctionService,
encryptService, encryptService,
platformUtilsService, platformUtilsService,

View File

@ -3,9 +3,9 @@ import { Subject } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
@ -28,7 +28,7 @@ export class AccessService {
constructor( constructor(
private cryptoService: CryptoService, private cryptoService: CryptoService,
private apiService: ApiService, private apiService: ApiService,
private cryptoFunctionService: CryptoFunctionService, private keyGenerationService: KeyGenerationService,
private encryptService: EncryptService, private encryptService: EncryptService,
) {} ) {}
@ -53,19 +53,15 @@ export class AccessService {
serviceAccountId: string, serviceAccountId: string,
accessTokenView: AccessTokenView, accessTokenView: AccessTokenView,
): Promise<string> { ): Promise<string> {
const keyMaterial = await this.cryptoFunctionService.aesGenerateKey(128); const key = await this.keyGenerationService.createKeyWithPurpose(
const key = await this.cryptoFunctionService.hkdf( 128,
keyMaterial,
"bitwarden-accesstoken",
"sm-access-token", "sm-access-token",
64, "bitwarden-accesstoken",
"sha256",
); );
const encryptionKey = new SymmetricCryptoKey(key);
const request = await this.createAccessTokenRequest( const request = await this.createAccessTokenRequest(
organizationId, organizationId,
encryptionKey, key.derivedKey,
accessTokenView, accessTokenView,
); );
const r = await this.apiService.send( const r = await this.apiService.send(
@ -77,8 +73,8 @@ export class AccessService {
); );
const result = new AccessTokenCreationResponse(r); const result = new AccessTokenCreationResponse(r);
this._accessToken.next(null); this._accessToken.next(null);
const b64Key = Utils.fromBufferToB64(keyMaterial); const keyB64 = Utils.fromBufferToB64(key.material);
return `${this._accessTokenVersion}.${result.id}.${result.clientSecret}:${b64Key}`; return `${this._accessTokenVersion}.${result.id}.${result.clientSecret}:${keyB64}`;
} }
async revokeAccessTokens(serviceAccountId: string, accessTokenIds: string[]): Promise<void> { async revokeAccessTokens(serviceAccountId: string, accessTokenIds: string[]): Promise<void> {

View File

@ -102,6 +102,7 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.
import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/platform/abstractions/environment.service"; import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/platform/abstractions/environment.service";
import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.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 { 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 { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -125,6 +126,7 @@ import { EncryptServiceImplementation } from "@bitwarden/common/platform/service
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation"; import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service"; import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.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 { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { NoopNotificationsService } from "@bitwarden/common/platform/services/noop-notifications.service"; import { NoopNotificationsService } from "@bitwarden/common/platform/services/noop-notifications.service";
@ -431,10 +433,16 @@ import { ModalService } from "./modal.service";
deps: [CryptoFunctionServiceAbstraction, LogService, StateServiceAbstraction], deps: [CryptoFunctionServiceAbstraction, LogService, StateServiceAbstraction],
}, },
{ provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] }, { provide: TokenServiceAbstraction, useClass: TokenService, deps: [StateServiceAbstraction] },
{
provide: KeyGenerationServiceAbstraction,
useClass: KeyGenerationService,
deps: [CryptoFunctionServiceAbstraction],
},
{ {
provide: CryptoServiceAbstraction, provide: CryptoServiceAbstraction,
useClass: CryptoService, useClass: CryptoService,
deps: [ deps: [
KeyGenerationServiceAbstraction,
CryptoFunctionServiceAbstraction, CryptoFunctionServiceAbstraction,
EncryptService, EncryptService,
PlatformUtilsServiceAbstraction, PlatformUtilsServiceAbstraction,
@ -476,7 +484,7 @@ import { ModalService } from "./modal.service";
deps: [ deps: [
CryptoServiceAbstraction, CryptoServiceAbstraction,
I18nServiceAbstraction, I18nServiceAbstraction,
CryptoFunctionServiceAbstraction, KeyGenerationServiceAbstraction,
StateServiceAbstraction, StateServiceAbstraction,
], ],
}, },
@ -683,7 +691,7 @@ import { ModalService } from "./modal.service";
TokenServiceAbstraction, TokenServiceAbstraction,
LogService, LogService,
OrganizationServiceAbstraction, OrganizationServiceAbstraction,
CryptoFunctionServiceAbstraction, KeyGenerationServiceAbstraction,
LOGOUT_CALLBACK, LOGOUT_CALLBACK,
], ],
}, },
@ -825,6 +833,7 @@ import { ModalService } from "./modal.service";
provide: DeviceTrustCryptoServiceAbstraction, provide: DeviceTrustCryptoServiceAbstraction,
useClass: DeviceTrustCryptoService, useClass: DeviceTrustCryptoService,
deps: [ deps: [
KeyGenerationServiceAbstraction,
CryptoFunctionServiceAbstraction, CryptoFunctionServiceAbstraction,
CryptoServiceAbstraction, CryptoServiceAbstraction,
EncryptService, EncryptService,

View File

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

View File

@ -7,6 +7,7 @@ import { CryptoFunctionService } from "../../platform/abstractions/crypto-functi
import { CryptoService } from "../../platform/abstractions/crypto.service"; import { CryptoService } from "../../platform/abstractions/crypto.service";
import { EncryptService } from "../../platform/abstractions/encrypt.service"; import { EncryptService } from "../../platform/abstractions/encrypt.service";
import { I18nService } from "../../platform/abstractions/i18n.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 { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { StateService } from "../../platform/abstractions/state.service"; import { StateService } from "../../platform/abstractions/state.service";
import { EncryptionType } from "../../platform/enums/encryption-type.enum"; import { EncryptionType } from "../../platform/enums/encryption-type.enum";
@ -24,6 +25,7 @@ import { DeviceTrustCryptoService } from "./device-trust-crypto.service.implemen
describe("deviceTrustCryptoService", () => { describe("deviceTrustCryptoService", () => {
let deviceTrustCryptoService: DeviceTrustCryptoService; let deviceTrustCryptoService: DeviceTrustCryptoService;
const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>(); const cryptoFunctionService = mock<CryptoFunctionService>();
const cryptoService = mock<CryptoService>(); const cryptoService = mock<CryptoService>();
const encryptService = mock<EncryptService>(); const encryptService = mock<EncryptService>();
@ -37,6 +39,7 @@ describe("deviceTrustCryptoService", () => {
jest.clearAllMocks(); jest.clearAllMocks();
deviceTrustCryptoService = new DeviceTrustCryptoService( deviceTrustCryptoService = new DeviceTrustCryptoService(
keyGenerationService,
cryptoFunctionService, cryptoFunctionService,
cryptoService, cryptoService,
encryptService, encryptService,
@ -166,17 +169,18 @@ describe("deviceTrustCryptoService", () => {
describe("makeDeviceKey", () => { describe("makeDeviceKey", () => {
it("creates a new non-null 64 byte device key, securely stores it, and returns it", async () => { 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 mockRandomBytes = new Uint8Array(deviceKeyBytesLength) as CsprngArray;
const mockDeviceKey = new SymmetricCryptoKey(mockRandomBytes) as DeviceKey;
const cryptoFuncSvcGenerateKeySpy = jest const keyGenSvcGenerateKeySpy = jest
.spyOn(cryptoFunctionService, "aesGenerateKey") .spyOn(keyGenerationService, "createKey")
.mockResolvedValue(mockRandomBytes); .mockResolvedValue(mockDeviceKey);
// TypeScript will allow calling private methods if the object is of type 'any' // TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests // This is a hacky workaround, but it allows for cleaner tests
const deviceKey = await (deviceTrustCryptoService as any).makeDeviceKey(); const deviceKey = await (deviceTrustCryptoService as any).makeDeviceKey();
expect(cryptoFuncSvcGenerateKeySpy).toHaveBeenCalledTimes(1); expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledTimes(1);
expect(cryptoFuncSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8); expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8);
expect(deviceKey).not.toBeNull(); expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey); 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 { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationUserType } from "../../admin-console/enums"; import { OrganizationUserType } from "../../admin-console/enums";
import { KeysRequest } from "../../models/request/keys.request"; import { KeysRequest } from "../../models/request/keys.request";
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
import { CryptoService } from "../../platform/abstractions/crypto.service"; import { CryptoService } from "../../platform/abstractions/crypto.service";
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
import { LogService } from "../../platform/abstractions/log.service"; import { LogService } from "../../platform/abstractions/log.service";
import { StateService } from "../../platform/abstractions/state.service"; import { StateService } from "../../platform/abstractions/state.service";
import { Utils } from "../../platform/misc/utils"; import { Utils } from "../../platform/misc/utils";
@ -24,7 +24,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
private tokenService: TokenService, private tokenService: TokenService,
private logService: LogService, private logService: LogService,
private organizationService: OrganizationService, private organizationService: OrganizationService,
private cryptoFunctionService: CryptoFunctionService, private keyGenerationService: KeyGenerationService,
private logoutCallback: (expired: boolean, userId?: string) => Promise<void>, private logoutCallback: (expired: boolean, userId?: string) => Promise<void>,
) {} ) {}
@ -94,11 +94,11 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
keyConnectorUrl: legacyKeyConnectorUrl, keyConnectorUrl: legacyKeyConnectorUrl,
userDecryptionOptions, userDecryptionOptions,
} = tokenResponse; } = tokenResponse;
const password = await this.cryptoFunctionService.aesGenerateKey(512); const password = await this.keyGenerationService.createKey(512);
const kdfConfig = new KdfConfig(kdfIterations, kdfMemory, kdfParallelism); const kdfConfig = new KdfConfig(kdfIterations, kdfMemory, kdfParallelism);
const masterKey = await this.cryptoService.makeMasterKey( const masterKey = await this.cryptoService.makeMasterKey(
Utils.fromBufferToB64(password), password.keyB64,
await this.tokenService.getEmail(), await this.tokenService.getEmail(),
kdf, kdf,
kdfConfig, kdfConfig,

View File

@ -0,0 +1,59 @@
import { KdfConfig } from "../../auth/models/domain/kdf-config";
import { CsprngArray } from "../../types/csprng";
import { KdfType } from "../enums";
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.
*/
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.
*/
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.
*/
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 kdf Key derivation function to use.
* @param kdfConfig Configuration for the key derivation function.
* @returns 32 byte derived key.
*/
deriveKeyFromPassword: (
password: string | Uint8Array,
salt: string | Uint8Array,
kdf: KdfType,
kdfConfig: KdfConfig,
) => Promise<SymmetricCryptoKey>;
}

View File

@ -10,6 +10,7 @@ import { UserId } from "../../types/guid";
import { UserKey, MasterKey, PinKey } from "../../types/key"; import { UserKey, MasterKey, PinKey } from "../../types/key";
import { CryptoFunctionService } from "../abstractions/crypto-function.service"; import { CryptoFunctionService } from "../abstractions/crypto-function.service";
import { EncryptService } from "../abstractions/encrypt.service"; import { EncryptService } from "../abstractions/encrypt.service";
import { KeyGenerationService } from "../abstractions/key-generation.service";
import { LogService } from "../abstractions/log.service"; import { LogService } from "../abstractions/log.service";
import { PlatformUtilsService } from "../abstractions/platform-utils.service"; import { PlatformUtilsService } from "../abstractions/platform-utils.service";
import { StateService } from "../abstractions/state.service"; import { StateService } from "../abstractions/state.service";
@ -23,6 +24,7 @@ import { USER_EVER_HAD_USER_KEY, USER_KEY } from "./key-state/user-key.state";
describe("cryptoService", () => { describe("cryptoService", () => {
let cryptoService: CryptoService; let cryptoService: CryptoService;
const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>(); const cryptoFunctionService = mock<CryptoFunctionService>();
const encryptService = mock<EncryptService>(); const encryptService = mock<EncryptService>();
const platformUtilService = mock<PlatformUtilsService>(); const platformUtilService = mock<PlatformUtilsService>();
@ -38,6 +40,7 @@ describe("cryptoService", () => {
stateProvider = new FakeStateProvider(accountService); stateProvider = new FakeStateProvider(accountService);
cryptoService = new CryptoService( cryptoService = new CryptoService(
keyGenerationService,
cryptoFunctionService, cryptoFunctionService,
encryptService, encryptService,
platformUtilService, platformUtilService,

View File

@ -9,6 +9,7 @@ import { AccountService } from "../../auth/abstractions/account.service";
import { AuthenticationStatus } from "../../auth/enums/authentication-status"; import { AuthenticationStatus } from "../../auth/enums/authentication-status";
import { KdfConfig } from "../../auth/models/domain/kdf-config"; import { KdfConfig } from "../../auth/models/domain/kdf-config";
import { Utils } from "../../platform/misc/utils"; import { Utils } from "../../platform/misc/utils";
import { CsprngArray } from "../../types/csprng";
import { OrganizationId, ProviderId, UserId } from "../../types/guid"; import { OrganizationId, ProviderId, UserId } from "../../types/guid";
import { import {
OrgKey, OrgKey,
@ -23,6 +24,7 @@ import {
import { CryptoFunctionService } from "../abstractions/crypto-function.service"; import { CryptoFunctionService } from "../abstractions/crypto-function.service";
import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service";
import { EncryptService } from "../abstractions/encrypt.service"; import { EncryptService } from "../abstractions/encrypt.service";
import { KeyGenerationService } from "../abstractions/key-generation.service";
import { LogService } from "../abstractions/log.service"; import { LogService } from "../abstractions/log.service";
import { PlatformUtilsService } from "../abstractions/platform-utils.service"; import { PlatformUtilsService } from "../abstractions/platform-utils.service";
import { StateService } from "../abstractions/state.service"; import { StateService } from "../abstractions/state.service";
@ -80,6 +82,7 @@ export class CryptoService implements CryptoServiceAbstraction {
readonly everHadUserKey$: Observable<boolean>; readonly everHadUserKey$: Observable<boolean>;
constructor( constructor(
protected keyGenerationService: KeyGenerationService,
protected cryptoFunctionService: CryptoFunctionService, protected cryptoFunctionService: CryptoFunctionService,
protected encryptService: EncryptService, protected encryptService: EncryptService,
protected platformUtilService: PlatformUtilsService, protected platformUtilService: PlatformUtilsService,
@ -219,8 +222,8 @@ export class CryptoService implements CryptoServiceAbstraction {
throw new Error("No Master Key found."); throw new Error("No Master Key found.");
} }
const newUserKey = await this.cryptoFunctionService.aesGenerateKey(512); const newUserKey = await this.keyGenerationService.createKey(512);
return this.buildProtectedSymmetricKey(masterKey, newUserKey); return this.buildProtectedSymmetricKey(masterKey, newUserKey.key);
} }
async clearUserKey(clearStoredKeys = true, userId?: UserId): Promise<void> { async clearUserKey(clearStoredKeys = true, userId?: UserId): Promise<void> {
@ -294,7 +297,12 @@ export class CryptoService implements CryptoServiceAbstraction {
kdf: KdfType, kdf: KdfType,
KdfConfig: KdfConfig, KdfConfig: KdfConfig,
): Promise<MasterKey> { ): 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> { async clearMasterKey(userId?: UserId): Promise<void> {
@ -452,8 +460,8 @@ export class CryptoService implements CryptoServiceAbstraction {
throw new Error("No key provided"); throw new Error("No key provided");
} }
const newSymKey = await this.cryptoFunctionService.aesGenerateKey(512); const newSymKey = await this.keyGenerationService.createKey(512);
return this.buildProtectedSymmetricKey(key, newSymKey); return this.buildProtectedSymmetricKey(key, newSymKey.key);
} }
async clearOrgKeys(memoryOnly?: boolean, userId?: UserId): Promise<void> { 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]> { 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 publicKey = await this.getPublicKey();
const encShareKey = await this.rsaEncrypt(shareKey, publicKey); const encShareKey = await this.rsaEncrypt(shareKey.key, publicKey);
return [encShareKey, new SymmetricCryptoKey(shareKey) as T]; return [encShareKey, shareKey as T];
} }
async setPrivateKey(encPrivateKey: EncryptedString): Promise<void> { 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> { 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; return (await this.stretchKey(pinKey)) as PinKey;
} }
@ -636,20 +644,16 @@ export class CryptoService implements CryptoServiceAbstraction {
return new SymmetricCryptoKey(masterKey) as MasterKey; return new SymmetricCryptoKey(masterKey) as MasterKey;
} }
async makeSendKey(keyMaterial: Uint8Array): Promise<SymmetricCryptoKey> { async makeSendKey(keyMaterial: CsprngArray): Promise<SymmetricCryptoKey> {
const sendKey = await this.cryptoFunctionService.hkdf( return await this.keyGenerationService.deriveKeyFromMaterial(
keyMaterial, keyMaterial,
"bitwarden-send", "bitwarden-send",
"send", "send",
64,
"sha256",
); );
return new SymmetricCryptoKey(sendKey);
} }
async makeCipherKey(): Promise<CipherKey> { async makeCipherKey(): Promise<CipherKey> {
const randomBytes = await this.cryptoFunctionService.aesGenerateKey(512); return (await this.keyGenerationService.createKey(512)) as CipherKey;
return new SymmetricCryptoKey(randomBytes) as CipherKey;
} }
async clearKeys(userId?: UserId): Promise<any> { async clearKeys(userId?: UserId): Promise<any> {
@ -802,8 +806,7 @@ export class CryptoService implements CryptoServiceAbstraction {
publicKey: string; publicKey: string;
privateKey: EncString; privateKey: EncString;
}> { }> {
const rawKey = await this.cryptoFunctionService.aesGenerateKey(512); const userKey = (await this.keyGenerationService.createKey(512)) as UserKey;
const userKey = new SymmetricCryptoKey(rawKey) as UserKey;
const [publicKey, privateKey] = await this.makeKeyPair(userKey); const [publicKey, privateKey] = await this.makeKeyPair(userKey);
await this.setUserKey(userKey); await this.setUserKey(userKey);
await this.activeUserEncryptedPrivateKeyState.update(() => privateKey.encryptedString); await this.activeUserEncryptedPrivateKeyState.update(() => privateKey.encryptedString);
@ -986,46 +989,6 @@ export class CryptoService implements CryptoServiceAbstraction {
return [new SymmetricCryptoKey(newSymKey) as T, protectedSymKey]; 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-- // --LEGACY METHODS--
// We previously used the master key for additional keys, but now we use the user key. // 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. // These methods support migrating the old keys to the new ones.

View File

@ -0,0 +1,102 @@
import { mock } from "jest-mock-extended";
import { KdfConfig } from "../../auth/models/domain/kdf-config";
import { CsprngArray } from "../../types/csprng";
import { CryptoFunctionService } from "../abstractions/crypto-function.service";
import { KdfType } from "../enums";
import { KeyGenerationService } from "./key-generation.service";
describe("KeyGenerationService", () => {
let sut: KeyGenerationService;
const cryptoFunctionService = mock<CryptoFunctionService>();
beforeEach(() => {
sut = new KeyGenerationService(cryptoFunctionService);
});
describe("createKey", () => {
test.each([256, 512])(
"it should delegate key creation to crypto function service",
async (bitLength: 256 | 512) => {
cryptoFunctionService.aesGenerateKey
.calledWith(bitLength)
.mockResolvedValue(new Uint8Array(bitLength / 8) as CsprngArray);
await sut.createKey(bitLength);
expect(cryptoFunctionService.aesGenerateKey).toHaveBeenCalledWith(bitLength);
},
);
});
describe("createMaterialAndKey", () => {
test.each([128, 192, 256, 512])(
"should create a 64 byte key from different material lengths",
async (bitLength: 128 | 192 | 256 | 512) => {
const inputMaterial = new Uint8Array(bitLength / 8) as CsprngArray;
const inputSalt = "salt";
const purpose = "purpose";
cryptoFunctionService.aesGenerateKey.calledWith(bitLength).mockResolvedValue(inputMaterial);
cryptoFunctionService.hkdf
.calledWith(inputMaterial, inputSalt, purpose, 64, "sha256")
.mockResolvedValue(new Uint8Array(64));
const { salt, material, derivedKey } = await sut.createKeyWithPurpose(
bitLength,
purpose,
inputSalt,
);
expect(salt).toEqual(inputSalt);
expect(material).toEqual(inputMaterial);
expect(derivedKey.key.length).toEqual(64);
},
);
});
describe("deriveKeyFromMaterial", () => {
it("should derive a 64 byte key from material", async () => {
const material = new Uint8Array(32) as CsprngArray;
const salt = "salt";
const purpose = "purpose";
cryptoFunctionService.hkdf.mockResolvedValue(new Uint8Array(64));
const key = await sut.deriveKeyFromMaterial(material, salt, purpose);
expect(key.key.length).toEqual(64);
});
});
describe("deriveKeyFromPassword", () => {
it("should derive a 32 byte key from a password using pbkdf2", async () => {
const password = "password";
const salt = "salt";
const kdf = KdfType.PBKDF2_SHA256;
const kdfConfig = new KdfConfig(600_000);
cryptoFunctionService.pbkdf2.mockResolvedValue(new Uint8Array(32));
const key = await sut.deriveKeyFromPassword(password, salt, kdf, kdfConfig);
expect(key.key.length).toEqual(32);
});
it("should derive a 32 byte key from a password using argon2id", async () => {
const password = "password";
const salt = "salt";
const kdf = KdfType.Argon2id;
const kdfConfig = new KdfConfig(600_000, 15);
cryptoFunctionService.hash.mockResolvedValue(new Uint8Array(32));
cryptoFunctionService.argon2.mockResolvedValue(new Uint8Array(32));
const key = await sut.deriveKeyFromPassword(password, salt, kdf, kdfConfig);
expect(key.key.length).toEqual(32);
});
});
});

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);
}
}

View File

@ -1,10 +1,10 @@
import { any, mock, MockProxy } from "jest-mock-extended"; import { any, mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom } from "rxjs"; import { BehaviorSubject, firstValueFrom } from "rxjs";
import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service";
import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service";
import { I18nService } from "../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { StateService } from "../../../platform/abstractions/state.service"; import { StateService } from "../../../platform/abstractions/state.service";
import { EncString } from "../../../platform/models/domain/enc-string"; import { EncString } from "../../../platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
@ -24,7 +24,7 @@ import { SendService } from "./send.service";
describe("SendService", () => { describe("SendService", () => {
const cryptoService = mock<CryptoService>(); const cryptoService = mock<CryptoService>();
const i18nService = mock<I18nService>(); const i18nService = mock<I18nService>();
const cryptoFunctionService = mock<CryptoFunctionService>(); const keyGenerationService = mock<KeyGenerationService>();
const encryptService = mock<EncryptService>(); const encryptService = mock<EncryptService>();
let sendService: SendService; let sendService: SendService;
@ -50,7 +50,7 @@ describe("SendService", () => {
.calledWith(any()) .calledWith(any())
.mockResolvedValue([sendView("1", "Test Send")]); .mockResolvedValue([sendView("1", "Test Send")]);
sendService = new SendService(cryptoService, i18nService, cryptoFunctionService, stateService); sendService = new SendService(cryptoService, i18nService, keyGenerationService, stateService);
}); });
afterEach(() => { afterEach(() => {

View File

@ -1,9 +1,10 @@
import { BehaviorSubject, Observable, concatMap, distinctUntilChanged, map } from "rxjs"; import { BehaviorSubject, Observable, concatMap, distinctUntilChanged, map } from "rxjs";
import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service";
import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { I18nService } from "../../../platform/abstractions/i18n.service"; import { I18nService } from "../../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { StateService } from "../../../platform/abstractions/state.service"; import { StateService } from "../../../platform/abstractions/state.service";
import { KdfType } from "../../../platform/enums";
import { Utils } from "../../../platform/misc/utils"; import { Utils } from "../../../platform/misc/utils";
import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer"; import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer";
import { EncString } from "../../../platform/models/domain/enc-string"; import { EncString } from "../../../platform/models/domain/enc-string";
@ -21,6 +22,9 @@ import { SEND_KDF_ITERATIONS } from "../send-kdf";
import { InternalSendService as InternalSendServiceAbstraction } from "./send.service.abstraction"; import { InternalSendService as InternalSendServiceAbstraction } from "./send.service.abstraction";
export class SendService implements InternalSendServiceAbstraction { export class SendService implements InternalSendServiceAbstraction {
readonly sendKeySalt = "bitwarden-send";
readonly sendKeyPurpose = "send";
protected _sends: BehaviorSubject<Send[]> = new BehaviorSubject([]); protected _sends: BehaviorSubject<Send[]> = new BehaviorSubject([]);
protected _sendViews: BehaviorSubject<SendView[]> = new BehaviorSubject([]); protected _sendViews: BehaviorSubject<SendView[]> = new BehaviorSubject([]);
@ -30,7 +34,7 @@ export class SendService implements InternalSendServiceAbstraction {
constructor( constructor(
private cryptoService: CryptoService, private cryptoService: CryptoService,
private i18nService: I18nService, private i18nService: I18nService,
private cryptoFunctionService: CryptoFunctionService, private keyGenerationService: KeyGenerationService,
private stateService: StateService, private stateService: StateService,
) { ) {
this.stateService.activeAccountUnlocked$ this.stateService.activeAccountUnlocked$
@ -72,17 +76,22 @@ export class SendService implements InternalSendServiceAbstraction {
send.hideEmail = model.hideEmail; send.hideEmail = model.hideEmail;
send.maxAccessCount = model.maxAccessCount; send.maxAccessCount = model.maxAccessCount;
if (model.key == null) { if (model.key == null) {
model.key = await this.cryptoFunctionService.aesGenerateKey(128); const key = await this.keyGenerationService.createKeyWithPurpose(
model.cryptoKey = await this.cryptoService.makeSendKey(model.key); 128,
this.sendKeyPurpose,
this.sendKeySalt,
);
model.key = key.material;
model.cryptoKey = key.derivedKey;
} }
if (password != null) { if (password != null) {
const passwordHash = await this.cryptoFunctionService.pbkdf2( const passwordKey = await this.keyGenerationService.deriveKeyFromPassword(
password, password,
model.key, model.key,
"sha256", KdfType.PBKDF2_SHA256,
SEND_KDF_ITERATIONS, { iterations: SEND_KDF_ITERATIONS },
); );
send.password = Utils.fromBufferToB64(passwordHash); send.password = passwordKey.keyB64;
} }
send.key = await this.cryptoService.encrypt(model.key, key); send.key = await this.cryptoService.encrypt(model.key, key);
send.name = await this.cryptoService.encrypt(model.name, model.cryptoKey); send.name = await this.cryptoService.encrypt(model.name, model.cryptoKey);