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:
@@ -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: (
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface EncryptedOrganizationKeyStore {
|
||||
key: string;
|
||||
providerId?: string;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
47
common/src/models/domain/account/encryptedKey.ts
Normal file
47
common/src/models/domain/account/encryptedKey.ts
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user