From 5c4c6cdea168d54a38978a3843267d4fb810bc99 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 5 Mar 2025 16:42:04 -0800 Subject: [PATCH] Push decryption to SDK and prep for feature flag control --- libs/common/src/enums/feature-flag.enum.ts | 6 ++ .../crypto/abstractions/encrypt.service.ts | 5 +- .../encrypt.service.implementation.ts | 58 ++++++++++++++----- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index c08c4e7eef4..4527a0d1cb6 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -24,6 +24,9 @@ export enum FeatureFlag { NotificationRefresh = "notification-refresh", UseTreeWalkerApiForPageDetailsCollection = "use-tree-walker-api-for-page-details-collection", + /* Key Management */ + UseSDKForDecryption = "use-sdk-for-decryption", + /* Tools */ ItemShare = "item-share", CriticalApps = "pm-14466-risk-insights-critical-application", @@ -81,6 +84,9 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.NotificationRefresh]: FALSE, [FeatureFlag.UseTreeWalkerApiForPageDetailsCollection]: FALSE, + /* Key Management */ + [FeatureFlag.UseSDKForDecryption]: FALSE, + /* Tools */ [FeatureFlag.ItemShare]: FALSE, [FeatureFlag.CriticalApps]: FALSE, diff --git a/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts index 484327bcd27..a8004019b08 100644 --- a/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts +++ b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts @@ -1,3 +1,5 @@ +import { OnServerConfigChange } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config"; import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; import { Encrypted } from "@bitwarden/common/platform/interfaces/encrypted"; import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; @@ -5,7 +7,7 @@ import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-arr import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -export abstract class EncryptService { +export abstract class EncryptService implements OnServerConfigChange { abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise; abstract encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise; /** @@ -55,4 +57,5 @@ export abstract class EncryptService { value: string | Uint8Array, algorithm: "sha1" | "sha256" | "sha512", ): Promise; + abstract onServerConfigChange(newConfig: ServerConfig): void; } diff --git a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts index 8a001886837..5489b828dba 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts @@ -14,16 +14,31 @@ import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-arr import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { EncryptedObject } from "@bitwarden/common/platform/models/domain/encrypted-object"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { PureCrypto } from "@bitwarden/sdk-internal"; +import { + DefaultFeatureFlagValue, + FeatureFlag, + getFeatureFlagValue, +} from "../../../enums/feature-flag.enum"; +import { ServerConfig } from "../../../platform/abstractions/config/server-config"; import { EncryptService } from "../abstractions/encrypt.service"; export class EncryptServiceImplementation implements EncryptService { + private useSDKForDecryption: boolean = DefaultFeatureFlagValue[FeatureFlag.UseSDKForDecryption]; + constructor( protected cryptoFunctionService: CryptoFunctionService, protected logService: LogService, protected logMacFailures: boolean, ) {} + onServerConfigChange(newConfig: ServerConfig): void { + const old = this.useSDKForDecryption; + this.useSDKForDecryption = getFeatureFlagValue(newConfig, FeatureFlag.UseSDKForDecryption); + this.logService.debug("updated sdk decryption flag", old, this.useSDKForDecryption); + } + async encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise { if (key == null) { throw new Error("No encryption key provided."); @@ -53,20 +68,7 @@ export class EncryptServiceImplementation implements EncryptService { } const encValue = await this.aesEncrypt(plainValue, key); - let macLen = 0; - if (encValue.mac != null) { - macLen = encValue.mac.byteLength; - } - - const encBytes = new Uint8Array(1 + encValue.iv.byteLength + macLen + encValue.data.byteLength); - encBytes.set([encValue.key.encType]); - encBytes.set(new Uint8Array(encValue.iv), 1); - if (encValue.mac != null) { - encBytes.set(new Uint8Array(encValue.mac), 1 + encValue.iv.byteLength); - } - - encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + macLen); - return new EncArrayBuffer(encBytes); + return EncArrayBuffer.fromParts(encValue.key.encType, encValue.iv, encValue.data, encValue.mac); } async decryptToUtf8( @@ -74,6 +76,15 @@ export class EncryptServiceImplementation implements EncryptService { key: SymmetricCryptoKey, decryptContext: string = "no context", ): Promise { + if (this.useSDKForDecryption) { + this.logService.debug("decrypting with SDK"); + if (encString == null || encString.encryptedString == null) { + throw new Error("encString is null or undefined"); + } + return PureCrypto.symmetric_decrypt(encString.encryptedString, key.keyB64); + } + this.logService.debug("decrypting with javascript"); + if (key == null) { throw new Error("No key provided for decryption."); } @@ -137,6 +148,25 @@ export class EncryptServiceImplementation implements EncryptService { key: SymmetricCryptoKey, decryptContext: string = "no context", ): Promise { + if (this.useSDKForDecryption) { + this.logService.debug("decrypting bytes with SDK"); + if ( + encThing.encryptionType == null || + encThing.ivBytes == null || + encThing.dataBytes == null + ) { + throw new Error("Cannot decrypt, missing type, IV, or data bytes."); + } + const buffer = EncArrayBuffer.fromParts( + encThing.encryptionType, + encThing.ivBytes, + encThing.dataBytes, + encThing.macBytes, + ).buffer; + return PureCrypto.symmetric_decrypt_array_buffer(buffer, key.keyB64); + } + this.logService.debug("decrypting bytes with javascript"); + if (key == null) { throw new Error("No encryption key provided."); }