1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 13:10:17 +00:00
This commit is contained in:
Andreas Coroiu
2025-04-23 13:43:20 +02:00
parent 2833ec10ca
commit 5f54166519
6 changed files with 85 additions and 56 deletions

View File

@@ -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,

View File

@@ -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({

View File

@@ -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,

View File

@@ -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<CipherView>;
abstract decrypt(
cipher: Cipher,
userId: UserId,
sdkClient: Ref<BitwardenClient>,
): Promise<CipherView>;
/**
* Decrypts a list of ciphers using the SDK for the given userId.
*

View File

@@ -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<Record<CipherId, LocalData>> {
@@ -489,7 +492,9 @@ export class CipherService implements CipherServiceAbstraction {
*/
async decrypt(cipher: Cipher, userId: UserId): Promise<CipherView> {
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[] = [];

View File

@@ -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<CipherView> {
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<BitwardenClient>): Promise<CipherView> {
// 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);
// }),
// ),
// );
}
/**