mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-06 18:57:56 +01:00
more crypto service refactors
- check for auto key when getting user key - consolidate getUserKeyFromMemory and FromStorage methods - move bio key references out of base crypto service - update either pin key when setting user key instead of lock component - group deprecated methods - rename key legacy method
This commit is contained in:
parent
2efec1d880
commit
0debdc5514
@ -93,7 +93,7 @@ export class LockComponent extends BaseLockComponent {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async unlockBiometric(): Promise<boolean> {
|
||||
override async unlockBiometric(): Promise<boolean> {
|
||||
if (!this.biometricLock) {
|
||||
return;
|
||||
}
|
||||
|
@ -7,10 +7,22 @@ import {
|
||||
import { CryptoService } from "@bitwarden/common/platform/services/crypto.service";
|
||||
|
||||
export class BrowserCryptoService extends CryptoService {
|
||||
override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise<boolean> {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
return await this.stateService.getBiometricUnlock({ userId: userId });
|
||||
}
|
||||
return super.hasUserKeyStored(keySuffix, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser doesn't store biometric keys, so we retrieve them from the desktop and return
|
||||
* if we successfully saved it into memory as the User Key
|
||||
*/
|
||||
protected override async getKeyFromStorage(keySuffix: KeySuffixOptions): Promise<UserKey> {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
await this.platformUtilService.authenticateBiometric();
|
||||
const userKey = await this.getUserKeyFromMemory();
|
||||
// this will check for an auto key, but that is acceptable
|
||||
const userKey = await this.getUserKey();
|
||||
if (userKey) {
|
||||
return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey.keyB64).buffer) as UserKey;
|
||||
}
|
||||
|
@ -269,7 +269,6 @@ export class SettingsComponent implements OnInit {
|
||||
|
||||
this.form.controls.pin.setValue(await ref.onClosedPromise());
|
||||
} else {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutSettingsService.clear();
|
||||
}
|
||||
}
|
||||
@ -324,7 +323,7 @@ export class SettingsComponent implements OnInit {
|
||||
});
|
||||
|
||||
await this.stateService.setBiometricAwaitingAcceptance(true);
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.cryptoService.refreshAdditionalKeys();
|
||||
|
||||
await Promise.race([
|
||||
submitted.then(async (result) => {
|
||||
|
@ -568,7 +568,7 @@ export class LoginCommand {
|
||||
const newPasswordHash = await this.cryptoService.hashPassword(masterPassword, newMasterKey);
|
||||
|
||||
// Grab user key
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
if (!userKey) {
|
||||
throw new Error("User key not found.");
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import * as koa from "koa";
|
||||
import * as koaBodyParser from "koa-bodyparser";
|
||||
import * as koaJson from "koa-json";
|
||||
|
||||
import { KeySuffixOptions } from "@bitwarden/common/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
|
||||
import { ConfirmCommand } from "../admin-console/commands/confirm.command";
|
||||
@ -425,14 +424,7 @@ export class ServeCommand {
|
||||
this.processResponse(res, Response.error("You are not logged in."));
|
||||
return true;
|
||||
}
|
||||
if (await this.main.cryptoService.hasUserKeyInMemory()) {
|
||||
return false;
|
||||
} else if (await this.main.cryptoService.hasUserKeyStored(KeySuffixOptions.Auto)) {
|
||||
// load key into memory
|
||||
const userAutoKey = await this.main.cryptoService.getUserKeyFromStorage(
|
||||
KeySuffixOptions.Auto
|
||||
);
|
||||
await this.main.cryptoService.setUserKey(userAutoKey);
|
||||
if (await this.main.cryptoService.hasUserKey()) {
|
||||
return false;
|
||||
}
|
||||
this.processResponse(res, Response.error("Vault is locked."));
|
||||
|
@ -2,7 +2,6 @@ import * as chalk from "chalk";
|
||||
import * as program from "commander";
|
||||
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/enums";
|
||||
|
||||
import { LockCommand } from "./auth/commands/lock.command";
|
||||
import { LoginCommand } from "./auth/commands/login.command";
|
||||
@ -597,14 +596,8 @@ export class Program {
|
||||
|
||||
protected async exitIfLocked() {
|
||||
await this.exitIfNotAuthed();
|
||||
if (await this.main.cryptoService.hasUserKeyInMemory()) {
|
||||
if (await this.main.cryptoService.hasUserKey()) {
|
||||
return;
|
||||
} else if (await this.main.cryptoService.hasUserKeyStored(KeySuffixOptions.Auto)) {
|
||||
// load key into memory
|
||||
const userAutoKey = await this.main.cryptoService.getUserKeyFromStorage(
|
||||
KeySuffixOptions.Auto
|
||||
);
|
||||
await this.main.cryptoService.setUserKey(userAutoKey);
|
||||
} else if (process.env.BW_NOINTERACTION !== "true") {
|
||||
// must unlock
|
||||
if (await this.main.keyConnectorService.getUsesKeyConnector()) {
|
||||
|
@ -126,7 +126,7 @@ export class CreateCommand {
|
||||
return Response.error("Premium status is required to use this feature.");
|
||||
}
|
||||
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
if (userKey == null) {
|
||||
return Response.error(
|
||||
"You must update your encryption key before you can use this feature. " +
|
||||
|
@ -374,7 +374,6 @@ export class SettingsComponent implements OnInit {
|
||||
this.form.controls.pin.setValue(await ref.onClosedPromise());
|
||||
}
|
||||
if (!this.form.value.pin) {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutSettingsService.clear();
|
||||
}
|
||||
}
|
||||
@ -388,7 +387,7 @@ export class SettingsComponent implements OnInit {
|
||||
if (!this.form.value.biometric || !this.supportsBiometric) {
|
||||
this.form.controls.biometric.setValue(false);
|
||||
await this.stateService.setBiometricUnlock(null);
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.cryptoService.refreshAdditionalKeys();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -401,7 +400,7 @@ export class SettingsComponent implements OnInit {
|
||||
await this.stateService.setBiometricRequirePasswordOnStart(true);
|
||||
await this.stateService.setDismissedBiometricRequirePasswordOnStart();
|
||||
}
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.cryptoService.refreshAdditionalKeys();
|
||||
|
||||
// Validate the key is stored in case biometrics fail.
|
||||
const biometricSet = await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric);
|
||||
@ -435,7 +434,7 @@ export class SettingsComponent implements OnInit {
|
||||
await this.stateService.setBiometricEncryptionClientKeyHalf(null);
|
||||
}
|
||||
await this.stateService.setDismissedBiometricRequirePasswordOnStart();
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.cryptoService.refreshAdditionalKeys();
|
||||
}
|
||||
|
||||
async saveFavicons() {
|
||||
|
@ -26,6 +26,23 @@ export class ElectronCryptoService extends CryptoService {
|
||||
super(cryptoFunctionService, encryptService, platformUtilsService, logService, stateService);
|
||||
}
|
||||
|
||||
override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise<boolean> {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
const oldKey = await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId });
|
||||
return oldKey || (await this.stateService.hasUserKeyBiometric({ userId: userId }));
|
||||
}
|
||||
return super.hasUserKeyStored(keySuffix, userId);
|
||||
}
|
||||
|
||||
override async clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: string): Promise<void> {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
this.stateService.setUserKeyBiometric(null, { userId: userId });
|
||||
this.clearDeprecatedKeys(KeySuffixOptions.Biometric, userId);
|
||||
return;
|
||||
}
|
||||
super.clearStoredUserKey(keySuffix, userId);
|
||||
}
|
||||
|
||||
protected override async storeAdditionalKeys(key: UserKey, userId?: string) {
|
||||
await super.storeAdditionalKeys(key, userId);
|
||||
|
||||
@ -36,7 +53,7 @@ export class ElectronCryptoService extends CryptoService {
|
||||
} else {
|
||||
await this.stateService.setUserKeyBiometric(null, { userId: userId });
|
||||
}
|
||||
await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId });
|
||||
await this.clearDeprecatedKeys(KeySuffixOptions.Biometric, userId);
|
||||
}
|
||||
|
||||
protected override async getKeyFromStorage(
|
||||
@ -70,13 +87,18 @@ export class ElectronCryptoService extends CryptoService {
|
||||
return await super.shouldStoreKey(keySuffix, userId);
|
||||
}
|
||||
|
||||
protected override async clearAllStoredUserKeys(userId?: string): Promise<void> {
|
||||
await this.stateService.setUserKeyBiometric(null, { userId: userId });
|
||||
super.clearAllStoredUserKeys(userId);
|
||||
}
|
||||
|
||||
private async getBiometricEncryptionClientKeyHalf(userId?: string): Promise<CsprngString | null> {
|
||||
try {
|
||||
let biometricKey = await this.stateService
|
||||
.getBiometricEncryptionClientKeyHalf({ userId })
|
||||
.then((result) => result?.decrypt(null /* user encrypted */))
|
||||
.then((result) => result as CsprngString);
|
||||
const userKey = await this.getKeyForUserEncryption();
|
||||
const userKey = await this.getUserKeyWithLegacySupport();
|
||||
if (biometricKey == null && userKey != null) {
|
||||
const keyBytes = await this.cryptoFunctionService.randomBytes(32);
|
||||
biometricKey = Utils.fromBufferToUtf8(keyBytes) as CsprngString;
|
||||
@ -90,6 +112,18 @@ export class ElectronCryptoService extends CryptoService {
|
||||
}
|
||||
}
|
||||
|
||||
// --LEGACY METHODS--
|
||||
// We previously used the master key for additional keys, but now we use the user key.
|
||||
// These methods support migrating the old keys to the new ones.
|
||||
|
||||
override async clearDeprecatedKeys(keySuffix: KeySuffixOptions, userId?: string) {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId });
|
||||
}
|
||||
|
||||
super.clearDeprecatedKeys(keySuffix, userId);
|
||||
}
|
||||
|
||||
private async migrateBiometricKeyIfNeeded(userId?: string) {
|
||||
if (await this.stateService.hasCryptoMasterKeyBiometric({ userId })) {
|
||||
const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId });
|
||||
|
@ -58,7 +58,7 @@ export class EnrollMasterPasswordReset {
|
||||
const publicKey = Utils.fromB64ToArray(orgKeys.publicKey);
|
||||
|
||||
// RSA Encrypt user's encKey.key with organization public key
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey.buffer);
|
||||
keyString = encryptedKey.encryptedString;
|
||||
toastStringRef = "enrollPasswordResetSuccess";
|
||||
|
@ -140,7 +140,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
|
||||
const publicKey = Utils.fromB64ToArray(response.publicKey);
|
||||
|
||||
// RSA Encrypt user's encKey.key with organization public key
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
const encryptedKey = await this.cryptoService.rsaEncrypt(userKey.key, publicKey.buffer);
|
||||
|
||||
// Add reset password key to accept request
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
} from "@bitwarden/common/auth/models/response/emergency-access.response";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/enums";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -303,8 +302,7 @@ export class EmergencyAccessComponent implements OnInit {
|
||||
|
||||
// Encrypt the user key with the grantees public key, and send it to bitwarden for escrow.
|
||||
private async doConfirmation(details: EmergencyAccessGranteeDetailsResponse) {
|
||||
let userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
userKey ||= await this.cryptoService.getUserKeyFromStorage(KeySuffixOptions.Auto);
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
if (!userKey) {
|
||||
throw new Error("No user key found");
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
let newProtectedUserKey: [UserKey, EncString] = null;
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
if (userKey == null) {
|
||||
newProtectedUserKey = await this.cryptoService.makeUserKey(newMasterKey);
|
||||
} else {
|
||||
|
@ -287,17 +287,6 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
|
||||
|
||||
// if MP on restart is enabled, use it to get the PIN and store the ephemeral
|
||||
// pin protected user key
|
||||
if (this.pinStatus === `ENABLED_WITH_MP_ON_RESET`) {
|
||||
const protectedPin = await this.stateService.getProtectedPin();
|
||||
const pin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), userKey);
|
||||
const pinKey = await this.cryptoService.makePinKey(pin, this.email, kdf, kdfConfig);
|
||||
await this.stateService.setUserKeyPinEphemeral(
|
||||
await this.cryptoService.encrypt(userKey.key, pinKey)
|
||||
);
|
||||
}
|
||||
await this.setKeyAndContinue(userKey, true);
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
||||
);
|
||||
|
||||
// Grab user key
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
|
||||
// Encrypt user key with new master key
|
||||
const newProtectedUserKey = await this.cryptoService.encryptUserKeyWithMasterKey(
|
||||
|
@ -132,7 +132,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
||||
const publicKey = Utils.fromB64ToArray(response.publicKey);
|
||||
|
||||
// RSA Encrypt user key with organization public key
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
const encryptedUserKey = await this.cryptoService.rsaEncrypt(
|
||||
userKey.key,
|
||||
publicKey.buffer
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Directive, OnInit } from "@angular/core";
|
||||
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { KeySuffixOptions } from "@bitwarden/common/enums/key-suffix-options.enum";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@ -41,7 +42,7 @@ export class SetPinComponent implements OnInit {
|
||||
await this.stateService.getKdfType(),
|
||||
await this.stateService.getKdfConfig()
|
||||
);
|
||||
const userKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey = await this.cryptoService.getUserKey();
|
||||
const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey);
|
||||
const encPin = await this.cryptoService.encrypt(this.pin, userKey);
|
||||
await this.stateService.setProtectedPin(encPin.encryptedString);
|
||||
@ -50,7 +51,7 @@ export class SetPinComponent implements OnInit {
|
||||
} else {
|
||||
await this.stateService.setUserKeyPin(pinProtectedKey);
|
||||
}
|
||||
await this.cryptoService.clearDeprecatedPinKeys();
|
||||
await this.cryptoService.clearDeprecatedKeys(KeySuffixOptions.Pin);
|
||||
|
||||
this.modalRef.close(true);
|
||||
}
|
||||
|
@ -15,45 +15,44 @@ import {
|
||||
} from "../models/domain/symmetric-crypto-key";
|
||||
|
||||
export abstract class CryptoService {
|
||||
/**
|
||||
* Use for encryption/decryption of data in order to support legacy
|
||||
* encryption models. It will return the user key if available,
|
||||
* if not it will return the master key.
|
||||
*/
|
||||
getKeyForUserEncryption: (key?: SymmetricCryptoKey) => Promise<SymmetricCryptoKey>;
|
||||
|
||||
/**
|
||||
* Sets the provided user key and stores
|
||||
* any other necessary versions, such as biometrics
|
||||
* any other necessary versions (such as auto, biometrics,
|
||||
* or pin)
|
||||
* @param key The user key to set
|
||||
* @param userId The desired user
|
||||
*/
|
||||
setUserKey: (key: UserKey) => Promise<void>;
|
||||
/**
|
||||
* Gets the user key from memory and sets it again,
|
||||
* kicking off a refresh of any additional keys that are needed.
|
||||
* kicking off a refresh of any additional keys
|
||||
* (such as auto, biometrics, or pin)
|
||||
*/
|
||||
toggleKey: () => Promise<void>;
|
||||
refreshAdditionalKeys: () => Promise<void>;
|
||||
|
||||
/**
|
||||
* Retrieves the user key
|
||||
* @param keySuffix The desired version of the user's key to retrieve
|
||||
* from storage if it is not available in memory
|
||||
* @param userId The desired user
|
||||
* @returns The user key
|
||||
*/
|
||||
getUserKeyFromMemory: (userId?: string) => Promise<UserKey>;
|
||||
getUserKey: (userId?: string) => Promise<UserKey>;
|
||||
/**
|
||||
* Use for encryption/decryption of data in order to support legacy
|
||||
* encryption models. It will return the user key if available,
|
||||
* if not it will return the master key.
|
||||
* @param userId The desired user
|
||||
*/
|
||||
getUserKeyWithLegacySupport: (userId?: string) => Promise<UserKey>;
|
||||
/**
|
||||
* Retrieves the user key from storage
|
||||
* @param keySuffix The desired version of the user's key to retrieve
|
||||
* @param userId The desired user
|
||||
* @returns The user key
|
||||
*/
|
||||
getUserKeyFromStorage: (
|
||||
keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric,
|
||||
userId?: string
|
||||
) => Promise<UserKey>;
|
||||
getUserKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise<UserKey>;
|
||||
|
||||
/**
|
||||
* @returns True if any version of the user key is available
|
||||
* @returns True if the user key is available
|
||||
*/
|
||||
hasUserKey: () => Promise<boolean>;
|
||||
/**
|
||||
@ -66,10 +65,7 @@ export abstract class CryptoService {
|
||||
* @param userId The desired user
|
||||
* @returns True if the provided version of the user key is stored
|
||||
*/
|
||||
hasUserKeyStored: (
|
||||
keySuffix?: KeySuffixOptions.Auto | KeySuffixOptions.Biometric,
|
||||
userId?: string
|
||||
) => Promise<boolean>;
|
||||
hasUserKeyStored: (keySuffix: KeySuffixOptions, userId?: string) => Promise<boolean>;
|
||||
/**
|
||||
* Generates a new user key
|
||||
* @param masterKey The user's master key
|
||||
@ -285,15 +281,12 @@ export abstract class CryptoService {
|
||||
*/
|
||||
makePinKey: (pin: string, salt: string, kdf: KdfType, kdfConfig: KdfConfig) => Promise<PinKey>;
|
||||
/**
|
||||
* Clears the user's pin protected user key
|
||||
* Clears the user's pin keys from storage
|
||||
* Note: This will remove the stored pin and as a result,
|
||||
* disable pin protection for the user
|
||||
* @param userId The desired user
|
||||
*/
|
||||
clearPinProtectedKey: (userId?: string) => Promise<void>;
|
||||
/**
|
||||
* Clears the user's old pin keys from storage
|
||||
* @param userId The desired user
|
||||
*/
|
||||
clearDeprecatedPinKeys: (userId?: string) => Promise<void>;
|
||||
clearPinKeys: (userId?: string) => Promise<void>;
|
||||
/**
|
||||
* Decrypts the user key with their pin
|
||||
* @param pin The user's PIN
|
||||
@ -347,6 +340,14 @@ export abstract class CryptoService {
|
||||
kdfConfig: KdfConfig,
|
||||
protectedKeyCs?: EncString
|
||||
) => Promise<MasterKey>;
|
||||
/**
|
||||
* Previously, the master key was used for any additional key like the biometrics or pin key.
|
||||
* We have switched to using the user key for these purposes. This method is for clearing the state
|
||||
* of the older keys on logout or post migration.
|
||||
* @param keySuffix The desired type of key to clear
|
||||
* @param userId The desired user
|
||||
*/
|
||||
clearDeprecatedKeys: (keySuffix: KeySuffixOptions, userId?: string) => Promise<void>;
|
||||
/**
|
||||
* @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
|
||||
* and then call encryptService.encrypt
|
||||
|
@ -225,7 +225,7 @@ describe("EncString", () => {
|
||||
|
||||
await encString.decrypt(null, key);
|
||||
|
||||
expect(cryptoService.getKeyForUserEncryption).not.toHaveBeenCalled();
|
||||
expect(cryptoService.getUserKeyWithLegacySupport).not.toHaveBeenCalled();
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, key);
|
||||
});
|
||||
|
||||
@ -243,11 +243,11 @@ describe("EncString", () => {
|
||||
it("gets the user's decryption key if required", async () => {
|
||||
const userKey = mock<SymmetricCryptoKey>();
|
||||
|
||||
cryptoService.getKeyForUserEncryption.mockResolvedValue(userKey);
|
||||
cryptoService.getUserKeyWithLegacySupport.mockResolvedValue(userKey);
|
||||
|
||||
await encString.decrypt(null, null);
|
||||
|
||||
expect(cryptoService.getKeyForUserEncryption).toHaveBeenCalledWith();
|
||||
expect(cryptoService.getUserKeyWithLegacySupport).toHaveBeenCalledWith();
|
||||
expect(encryptService.decryptToUtf8).toHaveBeenCalledWith(encString, userKey);
|
||||
});
|
||||
});
|
||||
|
@ -162,7 +162,7 @@ export class EncString implements Encrypted {
|
||||
const cryptoService = Utils.getContainerService().getCryptoService();
|
||||
return orgId != null
|
||||
? await cryptoService.getOrgKey(orgId)
|
||||
: await cryptoService.getKeyForUserEncryption();
|
||||
: await cryptoService.getUserKeyWithLegacySupport();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ describe("cryptoService", () => {
|
||||
it("returns the user key if available", async () => {
|
||||
stateSvcGetUserKey.mockResolvedValue(mockUserKey);
|
||||
|
||||
const encryptionKey = await cryptoService.getKeyForUserEncryption();
|
||||
const encryptionKey = await cryptoService.getUserKeyWithLegacySupport();
|
||||
|
||||
expect(stateSvcGetUserKey).toHaveBeenCalled();
|
||||
expect(stateSvcGetMasterKey).not.toHaveBeenCalled();
|
||||
@ -73,7 +73,7 @@ describe("cryptoService", () => {
|
||||
stateSvcGetUserKey.mockResolvedValue(null);
|
||||
stateSvcGetMasterKey.mockResolvedValue(mockMasterKey);
|
||||
|
||||
const encryptionKey = await cryptoService.getKeyForUserEncryption();
|
||||
const encryptionKey = await cryptoService.getUserKeyWithLegacySupport();
|
||||
|
||||
expect(stateSvcGetMasterKey).toHaveBeenCalled();
|
||||
expect(encryptionKey).toEqual(mockMasterKey);
|
||||
|
@ -44,53 +44,57 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
protected stateService: StateService
|
||||
) {}
|
||||
|
||||
async getKeyForUserEncryption(): Promise<SymmetricCryptoKey> {
|
||||
const userKey = await this.getUserKeyFromMemory();
|
||||
if (userKey != null) {
|
||||
return userKey;
|
||||
}
|
||||
|
||||
// Legacy support: encryption used to be done with the master key (derived from master password).
|
||||
// Users who have not migrated will have a null user key and must use the master key instead.
|
||||
return await this.getMasterKey();
|
||||
}
|
||||
|
||||
async setUserKey(key: UserKey, userId?: string): Promise<void> {
|
||||
await this.stateService.setUserKey(key, { userId: userId });
|
||||
await this.storeAdditionalKeys(key, userId);
|
||||
}
|
||||
|
||||
async toggleKey(): Promise<void> {
|
||||
const key = await this.getUserKeyFromMemory();
|
||||
async refreshAdditionalKeys(): Promise<void> {
|
||||
const key = await this.getUserKey();
|
||||
await this.setUserKey(key);
|
||||
}
|
||||
|
||||
async getUserKeyFromMemory(userId?: string): Promise<UserKey> {
|
||||
return await this.stateService.getUserKey({ userId: userId });
|
||||
}
|
||||
|
||||
async getUserKeyFromStorage(
|
||||
keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric,
|
||||
userId?: string
|
||||
): Promise<UserKey> {
|
||||
const userKey = await this.getKeyFromStorage(keySuffix, userId);
|
||||
if (userKey != null) {
|
||||
if (!(await this.validateUserKey(userKey))) {
|
||||
this.logService.warning("Wrong key, throwing away stored key");
|
||||
await this.clearAllStoredUserKeys(userId);
|
||||
return null;
|
||||
}
|
||||
|
||||
async getUserKey(userId?: string): Promise<UserKey> {
|
||||
let userKey = await this.stateService.getUserKey({ userId: userId });
|
||||
if (userKey) {
|
||||
return userKey;
|
||||
}
|
||||
|
||||
// If the user has set their vault timeout to 'Never', we can load the user key from storage
|
||||
if (await this.hasUserKeyStored(KeySuffixOptions.Auto)) {
|
||||
userKey = await this.getKeyFromStorage(KeySuffixOptions.Auto, userId);
|
||||
if (userKey) {
|
||||
await this.setUserKey(userKey, userId);
|
||||
return userKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getUserKeyWithLegacySupport(userId?: string): Promise<UserKey> {
|
||||
const userKey = await this.getUserKey(userId);
|
||||
if (userKey) {
|
||||
return userKey;
|
||||
}
|
||||
|
||||
// Legacy support: encryption used to be done with the master key (derived from master password).
|
||||
// Users who have not migrated will have a null user key and must use the master key instead.
|
||||
return (await this.getMasterKey(userId)) as any as UserKey;
|
||||
}
|
||||
|
||||
async getUserKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string): Promise<UserKey> {
|
||||
const userKey = await this.getKeyFromStorage(keySuffix, userId);
|
||||
if (userKey) {
|
||||
if (!(await this.validateUserKey(userKey))) {
|
||||
this.logService.warning("Invalid key, throwing away stored keys");
|
||||
await this.clearAllStoredUserKeys(userId);
|
||||
}
|
||||
return userKey;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async hasUserKey(): Promise<boolean> {
|
||||
return (
|
||||
(await this.hasUserKeyInMemory()) ||
|
||||
(await this.hasUserKeyStored(KeySuffixOptions.Auto)) ||
|
||||
(await this.hasUserKeyStored(KeySuffixOptions.Biometric))
|
||||
(await this.hasUserKeyInMemory()) || (await this.hasUserKeyStored(KeySuffixOptions.Auto))
|
||||
);
|
||||
}
|
||||
|
||||
@ -98,14 +102,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return (await this.stateService.getUserKey({ userId: userId })) != null;
|
||||
}
|
||||
|
||||
async hasUserKeyStored(
|
||||
keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric,
|
||||
userId?: string
|
||||
): Promise<boolean> {
|
||||
if (keySuffix === KeySuffixOptions.Biometric) {
|
||||
const oldKey = await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId });
|
||||
return oldKey || (await this.stateService.hasUserKeyBiometric({ userId: userId }));
|
||||
}
|
||||
async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: string): Promise<boolean> {
|
||||
return (await this.getKeyFromStorage(keySuffix, userId)) != null;
|
||||
}
|
||||
|
||||
@ -127,16 +124,13 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async clearStoredUserKey(keySuffix: KeySuffixOptions, userId?: string): Promise<void> {
|
||||
switch (keySuffix) {
|
||||
case KeySuffixOptions.Auto:
|
||||
this.stateService.setUserKeyAuto(null, { userId: userId });
|
||||
break;
|
||||
case KeySuffixOptions.Biometric:
|
||||
this.stateService.setUserKeyBiometric(null, { userId: userId });
|
||||
break;
|
||||
case KeySuffixOptions.Pin:
|
||||
this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
break;
|
||||
if (keySuffix === KeySuffixOptions.Auto) {
|
||||
this.stateService.setUserKeyAuto(null, { userId: userId });
|
||||
this.clearDeprecatedKeys(KeySuffixOptions.Auto, userId);
|
||||
}
|
||||
if (keySuffix === KeySuffixOptions.Pin) {
|
||||
this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
this.clearDeprecatedKeys(KeySuffixOptions.Pin, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +168,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
masterKey: MasterKey,
|
||||
userKey?: UserKey
|
||||
): Promise<[UserKey, EncString]> {
|
||||
userKey ||= await this.getUserKeyFromMemory();
|
||||
userKey ||= await this.getUserKey();
|
||||
return this.buildProtectedSymmetricKey(masterKey, userKey.key);
|
||||
}
|
||||
|
||||
@ -463,7 +457,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
|
||||
const privateKey = await this.encryptService.decryptToBytes(
|
||||
new EncString(encPrivateKey),
|
||||
await this.getKeyForUserEncryption()
|
||||
await this.getUserKeyWithLegacySupport()
|
||||
);
|
||||
await this.stateService.setDecryptedPrivateKey(privateKey);
|
||||
return privateKey;
|
||||
@ -487,7 +481,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
async makeKeyPair(key?: SymmetricCryptoKey): Promise<[string, EncString]> {
|
||||
key ||= await this.getUserKeyFromMemory();
|
||||
key ||= await this.getUserKey();
|
||||
|
||||
const keyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
|
||||
const publicB64 = Utils.fromBufferToB64(keyPair[0]);
|
||||
@ -511,14 +505,11 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return (await this.stretchKey(pinKey)) as PinKey;
|
||||
}
|
||||
|
||||
async clearPinProtectedKey(userId?: string): Promise<void> {
|
||||
async clearPinKeys(userId?: string): Promise<void> {
|
||||
await this.stateService.setUserKeyPin(null, { userId: userId });
|
||||
await this.clearDeprecatedPinKeys(userId);
|
||||
}
|
||||
|
||||
async clearDeprecatedPinKeys(userId?: string): Promise<void> {
|
||||
await this.stateService.setEncryptedPinProtected(null, { userId: userId });
|
||||
await this.stateService.setDecryptedPinProtected(null, { userId: userId });
|
||||
await this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
await this.stateService.setProtectedPin(null, { userId: userId });
|
||||
await this.clearDeprecatedKeys(KeySuffixOptions.Pin, userId);
|
||||
}
|
||||
|
||||
async decryptUserKeyWithPin(
|
||||
@ -529,6 +520,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
pinProtectedUserKey?: EncString
|
||||
): Promise<UserKey> {
|
||||
pinProtectedUserKey ||= await this.stateService.getUserKeyPin();
|
||||
pinProtectedUserKey ||= await this.stateService.getUserKeyPinEphemeral();
|
||||
if (!pinProtectedUserKey) {
|
||||
throw new Error("No PIN protected key found.");
|
||||
}
|
||||
@ -573,7 +565,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
await this.clearOrgKeys(false, userId);
|
||||
await this.clearProviderKeys(false, userId);
|
||||
await this.clearKeyPair(false, userId);
|
||||
await this.clearPinProtectedKey(userId);
|
||||
await this.clearPinKeys(userId);
|
||||
}
|
||||
|
||||
async rsaEncrypt(data: ArrayBuffer, publicKey?: ArrayBuffer): Promise<EncString> {
|
||||
@ -702,8 +694,10 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates any additional keys if needed. Useful to make sure
|
||||
* other keys stay in sync when the user key has been rotated.
|
||||
* Generates any additional keys if needed. Additional keys are
|
||||
* keys such as biometrics, auto, and pin keys.
|
||||
* Useful to make sure other keys stay in sync when the user key
|
||||
* has been rotated.
|
||||
* @param key The user key
|
||||
* @param userId The desired user
|
||||
*/
|
||||
@ -714,27 +708,43 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
} else {
|
||||
await this.stateService.setUserKeyAuto(null, { userId: userId });
|
||||
}
|
||||
await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||
await this.clearDeprecatedKeys(KeySuffixOptions.Auto, userId);
|
||||
|
||||
const storePin = await this.shouldStoreKey(KeySuffixOptions.Pin, userId);
|
||||
if (storePin) {
|
||||
await this.storePinKey(key);
|
||||
await this.clearDeprecatedPinKeys(userId);
|
||||
// We can't always clear deprecated keys because the pin is only
|
||||
// migrated once used to unlock
|
||||
await this.clearDeprecatedKeys(KeySuffixOptions.Pin, userId);
|
||||
} else {
|
||||
await this.stateService.setUserKeyPin(null, { userId: userId });
|
||||
await this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the pin key if needed. If MP on Reset is enabled, stores the
|
||||
* ephemeral version.
|
||||
* @param key The user key
|
||||
*/
|
||||
protected async storePinKey(key: UserKey) {
|
||||
const email = await this.stateService.getEmail();
|
||||
const kdf = await this.stateService.getKdfType();
|
||||
const kdfConfig = await this.stateService.getKdfConfig();
|
||||
const pin = await this.encryptService.decryptToUtf8(
|
||||
new EncString(await this.stateService.getProtectedPin()),
|
||||
key
|
||||
);
|
||||
const pinKey = await this.makePinKey(pin, email, kdf, kdfConfig);
|
||||
await this.stateService.setUserKeyPin(await this.encryptService.encrypt(key.key, pinKey));
|
||||
const pinKey = await this.makePinKey(
|
||||
pin,
|
||||
await this.stateService.getEmail(),
|
||||
await this.stateService.getKdfType(),
|
||||
await this.stateService.getKdfConfig()
|
||||
);
|
||||
const encPin = await this.encryptService.encrypt(key.key, pinKey);
|
||||
|
||||
if ((await this.stateService.getUserKeyPin()) != null) {
|
||||
await this.stateService.setUserKeyPin(encPin);
|
||||
} else {
|
||||
await this.stateService.setUserKeyPinEphemeral(encPin);
|
||||
}
|
||||
}
|
||||
|
||||
protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string) {
|
||||
@ -747,12 +757,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
}
|
||||
case KeySuffixOptions.Pin: {
|
||||
const protectedPin = await this.stateService.getProtectedPin({ userId: userId });
|
||||
// This could cause a possible timing issue. Need to make sure the ephemeral key is set before
|
||||
// we set our user key
|
||||
const userKeyPinEphemeral = await this.stateService.getUserKeyPinEphemeral({
|
||||
userId: userId,
|
||||
});
|
||||
shouldStoreKey = !!protectedPin && !userKeyPinEphemeral;
|
||||
shouldStoreKey = !!protectedPin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -773,21 +778,9 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return null;
|
||||
}
|
||||
|
||||
private async migrateAutoKeyIfNeeded(userId?: string) {
|
||||
const oldAutoKey = await this.stateService.getCryptoMasterKeyAuto({ userId: userId });
|
||||
if (oldAutoKey) {
|
||||
// decrypt
|
||||
const masterKey = new SymmetricCryptoKey(
|
||||
Utils.fromB64ToArray(oldAutoKey).buffer
|
||||
) as MasterKey;
|
||||
const userKey = await this.decryptUserKeyWithMasterKey(
|
||||
masterKey,
|
||||
new EncString(await this.stateService.getEncryptedCryptoSymmetricKey())
|
||||
);
|
||||
// migrate
|
||||
await this.stateService.setUserKeyAuto(userKey.keyB64, { userId: userId });
|
||||
await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||
}
|
||||
protected async clearAllStoredUserKeys(userId?: string): Promise<void> {
|
||||
await this.stateService.setUserKeyAuto(null, { userId: userId });
|
||||
await this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
}
|
||||
|
||||
private async stretchKey(key: SymmetricCryptoKey): Promise<SymmetricCryptoKey> {
|
||||
@ -835,13 +828,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return [new SymmetricCryptoKey(newSymKey) as T, protectedSymKey];
|
||||
}
|
||||
|
||||
private async clearAllStoredUserKeys(userId?: string): Promise<void> {
|
||||
await this.stateService.setUserKeyAuto(null, { userId: userId });
|
||||
await this.stateService.setUserKeyBiometric(null, { userId: userId });
|
||||
await this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
}
|
||||
|
||||
async makeKey(
|
||||
private async makeKey(
|
||||
password: string,
|
||||
salt: string,
|
||||
kdf: KdfType,
|
||||
@ -890,12 +877,44 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return new SymmetricCryptoKey(key);
|
||||
}
|
||||
|
||||
// --LEGACY METHODS--
|
||||
// We previously used the master key for additional keys, but now we use the user key.
|
||||
// These methods support migrating the old keys to the new ones.
|
||||
|
||||
async clearDeprecatedKeys(keySuffix: KeySuffixOptions, userId?: string) {
|
||||
if (keySuffix === KeySuffixOptions.Auto) {
|
||||
await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||
} else if (keySuffix === KeySuffixOptions.Pin) {
|
||||
await this.stateService.setEncryptedPinProtected(null, { userId: userId });
|
||||
await this.stateService.setDecryptedPinProtected(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
private async migrateAutoKeyIfNeeded(userId?: string) {
|
||||
const oldAutoKey = await this.stateService.getCryptoMasterKeyAuto({ userId: userId });
|
||||
if (oldAutoKey) {
|
||||
// decrypt
|
||||
const masterKey = new SymmetricCryptoKey(
|
||||
Utils.fromB64ToArray(oldAutoKey).buffer
|
||||
) as MasterKey;
|
||||
const userKey = await this.decryptUserKeyWithMasterKey(
|
||||
masterKey,
|
||||
new EncString(await this.stateService.getEncryptedCryptoSymmetricKey())
|
||||
);
|
||||
// migrate
|
||||
await this.stateService.setUserKeyAuto(userKey.keyB64, { userId: userId });
|
||||
await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
|
||||
// --DEPRECATED METHODS--
|
||||
|
||||
/**
|
||||
* @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
|
||||
* and then call encryptService.encrypt
|
||||
*/
|
||||
async encrypt(plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey): Promise<EncString> {
|
||||
key ||= await this.getKeyForUserEncryption();
|
||||
key ||= await this.getUserKeyWithLegacySupport();
|
||||
return await this.encryptService.encrypt(plainValue, key);
|
||||
}
|
||||
|
||||
@ -904,7 +923,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
* and then call encryptService.encryptToBytes
|
||||
*/
|
||||
async encryptToBytes(plainValue: ArrayBuffer, key?: SymmetricCryptoKey): Promise<EncArrayBuffer> {
|
||||
key ||= await this.getKeyForUserEncryption();
|
||||
key ||= await this.getUserKeyWithLegacySupport();
|
||||
return this.encryptService.encryptToBytes(plainValue, key);
|
||||
}
|
||||
|
||||
@ -913,7 +932,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
* and then call encryptService.decryptToBytes
|
||||
*/
|
||||
async decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise<ArrayBuffer> {
|
||||
key ||= await this.getKeyForUserEncryption();
|
||||
key ||= await this.getUserKeyWithLegacySupport();
|
||||
return this.encryptService.decryptToBytes(encString, key);
|
||||
}
|
||||
|
||||
@ -922,7 +941,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
* and then call encryptService.decryptToUtf8
|
||||
*/
|
||||
async decryptToUtf8(encString: EncString, key?: SymmetricCryptoKey): Promise<string> {
|
||||
key ||= await this.getKeyForUserEncryption();
|
||||
key ||= await this.getUserKeyWithLegacySupport();
|
||||
return await this.encryptService.decryptToUtf8(encString, key);
|
||||
}
|
||||
|
||||
@ -935,7 +954,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
throw new Error("No buffer provided for decryption.");
|
||||
}
|
||||
|
||||
key ||= await this.getKeyForUserEncryption();
|
||||
key ||= await this.getUserKeyWithLegacySupport();
|
||||
|
||||
return this.encryptService.decryptToBytes(encBuffer, key);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export class DeviceCryptoService implements DeviceCryptoServiceAbstraction {
|
||||
|
||||
async trustDevice(): Promise<DeviceResponse> {
|
||||
// Attempt to get user key
|
||||
const userKey: UserKey = await this.cryptoService.getUserKeyFromMemory();
|
||||
const userKey: UserKey = await this.cryptoService.getUserKey();
|
||||
|
||||
// If user key is not found, throw error
|
||||
if (!userKey) {
|
||||
|
@ -151,7 +151,7 @@ describe("deviceCryptoService", () => {
|
||||
|
||||
let makeDeviceKeySpy: jest.SpyInstance;
|
||||
let rsaGenerateKeyPairSpy: jest.SpyInstance;
|
||||
let cryptoSvcGetUserKeyFromMemorySpy: jest.SpyInstance;
|
||||
let cryptoSvcGetUserKeySpy: jest.SpyInstance;
|
||||
let cryptoSvcRsaEncryptSpy: jest.SpyInstance;
|
||||
let encryptServiceEncryptSpy: jest.SpyInstance;
|
||||
let appIdServiceGetAppIdSpy: jest.SpyInstance;
|
||||
@ -198,8 +198,8 @@ describe("deviceCryptoService", () => {
|
||||
.spyOn(cryptoFunctionService, "rsaGenerateKeyPair")
|
||||
.mockResolvedValue(mockDeviceRsaKeyPair);
|
||||
|
||||
cryptoSvcGetUserKeyFromMemorySpy = jest
|
||||
.spyOn(cryptoService, "getUserKeyFromMemory")
|
||||
cryptoSvcGetUserKeySpy = jest
|
||||
.spyOn(cryptoService, "getUserKey")
|
||||
.mockResolvedValue(mockUserKey);
|
||||
|
||||
cryptoSvcRsaEncryptSpy = jest
|
||||
@ -231,7 +231,7 @@ describe("deviceCryptoService", () => {
|
||||
|
||||
expect(makeDeviceKeySpy).toHaveBeenCalledTimes(1);
|
||||
expect(rsaGenerateKeyPairSpy).toHaveBeenCalledTimes(1);
|
||||
expect(cryptoSvcGetUserKeyFromMemorySpy).toHaveBeenCalledTimes(1);
|
||||
expect(cryptoSvcGetUserKeySpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(cryptoSvcRsaEncryptSpy).toHaveBeenCalledTimes(1);
|
||||
expect(encryptServiceEncryptSpy).toHaveBeenCalledTimes(2);
|
||||
@ -251,17 +251,17 @@ describe("deviceCryptoService", () => {
|
||||
|
||||
it("throws specific error if user key is not found", async () => {
|
||||
// setup the spy to return null
|
||||
cryptoSvcGetUserKeyFromMemorySpy.mockResolvedValue(null);
|
||||
cryptoSvcGetUserKeySpy.mockResolvedValue(null);
|
||||
// check if the expected error is thrown
|
||||
await expect(deviceCryptoService.trustDevice()).rejects.toThrow(
|
||||
"User symmetric key not found"
|
||||
);
|
||||
|
||||
// reset the spy
|
||||
cryptoSvcGetUserKeyFromMemorySpy.mockReset();
|
||||
cryptoSvcGetUserKeySpy.mockReset();
|
||||
|
||||
// setup the spy to return undefined
|
||||
cryptoSvcGetUserKeyFromMemorySpy.mockResolvedValue(undefined);
|
||||
cryptoSvcGetUserKeySpy.mockResolvedValue(undefined);
|
||||
// check if the expected error is thrown
|
||||
await expect(deviceCryptoService.trustDevice()).rejects.toThrow(
|
||||
"User symmetric key not found"
|
||||
@ -280,9 +280,9 @@ describe("deviceCryptoService", () => {
|
||||
errorText: "rsaGenerateKeyPair error",
|
||||
},
|
||||
{
|
||||
method: "getUserKeyFromMemory",
|
||||
spy: () => cryptoSvcGetUserKeyFromMemorySpy,
|
||||
errorText: "getUserKeyFromMemory error",
|
||||
method: "getUserKey",
|
||||
spy: () => cryptoSvcGetUserKeySpy,
|
||||
errorText: "getUserKey error",
|
||||
},
|
||||
{
|
||||
method: "rsaEncrypt",
|
||||
|
@ -2,6 +2,7 @@ import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction }
|
||||
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "../../admin-console/enums";
|
||||
import { TokenService } from "../../auth/abstractions/token.service";
|
||||
import { KeySuffixOptions } from "../../enums/key-suffix-options.enum";
|
||||
import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
|
||||
import { CryptoService } from "../../platform/abstractions/crypto.service";
|
||||
import { StateService } from "../../platform/abstractions/state.service";
|
||||
@ -43,7 +44,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
|
||||
await this.tokenService.setClientId(clientId);
|
||||
await this.tokenService.setClientSecret(clientSecret);
|
||||
|
||||
await this.cryptoService.toggleKey();
|
||||
await this.cryptoService.refreshAdditionalKeys();
|
||||
}
|
||||
|
||||
async isPinLockSet(): Promise<PinLockType> {
|
||||
@ -116,8 +117,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
|
||||
|
||||
async clear(userId?: string): Promise<void> {
|
||||
await this.stateService.setEverBeenUnlocked(false, { userId: userId });
|
||||
await this.stateService.setUserKeyPinEphemeral(null, { userId: userId });
|
||||
await this.stateService.setProtectedPin(null, { userId: userId });
|
||||
await this.cryptoService.clearDeprecatedPinKeys(userId);
|
||||
await this.cryptoService.clearPinKeys(userId);
|
||||
await this.cryptoService.clearDeprecatedKeys(KeySuffixOptions.Pin, userId);
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ describe("Attachment", () => {
|
||||
|
||||
await attachment.decrypt(null, providedKey);
|
||||
|
||||
expect(cryptoService.getKeyForUserEncryption).not.toHaveBeenCalled();
|
||||
expect(cryptoService.getUserKeyWithLegacySupport).not.toHaveBeenCalled();
|
||||
expect(encryptService.decryptToBytes).toHaveBeenCalledWith(attachment.key, providedKey);
|
||||
});
|
||||
|
||||
@ -121,11 +121,11 @@ describe("Attachment", () => {
|
||||
|
||||
it("gets the user's decryption key if required", async () => {
|
||||
const userKey = mock<SymmetricCryptoKey>();
|
||||
cryptoService.getKeyForUserEncryption.mockResolvedValue(userKey);
|
||||
cryptoService.getUserKeyWithLegacySupport.mockResolvedValue(userKey);
|
||||
|
||||
await attachment.decrypt(null, null);
|
||||
|
||||
expect(cryptoService.getKeyForUserEncryption).toHaveBeenCalled();
|
||||
expect(cryptoService.getUserKeyWithLegacySupport).toHaveBeenCalled();
|
||||
expect(encryptService.decryptToBytes).toHaveBeenCalledWith(attachment.key, userKey);
|
||||
});
|
||||
});
|
||||
|
@ -71,7 +71,7 @@ export class Attachment extends Domain {
|
||||
const cryptoService = Utils.getContainerService().getCryptoService();
|
||||
return orgId != null
|
||||
? await cryptoService.getOrgKey(orgId)
|
||||
: await cryptoService.getKeyForUserEncryption();
|
||||
: await cryptoService.getUserKeyWithLegacySupport();
|
||||
}
|
||||
|
||||
toAttachmentData(): AttachmentData {
|
||||
|
@ -336,7 +336,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
const ciphers = await this.getAll();
|
||||
const orgKeys = await this.cryptoService.getOrgKeys();
|
||||
const userKey = await this.cryptoService.getKeyForUserEncryption();
|
||||
const userKey = await this.cryptoService.getUserKeyWithLegacySupport();
|
||||
|
||||
// Group ciphers by orgId or under 'null' for the user's ciphers
|
||||
const grouped = ciphers.reduce((agg, c) => {
|
||||
@ -639,7 +639,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
): Promise<Cipher> {
|
||||
let encKey: UserKey | OrgKey;
|
||||
encKey = await this.cryptoService.getOrgKey(cipher.organizationId);
|
||||
encKey ||= (await this.cryptoService.getKeyForUserEncryption()) as UserKey;
|
||||
encKey ||= await this.cryptoService.getUserKeyWithLegacySupport();
|
||||
|
||||
const dataEncKey = await this.cryptoService.makeDataEncKey(encKey);
|
||||
|
||||
@ -956,7 +956,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
let encKey: UserKey | OrgKey;
|
||||
encKey = await this.cryptoService.getOrgKey(organizationId);
|
||||
encKey ||= (await this.cryptoService.getKeyForUserEncryption()) as UserKey;
|
||||
encKey ||= (await this.cryptoService.getUserKeyWithLegacySupport()) as UserKey;
|
||||
|
||||
const dataEncKey = await this.cryptoService.makeDataEncKey(encKey);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user