Fix separate key storage for non desktop (#409)
* Handle non-desktop, non-split key storage * Reset vaultTimeoutService on clear. Fixes issues where unlock was required after login * Specify electron as desktop client * Use ElelectronCryptoService to handle desktop-specific tasks * Linter fixes
This commit is contained in:
parent
d63ee1858d
commit
1f83c3c1ba
|
@ -25,7 +25,7 @@ import { sequentialize } from '../misc/sequentialize';
|
||||||
import { Utils } from '../misc/utils';
|
import { Utils } from '../misc/utils';
|
||||||
import { EEFLongWordList } from '../misc/wordlist';
|
import { EEFLongWordList } from '../misc/wordlist';
|
||||||
|
|
||||||
const Keys = {
|
export const Keys = {
|
||||||
key: 'key', // Master Key
|
key: 'key', // Master Key
|
||||||
encOrgKeys: 'encOrgKeys',
|
encOrgKeys: 'encOrgKeys',
|
||||||
encPrivateKey: 'encPrivateKey',
|
encPrivateKey: 'encPrivateKey',
|
||||||
|
@ -42,25 +42,15 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
private privateKey: ArrayBuffer;
|
private privateKey: ArrayBuffer;
|
||||||
private orgKeys: Map<string, SymmetricCryptoKey>;
|
private orgKeys: Map<string, SymmetricCryptoKey>;
|
||||||
|
|
||||||
constructor(private storageService: StorageService, private secureStorageService: StorageService,
|
constructor(private storageService: StorageService, protected secureStorageService: StorageService,
|
||||||
private cryptoFunctionService: CryptoFunctionService, private platformUtilService: PlatformUtilsService,
|
private cryptoFunctionService: CryptoFunctionService, protected platformUtilService: PlatformUtilsService,
|
||||||
private logService: LogService) {
|
protected logService: LogService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setKey(key: SymmetricCryptoKey): Promise<any> {
|
async setKey(key: SymmetricCryptoKey): Promise<any> {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
|
||||||
if (await this.shouldStoreKey('auto')) {
|
await this.storeKey(key);
|
||||||
await this.secureStorageService.save(Keys.key, key.keyB64, { keySuffix: 'auto' });
|
|
||||||
} else {
|
|
||||||
this.clearStoredKey('auto');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await this.shouldStoreKey('biometric')) {
|
|
||||||
await this.secureStorageService.save(Keys.key, key.keyB64, { keySuffix: 'biometric' });
|
|
||||||
} else {
|
|
||||||
this.clearStoredKey('biometric');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setKeyHash(keyHash: string): Promise<{}> {
|
setKeyHash(keyHash: string): Promise<{}> {
|
||||||
|
@ -288,9 +278,8 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
return this.key != null;
|
return this.key != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasKeyStored(keySuffix: KeySuffixOptions): Promise<boolean> {
|
hasKeyStored(keySuffix: KeySuffixOptions): Promise<boolean> {
|
||||||
await this.upgradeSecurelyStoredKey();
|
return this.secureStorageService.has(Keys.key, { keySuffix: keySuffix });
|
||||||
return await this.secureStorageService.has(Keys.key, { keySuffix: keySuffix });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasEncKey(): Promise<boolean> {
|
async hasEncKey(): Promise<boolean> {
|
||||||
|
@ -651,7 +640,15 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
private async shouldStoreKey(keySuffix: KeySuffixOptions) {
|
protected async storeKey(key: SymmetricCryptoKey) {
|
||||||
|
if (await this.shouldStoreKey('auto') || await this.shouldStoreKey('biometric')) {
|
||||||
|
this.secureStorageService.save(Keys.key, key.keyB64);
|
||||||
|
} else {
|
||||||
|
this.secureStorageService.remove(Keys.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async shouldStoreKey(keySuffix: KeySuffixOptions) {
|
||||||
let shouldStoreKey = false;
|
let shouldStoreKey = false;
|
||||||
if (keySuffix === 'auto') {
|
if (keySuffix === 'auto') {
|
||||||
const vaultTimeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
|
const vaultTimeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
|
||||||
|
@ -663,37 +660,8 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
return shouldStoreKey;
|
return shouldStoreKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async retrieveKeyFromStorage(keySuffix: KeySuffixOptions) {
|
protected retrieveKeyFromStorage(keySuffix: KeySuffixOptions) {
|
||||||
await this.upgradeSecurelyStoredKey();
|
return this.secureStorageService.get<string>(Keys.key, { keySuffix: keySuffix });
|
||||||
|
|
||||||
return await this.secureStorageService.get<string>(Keys.key, { keySuffix: keySuffix });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4 Jun 2021 This is temporary upgrade method to move from a single shared stored key to
|
|
||||||
* multiple, unique stored keys for each use, e.g. never logout vs. biometric authentication.
|
|
||||||
*/
|
|
||||||
private async upgradeSecurelyStoredKey() {
|
|
||||||
// attempt key upgrade, but if we fail just delete it. Keys will be stored property upon unlock anyway.
|
|
||||||
const key = await this.secureStorageService.get<string>(Keys.key);
|
|
||||||
|
|
||||||
if (key == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (await this.shouldStoreKey('auto')) {
|
|
||||||
await this.secureStorageService.save(Keys.key, key, { keySuffix: 'auto' });
|
|
||||||
}
|
|
||||||
if (await this.shouldStoreKey('biometric')) {
|
|
||||||
await this.secureStorageService.save(Keys.key, key, { keySuffix: 'biometric' });
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(`Encountered error while upgrading obsolete Bitwarden secure storage item:`);
|
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.secureStorageService.remove(Keys.key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
|
||||||
|
|
|
@ -148,6 +148,8 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(): Promise<any> {
|
clear(): Promise<any> {
|
||||||
|
this.everBeenUnlocked = false;
|
||||||
|
this.manuallyOrTimerLocked = false;
|
||||||
this.pinProtectedKey = null;
|
this.pinProtectedKey = null;
|
||||||
return this.storageService.remove(ConstantsService.protectedPin);
|
return this.storageService.remove(ConstantsService.protectedPin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||||
|
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
|
import { KeySuffixOptions, StorageService } from 'jslib-common/abstractions/storage.service';
|
||||||
|
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
|
||||||
|
import { CryptoService, Keys } from 'jslib-common/services/crypto.service';
|
||||||
|
|
||||||
|
export class ElectronCryptoService extends CryptoService {
|
||||||
|
|
||||||
|
constructor(storageService: StorageService, secureStorageService: StorageService,
|
||||||
|
cryptoFunctionService: CryptoFunctionService, platformUtilService: PlatformUtilsService,
|
||||||
|
logService: LogService) {
|
||||||
|
super(storageService, secureStorageService, cryptoFunctionService, platformUtilService, logService);
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasKeyStored(keySuffix: KeySuffixOptions): Promise<boolean> {
|
||||||
|
await this.upgradeSecurelyStoredKey();
|
||||||
|
return super.hasKeyStored(keySuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async storeKey(key: SymmetricCryptoKey) {
|
||||||
|
if (await this.shouldStoreKey('auto')) {
|
||||||
|
await this.secureStorageService.save(Keys.key, key.keyB64, { keySuffix: 'auto' });
|
||||||
|
} else {
|
||||||
|
this.clearStoredKey('auto');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await this.shouldStoreKey('biometric')) {
|
||||||
|
await this.secureStorageService.save(Keys.key, key.keyB64, { keySuffix: 'biometric' });
|
||||||
|
} else {
|
||||||
|
this.clearStoredKey('biometric');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions) {
|
||||||
|
await this.upgradeSecurelyStoredKey();
|
||||||
|
return super.retrieveKeyFromStorage(keySuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 4 Jun 2021 This is temporary upgrade method to move from a single shared stored key to
|
||||||
|
* multiple, unique stored keys for each use, e.g. never logout vs. biometric authentication.
|
||||||
|
*/
|
||||||
|
private async upgradeSecurelyStoredKey() {
|
||||||
|
// attempt key upgrade, but if we fail just delete it. Keys will be stored property upon unlock anyway.
|
||||||
|
const key = await this.secureStorageService.get<string>(Keys.key);
|
||||||
|
|
||||||
|
if (key == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (await this.shouldStoreKey('auto')) {
|
||||||
|
await this.secureStorageService.save(Keys.key, key, { keySuffix: 'auto' });
|
||||||
|
}
|
||||||
|
if (await this.shouldStoreKey('biometric')) {
|
||||||
|
await this.secureStorageService.save(Keys.key, key, { keySuffix: 'biometric' });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(`Encountered error while upgrading obsolete Bitwarden secure storage item:`);
|
||||||
|
this.logService.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.secureStorageService.remove(Keys.key);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue