1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-22 19:23:52 +00:00

[PM-19838] Untrust devices that cannot be rotated (#14165)

* Untrust devices that cannot be rotated

* Add tests and only send request on more than 0 failed devices

* Address feedback
This commit is contained in:
Bernd Schoolmann
2025-04-09 14:26:20 +02:00
committed by GitHub
parent a4040b6b6b
commit a2cd3ecc1c
6 changed files with 100 additions and 2 deletions

View File

@@ -204,7 +204,8 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
}
const devices = await this.devicesApiService.getDevices();
return await Promise.all(
const devicesToUntrust: string[] = [];
const rotatedData = await Promise.all(
devices.data
.filter((device) => device.isTrusted)
.map(async (device) => {
@@ -213,6 +214,13 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
deviceWithKeys.encryptedPublicKey,
oldUserKey,
);
if (!publicKey) {
// Device was trusted but encryption is broken. This should be untrusted
devicesToUntrust.push(device.id);
return null;
}
const newEncryptedPublicKey = await this.encryptService.encrypt(publicKey, newUserKey);
const newEncryptedUserKey = await this.encryptService.rsaEncrypt(
newUserKey.key,
@@ -229,8 +237,14 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
request.encryptedUserKey = newRotateableKeySet.encryptedUserKey.encryptedString;
request.deviceId = device.id;
return request;
}),
})
.filter((otherDeviceKeysUpdateRequest) => otherDeviceKeysUpdateRequest != null),
);
if (rotatedData.length > 0) {
this.logService.info("[Device trust rotation] Distrusting devices that failed to decrypt.");
await this.devicesApiService.untrustDevices(devicesToUntrust);
}
return rotatedData;
}
async rotateDevicesTrust(

View File

@@ -679,6 +679,52 @@ describe("deviceTrustService", () => {
).rejects.toThrow("New user key is required. Cannot get rotated data.");
});
it("untrusts devices that failed to decrypt", async () => {
const deviceResponse = {
id: "id",
userId: "",
name: "",
identifier: "",
type: DeviceType.Android,
creationDate: "",
revisionDate: "",
isTrusted: true,
};
devicesApiService.getDevices.mockResolvedValue(
new ListResponse(
{
data: [deviceResponse],
},
DeviceResponse,
),
);
encryptService.decryptToBytes.mockResolvedValue(null);
encryptService.encrypt.mockResolvedValue(new EncString("test_encrypted_data"));
encryptService.rsaEncrypt.mockResolvedValue(new EncString("test_encrypted_data"));
const protectedDeviceResponse = new ProtectedDeviceResponse({
id: "id",
creationDate: "",
identifier: "test_device_identifier",
name: "Firefox",
type: DeviceType.FirefoxBrowser,
encryptedPublicKey: "",
encryptedUserKey: "",
});
devicesApiService.getDeviceKeys.mockResolvedValue(protectedDeviceResponse);
const fakeOldUserKeyData = new Uint8Array(64);
fakeOldUserKeyData.fill(5, 0, 1);
fakeOldUserKey = new SymmetricCryptoKey(fakeOldUserKeyData) as UserKey;
const fakeNewUserKeyData = new Uint8Array(64);
fakeNewUserKeyData.fill(1, 0, 1);
fakeNewUserKey = new SymmetricCryptoKey(fakeNewUserKeyData) as UserKey;
await deviceTrustService.getRotatedData(fakeOldUserKey, fakeNewUserKey, userId);
expect(devicesApiService.untrustDevices).toHaveBeenCalledWith(["id"]);
});
it("returns the expected data when all required parameters are provided", async () => {
const deviceResponse = {
id: "",