1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-12423] Migrate Cipher Decryption to Use SDK (#14206)

* Created mappings for client domain object to SDK

* Add abstract decrypt observable

* Added todo for future consideration

* Added implementation to cipher service

* Added adapter and unit tests

* Created cipher encryption abstraction and service

* Register cipher encryption service

* Added tests for the cipher encryption service

* changed signature

* Updated feature flag name

* added new function to be used for decrypting ciphers

* Added new encryptedKey field

* added new function to be used for decrypting ciphers

* Manually set fields

* Added encrypted key in attachment view

* Fixed test

* Updated references to use decrypt with feature flag

* Added dependency

* updated package.json

* lint fix

* fixed tests

* Fixed small mapping issues

* Fixed test

* Added function to decrypt fido2 key value

* Added function to decrypt fido2 key value and updated test

* updated to use sdk function without prociding the key

* updated localdata sdk type change

* decrypt attachment content using sdk

* Fixed dependency issues

* updated package.json

* Refactored service to handle getting decrypted buffer using the legacy and sdk implementations

* updated services and component to use refactored version

* Updated decryptCiphersWithSdk to use decryptManyLegacy for batch decryption, ensuring the SDK is only called once per batch

* Fixed merge conflicts

* Fixed merge conflicts

* Fixed merge conflicts

* Fixed lint issues

* Moved getDecryptedAttachmentBuffer to cipher service

* Moved getDecryptedAttachmentBuffer to cipher service

* ensure CipherView properties are null instead of undefined

* Fixed test

* ensure AttachmentView properties are null instead of undefined

* Linked ticket in comment

* removed unused orgKey
This commit is contained in:
SmithThe4th
2025-05-14 10:30:01 -04:00
committed by GitHub
parent 3e0cc7ca7f
commit ad3121f535
85 changed files with 2171 additions and 218 deletions

View File

@@ -10,7 +10,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
@@ -172,6 +172,8 @@ describe("VaultExportService", () => {
let apiService: MockProxy<ApiService>;
let fetchMock: jest.Mock;
const userId = "" as UserId;
beforeEach(() => {
cryptoFunctionService = mock<CryptoFunctionService>();
cipherService = mock<CipherService>();
@@ -185,7 +187,6 @@ describe("VaultExportService", () => {
keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any));
const userId = "" as UserId;
const accountInfo: AccountInfo = {
email: "",
emailVerified: true,
@@ -338,7 +339,9 @@ describe("VaultExportService", () => {
cipherService.getAllDecrypted.mockResolvedValue([cipherView]);
folderService.getAllDecryptedFromState.mockResolvedValue([]);
encryptService.decryptFileData.mockResolvedValue(new Uint8Array(255));
cipherService.getDecryptedAttachmentBuffer.mockRejectedValue(
new Error("Error decrypting attachment"),
);
global.fetch = jest.fn(() =>
Promise.resolve({
@@ -356,13 +359,17 @@ describe("VaultExportService", () => {
it("contains attachments with folders", async () => {
const cipherData = new CipherData();
cipherData.id = "mock-id";
const cipherRecord: Record<CipherId, CipherData> = {
["mock-id" as CipherId]: cipherData,
};
const cipherView = new CipherView(new Cipher(cipherData));
const attachmentView = new AttachmentView(new Attachment(new AttachmentData()));
attachmentView.fileName = "mock-file-name";
cipherView.attachments = [attachmentView];
cipherService.ciphers$.mockReturnValue(of(cipherRecord));
cipherService.getAllDecrypted.mockResolvedValue([cipherView]);
folderService.getAllDecryptedFromState.mockResolvedValue([]);
encryptService.decryptFileData.mockResolvedValue(new Uint8Array(255));
cipherService.getDecryptedAttachmentBuffer.mockResolvedValue(new Uint8Array(255));
global.fetch = jest.fn(() =>
Promise.resolve({
status: 200,

View File

@@ -12,14 +12,12 @@ import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/a
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { Folder } from "@bitwarden/common/vault/models/domain/folder";
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { KdfConfigService, KeyService } from "@bitwarden/key-management";
@@ -118,8 +116,19 @@ export class IndividualVaultExportService
const cipherFolder = attachmentsFolder.folder(cipher.id);
for (const attachment of cipher.attachments) {
const response = await this.downloadAttachment(cipher.id, attachment.id);
const decBuf = await this.decryptAttachment(cipher, attachment, response);
cipherFolder.file(attachment.fileName, decBuf);
try {
const decBuf = await this.cipherService.getDecryptedAttachmentBuffer(
cipher.id as CipherId,
attachment,
response,
activeUserId,
);
cipherFolder.file(attachment.fileName, decBuf);
} catch {
throw new Error("Error decrypting attachment");
}
}
}
@@ -146,23 +155,6 @@ export class IndividualVaultExportService
return response;
}
private async decryptAttachment(
cipher: CipherView,
attachment: AttachmentView,
response: Response,
) {
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
const key =
attachment.key != null
? attachment.key
: await this.keyService.getOrgKey(cipher.organizationId);
return await this.encryptService.decryptFileData(encBuf, key);
} catch {
throw new Error("Error decrypting attachment");
}
}
private async getDecryptedExport(
activeUserId: UserId,
format: "json" | "csv",

View File

@@ -155,12 +155,9 @@ export class OrganizationVaultExportService
.forEach(async (c) => {
const cipher = new Cipher(new CipherData(c));
exportPromises.push(
this.cipherService
.getKeyForCipherKeyDecryption(cipher, activeUserId)
.then((key) => cipher.decrypt(key))
.then((decCipher) => {
decCiphers.push(decCipher);
}),
this.cipherService.decrypt(cipher, activeUserId).then((decCipher) => {
decCiphers.push(decCipher);
}),
);
});
}