Move and refactor rsaDecrypt

* move to DecryptService
* require key explicitly
* make callers fetch and pass in key
This commit is contained in:
Thomas Rittson 2022-06-20 16:10:07 +10:00
parent cef96dfad2
commit 03af238b56
7 changed files with 88 additions and 61 deletions

View File

@ -146,7 +146,7 @@ export class ResetPasswordComponent implements OnInit {
);
// Decrypt User's Reset Password Key to get EncKey
const decValue = await this.cryptoService.rsaDecrypt(resetPasswordKey, decPrivateKey);
const decValue = await this.decryptService.rsaDecrypt(resetPasswordKey, decPrivateKey);
const userEncKey = new SymmetricCryptoKey(decValue);
// Create new key and hash new password

View File

@ -3,6 +3,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { ChangePasswordComponent } from "@bitwarden/angular/components/change-password.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { DecryptService } from "@bitwarden/common/abstractions/decrypt.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
@ -40,7 +41,8 @@ export class EmergencyAccessTakeoverComponent extends ChangePasswordComponent im
platformUtilsService: PlatformUtilsService,
policyService: PolicyService,
private apiService: ApiService,
private logService: LogService
private logService: LogService,
private decryptService: DecryptService
) {
super(
i18nService,
@ -74,7 +76,11 @@ export class EmergencyAccessTakeoverComponent extends ChangePasswordComponent im
this.emergencyAccessId
);
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(takeoverResponse.keyEncrypted);
const privateKey = await this.cryptoService.getPrivateKey();
const oldKeyBuffer = await this.decryptService.rsaDecrypt(
takeoverResponse.keyEncrypted,
privateKey
);
const oldEncKey = new SymmetricCryptoKey(oldKeyBuffer);
if (oldEncKey == null) {

View File

@ -5,6 +5,7 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { DecryptService } from "@bitwarden/common/abstractions/decrypt.service";
import { CipherData } from "@bitwarden/common/models/data/cipherData";
import { Cipher } from "@bitwarden/common/models/domain/cipher";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetricCryptoKey";
@ -34,7 +35,8 @@ export class EmergencyAccessViewComponent implements OnInit {
private modalService: ModalService,
private router: Router,
private route: ActivatedRoute,
private apiService: ApiService
private apiService: ApiService,
private decryptService: DecryptService
) {}
ngOnInit() {
@ -84,7 +86,8 @@ export class EmergencyAccessViewComponent implements OnInit {
const ciphers = response.ciphers;
const decCiphers: CipherView[] = [];
const oldKeyBuffer = await this.cryptoService.rsaDecrypt(response.keyEncrypted);
const privateKey = await this.cryptoService.getPrivateKey();
const oldKeyBuffer = await this.decryptService.rsaDecrypt(response.keyEncrypted, privateKey);
const oldEncKey = new SymmetricCryptoKey(oldKeyBuffer);
const promises: any[] = [];

View File

@ -10,6 +10,7 @@ import { ProfileProviderResponse } from "../models/response/profileProviderRespo
import { AttachmentView } from "../models/view/attachmentView";
export abstract class CryptoService {
// Set keys
setKey: (key: SymmetricCryptoKey) => Promise<any>;
setKeyHash: (keyHash: string) => Promise<void>;
setEncKey: (encKey: string) => Promise<void>;
@ -19,6 +20,8 @@ export abstract class CryptoService {
providerOrgs: ProfileProviderOrganizationResponse[]
) => Promise<void>;
setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise<void>;
// Get keys
getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
getKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
getKeyHash: () => Promise<string>;
@ -39,10 +42,14 @@ export abstract class CryptoService {
getOrgKeys: () => Promise<Map<string, SymmetricCryptoKey>>;
getOrgKey: (orgId: string) => Promise<SymmetricCryptoKey>;
getProviderKey: (providerId: string) => Promise<SymmetricCryptoKey>;
// Has keys
hasKey: () => Promise<boolean>;
hasKeyInMemory: (userId?: string) => Promise<boolean>;
hasKeyStored: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<boolean>;
hasEncKey: () => Promise<boolean>;
// Clear keys
clearKey: (clearSecretStorage?: boolean, userId?: string) => Promise<any>;
clearKeyHash: () => Promise<any>;
clearEncKey: (memoryOnly?: boolean, userId?: string) => Promise<any>;
@ -52,6 +59,8 @@ export abstract class CryptoService {
clearPinProtectedKey: () => Promise<any>;
clearKeys: (userId?: string) => Promise<any>;
toggleKey: () => Promise<any>;
// Make keys
makeKey: (
password: string,
salt: string,
@ -84,10 +93,13 @@ export abstract class CryptoService {
key: SymmetricCryptoKey,
encKey?: SymmetricCryptoKey
) => Promise<[SymmetricCryptoKey, EncString]>;
// Encrypt
encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncString>;
encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncArrayBuffer>;
rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise<EncString>;
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
// Helpers
randomNumber: (min: number, max: number) => Promise<number>;
validateKey: (key: SymmetricCryptoKey) => Promise<boolean>;
}

View File

@ -6,4 +6,5 @@ export abstract class DecryptService {
decryptToBytes: (encString: EncString, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
decryptToUtf8: (encString: EncString, key: SymmetricCryptoKey) => Promise<string>;
decryptFromBytes: (encBuf: EncArrayBuffer, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
}

View File

@ -269,7 +269,10 @@ export class CryptoService implements CryptoServiceAbstraction {
continue;
}
const decValue = await this.rsaDecrypt(encOrgKeys[orgId]);
const decValue = await this.decryptService.rsaDecrypt(
encOrgKeys[orgId],
await this.getPrivateKey()
);
orgKeys.set(orgId, new SymmetricCryptoKey(decValue));
setKey = true;
}
@ -315,7 +318,10 @@ export class CryptoService implements CryptoServiceAbstraction {
continue;
}
const decValue = await this.rsaDecrypt(encProviderKeys[orgId]);
const decValue = await this.decryptService.rsaDecrypt(
encProviderKeys[orgId],
await this.getPrivateKey()
);
providerKeys.set(orgId, new SymmetricCryptoKey(decValue));
setKey = true;
}
@ -593,59 +599,6 @@ export class CryptoService implements CryptoServiceAbstraction {
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Utils.fromBufferToB64(encBytes));
}
async rsaDecrypt(encValue: string, privateKeyValue?: ArrayBuffer): Promise<ArrayBuffer> {
const headerPieces = encValue.split(".");
let encType: EncryptionType = null;
let encPieces: string[];
if (headerPieces.length === 1) {
encType = EncryptionType.Rsa2048_OaepSha256_B64;
encPieces = [headerPieces[0]];
} else if (headerPieces.length === 2) {
try {
encType = parseInt(headerPieces[0], null);
encPieces = headerPieces[1].split("|");
} catch (e) {
this.logService.error(e);
}
}
switch (encType) {
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: // HmacSha256 types are deprecated
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
break;
default:
throw new Error("encType unavailable.");
}
if (encPieces == null || encPieces.length <= 0) {
throw new Error("encPieces unavailable.");
}
const data = Utils.fromB64ToArray(encPieces[0]).buffer;
const privateKey = privateKeyValue ?? (await this.getPrivateKey());
if (privateKey == null) {
throw new Error("No private key.");
}
let alg: "sha1" | "sha256" = "sha1";
switch (encType) {
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
alg = "sha256";
break;
case EncryptionType.Rsa2048_OaepSha1_B64:
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
break;
default:
throw new Error("encType unavailable.");
}
return this.cryptoFunctionService.rsaDecrypt(data, privateKey, alg);
}
// EFForg/OpenWireless
// ref https://github.com/EFForg/OpenWireless/blob/master/app/js/diceware.js
async randomNumber(min: number, max: number): Promise<number> {

View File

@ -128,4 +128,56 @@ export class DecryptService implements DecryptServiceAbstraction {
return await this.cryptoFunctionService.aesDecrypt(data, iv, key.encKey);
}
async rsaDecrypt(encValue: string, privateKeyValue?: ArrayBuffer): Promise<ArrayBuffer> {
if (privateKeyValue == null) {
throw new Error("No private key.");
}
const headerPieces = encValue.split(".");
let encType: EncryptionType = null;
let encPieces: string[];
if (headerPieces.length === 1) {
encType = EncryptionType.Rsa2048_OaepSha256_B64;
encPieces = [headerPieces[0]];
} else if (headerPieces.length === 2) {
try {
encType = parseInt(headerPieces[0], null);
encPieces = headerPieces[1].split("|");
} catch (e) {
this.logService.error(e);
}
}
switch (encType) {
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha1_B64:
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: // HmacSha256 types are deprecated
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
break;
default:
throw new Error("encType unavailable.");
}
if (encPieces == null || encPieces.length <= 0) {
throw new Error("encPieces unavailable.");
}
const data = Utils.fromB64ToArray(encPieces[0]).buffer;
let alg: "sha1" | "sha256" = "sha1";
switch (encType) {
case EncryptionType.Rsa2048_OaepSha256_B64:
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
alg = "sha256";
break;
case EncryptionType.Rsa2048_OaepSha1_B64:
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
break;
default:
throw new Error("encType unavailable.");
}
return this.cryptoFunctionService.rsaDecrypt(data, privateKeyValue, alg);
}
}