diff --git a/apps/web/src/app/organizations/manage/reset-password.component.ts b/apps/web/src/app/organizations/manage/reset-password.component.ts index 141bba075c..d9210b00f7 100644 --- a/apps/web/src/app/organizations/manage/reset-password.component.ts +++ b/apps/web/src/app/organizations/manage/reset-password.component.ts @@ -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 diff --git a/apps/web/src/app/settings/emergency-access-takeover.component.ts b/apps/web/src/app/settings/emergency-access-takeover.component.ts index dea4d3862a..8985ec3cda 100644 --- a/apps/web/src/app/settings/emergency-access-takeover.component.ts +++ b/apps/web/src/app/settings/emergency-access-takeover.component.ts @@ -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) { diff --git a/apps/web/src/app/settings/emergency-access-view.component.ts b/apps/web/src/app/settings/emergency-access-view.component.ts index acdf6e43e2..e25277e075 100644 --- a/apps/web/src/app/settings/emergency-access-view.component.ts +++ b/apps/web/src/app/settings/emergency-access-view.component.ts @@ -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[] = []; diff --git a/libs/common/src/abstractions/crypto.service.ts b/libs/common/src/abstractions/crypto.service.ts index 818e412006..3bd652727c 100644 --- a/libs/common/src/abstractions/crypto.service.ts +++ b/libs/common/src/abstractions/crypto.service.ts @@ -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; setKeyHash: (keyHash: string) => Promise; setEncKey: (encKey: string) => Promise; @@ -19,6 +20,8 @@ export abstract class CryptoService { providerOrgs: ProfileProviderOrganizationResponse[] ) => Promise; setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise; + + // Get keys getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise; getKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise; getKeyHash: () => Promise; @@ -39,10 +42,14 @@ export abstract class CryptoService { getOrgKeys: () => Promise>; getOrgKey: (orgId: string) => Promise; getProviderKey: (providerId: string) => Promise; + + // Has keys hasKey: () => Promise; hasKeyInMemory: (userId?: string) => Promise; hasKeyStored: (keySuffix?: KeySuffixOptions, userId?: string) => Promise; hasEncKey: () => Promise; + + // Clear keys clearKey: (clearSecretStorage?: boolean, userId?: string) => Promise; clearKeyHash: () => Promise; clearEncKey: (memoryOnly?: boolean, userId?: string) => Promise; @@ -52,6 +59,8 @@ export abstract class CryptoService { clearPinProtectedKey: () => Promise; clearKeys: (userId?: string) => Promise; toggleKey: () => Promise; + + // 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; encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise; rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise; - rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise; + + // Helpers randomNumber: (min: number, max: number) => Promise; validateKey: (key: SymmetricCryptoKey) => Promise; } diff --git a/libs/common/src/abstractions/decrypt.service.ts b/libs/common/src/abstractions/decrypt.service.ts index 4987c01e6f..59cbf768e0 100644 --- a/libs/common/src/abstractions/decrypt.service.ts +++ b/libs/common/src/abstractions/decrypt.service.ts @@ -6,4 +6,5 @@ export abstract class DecryptService { decryptToBytes: (encString: EncString, key: SymmetricCryptoKey) => Promise; decryptToUtf8: (encString: EncString, key: SymmetricCryptoKey) => Promise; decryptFromBytes: (encBuf: EncArrayBuffer, key: SymmetricCryptoKey) => Promise; + rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise; } diff --git a/libs/common/src/services/crypto.service.ts b/libs/common/src/services/crypto.service.ts index 3e234bfffd..cfd2d73b35 100644 --- a/libs/common/src/services/crypto.service.ts +++ b/libs/common/src/services/crypto.service.ts @@ -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 { - 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 { diff --git a/libs/common/src/services/decrypt.service.ts b/libs/common/src/services/decrypt.service.ts index b49f415e77..a48f7d5ed2 100644 --- a/libs/common/src/services/decrypt.service.ts +++ b/libs/common/src/services/decrypt.service.ts @@ -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 { + 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); + } }