[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:
parent
071959317c
commit
19a373d87e
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
|
||||||
|
|
||||||
export interface AbstractKeyGenerationService {
|
|
||||||
makeEphemeralKey(numBytes?: number): Promise<SymmetricCryptoKey>;
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 () => {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>;
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue