mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PM-22516] Fix cipher key decryption to handle new error-based API instead of null returns (#15124)
* Replace null check in cipher key decryption * Handle decryption error properly in user asymmetric key regeneration service
This commit is contained in:
@@ -68,6 +68,68 @@ describe("Cipher DTO", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Decrypt should handle cipher key error", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Login;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.creationDate = new Date("2022-01-01T12:00:00.000Z");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
cipher.key = mockEnc("EncKey");
|
||||||
|
cipher.permissions = new CipherPermissionsApi();
|
||||||
|
|
||||||
|
const loginView = new LoginView();
|
||||||
|
loginView.username = "username";
|
||||||
|
loginView.password = "password";
|
||||||
|
|
||||||
|
const login = mock<Login>();
|
||||||
|
login.decrypt.mockResolvedValue(loginView);
|
||||||
|
cipher.login = login;
|
||||||
|
|
||||||
|
const keyService = mock<KeyService>();
|
||||||
|
const encryptService = mock<EncryptService>();
|
||||||
|
const cipherService = mock<CipherService>();
|
||||||
|
|
||||||
|
encryptService.unwrapSymmetricKey.mockRejectedValue(new Error("Failed to unwrap key"));
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt(
|
||||||
|
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "[error: cannot decrypt]",
|
||||||
|
type: 1,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
decryptionFailure: true,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creationDate: new Date("2022-01-01T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
permissions: new CipherPermissionsApi(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(login.decrypt).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
describe("LoginCipher", () => {
|
describe("LoginCipher", () => {
|
||||||
let cipherData: CipherData;
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
|||||||
@@ -145,14 +145,15 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
|||||||
if (this.key != null) {
|
if (this.key != null) {
|
||||||
const encryptService = Utils.getContainerService().getEncryptService();
|
const encryptService = Utils.getContainerService().getEncryptService();
|
||||||
|
|
||||||
const cipherKey = await encryptService.unwrapSymmetricKey(this.key, encKey);
|
try {
|
||||||
if (cipherKey == null) {
|
const cipherKey = await encryptService.unwrapSymmetricKey(this.key, encKey);
|
||||||
|
encKey = cipherKey;
|
||||||
|
bypassValidation = false;
|
||||||
|
} catch {
|
||||||
model.name = "[error: cannot decrypt]";
|
model.name = "[error: cannot decrypt]";
|
||||||
model.decryptionFailure = true;
|
model.decryptionFailure = true;
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
encKey = cipherKey;
|
|
||||||
bypassValidation = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.decryptObj<Cipher, CipherView>(
|
await this.decryptObj<Cipher, CipherView>(
|
||||||
|
|||||||
@@ -162,17 +162,26 @@ export class DefaultUserAsymmetricKeysRegenerationService
|
|||||||
const ciphers = await this.cipherService.getAll(userId);
|
const ciphers = await this.cipherService.getAll(userId);
|
||||||
const cipher = ciphers.find((cipher) => cipher.organizationId == null);
|
const cipher = ciphers.find((cipher) => cipher.organizationId == null);
|
||||||
|
|
||||||
if (cipher != null) {
|
if (!cipher) {
|
||||||
try {
|
return false;
|
||||||
await cipher.decrypt(userKey);
|
}
|
||||||
return true;
|
|
||||||
} catch (error) {
|
try {
|
||||||
|
const cipherView = await cipher.decrypt(userKey);
|
||||||
|
|
||||||
|
if (cipherView.decryptionFailure) {
|
||||||
this.logService.error(
|
this.logService.error(
|
||||||
"[UserAsymmetricKeyRegeneration] User Symmetric Key validation error: " + error,
|
"[UserAsymmetricKeyRegeneration] User Symmetric Key validation error: Cipher decryption failed",
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
this.logService.error(
|
||||||
|
"[UserAsymmetricKeyRegeneration] User Symmetric Key validation error: " + error,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user