mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
[PM-22136] Implement SDK cipher encryption (#15337)
* [PM-22136] Update sdk cipher view map to support uknown uuid type * [PM-22136] Add key to CipherView for copying to SdkCipherView for encryption * [PM-22136] Add fromSdk* helpers to Cipher domain objects * [PM-22136] Add toSdk* helpers to Cipher View objects * [PM-22136] Add encrypt() to cipher encryption service * [PM-22136] Add feature flag * [PM-22136] Use new SDK encrypt method when feature flag is enabled * [PM-22136] Filter out null/empty URIs * [PM-22136] Change default value for cipher view arrays to []. See ADR-0014. * [PM-22136] Keep encrypted key value on attachment so that it is passed to the SDK * [PM-22136] Keep encrypted key value on CipherView so that it is passed to the SDK during encryption * [PM-22136] Update failing attachment test * [PM-22136] Update failing importer tests due to new default value for arrays * [PM-22136] Update CipherView.fromJson to handle the prototype of EncString for the cipher key * [PM-22136] Add tickets for followup work * [PM-22136] Use new set_fido2_credentials SDK method instead * [PM-22136] Fix missing prototype when decrypting Fido2Credentials * [PM-22136] Fix test after sdk change * [PM-22136] Update @bitwarden/sdk-internal version * [PM-22136] Fix some strict typing errors * [PM-23348] Migrate move cipher to org to SDK (#15567) * [PM-23348] Add moveToOrganization method to cipher-encryption.service.ts * [PM-23348] Use cipherEncryptionService.moveToOrganization in cipherService shareWithServer and shareManyWithServer methods * [PM-23348] Update cipherFormService to use the shareWithServer() method instead of encrypt() * [PM-23348] Fix typo * [PM-23348] Add missing docs * [PM-22136] Fix EncString import after merge with main
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import { EMPTY, catchError, firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { CipherListView } from "@bitwarden/sdk-internal";
|
||||
import { EncryptionContext } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import {
|
||||
CipherListView,
|
||||
BitwardenClient,
|
||||
CipherView as SdkCipherView,
|
||||
} from "@bitwarden/sdk-internal";
|
||||
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { SdkService } from "../../platform/abstractions/sdk/sdk.service";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { SdkService, asUuid } from "../../platform/abstractions/sdk/sdk.service";
|
||||
import { UserId, OrganizationId } from "../../types/guid";
|
||||
import { CipherEncryptionService } from "../abstractions/cipher-encryption.service";
|
||||
import { CipherType } from "../enums";
|
||||
import { Cipher } from "../models/domain/cipher";
|
||||
@@ -18,6 +23,67 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
|
||||
private logService: LogService,
|
||||
) {}
|
||||
|
||||
async encrypt(model: CipherView, userId: UserId): Promise<EncryptionContext | undefined> {
|
||||
return firstValueFrom(
|
||||
this.sdkService.userClient$(userId).pipe(
|
||||
map((sdk) => {
|
||||
if (!sdk) {
|
||||
throw new Error("SDK not available");
|
||||
}
|
||||
|
||||
using ref = sdk.take();
|
||||
const sdkCipherView = this.toSdkCipherView(model, ref.value);
|
||||
|
||||
const encryptionContext = ref.value.vault().ciphers().encrypt(sdkCipherView);
|
||||
|
||||
return {
|
||||
cipher: Cipher.fromSdkCipher(encryptionContext.cipher)!,
|
||||
encryptedFor: asUuid<UserId>(encryptionContext.encryptedFor),
|
||||
};
|
||||
}),
|
||||
catchError((error: unknown) => {
|
||||
this.logService.error(`Failed to encrypt cipher: ${error}`);
|
||||
return EMPTY;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async moveToOrganization(
|
||||
model: CipherView,
|
||||
organizationId: OrganizationId,
|
||||
userId: UserId,
|
||||
): Promise<EncryptionContext | undefined> {
|
||||
return firstValueFrom(
|
||||
this.sdkService.userClient$(userId).pipe(
|
||||
map((sdk) => {
|
||||
if (!sdk) {
|
||||
throw new Error("SDK not available");
|
||||
}
|
||||
|
||||
using ref = sdk.take();
|
||||
const sdkCipherView = this.toSdkCipherView(model, ref.value);
|
||||
|
||||
const movedCipherView = ref.value
|
||||
.vault()
|
||||
.ciphers()
|
||||
.move_to_organization(sdkCipherView, asUuid(organizationId));
|
||||
|
||||
const encryptionContext = ref.value.vault().ciphers().encrypt(movedCipherView);
|
||||
|
||||
return {
|
||||
cipher: Cipher.fromSdkCipher(encryptionContext.cipher)!,
|
||||
encryptedFor: asUuid<UserId>(encryptionContext.encryptedFor),
|
||||
};
|
||||
}),
|
||||
catchError((error: unknown) => {
|
||||
this.logService.error(`Failed to move cipher to organization: ${error}`);
|
||||
return EMPTY;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
async decrypt(cipher: Cipher, userId: UserId): Promise<CipherView> {
|
||||
return firstValueFrom(
|
||||
this.sdkService.userClient$(userId).pipe(
|
||||
@@ -51,11 +117,8 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
|
||||
clientCipherView.login.fido2Credentials = fido2CredentialViews
|
||||
.map((f) => {
|
||||
const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!;
|
||||
|
||||
return {
|
||||
...view,
|
||||
keyValue: decryptedKeyValue,
|
||||
};
|
||||
view.keyValue = decryptedKeyValue;
|
||||
return view;
|
||||
})
|
||||
.filter((view): view is Fido2CredentialView => view !== undefined);
|
||||
}
|
||||
@@ -104,10 +167,8 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
|
||||
clientCipherView.login.fido2Credentials = fido2CredentialViews
|
||||
.map((f) => {
|
||||
const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!;
|
||||
return {
|
||||
...view,
|
||||
keyValue: decryptedKeyValue,
|
||||
};
|
||||
view.keyValue = decryptedKeyValue;
|
||||
return view;
|
||||
})
|
||||
.filter((view): view is Fido2CredentialView => view !== undefined);
|
||||
}
|
||||
@@ -187,4 +248,25 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to convert a CipherView model to an SDK CipherView. Has special handling for Fido2 credentials
|
||||
* that need to be encrypted before being sent to the SDK.
|
||||
* @param model The CipherView model to convert
|
||||
* @param sdk An instance of SDK client
|
||||
* @private
|
||||
*/
|
||||
private toSdkCipherView(model: CipherView, sdk: BitwardenClient): SdkCipherView {
|
||||
let sdkCipherView = model.toSdkCipherView();
|
||||
|
||||
if (model.type === CipherType.Login && model.login?.hasFido2Credentials) {
|
||||
// Encrypt Fido2 credentials separately
|
||||
const fido2Credentials = model.login.fido2Credentials?.map((f) =>
|
||||
f.toSdkFido2CredentialFullView(),
|
||||
);
|
||||
sdkCipherView = sdk.vault().ciphers().set_fido2_credentials(sdkCipherView, fido2Credentials);
|
||||
}
|
||||
|
||||
return sdkCipherView;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user