mirror of
https://github.com/bitwarden/browser
synced 2026-02-17 09:59:41 +00:00
WIP
This removes all bulk encryption/decryption.
This commit is contained in:
@@ -160,7 +160,10 @@ import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwar
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
|
||||
import {
|
||||
SdkClientFactory,
|
||||
SdkPureClientFactory,
|
||||
} from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
|
||||
import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
@@ -181,6 +184,8 @@ import { ConfigApiService } from "@bitwarden/common/platform/services/config/con
|
||||
import { DefaultConfigService } from "@bitwarden/common/platform/services/config/default-config.service";
|
||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { BulkEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/bulk-encrypt.service.implementation";
|
||||
import { FallbackBulkEncryptService } from "@bitwarden/common/platform/services/cryptography/fallback-bulk-encrypt.service";
|
||||
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
|
||||
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
|
||||
import { DefaultBroadcasterService } from "@bitwarden/common/platform/services/default-broadcaster.service";
|
||||
import { DefaultEnvironmentService } from "@bitwarden/common/platform/services/default-environment.service";
|
||||
@@ -886,13 +891,13 @@ const safeProviders: SafeProvider[] = [
|
||||
}),
|
||||
safeProvider({
|
||||
provide: EncryptService,
|
||||
useClass: MultithreadEncryptServiceImplementation,
|
||||
deps: [CryptoFunctionServiceAbstraction, LogService, LOG_MAC_FAILURES],
|
||||
useClass: EncryptServiceImplementation,
|
||||
deps: [SdkPureClientFactory, CryptoFunctionServiceAbstraction, LogService, LOG_MAC_FAILURES],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: BulkEncryptService,
|
||||
useClass: BulkEncryptServiceImplementation,
|
||||
deps: [CryptoFunctionServiceAbstraction, LogService],
|
||||
useClass: FallbackBulkEncryptService,
|
||||
deps: [EncryptService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: EventUploadServiceAbstraction,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BitwardenClient } from "@bitwarden/sdk-internal";
|
||||
import type { BitwardenClient, BitwardenPure } from "@bitwarden/sdk-internal";
|
||||
|
||||
/**
|
||||
* Factory for creating SDK clients.
|
||||
@@ -8,3 +8,7 @@ export abstract class SdkClientFactory {
|
||||
...args: ConstructorParameters<typeof BitwardenClient>
|
||||
): Promise<BitwardenClient>;
|
||||
}
|
||||
|
||||
export abstract class SdkPureClientFactory {
|
||||
abstract createPureSdkClient(): Promise<BitwardenPure>;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { SymmetricCryptoKey } from "./symmetric-crypto-key";
|
||||
export const DECRYPT_ERROR = "[error: cannot decrypt]";
|
||||
|
||||
export class EncString implements Encrypted {
|
||||
encryptedString?: EncryptedString;
|
||||
encryptedString: EncryptedString;
|
||||
encryptionType?: EncryptionType;
|
||||
decryptedValue?: string;
|
||||
data?: string;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports -- TODO MDG: fix this
|
||||
import { SdkPureClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory";
|
||||
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { CryptoFunctionService } from "../../abstractions/crypto-function.service";
|
||||
import { EncryptService } from "../../abstractions/encrypt.service";
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { EncryptionType, encryptionTypeToString as encryptionTypeName } from "../../enums";
|
||||
import { EncryptionType } from "../../enums";
|
||||
import { Decryptable } from "../../interfaces/decryptable.interface";
|
||||
import { Encrypted } from "../../interfaces/encrypted";
|
||||
import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface";
|
||||
@@ -15,6 +19,7 @@ import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
|
||||
|
||||
export class EncryptServiceImplementation implements EncryptService {
|
||||
constructor(
|
||||
protected readonly sdkPureClientFactory: SdkPureClientFactory,
|
||||
protected cryptoFunctionService: CryptoFunctionService,
|
||||
protected logService: LogService,
|
||||
protected logMacFailures: boolean,
|
||||
@@ -76,56 +81,10 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
|
||||
key = this.resolveLegacyKey(key, encString);
|
||||
|
||||
// DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality.
|
||||
if (key.macKey != null && encString?.mac == null) {
|
||||
this.logService.error(
|
||||
"[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
"Payload type " +
|
||||
encryptionTypeName(encString.encryptionType),
|
||||
"Decrypt context: " + decryptContext,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const pure = await this.sdkPureClientFactory.createPureSdkClient();
|
||||
const decrypted = pure.crypto().symmetric_decrypt(encString.encryptedString, key.keyB64);
|
||||
|
||||
if (key.encType !== encString.encryptionType) {
|
||||
this.logService.error(
|
||||
"[Encrypt service] Key encryption type does not match payload encryption type. Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
"Payload type " +
|
||||
encryptionTypeName(encString.encryptionType),
|
||||
"Decrypt context: " + decryptContext,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(
|
||||
encString.data,
|
||||
encString.iv,
|
||||
encString.mac,
|
||||
key,
|
||||
);
|
||||
if (fastParams.macKey != null && fastParams.mac != null) {
|
||||
const computedMac = await this.cryptoFunctionService.hmacFast(
|
||||
fastParams.macData,
|
||||
fastParams.macKey,
|
||||
"sha256",
|
||||
);
|
||||
const macsEqual = await this.cryptoFunctionService.compareFast(fastParams.mac, computedMac);
|
||||
if (!macsEqual) {
|
||||
this.logMacFailed(
|
||||
"[Encrypt service] MAC comparison failed. Key or payload has changed. Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
"Payload type " +
|
||||
encryptionTypeName(encString.encryptionType) +
|
||||
" Decrypt context: " +
|
||||
decryptContext,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return await this.cryptoFunctionService.aesDecryptFast(fastParams, "cbc");
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
async decryptToBytes(encThing: Encrypted, key: SymmetricCryptoKey): Promise<Uint8Array> {
|
||||
@@ -139,64 +98,18 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
|
||||
key = this.resolveLegacyKey(key, encThing);
|
||||
|
||||
// DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality.
|
||||
if (key.macKey != null && encThing.macBytes == null) {
|
||||
this.logService.error(
|
||||
"[Encrypt service] Key has mac key but payload is missing mac bytes. Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
" Payload type " +
|
||||
encryptionTypeName(encThing.encryptionType),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (key.encType !== encThing.encryptionType) {
|
||||
this.logService.error(
|
||||
"[Encrypt service] Key encryption type does not match payload encryption type. Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
" Payload type " +
|
||||
encryptionTypeName(encThing.encryptionType),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (key.macKey != null && encThing.macBytes != null) {
|
||||
const macData = new Uint8Array(encThing.ivBytes.byteLength + encThing.dataBytes.byteLength);
|
||||
macData.set(new Uint8Array(encThing.ivBytes), 0);
|
||||
macData.set(new Uint8Array(encThing.dataBytes), encThing.ivBytes.byteLength);
|
||||
const computedMac = await this.cryptoFunctionService.hmac(macData, key.macKey, "sha256");
|
||||
if (computedMac === null) {
|
||||
this.logMacFailed(
|
||||
"[Encrypt service] Failed to compute MAC." +
|
||||
" Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
" Payload type " +
|
||||
encryptionTypeName(encThing.encryptionType),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const macsMatch = await this.cryptoFunctionService.compare(encThing.macBytes, computedMac);
|
||||
if (!macsMatch) {
|
||||
this.logMacFailed(
|
||||
"[Encrypt service] MAC comparison failed. Key or payload has changed." +
|
||||
" Key type " +
|
||||
encryptionTypeName(key.encType) +
|
||||
" Payload type " +
|
||||
encryptionTypeName(encThing.encryptionType),
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.cryptoFunctionService.aesDecrypt(
|
||||
encThing.dataBytes,
|
||||
encThing.ivBytes,
|
||||
key.encKey,
|
||||
"cbc",
|
||||
const pure = await this.sdkPureClientFactory.createPureSdkClient();
|
||||
const encString = new EncString(
|
||||
encThing.encryptionType,
|
||||
Utils.fromBufferToB64(encThing.dataBytes),
|
||||
Utils.fromBufferToB64(encThing.ivBytes),
|
||||
Utils.fromBufferToB64(encThing.macBytes),
|
||||
);
|
||||
const decrypted = pure
|
||||
.crypto()
|
||||
.symmetric_decrypt_to_bytes(encString.encryptedString, key.keyB64);
|
||||
|
||||
return result ?? null;
|
||||
return decrypted ?? null;
|
||||
}
|
||||
|
||||
async rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString> {
|
||||
@@ -262,18 +175,18 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
}
|
||||
|
||||
private async aesEncrypt(data: Uint8Array, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
||||
const pure = await this.sdkPureClientFactory.createPureSdkClient();
|
||||
const encString = new EncString(
|
||||
pure.crypto().symmetric_encrypt(Utils.fromBufferToUtf8(data), key.keyB64),
|
||||
);
|
||||
const obj = new EncryptedObject();
|
||||
obj.key = key;
|
||||
obj.iv = await this.cryptoFunctionService.randomBytes(16);
|
||||
obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, obj.key.encKey);
|
||||
obj.iv = encString.ivBytes;
|
||||
obj.data = encString.dataBytes;
|
||||
|
||||
if (obj.key.macKey != null) {
|
||||
const macData = new Uint8Array(obj.iv.byteLength + obj.data.byteLength);
|
||||
macData.set(new Uint8Array(obj.iv), 0);
|
||||
macData.set(new Uint8Array(obj.data), obj.iv.byteLength);
|
||||
obj.mac = await this.cryptoFunctionService.hmac(macData, obj.key.macKey, "sha256");
|
||||
obj.mac = encString.macBytes;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports -- TODO MDG: this is a bug in the restricted import rule
|
||||
import { DefaultSdkPureClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory";
|
||||
|
||||
import { Decryptable } from "../../interfaces/decryptable.interface";
|
||||
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
|
||||
import { ConsoleLogService } from "../console-log.service";
|
||||
@@ -22,7 +25,13 @@ let encryptService: EncryptServiceImplementation;
|
||||
export function init() {
|
||||
const cryptoFunctionService = new WebCryptoFunctionService(self);
|
||||
const logService = new ConsoleLogService(false);
|
||||
encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true);
|
||||
const pureSdkFactory = new DefaultSdkPureClientFactory();
|
||||
encryptService = new EncryptServiceImplementation(
|
||||
pureSdkFactory,
|
||||
cryptoFunctionService,
|
||||
logService,
|
||||
true,
|
||||
);
|
||||
|
||||
const bitwardenContainerService = new ContainerService(null, encryptService);
|
||||
bitwardenContainerService.attachToGlobal(self);
|
||||
|
||||
@@ -22,11 +22,11 @@ export class FallbackBulkEncryptService implements BulkEncryptService {
|
||||
items: Decryptable<T>[],
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<T[]> {
|
||||
if (this.featureFlagEncryptService != null) {
|
||||
return await this.featureFlagEncryptService.decryptItems(items, key);
|
||||
} else {
|
||||
return await this.encryptService.decryptItems(items, key);
|
||||
}
|
||||
return await this.encryptService.decryptItems(items, key);
|
||||
// if (this.featureFlagEncryptService != null) {
|
||||
// return await this.featureFlagEncryptService.decryptItems(items, key);
|
||||
// } else {
|
||||
// }
|
||||
}
|
||||
|
||||
async setFeatureFlagEncryptService(featureFlagEncryptService: BulkEncryptService) {
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import * as sdk from "@bitwarden/sdk-internal";
|
||||
import * as module from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm";
|
||||
|
||||
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
|
||||
import { SdkClientFactory, SdkPureClientFactory } from "../../abstractions/sdk/sdk-client-factory";
|
||||
|
||||
export class DefaultSdkPureClientFactory implements SdkPureClientFactory {
|
||||
async createPureSdkClient(): Promise<sdk.BitwardenPure> {
|
||||
(sdk as any).init(module);
|
||||
|
||||
return Promise.resolve(new sdk.BitwardenPure());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly imports the Bitwarden SDK and initializes it.
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { BitwardenClient } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
|
||||
import { SdkClientFactory, SdkPureClientFactory } from "../../abstractions/sdk/sdk-client-factory";
|
||||
|
||||
export class NoopSdkPureClientFactory implements SdkPureClientFactory {
|
||||
createPureSdkClient(): Promise<never> {
|
||||
return Promise.reject(new Error("SDK not available"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Noop SDK client factory.
|
||||
|
||||
Reference in New Issue
Block a user