Skip to content

Commit

Permalink
Tweak device trust crypto service implementation to match mobile late… (
Browse files Browse the repository at this point in the history
#5744)

* Tweak device trust crypto service implementation to match mobile latest which results in more single responsibility methods

* Update tests to match device trust crypto service implementation changes
  • Loading branch information
JaredSnider-Bitwarden authored Jul 5, 2023
1 parent 87468a2 commit e50e524
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,33 +69,31 @@ export class DeviceTrustCryptoService implements DeviceTrustCryptoServiceAbstrac

// Send encrypted keys to server
const deviceIdentifier = await this.appIdService.getAppId();
return this.devicesApiService.updateTrustedDeviceKeys(
const deviceResponse = await this.devicesApiService.updateTrustedDeviceKeys(
deviceIdentifier,
devicePublicKeyEncryptedUserKey.encryptedString,
userKeyEncryptedDevicePublicKey.encryptedString,
deviceKeyEncryptedDevicePrivateKey.encryptedString
);

// store device key in local/secure storage if enc keys posted to server successfully
await this.setDeviceKey(deviceKey);
return deviceResponse;
}

async getDeviceKey(): Promise<DeviceKey> {
// Check if device key is already stored
const existingDeviceKey = await this.stateService.getDeviceKey();
return await this.stateService.getDeviceKey();
}

if (existingDeviceKey != null) {
return existingDeviceKey;
} else {
return this.makeDeviceKey();
}
private async setDeviceKey(deviceKey: DeviceKey): Promise<void> {
await this.stateService.setDeviceKey(deviceKey);
}

private async makeDeviceKey(): Promise<DeviceKey> {
// Create 512-bit device key
const randomBytes: CsprngArray = await this.cryptoFunctionService.randomBytes(64);
const deviceKey = new SymmetricCryptoKey(randomBytes) as DeviceKey;

// Save device key in secure storage
await this.stateService.setDeviceKey(deviceKey);

return deviceKey;
}

Expand All @@ -106,7 +104,7 @@ export class DeviceTrustCryptoService implements DeviceTrustCryptoServiceAbstrac
encryptedUserKey: any
): Promise<UserKey> {
// get device key
const existingDeviceKey = await this.stateService.getDeviceKey();
const existingDeviceKey = await this.getDeviceKey();

if (!existingDeviceKey) {
// TODO: not sure what to do here
Expand Down
39 changes: 20 additions & 19 deletions libs/common/src/auth/services/device-trust-crypto.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,51 +87,57 @@ describe("deviceTrustCryptoService", () => {
const userKeyBytesLength = 64;

describe("getDeviceKey", () => {
let mockRandomBytes: CsprngArray;
let mockDeviceKey: SymmetricCryptoKey;
let existingDeviceKey: DeviceKey;
let stateSvcGetDeviceKeySpy: jest.SpyInstance;
let makeDeviceKeySpy: jest.SpyInstance;

beforeEach(() => {
mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray;
mockDeviceKey = new SymmetricCryptoKey(mockRandomBytes);
existingDeviceKey = new SymmetricCryptoKey(
new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray
) as DeviceKey;

stateSvcGetDeviceKeySpy = jest.spyOn(stateService, "getDeviceKey");
makeDeviceKeySpy = jest.spyOn(deviceTrustCryptoService as any, "makeDeviceKey");
});

it("gets a device key when there is not an existing device key", async () => {
it("returns null when there is not an existing device key", async () => {
stateSvcGetDeviceKeySpy.mockResolvedValue(null);
makeDeviceKeySpy.mockResolvedValue(mockDeviceKey);

const deviceKey = await deviceTrustCryptoService.getDeviceKey();

expect(stateSvcGetDeviceKeySpy).toHaveBeenCalledTimes(1);
expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1);

expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey);
expect(deviceKey).toEqual(mockDeviceKey);
expect(deviceKey).toBeNull();
});

it("returns the existing device key without creating a new one when there is an existing device key", async () => {
it("returns the device key when there is an existing device key", async () => {
stateSvcGetDeviceKeySpy.mockResolvedValue(existingDeviceKey);

const deviceKey = await deviceTrustCryptoService.getDeviceKey();

expect(stateSvcGetDeviceKeySpy).toHaveBeenCalledTimes(1);
expect(makeDeviceKeySpy).not.toHaveBeenCalled();

expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey);
expect(deviceKey).toEqual(existingDeviceKey);
});
});

describe("setDeviceKey", () => {
it("sets the device key in the state service", async () => {
const stateSvcSetDeviceKeySpy = jest.spyOn(stateService, "setDeviceKey");

const deviceKey = new SymmetricCryptoKey(
new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray
) as DeviceKey;

// 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(deviceKey);

expect(stateSvcSetDeviceKeySpy).toHaveBeenCalledTimes(1);
expect(stateSvcSetDeviceKeySpy).toHaveBeenCalledWith(deviceKey);
});
});

describe("makeDeviceKey", () => {
it("creates a new non-null 64 byte device key, securely stores it, and returns it", async () => {
const mockRandomBytes = new Uint8Array(deviceKeyBytesLength).buffer as CsprngArray;
Expand All @@ -140,8 +146,6 @@ describe("deviceTrustCryptoService", () => {
.spyOn(cryptoFunctionService, "randomBytes")
.mockResolvedValue(mockRandomBytes);

const stateSvcSetDeviceKeySpy = jest.spyOn(stateService, "setDeviceKey");

// 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();
Expand All @@ -151,9 +155,6 @@ describe("deviceTrustCryptoService", () => {

expect(deviceKey).not.toBeNull();
expect(deviceKey).toBeInstanceOf(SymmetricCryptoKey);

expect(stateSvcSetDeviceKeySpy).toHaveBeenCalledTimes(1);
expect(stateSvcSetDeviceKeySpy).toHaveBeenCalledWith(deviceKey);
});
});

Expand Down

0 comments on commit e50e524

Please sign in to comment.