1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

PM-6787 - Rename DeviceTrustCryptoService to DeviceTrustService (#8819)

This commit is contained in:
Jared Snider
2024-04-24 12:54:54 -04:00
committed by GitHub
parent a12c140792
commit 5dc83cd34c
32 changed files with 182 additions and 194 deletions

View File

@@ -3,9 +3,10 @@ import { Observable } from "rxjs";
import { EncString } from "../../platform/models/domain/enc-string";
import { UserId } from "../../types/guid";
import { DeviceKey, UserKey } from "../../types/key";
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
export abstract class DeviceTrustCryptoServiceAbstraction {
import { DeviceResponse } from "./devices/responses/device.response";
export abstract class DeviceTrustServiceAbstraction {
supportsDeviceTrust$: Observable<boolean>;
/**
* @description Retrieves the users choice to trust the device which can only happen after decryption

View File

@@ -17,7 +17,7 @@ import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypt
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../platform/state";
import { UserId } from "../../types/guid";
import { UserKey, DeviceKey } from "../../types/key";
import { DeviceTrustCryptoServiceAbstraction } from "../abstractions/device-trust-crypto.service.abstraction";
import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction";
import { SecretVerificationRequest } from "../models/request/secret-verification.request";
@@ -42,7 +42,7 @@ export const SHOULD_TRUST_DEVICE = new UserKeyDefinition<boolean>(
},
);
export class DeviceTrustCryptoService implements DeviceTrustCryptoServiceAbstraction {
export class DeviceTrustService implements DeviceTrustServiceAbstraction {
private readonly platformSupportsSecureStorage =
this.platformUtilsService.supportsSecureStorage();
private readonly deviceKeySecureStorageKey: string = "_deviceKey";

View File

@@ -33,11 +33,11 @@ import { ProtectedDeviceResponse } from "../models/response/protected-device.res
import {
SHOULD_TRUST_DEVICE,
DEVICE_KEY,
DeviceTrustCryptoService,
} from "./device-trust-crypto.service.implementation";
DeviceTrustService,
} from "./device-trust.service.implementation";
describe("deviceTrustCryptoService", () => {
let deviceTrustCryptoService: DeviceTrustCryptoService;
describe("deviceTrustService", () => {
let deviceTrustService: DeviceTrustService;
const keyGenerationService = mock<KeyGenerationService>();
const cryptoFunctionService = mock<CryptoFunctionService>();
@@ -70,11 +70,11 @@ describe("deviceTrustCryptoService", () => {
jest.clearAllMocks();
const supportsSecureStorage = false; // default to false; tests will override as needed
// By default all the tests will have a mocked active user in state provider.
deviceTrustCryptoService = createDeviceTrustCryptoService(mockUserId, supportsSecureStorage);
deviceTrustService = createDeviceTrustService(mockUserId, supportsSecureStorage);
});
it("instantiates", () => {
expect(deviceTrustCryptoService).not.toBeFalsy();
expect(deviceTrustService).not.toBeFalsy();
});
describe("User Trust Device Choice For Decryption", () => {
@@ -84,7 +84,7 @@ describe("deviceTrustCryptoService", () => {
await stateProvider.setUserState(SHOULD_TRUST_DEVICE, newValue, mockUserId);
const result = await deviceTrustCryptoService.getShouldTrustDevice(mockUserId);
const result = await deviceTrustService.getShouldTrustDevice(mockUserId);
expect(result).toEqual(newValue);
});
@@ -95,9 +95,9 @@ describe("deviceTrustCryptoService", () => {
await stateProvider.setUserState(SHOULD_TRUST_DEVICE, false, mockUserId);
const newValue = true;
await deviceTrustCryptoService.setShouldTrustDevice(mockUserId, newValue);
await deviceTrustService.setShouldTrustDevice(mockUserId, newValue);
const result = await deviceTrustCryptoService.getShouldTrustDevice(mockUserId);
const result = await deviceTrustService.getShouldTrustDevice(mockUserId);
expect(result).toEqual(newValue);
});
});
@@ -105,25 +105,25 @@ describe("deviceTrustCryptoService", () => {
describe("trustDeviceIfRequired", () => {
it("should trust device and reset when getShouldTrustDevice returns true", async () => {
jest.spyOn(deviceTrustCryptoService, "getShouldTrustDevice").mockResolvedValue(true);
jest.spyOn(deviceTrustCryptoService, "trustDevice").mockResolvedValue({} as DeviceResponse);
jest.spyOn(deviceTrustCryptoService, "setShouldTrustDevice").mockResolvedValue();
jest.spyOn(deviceTrustService, "getShouldTrustDevice").mockResolvedValue(true);
jest.spyOn(deviceTrustService, "trustDevice").mockResolvedValue({} as DeviceResponse);
jest.spyOn(deviceTrustService, "setShouldTrustDevice").mockResolvedValue();
await deviceTrustCryptoService.trustDeviceIfRequired(mockUserId);
await deviceTrustService.trustDeviceIfRequired(mockUserId);
expect(deviceTrustCryptoService.getShouldTrustDevice).toHaveBeenCalledTimes(1);
expect(deviceTrustCryptoService.trustDevice).toHaveBeenCalledTimes(1);
expect(deviceTrustCryptoService.setShouldTrustDevice).toHaveBeenCalledWith(mockUserId, false);
expect(deviceTrustService.getShouldTrustDevice).toHaveBeenCalledTimes(1);
expect(deviceTrustService.trustDevice).toHaveBeenCalledTimes(1);
expect(deviceTrustService.setShouldTrustDevice).toHaveBeenCalledWith(mockUserId, false);
});
it("should not trust device nor reset when getShouldTrustDevice returns false", async () => {
const getShouldTrustDeviceSpy = jest
.spyOn(deviceTrustCryptoService, "getShouldTrustDevice")
.spyOn(deviceTrustService, "getShouldTrustDevice")
.mockResolvedValue(false);
const trustDeviceSpy = jest.spyOn(deviceTrustCryptoService, "trustDevice");
const setShouldTrustDeviceSpy = jest.spyOn(deviceTrustCryptoService, "setShouldTrustDevice");
const trustDeviceSpy = jest.spyOn(deviceTrustService, "trustDevice");
const setShouldTrustDeviceSpy = jest.spyOn(deviceTrustService, "setShouldTrustDevice");
await deviceTrustCryptoService.trustDeviceIfRequired(mockUserId);
await deviceTrustService.trustDeviceIfRequired(mockUserId);
expect(getShouldTrustDeviceSpy).toHaveBeenCalledTimes(1);
expect(trustDeviceSpy).not.toHaveBeenCalled();
@@ -151,7 +151,7 @@ describe("deviceTrustCryptoService", () => {
it("returns null when there is not an existing device key", async () => {
await stateProvider.setUserState(DEVICE_KEY, null, mockUserId);
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId);
const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
expect(deviceKey).toBeNull();
expect(secureStorageService.get).not.toHaveBeenCalled();
@@ -160,7 +160,7 @@ describe("deviceTrustCryptoService", () => {
it("returns the device key when there is an existing device key", async () => {
await stateProvider.setUserState(DEVICE_KEY, existingDeviceKey, mockUserId);
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId);
const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey);
@@ -172,17 +172,14 @@ describe("deviceTrustCryptoService", () => {
describe("Secure Storage supported", () => {
beforeEach(() => {
const supportsSecureStorage = true;
deviceTrustCryptoService = createDeviceTrustCryptoService(
mockUserId,
supportsSecureStorage,
);
deviceTrustService = createDeviceTrustService(mockUserId, supportsSecureStorage);
});
it("returns null when there is not an existing device key for the passed in user id", async () => {
secureStorageService.get.mockResolvedValue(null);
// Act
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId);
const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
// Assert
expect(deviceKey).toBeNull();
@@ -193,7 +190,7 @@ describe("deviceTrustCryptoService", () => {
secureStorageService.get.mockResolvedValue(existingDeviceKeyB64);
// Act
const deviceKey = await deviceTrustCryptoService.getDeviceKey(mockUserId);
const deviceKey = await deviceTrustService.getDeviceKey(mockUserId);
// Assert
expect(deviceKey).not.toBeNull();
@@ -203,7 +200,7 @@ describe("deviceTrustCryptoService", () => {
});
it("throws an error when no user id is passed in", async () => {
await expect(deviceTrustCryptoService.getDeviceKey(null)).rejects.toThrow(
await expect(deviceTrustService.getDeviceKey(null)).rejects.toThrow(
"UserId is required. Cannot get device key.",
);
});
@@ -220,7 +217,7 @@ describe("deviceTrustCryptoService", () => {
// TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests
await (deviceTrustCryptoService as any).setDeviceKey(mockUserId, newDeviceKey);
await (deviceTrustService as any).setDeviceKey(mockUserId, newDeviceKey);
expect(stateProvider.mock.setUserState).toHaveBeenLastCalledWith(
DEVICE_KEY,
@@ -232,10 +229,7 @@ describe("deviceTrustCryptoService", () => {
describe("Secure Storage supported", () => {
beforeEach(() => {
const supportsSecureStorage = true;
deviceTrustCryptoService = createDeviceTrustCryptoService(
mockUserId,
supportsSecureStorage,
);
deviceTrustService = createDeviceTrustService(mockUserId, supportsSecureStorage);
});
it("successfully sets the device key in secure storage", async () => {
@@ -251,7 +245,7 @@ describe("deviceTrustCryptoService", () => {
// Act
// TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests
await (deviceTrustCryptoService as any).setDeviceKey(mockUserId, newDeviceKey);
await (deviceTrustService as any).setDeviceKey(mockUserId, newDeviceKey);
// Assert
expect(stateProvider.mock.setUserState).not.toHaveBeenCalledTimes(2);
@@ -268,9 +262,9 @@ describe("deviceTrustCryptoService", () => {
new Uint8Array(deviceKeyBytesLength) as CsprngArray,
) as DeviceKey;
await expect(
(deviceTrustCryptoService as any).setDeviceKey(null, newDeviceKey),
).rejects.toThrow("UserId is required. Cannot set device key.");
await expect((deviceTrustService as any).setDeviceKey(null, newDeviceKey)).rejects.toThrow(
"UserId is required. Cannot set device key.",
);
});
});
@@ -285,7 +279,7 @@ describe("deviceTrustCryptoService", () => {
// TypeScript will allow calling private methods if the object is of type 'any'
// This is a hacky workaround, but it allows for cleaner tests
const deviceKey = await (deviceTrustCryptoService as any).makeDeviceKey();
const deviceKey = await (deviceTrustService as any).makeDeviceKey();
expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledTimes(1);
expect(keyGenSvcGenerateKeySpy).toHaveBeenCalledWith(deviceKeyBytesLength * 8);
@@ -362,7 +356,7 @@ describe("deviceTrustCryptoService", () => {
// TypeScript will allow calling private methods if the object is of type 'any'
makeDeviceKeySpy = jest
.spyOn(deviceTrustCryptoService as any, "makeDeviceKey")
.spyOn(deviceTrustService as any, "makeDeviceKey")
.mockResolvedValue(mockDeviceKey);
rsaGenerateKeyPairSpy = jest
@@ -398,7 +392,7 @@ describe("deviceTrustCryptoService", () => {
});
it("calls the required methods with the correct arguments and returns a DeviceResponse", async () => {
const response = await deviceTrustCryptoService.trustDevice(mockUserId);
const response = await deviceTrustService.trustDevice(mockUserId);
expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1);
expect(rsaGenerateKeyPairSpy).toHaveBeenCalledTimes(1);
@@ -429,7 +423,7 @@ describe("deviceTrustCryptoService", () => {
// setup the spy to return null
cryptoSvcGetUserKeySpy.mockResolvedValue(null);
// check if the expected error is thrown
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow(
await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow(
"User symmetric key not found",
);
@@ -439,7 +433,7 @@ describe("deviceTrustCryptoService", () => {
// setup the spy to return undefined
cryptoSvcGetUserKeySpy.mockResolvedValue(undefined);
// check if the expected error is thrown
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow(
await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow(
"User symmetric key not found",
);
});
@@ -479,9 +473,7 @@ describe("deviceTrustCryptoService", () => {
it(`throws an error if ${method} fails`, async () => {
const methodSpy = spy();
methodSpy.mockRejectedValue(new Error(errorText));
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow(
errorText,
);
await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow(errorText);
});
test.each([null, undefined])(
@@ -489,14 +481,14 @@ describe("deviceTrustCryptoService", () => {
async (invalidValue) => {
const methodSpy = spy();
methodSpy.mockResolvedValue(invalidValue);
await expect(deviceTrustCryptoService.trustDevice(mockUserId)).rejects.toThrow();
await expect(deviceTrustService.trustDevice(mockUserId)).rejects.toThrow();
},
);
},
);
it("throws an error when a null user id is passed in", async () => {
await expect(deviceTrustCryptoService.trustDevice(null)).rejects.toThrow(
await expect(deviceTrustService.trustDevice(null)).rejects.toThrow(
"UserId is required. Cannot trust device.",
);
});
@@ -530,7 +522,7 @@ describe("deviceTrustCryptoService", () => {
it("throws an error when a null user id is passed in", async () => {
await expect(
deviceTrustCryptoService.decryptUserKeyWithDeviceKey(
deviceTrustService.decryptUserKeyWithDeviceKey(
null,
mockEncryptedDevicePrivateKey,
mockEncryptedUserKey,
@@ -540,7 +532,7 @@ describe("deviceTrustCryptoService", () => {
});
it("returns null when device key isn't provided", async () => {
const result = await deviceTrustCryptoService.decryptUserKeyWithDeviceKey(
const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
mockUserId,
mockEncryptedDevicePrivateKey,
mockEncryptedUserKey,
@@ -558,7 +550,7 @@ describe("deviceTrustCryptoService", () => {
.spyOn(cryptoService, "rsaDecrypt")
.mockResolvedValue(new Uint8Array(userKeyBytesLength));
const result = await deviceTrustCryptoService.decryptUserKeyWithDeviceKey(
const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
mockUserId,
mockEncryptedDevicePrivateKey,
mockEncryptedUserKey,
@@ -574,9 +566,9 @@ describe("deviceTrustCryptoService", () => {
const decryptToBytesSpy = jest
.spyOn(encryptService, "decryptToBytes")
.mockRejectedValue(new Error("Decryption error"));
const setDeviceKeySpy = jest.spyOn(deviceTrustCryptoService as any, "setDeviceKey");
const setDeviceKeySpy = jest.spyOn(deviceTrustService as any, "setDeviceKey");
const result = await deviceTrustCryptoService.decryptUserKeyWithDeviceKey(
const result = await deviceTrustService.decryptUserKeyWithDeviceKey(
mockUserId,
mockEncryptedDevicePrivateKey,
mockEncryptedUserKey,
@@ -606,7 +598,7 @@ describe("deviceTrustCryptoService", () => {
it("throws an error when a null user id is passed in", async () => {
await expect(
deviceTrustCryptoService.rotateDevicesTrust(null, fakeNewUserKey, ""),
deviceTrustService.rotateDevicesTrust(null, fakeNewUserKey, ""),
).rejects.toThrow("UserId is required. Cannot rotate device's trust.");
});
@@ -615,7 +607,7 @@ describe("deviceTrustCryptoService", () => {
stateProvider.activeUser.getFake(DEVICE_KEY);
deviceKeyState.nextState(null);
await deviceTrustCryptoService.rotateDevicesTrust(mockUserId, fakeNewUserKey, "");
await deviceTrustService.rotateDevicesTrust(mockUserId, fakeNewUserKey, "");
expect(devicesApiService.updateTrust).not.toHaveBeenCalled();
});
@@ -691,7 +683,7 @@ describe("deviceTrustCryptoService", () => {
);
});
await deviceTrustCryptoService.rotateDevicesTrust(
await deviceTrustService.rotateDevicesTrust(
mockUserId,
fakeNewUserKey,
"my_password_hash",
@@ -713,10 +705,7 @@ describe("deviceTrustCryptoService", () => {
});
// Helpers
function createDeviceTrustCryptoService(
mockUserId: UserId | null,
supportsSecureStorage: boolean,
) {
function createDeviceTrustService(mockUserId: UserId | null, supportsSecureStorage: boolean) {
accountService = mockAccountServiceWith(mockUserId);
stateProvider = new FakeStateProvider(accountService);
@@ -725,7 +714,7 @@ describe("deviceTrustCryptoService", () => {
decryptionOptions.next({} as any);
userDecryptionOptionsService.userDecryptionOptions$ = decryptionOptions;
return new DeviceTrustCryptoService(
return new DeviceTrustService(
keyGenerationService,
cryptoFunctionService,
cryptoService,

View File

@@ -49,7 +49,7 @@ import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-
import { KeyConnectorMigrator } from "./migrations/50-move-key-connector-to-state-provider";
import { RememberedEmailMigrator } from "./migrations/51-move-remembered-email-to-state-providers";
import { DeleteInstalledVersion } from "./migrations/52-delete-installed-version";
import { DeviceTrustCryptoServiceStateProviderMigrator } from "./migrations/53-migrate-device-trust-crypto-svc-to-state-providers";
import { DeviceTrustServiceStateProviderMigrator } from "./migrations/53-migrate-device-trust-svc-to-state-providers";
import { SendMigrator } from "./migrations/54-move-encrypted-sends";
import { MoveMasterKeyStateToProviderMigrator } from "./migrations/55-move-master-key-state-to-provider";
import { AuthRequestMigrator } from "./migrations/56-move-auth-requests";
@@ -117,7 +117,7 @@ export function createMigrationBuilder() {
.with(KeyConnectorMigrator, 49, 50)
.with(RememberedEmailMigrator, 50, 51)
.with(DeleteInstalledVersion, 51, 52)
.with(DeviceTrustCryptoServiceStateProviderMigrator, 52, 53)
.with(DeviceTrustServiceStateProviderMigrator, 52, 53)
.with(SendMigrator, 53, 54)
.with(MoveMasterKeyStateToProviderMigrator, 54, 55)
.with(AuthRequestMigrator, 55, 56)

View File

@@ -5,9 +5,9 @@ import { mockMigrationHelper } from "../migration-helper.spec";
import {
DEVICE_KEY,
DeviceTrustCryptoServiceStateProviderMigrator,
DeviceTrustServiceStateProviderMigrator,
SHOULD_TRUST_DEVICE,
} from "./53-migrate-device-trust-crypto-svc-to-state-providers";
} from "./53-migrate-device-trust-svc-to-state-providers";
// Represents data in state service pre-migration
function preMigrationJson() {
@@ -79,14 +79,14 @@ function rollbackJSON() {
};
}
describe("DeviceTrustCryptoServiceStateProviderMigrator", () => {
describe("DeviceTrustServiceStateProviderMigrator", () => {
let helper: MockProxy<MigrationHelper>;
let sut: DeviceTrustCryptoServiceStateProviderMigrator;
let sut: DeviceTrustServiceStateProviderMigrator;
describe("migrate", () => {
beforeEach(() => {
helper = mockMigrationHelper(preMigrationJson(), 52);
sut = new DeviceTrustCryptoServiceStateProviderMigrator(52, 53);
sut = new DeviceTrustServiceStateProviderMigrator(52, 53);
});
// it should remove deviceKey and trustDeviceChoiceForDecryption from all accounts
@@ -126,7 +126,7 @@ describe("DeviceTrustCryptoServiceStateProviderMigrator", () => {
describe("rollback", () => {
beforeEach(() => {
helper = mockMigrationHelper(rollbackJSON(), 53);
sut = new DeviceTrustCryptoServiceStateProviderMigrator(52, 53);
sut = new DeviceTrustServiceStateProviderMigrator(52, 53);
});
it("should null out newly migrated entries in state provider framework", async () => {

View File

@@ -16,7 +16,7 @@ type ExpectedAccountType = {
};
export const DEVICE_KEY: KeyDefinitionLike = {
key: "deviceKey", // matches KeyDefinition.key in DeviceTrustCryptoService
key: "deviceKey", // matches KeyDefinition.key in DeviceTrustService
stateDefinition: {
name: "deviceTrust", // matches StateDefinition.name in StateDefinitions
},
@@ -29,7 +29,7 @@ export const SHOULD_TRUST_DEVICE: KeyDefinitionLike = {
},
};
export class DeviceTrustCryptoServiceStateProviderMigrator extends Migrator<52, 53> {
export class DeviceTrustServiceStateProviderMigrator extends Migrator<52, 53> {
async migrate(helper: MigrationHelper): Promise<void> {
const accounts = await helper.getAccounts<ExpectedAccountType>();
async function migrateAccount(userId: string, account: ExpectedAccountType): Promise<void> {

View File

@@ -4,7 +4,7 @@ import { IRREVERSIBLE, Migrator } from "../migrator";
type ExpectedAccountType = NonNullable<unknown>;
export const REFRESH_TOKEN_MIGRATED_TO_SECURE_STORAGE: KeyDefinitionLike = {
key: "refreshTokenMigratedToSecureStorage", // matches KeyDefinition.key in DeviceTrustCryptoService
key: "refreshTokenMigratedToSecureStorage", // matches KeyDefinition.key
stateDefinition: {
name: "token", // matches StateDefinition.name in StateDefinitions
},