From 5f5416651996beffd5c223fe8be416ef3fb56c5a Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 23 Apr 2025 13:43:20 +0200 Subject: [PATCH] wip --- apps/desktop/desktop_native/objc/src/lib.rs | 8 +- .../src/services/jslib-services.module.ts | 3 + libs/common/src/enums/feature-flag.enum.ts | 2 +- .../abstractions/cipher-encryption.service.ts | 9 +- .../src/vault/services/cipher.service.ts | 24 ++++- .../default-cipher-encryption.service.ts | 95 ++++++++++--------- 6 files changed, 85 insertions(+), 56 deletions(-) diff --git a/apps/desktop/desktop_native/objc/src/lib.rs b/apps/desktop/desktop_native/objc/src/lib.rs index f5a7623cfc3..eb969cb5f56 100644 --- a/apps/desktop/desktop_native/objc/src/lib.rs +++ b/apps/desktop/desktop_native/objc/src/lib.rs @@ -68,13 +68,13 @@ mod objc { use super::*; - unsafe extern "C" { - pub unsafe fn runCommand(context: *mut c_void, value: *const c_char); - pub unsafe fn freeObjCString(value: &ObjCString); + extern "C" { + pub fn runCommand(context: *mut c_void, value: *const c_char); + pub fn freeObjCString(value: &ObjCString); } /// This function is called from the ObjC code to return the output of the command - #[unsafe(no_mangle)] + #[no_mangle] pub extern "C" fn commandReturn(context: &mut CommandContext, value: ObjCString) -> bool { let value: String = match value.try_into() { Ok(value) => value, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 2dcc2d5a1b2..0da368110d6 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -508,6 +508,7 @@ const safeProviders: SafeProvider[] = [ accountService: AccountServiceAbstraction, logService: LogService, cipherEncryptionService: CipherEncryptionService, + sdkService: SdkService, ) => new CipherService( keyService, @@ -525,6 +526,7 @@ const safeProviders: SafeProvider[] = [ accountService, logService, cipherEncryptionService, + sdkService, ), deps: [ KeyService, @@ -542,6 +544,7 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, LogService, CipherEncryptionService, + SdkService, ], }), safeProvider({ diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 6ff919bd720..a3a8b6946de 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -108,7 +108,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.SecurityTasks]: FALSE, [FeatureFlag.CipherKeyEncryption]: FALSE, [FeatureFlag.EndUserNotifications]: FALSE, - [FeatureFlag.PM19941MigrateCipherDomainToSdk]: FALSE, + [FeatureFlag.PM19941MigrateCipherDomainToSdk]: true, /* Auth */ [FeatureFlag.PM9112_DeviceApprovalPersistence]: FALSE, diff --git a/libs/common/src/vault/abstractions/cipher-encryption.service.ts b/libs/common/src/vault/abstractions/cipher-encryption.service.ts index bb9a7bd4843..2f25b41bea8 100644 --- a/libs/common/src/vault/abstractions/cipher-encryption.service.ts +++ b/libs/common/src/vault/abstractions/cipher-encryption.service.ts @@ -1,5 +1,6 @@ -import { CipherListView } from "@bitwarden/sdk-internal"; +import { BitwardenClient, CipherListView } from "@bitwarden/sdk-internal"; +import { Ref } from "../../platform/misc/reference-counting/rc"; import { UserId } from "../../types/guid"; import { Attachment } from "../models/domain/attachment"; import { Cipher } from "../models/domain/cipher"; @@ -17,7 +18,11 @@ export abstract class CipherEncryptionService { * * @returns A promise that resolves to the decrypted cipher view */ - abstract decrypt(cipher: Cipher, userId: UserId): Promise; + abstract decrypt( + cipher: Cipher, + userId: UserId, + sdkClient: Ref, + ): Promise; /** * Decrypts a list of ciphers using the SDK for the given userId. * diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 29d4acf5cc5..8d3f9de7532 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { combineLatest, filter, firstValueFrom, map, Observable, Subject, switchMap } from "rxjs"; @@ -20,6 +21,7 @@ import { ListResponse } from "../../models/response/list.response"; import { View } from "../../models/view/view"; import { ConfigService } from "../../platform/abstractions/config/config.service"; import { I18nService } from "../../platform/abstractions/i18n.service"; +import { SdkService } from "../../platform/abstractions/sdk/sdk.service"; import { StateService } from "../../platform/abstractions/state.service"; import { Utils } from "../../platform/misc/utils"; import Domain from "../../platform/models/domain/domain-base"; @@ -105,6 +107,7 @@ export class CipherService implements CipherServiceAbstraction { private accountService: AccountService, private logService: LogService, private cipherEncryptionService: CipherEncryptionService, + private sdkService: SdkService, ) {} localData$(userId: UserId): Observable> { @@ -489,7 +492,9 @@ export class CipherService implements CipherServiceAbstraction { */ async decrypt(cipher: Cipher, userId: UserId): Promise { if (await this.configService.getFeatureFlag(FeatureFlag.PM19941MigrateCipherDomainToSdk)) { - return await this.cipherEncryptionService.decrypt(cipher, userId); + const sdkClient = await firstValueFrom(this.sdkService.userClient$(userId)); + using ref = sdkClient.take(); + return await this.cipherEncryptionService.decrypt(cipher, userId, ref); } else { const encKey = await this.getKeyForCipherKeyDecryption(cipher, userId); return await cipher.decrypt(encKey); @@ -1863,9 +1868,24 @@ export class CipherService implements CipherServiceAbstraction { ciphers: Cipher[], userId: UserId, ): Promise<[CipherView[], CipherView[]]> { + const sdkClient = await firstValueFrom(this.sdkService.userClient$(userId)); + using ref = sdkClient.take(); + + console.log("Decrypting ciphers with SDK: Promise.all"); + console.time("promise-all-decryption"); const decryptedViews = await Promise.all( - ciphers.map((cipher) => this.cipherEncryptionService.decrypt(cipher, userId)), + ciphers.map((cipher) => this.cipherEncryptionService.decrypt(cipher, userId, ref)), ); + console.timeEnd("promise-all-decryption"); + + console.log("Decrypting ciphers with SDK: FOR-OF Loop"); + console.time("for-of-decryption"); + const decryptedViews_loop = []; + for (const cipher of ciphers) { + const decryptedView = await this.cipherEncryptionService.decrypt(cipher, userId, ref); + decryptedViews_loop.push(decryptedView); + } + console.timeEnd("for-of-decryption"); const successful: CipherView[] = []; const failed: CipherView[] = []; diff --git a/libs/common/src/vault/services/default-cipher-encryption.service.ts b/libs/common/src/vault/services/default-cipher-encryption.service.ts index 915cb06ecd0..b3fda6b5b48 100644 --- a/libs/common/src/vault/services/default-cipher-encryption.service.ts +++ b/libs/common/src/vault/services/default-cipher-encryption.service.ts @@ -1,9 +1,10 @@ import { EMPTY, catchError, firstValueFrom, map, of } from "rxjs"; -import { CipherListView } from "@bitwarden/sdk-internal"; +import { BitwardenClient, CipherListView } from "@bitwarden/sdk-internal"; import { LogService } from "../../platform/abstractions/log.service"; import { SdkService } from "../../platform/abstractions/sdk/sdk.service"; +import { Ref } from "../../platform/misc/reference-counting/rc"; import { UserId } from "../../types/guid"; import { CipherEncryptionService } from "../abstractions/cipher-encryption.service"; import { CipherType } from "../enums"; @@ -21,61 +22,61 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService { /** * {@inheritdoc} */ - async decrypt(cipher: Cipher, userId: UserId): Promise { - return firstValueFrom( - this.sdkService.userClient$(userId).pipe( - map((sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } + async decrypt(cipher: Cipher, userId: UserId, ref: Ref): Promise { + // return firstValueFrom( + // this.sdkService.userClient$(userId).pipe( + // map((sdk) => { + // if (!sdk) { + // throw new Error("SDK not available"); + // } - using ref = sdk.take(); - const sdkCipherView = ref.value.vault().ciphers().decrypt(cipher.toSdkCipher()); + // using ref = sdk.take(); + const sdkCipherView = ref.value.vault().ciphers().decrypt(cipher.toSdkCipher()); - const clientCipherView = CipherView.fromSdkCipherView(sdkCipherView)!; + const clientCipherView = CipherView.fromSdkCipherView(sdkCipherView)!; - // Decrypt Fido2 credentials if available - if ( - clientCipherView.type === CipherType.Login && - sdkCipherView.login?.fido2Credentials?.length - ) { - const fido2CredentialViews = ref.value - .vault() - .ciphers() - .decrypt_fido2_credentials(sdkCipherView); + // Decrypt Fido2 credentials if available + if ( + clientCipherView.type === CipherType.Login && + sdkCipherView.login?.fido2Credentials?.length + ) { + const fido2CredentialViews = ref.value + .vault() + .ciphers() + .decrypt_fido2_credentials(sdkCipherView); - // TEMPORARY: Manually decrypt the keyValue for Fido2 credentials since don't currently use the SDK for Fido2 Authentication. - const decryptedKeyValue = ref.value - .vault() - .ciphers() - .decrypt_fido2_private_key(sdkCipherView); + // TEMPORARY: Manually decrypt the keyValue for Fido2 credentials since don't currently use the SDK for Fido2 Authentication. + const decryptedKeyValue = ref.value + .vault() + .ciphers() + .decrypt_fido2_private_key(sdkCipherView); - clientCipherView.login.fido2Credentials = fido2CredentialViews - .map((f) => { - const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!; + clientCipherView.login.fido2Credentials = fido2CredentialViews + .map((f) => { + const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!; - return { - ...view, - keyValue: decryptedKeyValue, - }; - }) - .filter((view): view is Fido2CredentialView => view !== undefined); - } + return { + ...view, + keyValue: decryptedKeyValue, + }; + }) + .filter((view): view is Fido2CredentialView => view !== undefined); + } - return clientCipherView; - }), - catchError((error: unknown) => { - this.logService.error(`Failed to decrypt cipher ${cipher.id}: ${error}`); + return clientCipherView; + // }), + // catchError((error: unknown) => { + // this.logService.error(`Failed to decrypt cipher ${cipher.id}: ${error}`); - // Return failed cipher view - const failedView = new CipherView(cipher); - failedView.decryptionFailure = true; - failedView.name = "[error: cannot decrypt]"; + // // Return failed cipher view + // const failedView = new CipherView(cipher); + // failedView.decryptionFailure = true; + // failedView.name = "[error: cannot decrypt]"; - return of(failedView); - }), - ), - ); + // return of(failedView); + // }), + // ), + // ); } /**