1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-28 02:23:25 +00:00

decrypt attachment content using sdk

This commit is contained in:
gbubemismith
2025-04-18 19:05:02 -04:00
parent 97e1c4ad94
commit d48aa21bd6
11 changed files with 360 additions and 30 deletions

View File

@@ -1,6 +1,7 @@
import { CipherListView } from "@bitwarden/sdk-internal";
import { UserId } from "../../types/guid";
import { Attachment } from "../models/domain/attachment";
import { Cipher } from "../models/domain/cipher";
import { CipherView } from "../models/view/cipher.view";
@@ -26,4 +27,20 @@ export abstract class CipherEncryptionService {
* @returns A promise that resolves to an array of decrypted cipher list views
*/
abstract decryptCipherList(ciphers: Cipher[], userId: UserId): Promise<CipherListView[]>;
/**
* Decrypts an array buffer using the SDK for the given userId.
*
* @param cipher The encrypted cipher object that owns the attachment
* @param attachment The encrypted attachment object
* @param encryptedContent The encrypted content as a Uint8Array
* @param userId The user ID whose key will be used for decryption
*
* @returns A promise that resolves to the decrypted content
*/
abstract decryptAttachmentContent(
cipher: Cipher,
attachment: Attachment,
encryptedContent: Uint8Array,
userId: UserId,
): Promise<Uint8Array>;
}

View File

@@ -6,6 +6,7 @@ import {
Cipher as SdkCipher,
CipherType as SdkCipherType,
CipherView as SdkCipherView,
Attachment as SdkAttachment,
} from "@bitwarden/sdk-internal";
import { mockEnc } from "../../../spec";
@@ -16,6 +17,7 @@ import { UserId } from "../../types/guid";
import { CipherRepromptType, CipherType } from "../enums";
import { CipherPermissionsApi } from "../models/api/cipher-permissions.api";
import { CipherData } from "../models/data/cipher.data";
import { Attachment } from "../models/domain/attachment";
import { Cipher } from "../models/domain/cipher";
import { CipherView } from "../models/view/cipher.view";
import { Fido2CredentialView } from "../models/view/fido2-credential.view";
@@ -82,6 +84,9 @@ describe("DefaultCipherEncryptionService", () => {
decrypt: jest.fn(),
decrypt_fido2_credentials: jest.fn(),
}),
attachments: jest.fn().mockReturnValue({
decrypt_buffer: jest.fn(),
}),
}),
};
const mockRef = {
@@ -232,4 +237,34 @@ describe("DefaultCipherEncryptionService", () => {
expect(result.name).toBe("[error: cannot decrypt]");
});
});
describe("decryptAttachmentContent", () => {
it("should decrypt attachment content successfully", async () => {
const cipher = new Cipher(cipherData);
const attachment = new Attachment(cipherData.attachments![0]);
const encryptedContent = new Uint8Array([1, 2, 3, 4]);
const expectedDecryptedContent = new Uint8Array([5, 6, 7, 8]);
jest.spyOn(cipher, "toSdkCipher").mockReturnValue({ id: "id" } as SdkCipher);
jest.spyOn(attachment, "toSdkAttachment").mockReturnValue({ id: "a1" } as SdkAttachment);
mockSdkClient.vault().attachments().decrypt_buffer.mockReturnValue(expectedDecryptedContent);
const result = await cipherEncryptionService.decryptAttachmentContent(
cipher,
attachment,
encryptedContent,
userId,
);
expect(result).toEqual(expectedDecryptedContent);
expect(cipher.toSdkCipher).toHaveBeenCalled();
expect(attachment.toSdkAttachment).toHaveBeenCalled();
expect(mockSdkClient.vault().attachments().decrypt_buffer).toHaveBeenCalledWith(
{ id: "id" },
{ id: "a1" },
encryptedContent,
);
});
});
});

View File

@@ -7,6 +7,7 @@ import { SdkService } from "../../platform/abstractions/sdk/sdk.service";
import { UserId } from "../../types/guid";
import { CipherEncryptionService } from "../abstractions/cipher-encryption.service";
import { CipherType } from "../enums";
import { Attachment } from "../models/domain/attachment";
import { Cipher } from "../models/domain/cipher";
import { CipherView } from "../models/view/cipher.view";
import { Fido2CredentialView } from "../models/view/fido2-credential.view";
@@ -102,4 +103,35 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
),
);
}
/**
* {@inheritdoc}
*/
async decryptAttachmentContent(
cipher: Cipher,
attachment: Attachment,
encryptedContent: Uint8Array,
userId: UserId,
): Promise<Uint8Array> {
return firstValueFrom(
this.sdkService.userClient$(userId).pipe(
map((sdk) => {
if (!sdk) {
throw new Error("SDK is undefined");
}
using ref = sdk.take();
return ref.value
.vault()
.attachments()
.decrypt_buffer(cipher.toSdkCipher(), attachment.toSdkAttachment(), encryptedContent);
}),
catchError((error: unknown) => {
this.logService.error(`Failed to decrypt cipher buffer: ${error}`);
return EMPTY;
}),
),
);
}
}