1
0
mirror of https://github.com/bitwarden/jslib synced 2026-01-08 11:33:15 +00:00

Refactor to use EncryptedOrganizationKey classes

This commit is contained in:
Thomas Rittson
2022-03-30 10:29:28 +10:00
parent 3bd8630f33
commit 5c66af8582
6 changed files with 90 additions and 45 deletions

View File

@@ -1,5 +1,10 @@
import { BehaviorSubject } from "rxjs";
import {
BaseEncryptedOrganizationKey,
EncryptedOrganizationKey,
} from "jslib-common/models/domain/account/encryptedKey";
import { KdfType } from "../enums/kdfType";
import { ThemeType } from "../enums/themeType";
import { UriMatchType } from "../enums/uriMatchType";
@@ -186,9 +191,9 @@ export abstract class StateService<T extends Account = Account> {
) => Promise<void>;
getEncryptedOrganizationKeys: (
options?: StorageOptions
) => Promise<{ [orgId: string]: EncryptedOrganizationKeyStore }>;
) => Promise<{ [orgId: string]: BaseEncryptedOrganizationKey }>;
setEncryptedOrganizationKeys: (
value: { [orgId: string]: EncryptedOrganizationKeyStore },
value: { [orgId: string]: BaseEncryptedOrganizationKey },
options?: StorageOptions
) => Promise<void>;
getEncryptedPasswordGenerationHistory: (

View File

@@ -1,4 +0,0 @@
export interface EncryptedOrganizationKeyStore {
key: string;
providerId?: string;
}

View File

@@ -21,7 +21,6 @@ import { GeneratedPasswordHistory } from "./generatedPasswordHistory";
import { Policy } from "./policy";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class EncryptionPair<TEncrypted, TDecrypted> {
encrypted?: TEncrypted;
decrypted?: TDecrypted;

View File

@@ -0,0 +1,47 @@
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { EncString } from "../encString";
import { SymmetricCryptoKey } from "../symmetricCryptoKey";
export abstract class BaseEncryptedOrganizationKey {
decrypt: (cryptoService: CryptoService) => Promise<SymmetricCryptoKey>;
toJSON: () => string;
static fromObj(obj: { key: string; providerId?: string }) {
if (obj.providerId != null) {
return new ProviderEncryptedOrganizationKey(obj.key, obj.providerId);
}
return new EncryptedOrganizationKey(obj.key);
}
}
export class EncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
constructor(private key: string) {}
async decrypt(cryptoService: CryptoService) {
const decValue = await cryptoService.rsaDecrypt(this.key);
return new SymmetricCryptoKey(decValue);
}
toJSON() {
return JSON.stringify({ key: this.key });
}
}
export class ProviderEncryptedOrganizationKey implements BaseEncryptedOrganizationKey {
constructor(private key: string, private providerId: string) {}
async decrypt(cryptoService: CryptoService) {
const providerKey = await cryptoService.getProviderKey(this.providerId);
const decValue = await cryptoService.decryptToBytes(new EncString(this.key), providerKey);
return new SymmetricCryptoKey(decValue);
}
toJSON() {
return JSON.stringify({
key: this.key,
providerId: this.providerId,
});
}
}

View File

@@ -1,6 +1,10 @@
import * as bigInt from "big-integer";
import { EncryptedOrganizationKeyStore } from 'jslib-common/interfaces/encryptedOrganizationKeyStore';
import { EncryptedOrganizationKeyStore } from "jslib-common/interfaces/encryptedOrganizationKeyStore";
import {
BaseEncryptedOrganizationKey,
EncryptedOrganizationKey,
} from "jslib-common/models/domain/account/encryptedKey";
import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service";
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service";
@@ -61,22 +65,15 @@ export class CryptoService implements CryptoServiceAbstraction {
orgs: ProfileOrganizationResponse[],
providerOrgs: ProfileProviderOrganizationResponse[]
): Promise<void> {
const orgKeyStore: { [orgId: string]: EncryptedOrganizationKeyStore } = {};
orgs.forEach((org) => {
orgKeyStore[org.id] = { key: org.key };
const encOrgKeys: { [orgId: string]: BaseEncryptedOrganizationKey } = {};
const allOrgs = orgs.concat(providerOrgs);
allOrgs.forEach((org) => {
encOrgKeys[org.id] = BaseEncryptedOrganizationKey.fromObj(org);
});
for (const providerOrg of providerOrgs) {
// Store providerId with the key later so it can be decrypted using the provider's key
// We can't decrypt it yet because we may not have all the required encryption keys available
orgKeyStore[providerOrg.id] = {
key: providerOrg.key,
providerId: providerOrg.providerId,
}
}
await this.stateService.setDecryptedOrganizationKeys(null);
return await this.stateService.setEncryptedOrganizationKeys(orgKeyStore);
return await this.stateService.setEncryptedOrganizationKeys(encOrgKeys);
}
async setProviderKeys(providers: ProfileProviderResponse[]): Promise<void> {
@@ -219,36 +216,21 @@ export class CryptoService implements CryptoServiceAbstraction {
return decryptedOrganizationKeys;
}
const encOrgKeyStore = await this.stateService.getEncryptedOrganizationKeys();
if (encOrgKeyStore == null) {
const encOrgKeys = await this.stateService.getEncryptedOrganizationKeys();
if (encOrgKeys == null) {
return null;
}
let setKey = false;
for (const orgId in encOrgKeyStore) {
for (const orgId in encOrgKeys) {
// eslint-disable-next-line
if (!encOrgKeyStore.hasOwnProperty(orgId)) {
if (!encOrgKeys.hasOwnProperty(orgId) || orgKeys.has(orgId)) {
continue;
}
if (orgKeys.has(orgId)) {
continue;
}
const encOrgKey = encOrgKeyStore[orgId].key;
const providerId = encOrgKeyStore[orgId].providerId;
let decValue = null;
if (providerId == null) {
// User encrypted
decValue = await this.rsaDecrypt(encOrgKey);
} else {
// Provider encrypted
const providerKey = await this.getProviderKey(providerId);
decValue = await this.decryptToBytes(new EncString(encOrgKey), providerKey);
}
orgKeys.set(orgId, new SymmetricCryptoKey(decValue));
const decryptedKey = await encOrgKeys[orgId].decrypt(this);
orgKeys.set(orgId, decryptedKey);
setKey = true;
}

View File

@@ -1,6 +1,10 @@
import { BehaviorSubject } from "rxjs";
import { EncryptedOrganizationKeyStore } from "jslib-common/interfaces/encryptedOrganizationKeyStore";
import {
BaseEncryptedOrganizationKey,
EncryptedOrganizationKey,
} from "jslib-common/models/domain/account/encryptedKey";
import { LogService } from "../abstractions/log.service";
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
@@ -1217,20 +1221,32 @@ export class StateService<
async getEncryptedOrganizationKeys(
options?: StorageOptions
): Promise<{ [orgId: string]: EncryptedOrganizationKeyStore }> {
return (
): Promise<{ [orgId: string]: BaseEncryptedOrganizationKey }> {
const data = (
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
)?.keys?.organizationKeys.encrypted;
const result: { [orgId: string]: BaseEncryptedOrganizationKey } = {};
for (const orgId in data) {
result[orgId] = BaseEncryptedOrganizationKey.fromObj(data[orgId]);
}
return result;
}
async setEncryptedOrganizationKeys(
value: { [orgId: string]: EncryptedOrganizationKeyStore },
options?: StorageOptions
): Promise<void> {
const data: { [orgId: string]: EncryptedOrganizationKey } = {};
for (const orgId in value) {
data[orgId] = value[orgId].toJSON();
}
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskOptions())
);
account.keys.organizationKeys.encrypted = value;
account.keys.organizationKeys.encrypted = data;
await this.saveAccount(
account,
this.reconcileOptions(options, await this.defaultOnDiskOptions())