1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 14:04:03 +00:00

Fix cipher service

This commit is contained in:
Bernd Schoolmann
2025-10-27 15:27:06 +01:00
parent 5aee44b5c2
commit c12868f789
2 changed files with 27 additions and 33 deletions

View File

@@ -119,6 +119,8 @@ describe("Cipher Service", () => {
beforeEach(() => {
encryptService.encryptFileData.mockReturnValue(Promise.resolve(ENCRYPTED_BYTES));
encryptService.encryptString.mockReturnValue(Promise.resolve(new EncString(ENCRYPTED_TEXT)));
keyService.userKey$.mockReturnValue(of(makeSymmetricCryptoKey(64) as UserKey));
keyService.orgKeys$.mockReturnValue(of({ [orgId]: makeSymmetricCryptoKey(64) as OrgKey }));
// Mock i18nService collator
i18nService.collator = {
@@ -156,9 +158,6 @@ describe("Cipher Service", () => {
it("should upload encrypted file contents with save attachments", async () => {
const fileName = "filename";
const fileData = new Uint8Array(10);
keyService.getOrgKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
);
keyService.makeDataEncKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32))),
);
@@ -181,9 +180,6 @@ describe("Cipher Service", () => {
const testCipher = new Cipher(cipherData);
const expectedRevisionDate = "2022-01-31T12:00:00.000Z";
keyService.getOrgKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
);
keyService.makeDataEncKey.mockReturnValue(
Promise.resolve([
new SymmetricCryptoKey(new Uint8Array(32)),
@@ -362,10 +358,6 @@ describe("Cipher Service", () => {
});
it("should return the encrypting user id", async () => {
keyService.getOrgKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
);
const { encryptedFor } = await cipherService.encrypt(cipherView, userId);
expect(encryptedFor).toEqual(userId);
});
@@ -377,10 +369,6 @@ describe("Cipher Service", () => {
{ uri: "uri", match: UriMatchStrategy.RegularExpression } as LoginUriView,
];
keyService.getOrgKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
);
const { cipher } = await cipherService.encrypt(cipherView, userId);
expect(cipher.login.uris).toEqual([
@@ -394,12 +382,6 @@ describe("Cipher Service", () => {
});
describe("cipher.key", () => {
beforeEach(() => {
keyService.getOrgKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
);
});
it("is null when feature flag is false", async () => {
configService.getFeatureFlag
.calledWith(FeatureFlag.CipherKeyEncryption)
@@ -437,9 +419,6 @@ describe("Cipher Service", () => {
describe("encryptCipherForRotation", () => {
beforeEach(() => {
jest.spyOn<any, string>(cipherService, "encryptCipherWithCipherKey");
keyService.getOrgKey.mockReturnValue(
Promise.resolve<any>(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
);
});
it("is not called when feature flag is false", async () => {
@@ -637,7 +616,7 @@ describe("Cipher Service", () => {
const result = await cipherService.decrypt(encryptionContext.cipher, userId);
expect(result).toEqual(new CipherView(encryptionContext.cipher));
expect(encryptionContext.cipher.decrypt).toHaveBeenCalledWith(mockUserKey);
expect(encryptionContext.cipher.decrypt).toHaveBeenCalledWith(mockUserKey, userId);
});
});

View File

@@ -726,7 +726,11 @@ export class CipherService implements CipherServiceAbstraction {
}
const ciphers = response.data.map((cr) => new Cipher(new CipherData(cr)));
const key = await this.keyService.getOrgKey(organizationId);
const key = await firstValueFrom(
this.keyService
.orgKeys$(userId)
.pipe(map((orgKeys) => orgKeys[organizationId as OrganizationId] ?? null)),
);
const decCiphers: CipherView[] = await Promise.all(
ciphers.map(async (cipher) => {
return await cipher.decrypt(key, userId);
@@ -1536,10 +1540,17 @@ export class CipherService implements CipherServiceAbstraction {
}
async getKeyForCipherKeyDecryption(cipher: Cipher, userId: UserId): Promise<UserKey | OrgKey> {
return (
(await this.keyService.getOrgKey(cipher.organizationId)) ||
((await this.keyService.getUserKey(userId)) as UserKey)
);
if (cipher.organizationId) {
const orgKey = await firstValueFrom(
this.keyService
.orgKeys$(userId)
.pipe(map((orgKeys) => orgKeys[cipher.organizationId as OrganizationId] ?? null)),
);
if (orgKey) {
return orgKey;
}
}
return (await firstValueFrom(this.keyService.userKey$(userId))) as UserKey;
}
async setAddEditCipherInfo(value: AddEditCipherInfo, userId: UserId) {
@@ -1672,7 +1683,7 @@ export class CipherService implements CipherServiceAbstraction {
// In the case of a cipher that is being shared with an organization, we want to decrypt the
// cipher key with the user's key and then re-encrypt it with the organization's key.
private async encryptSharedCipher(model: CipherView, userId: UserId): Promise<EncryptionContext> {
const keyForCipherKeyDecryption = await this.keyService.getUserKey(userId);
const keyForCipherKeyDecryption = await firstValueFrom(this.keyService.userKey$(userId));
return await this.encrypt(model, userId, null, keyForCipherKeyDecryption);
}
@@ -1749,12 +1760,16 @@ export class CipherService implements CipherServiceAbstraction {
}
const encBuf = await EncArrayBuffer.fromResponse(attachmentResponse);
const userKey = await this.keyService.getUserKey(activeUserId.id);
const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId.id));
const decBuf = await this.encryptService.decryptFileData(encBuf, userKey);
let encKey: UserKey | OrgKey;
encKey = await this.keyService.getOrgKey(organizationId);
encKey ||= (await this.keyService.getUserKey()) as UserKey;
encKey = await firstValueFrom(
this.keyService
.orgKeys$(activeUserId.id)
.pipe(map((orgKeys) => orgKeys[organizationId as OrganizationId] ?? null)),
);
encKey ||= (await firstValueFrom(this.keyService.userKey$(activeUserId.id))) as UserKey;
const dataEncKey = await this.keyService.makeDataEncKey(encKey);