diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 069efcbf13..6654352d23 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -20,7 +20,6 @@ import CipherService from '../services/cipher.service'; import CollectionService from '../services/collection.service'; import ConstantsService from '../services/constants.service'; import ContainerService from '../services/container.service'; -import CryptoService from '../services/crypto.service'; import EnvironmentService from '../services/environment.service'; import FolderService from '../services/folder.service'; import i18nService from '../services/i18n.service'; @@ -39,7 +38,7 @@ export default class MainBackground { platformUtilsService: Abstractions.PlatformUtilsService; utilsService: Abstractions.UtilsService; constantsService: ConstantsService; - cryptoService: CryptoService; + cryptoService: Abstractions.CryptoService; tokenService: TokenService; appIdService: AppIdService; apiService: ApiService; @@ -82,7 +81,7 @@ export default class MainBackground { this.storageService = new BrowserStorageService(this.platformUtilsService); this.i18nService = i18nService(this.platformUtilsService); this.constantsService = new ConstantsService(this.i18nService, this.platformUtilsService); - this.cryptoService = new CryptoService(this.storageService, + this.cryptoService = new Services.CryptoService(this.storageService, this.storageService); this.tokenService = new TokenService(this.storageService); this.appIdService = new AppIdService(this.storageService); diff --git a/src/popup/app/lock/lock.component.ts b/src/popup/app/lock/lock.component.ts index 19b84b4a69..bd19bb9888 100644 --- a/src/popup/app/lock/lock.component.ts +++ b/src/popup/app/lock/lock.component.ts @@ -1,8 +1,6 @@ import * as angular from 'angular'; import * as template from './lock.component.html'; -import { CryptoService } from '../../../services/abstractions/crypto.service'; - import { Abstractions } from '@bitwarden/jslib'; export class LockController { @@ -11,7 +9,7 @@ export class LockController { constructor(public $state: any, public i18nService: any, private $timeout: any, private platformUtilsService: Abstractions.PlatformUtilsService, - public cryptoService: CryptoService, public toastr: any, public userService: any, + public cryptoService: Abstractions.CryptoService, public toastr: any, public userService: any, public messagingService: Abstractions.MessagingService, public SweetAlert: any) { this.i18n = i18nService; } diff --git a/src/popup/app/services/auth.service.ts b/src/popup/app/services/auth.service.ts index 18957af3f1..bee6f5b9b4 100644 --- a/src/popup/app/services/auth.service.ts +++ b/src/popup/app/services/auth.service.ts @@ -1,9 +1,7 @@ -import { CryptoService } from '../../../services/abstractions/crypto.service'; - import { Abstractions, Request } from '@bitwarden/jslib'; class AuthService { - constructor(public cryptoService: CryptoService, public apiService: any, public userService: any, + constructor(public cryptoService: Abstractions.CryptoService, public apiService: any, public userService: any, public tokenService: any, public $rootScope: any, public appIdService: any, public platformUtilsService: Abstractions.PlatformUtilsService, public constantsService: any, public messagingService: Abstractions.MessagingService) { diff --git a/src/popup/app/services/background.service.ts b/src/popup/app/services/background.service.ts index d9168bff8c..f632ae96d1 100644 --- a/src/popup/app/services/background.service.ts +++ b/src/popup/app/services/background.service.ts @@ -1,5 +1,3 @@ -import { CryptoService } from '../../../services/abstractions/crypto.service'; - import { Abstractions } from '@bitwarden/jslib'; function getBackgroundService(service: string) { @@ -15,7 +13,7 @@ export const cryptoService = getBackgroundService('cryptoService'); export const userService = getBackgroundService('userService'); export const apiService = getBackgroundService('apiService'); export const folderService = getBackgroundService('folderService'); -export const cipherService = getBackgroundService('cipherService'); +export const cipherService = getBackgroundService('cipherService'); export const syncService = getBackgroundService('syncService'); export const autofillService = getBackgroundService('autofillService'); export const passwordGenerationService = getBackgroundService('passwordGenerationService'); diff --git a/src/popup/app/settings/settings.component.ts b/src/popup/app/settings/settings.component.ts index f9fe04772e..0efe0384f8 100644 --- a/src/popup/app/settings/settings.component.ts +++ b/src/popup/app/settings/settings.component.ts @@ -2,7 +2,6 @@ import * as angular from 'angular'; import { Abstractions, Enums } from '@bitwarden/jslib'; -import { CryptoService } from '../../../services/abstractions/crypto.service'; import ConstantsService from '../../../services/constants.service'; import * as template from './settings.component.html'; @@ -30,7 +29,7 @@ export class SettingsController { constructor(private $state: any, private SweetAlert: any, private platformUtilsService: Abstractions.PlatformUtilsService, private $analytics: any, private i18nService: any, private constantsService: ConstantsService, - private cryptoService: CryptoService, private lockService: any, + private cryptoService: Abstractions.CryptoService, private lockService: any, private storageService: Abstractions.StorageService, public messagingService: Abstractions.MessagingService, private $timeout: ng.ITimeoutService) { this.i18n = i18nService; diff --git a/src/popup/app/tools/export.component.ts b/src/popup/app/tools/export.component.ts index 960c54eada..bb2570817c 100644 --- a/src/popup/app/tools/export.component.ts +++ b/src/popup/app/tools/export.component.ts @@ -4,13 +4,11 @@ import * as template from './export.component.html'; import { Abstractions, Enums } from '@bitwarden/jslib'; -import { CryptoService } from '../../../services/abstractions/crypto.service'; - export class ExportController { i18n: any; masterPassword: string; - constructor(private $state: any, private cryptoService: CryptoService, + constructor(private $state: any, private cryptoService: Abstractions.CryptoService, private toastr: any, private utilsService: Abstractions.UtilsService, private $analytics: any, private i18nService: any, private folderService: any, private cipherService: any, private $window: any, private userService: any) { diff --git a/src/services/abstractions/crypto.service.ts b/src/services/abstractions/crypto.service.ts deleted file mode 100644 index cb321b676e..0000000000 --- a/src/services/abstractions/crypto.service.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Domain, Response } from '@bitwarden/jslib'; - -export interface CryptoService { - setKey(key: Domain.SymmetricCryptoKey): Promise; - setKeyHash(keyHash: string): Promise<{}>; - setEncKey(encKey: string): Promise<{}>; - setEncPrivateKey(encPrivateKey: string): Promise<{}>; - setOrgKeys(orgs: Response.ProfileOrganization[]): Promise<{}>; - getKey(): Promise; - getKeyHash(): Promise; - getEncKey(): Promise; - getPrivateKey(): Promise; - getOrgKeys(): Promise>; - getOrgKey(orgId: string): Promise; - clearKeys(): Promise; - toggleKey(): Promise; - makeKey(password: string, salt: string): Domain.SymmetricCryptoKey; - hashPassword(password: string, key: Domain.SymmetricCryptoKey): Promise; - makeEncKey(key: Domain.SymmetricCryptoKey): Promise; - encrypt(plainValue: string | Uint8Array, key?: Domain.SymmetricCryptoKey, - plainValueEncoding?: string): Promise; - encryptToBytes(plainValue: ArrayBuffer, key?: Domain.SymmetricCryptoKey): Promise; - decrypt(cipherString: Domain.CipherString, key?: Domain.SymmetricCryptoKey, - outputEncoding?: string): Promise; - decryptFromBytes(encBuf: ArrayBuffer, key: Domain.SymmetricCryptoKey): Promise; - rsaDecrypt(encValue: string): Promise; -} diff --git a/src/services/cipher.service.ts b/src/services/cipher.service.ts index d5ce86dc08..1ab29f01d7 100644 --- a/src/services/cipher.service.ts +++ b/src/services/cipher.service.ts @@ -2,7 +2,6 @@ import { Abstractions, Data, Domain, Enums, Request, Response } from '@bitwarden import ApiService from './api.service'; import ConstantsService from './constants.service'; -import CryptoService from './crypto.service'; import SettingsService from './settings.service'; import UserService from './user.service'; @@ -55,7 +54,7 @@ export default class CipherService { decryptedCipherCache: any[]; - constructor(private cryptoService: CryptoService, private userService: UserService, + constructor(private cryptoService: Abstractions.CryptoService, private userService: UserService, private settingsService: SettingsService, private apiService: ApiService, private storageService: Abstractions.StorageService) { } diff --git a/src/services/collection.service.ts b/src/services/collection.service.ts index e98851cf1a..c20e15a392 100644 --- a/src/services/collection.service.ts +++ b/src/services/collection.service.ts @@ -1,4 +1,3 @@ -import CryptoService from './crypto.service'; import UserService from './user.service'; import { Abstractions, Data, Domain } from '@bitwarden/jslib'; @@ -10,7 +9,7 @@ const Keys = { export default class CollectionService { decryptedCollectionCache: any[]; - constructor(private cryptoService: CryptoService, private userService: UserService, + constructor(private cryptoService: Abstractions.CryptoService, private userService: UserService, private storageService: Abstractions.StorageService) { } diff --git a/src/services/container.service.ts b/src/services/container.service.ts index 6000b797e1..9c5fd480e9 100644 --- a/src/services/container.service.ts +++ b/src/services/container.service.ts @@ -1,9 +1,7 @@ import { Abstractions } from '@bitwarden/jslib'; -import { CryptoService } from './abstractions/crypto.service'; - export default class ContainerService { - constructor(private cryptoService: CryptoService, + constructor(private cryptoService: Abstractions.CryptoService, private platformUtilsService: Abstractions.PlatformUtilsService) { } @@ -13,7 +11,7 @@ export default class ContainerService { } } - getCryptoService(): CryptoService { + getCryptoService(): Abstractions.CryptoService { return this.cryptoService; } diff --git a/src/services/crypto.service.ts b/src/services/crypto.service.ts deleted file mode 100644 index 893dc6bbac..0000000000 --- a/src/services/crypto.service.ts +++ /dev/null @@ -1,596 +0,0 @@ -import * as forge from 'node-forge'; - -import { Abstractions, Domain, Enums, Response, Services } from '@bitwarden/jslib'; - -import ConstantsService from './constants.service'; - -import { CryptoService as CryptoServiceInterface } from './abstractions/crypto.service'; - -const Keys = { - key: 'key', - encOrgKeys: 'encOrgKeys', - encPrivateKey: 'encPrivateKey', - encKey: 'encKey', - keyHash: 'keyHash', -}; - -const SigningAlgorithm = { - name: 'HMAC', - hash: { name: 'SHA-256' }, -}; - -const AesAlgorithm = { - name: 'AES-CBC', -}; - -const Crypto = window.crypto; -const Subtle = Crypto.subtle; - -export default class CryptoService implements CryptoServiceInterface { - private key: Domain.SymmetricCryptoKey; - private encKey: Domain.SymmetricCryptoKey; - private legacyEtmKey: Domain.SymmetricCryptoKey; - private keyHash: string; - private privateKey: ArrayBuffer; - private orgKeys: Map; - - constructor(private storageService: Abstractions.StorageService, - private secureStorageService: Abstractions.StorageService) { - } - - async setKey(key: Domain.SymmetricCryptoKey): Promise { - this.key = key; - - const option = await this.storageService.get(ConstantsService.lockOptionKey); - if (option != null) { - // if we have a lock option set, we do not store the key - return; - } - - return this.secureStorageService.save(Keys.key, key.keyB64); - } - - setKeyHash(keyHash: string): Promise<{}> { - this.keyHash = keyHash; - return this.storageService.save(Keys.keyHash, keyHash); - } - - async setEncKey(encKey: string): Promise<{}> { - if (encKey == null) { - return; - } - await this.storageService.save(Keys.encKey, encKey); - this.encKey = null; - } - - async setEncPrivateKey(encPrivateKey: string): Promise<{}> { - if (encPrivateKey == null) { - return; - } - - await this.storageService.save(Keys.encPrivateKey, encPrivateKey); - this.privateKey = null; - } - - setOrgKeys(orgs: Response.ProfileOrganization[]): Promise<{}> { - const orgKeys: any = {}; - orgs.forEach((org) => { - orgKeys[org.id] = org.key; - }); - - return this.storageService.save(Keys.encOrgKeys, orgKeys); - } - - async getKey(): Promise { - if (this.key != null) { - return this.key; - } - - const option = await this.storageService.get(ConstantsService.lockOptionKey); - if (option != null) { - return null; - } - - const key = await this.secureStorageService.get(Keys.key); - if (key) { - this.key = new Domain.SymmetricCryptoKey(key, true); - } - - return key == null ? null : this.key; - } - - getKeyHash(): Promise { - if (this.keyHash != null) { - return Promise.resolve(this.keyHash); - } - - return this.storageService.get(Keys.keyHash); - } - - async getEncKey(): Promise { - if (this.encKey != null) { - return this.encKey; - } - - const encKey = await this.storageService.get(Keys.encKey); - if (encKey == null) { - return null; - } - - const key = await this.getKey(); - if (key == null) { - return null; - } - - const decEncKey = await this.decrypt(new Domain.CipherString(encKey), key, 'raw'); - if (decEncKey == null) { - return null; - } - - this.encKey = new Domain.SymmetricCryptoKey(decEncKey); - return this.encKey; - } - - async getPrivateKey(): Promise { - if (this.privateKey != null) { - return this.privateKey; - } - - const encPrivateKey = await this.storageService.get(Keys.encPrivateKey); - if (encPrivateKey == null) { - return null; - } - - const privateKey = await this.decrypt(new Domain.CipherString(encPrivateKey), null, 'raw'); - const privateKeyB64 = forge.util.encode64(privateKey); - this.privateKey = Services.UtilsService.fromB64ToArray(privateKeyB64).buffer; - return this.privateKey; - } - - async getOrgKeys(): Promise> { - if (this.orgKeys != null && this.orgKeys.size > 0) { - return this.orgKeys; - } - - const self = this; - const encOrgKeys = await this.storageService.get(Keys.encOrgKeys); - if (!encOrgKeys) { - return null; - } - - const orgKeys: Map = new Map(); - let setKey = false; - - for (const orgId in encOrgKeys) { - if (!encOrgKeys.hasOwnProperty(orgId)) { - continue; - } - - const decValueB64 = await this.rsaDecrypt(encOrgKeys[orgId]); - orgKeys.set(orgId, new Domain.SymmetricCryptoKey(decValueB64, true)); - setKey = true; - } - - if (setKey) { - this.orgKeys = orgKeys; - } - - return this.orgKeys; - } - - async getOrgKey(orgId: string): Promise { - if (orgId == null) { - return null; - } - - const orgKeys = await this.getOrgKeys(); - if (orgKeys == null || !orgKeys.has(orgId)) { - return null; - } - - return orgKeys.get(orgId); - } - - clearKey(): Promise { - this.key = this.legacyEtmKey = null; - return this.secureStorageService.remove(Keys.key); - } - - clearKeyHash(): Promise { - this.keyHash = null; - return this.storageService.remove(Keys.keyHash); - } - - clearEncKey(memoryOnly?: boolean): Promise { - this.encKey = null; - if (memoryOnly) { - return Promise.resolve(); - } - return this.storageService.remove(Keys.encKey); - } - - clearPrivateKey(memoryOnly?: boolean): Promise { - this.privateKey = null; - if (memoryOnly) { - return Promise.resolve(); - } - return this.storageService.remove(Keys.encPrivateKey); - } - - clearOrgKeys(memoryOnly?: boolean): Promise { - this.orgKeys = null; - if (memoryOnly) { - return Promise.resolve(); - } - return this.storageService.remove(Keys.encOrgKeys); - } - - clearKeys(): Promise { - return Promise.all([ - this.clearKey(), - this.clearKeyHash(), - this.clearOrgKeys(), - this.clearEncKey(), - this.clearPrivateKey(), - ]); - } - - async toggleKey(): Promise { - const key = await this.getKey(); - const option = await this.storageService.get(ConstantsService.lockOptionKey); - if (option != null || option === 0) { - // if we have a lock option set, clear the key - await this.clearKey(); - this.key = key; - return; - } - - await this.setKey(key); - } - - makeKey(password: string, salt: string): Domain.SymmetricCryptoKey { - const keyBytes: string = (forge as any).pbkdf2(forge.util.encodeUtf8(password), forge.util.encodeUtf8(salt), - 5000, 256 / 8, 'sha256'); - return new Domain.SymmetricCryptoKey(keyBytes); - } - - async hashPassword(password: string, key: Domain.SymmetricCryptoKey): Promise { - const storedKey = await this.getKey(); - key = key || storedKey; - if (!password || !key) { - throw new Error('Invalid parameters.'); - } - - const hashBits = (forge as any).pbkdf2(key.key, forge.util.encodeUtf8(password), 1, 256 / 8, 'sha256'); - return forge.util.encode64(hashBits); - } - - makeEncKey(key: Domain.SymmetricCryptoKey): Promise { - const bytes = new Uint8Array(512 / 8); - Crypto.getRandomValues(bytes); - return this.encrypt(bytes, key, 'raw'); - } - - async encrypt(plainValue: string | Uint8Array, key?: Domain.SymmetricCryptoKey, - plainValueEncoding: string = 'utf8'): Promise { - if (!plainValue) { - return Promise.resolve(null); - } - - let plainValueArr: Uint8Array; - if (plainValueEncoding === 'utf8') { - plainValueArr = Services.UtilsService.fromUtf8ToArray(plainValue as string); - } else { - plainValueArr = plainValue as Uint8Array; - } - - const encValue = await this.aesEncrypt(plainValueArr.buffer, key); - const iv = Services.UtilsService.fromBufferToB64(encValue.iv.buffer); - const ct = Services.UtilsService.fromBufferToB64(encValue.ct.buffer); - const mac = encValue.mac ? Services.UtilsService.fromBufferToB64(encValue.mac.buffer) : null; - return new Domain.CipherString(encValue.key.encType, iv, ct, mac); - } - - async encryptToBytes(plainValue: ArrayBuffer, key?: Domain.SymmetricCryptoKey): Promise { - const encValue = await this.aesEncrypt(plainValue, key); - let macLen = 0; - if (encValue.mac) { - macLen = encValue.mac.length; - } - - const encBytes = new Uint8Array(1 + encValue.iv.length + macLen + encValue.ct.length); - encBytes.set([encValue.key.encType]); - encBytes.set(encValue.iv, 1); - if (encValue.mac) { - encBytes.set(encValue.mac, 1 + encValue.iv.length); - } - - encBytes.set(encValue.ct, 1 + encValue.iv.length + macLen); - return encBytes.buffer; - } - - async decrypt(cipherString: Domain.CipherString, key?: Domain.SymmetricCryptoKey, - outputEncoding: string = 'utf8'): Promise { - const ivBytes: string = forge.util.decode64(cipherString.initializationVector); - const ctBytes: string = forge.util.decode64(cipherString.cipherText); - const macBytes: string = cipherString.mac ? forge.util.decode64(cipherString.mac) : null; - const decipher = await this.aesDecrypt(cipherString.encryptionType, ctBytes, ivBytes, macBytes, key); - if (!decipher) { - return null; - } - - if (outputEncoding === 'utf8') { - return decipher.output.toString('utf8'); - } else { - return decipher.output.getBytes(); - } - } - - async decryptFromBytes(encBuf: ArrayBuffer, key: Domain.SymmetricCryptoKey): Promise { - if (!encBuf) { - throw new Error('no encBuf.'); - } - - const encBytes = new Uint8Array(encBuf); - const encType = encBytes[0]; - let ctBytes: Uint8Array = null; - let ivBytes: Uint8Array = null; - let macBytes: Uint8Array = null; - - switch (encType) { - case Enums.EncryptionType.AesCbc128_HmacSha256_B64: - case Enums.EncryptionType.AesCbc256_HmacSha256_B64: - if (encBytes.length <= 49) { // 1 + 16 + 32 + ctLength - return null; - } - - ivBytes = encBytes.slice(1, 17); - macBytes = encBytes.slice(17, 49); - ctBytes = encBytes.slice(49); - break; - case Enums.EncryptionType.AesCbc256_B64: - if (encBytes.length <= 17) { // 1 + 16 + ctLength - return null; - } - - ivBytes = encBytes.slice(1, 17); - ctBytes = encBytes.slice(17); - break; - default: - return null; - } - - return await this.aesDecryptWC(encType, ctBytes.buffer, ivBytes.buffer, macBytes ? macBytes.buffer : null, key); - } - - async rsaDecrypt(encValue: string): Promise { - const headerPieces = encValue.split('.'); - let encType: Enums.EncryptionType = null; - let encPieces: string[]; - - if (headerPieces.length === 1) { - encType = Enums.EncryptionType.Rsa2048_OaepSha256_B64; - encPieces = [headerPieces[0]]; - } else if (headerPieces.length === 2) { - try { - encType = parseInt(headerPieces[0], null); - encPieces = headerPieces[1].split('|'); - } catch (e) { } - } - - switch (encType) { - case Enums.EncryptionType.Rsa2048_OaepSha256_B64: - case Enums.EncryptionType.Rsa2048_OaepSha1_B64: - if (encPieces.length !== 1) { - throw new Error('Invalid cipher format.'); - } - break; - case Enums.EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: - case Enums.EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: - if (encPieces.length !== 2) { - throw new Error('Invalid cipher format.'); - } - break; - default: - throw new Error('encType unavailable.'); - } - - if (encPieces == null || encPieces.length <= 0) { - throw new Error('encPieces unavailable.'); - } - - const key = await this.getEncKey(); - if (key != null && key.macKey != null && encPieces.length > 1) { - const ctBytes: string = forge.util.decode64(encPieces[0]); - const macBytes: string = forge.util.decode64(encPieces[1]); - const computedMacBytes = await this.computeMac(ctBytes, key.macKey, false); - const macsEqual = await this.macsEqual(key.macKey, macBytes, computedMacBytes); - if (!macsEqual) { - throw new Error('MAC failed.'); - } - } - - const privateKeyBytes = await this.getPrivateKey(); - if (!privateKeyBytes) { - throw new Error('No private key.'); - } - - let rsaAlgorithm: any = null; - switch (encType) { - case Enums.EncryptionType.Rsa2048_OaepSha256_B64: - case Enums.EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: - rsaAlgorithm = { - name: 'RSA-OAEP', - hash: { name: 'SHA-256' }, - }; - break; - case Enums.EncryptionType.Rsa2048_OaepSha1_B64: - case Enums.EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: - rsaAlgorithm = { - name: 'RSA-OAEP', - hash: { name: 'SHA-1' }, - }; - break; - default: - throw new Error('encType unavailable.'); - } - - const privateKey = await Subtle.importKey('pkcs8', privateKeyBytes, rsaAlgorithm, false, ['decrypt']); - const ctArr = Services.UtilsService.fromB64ToArray(encPieces[0]); - const decBytes = await Subtle.decrypt(rsaAlgorithm, privateKey, ctArr.buffer); - const b64DecValue = Services.UtilsService.fromBufferToB64(decBytes); - return b64DecValue; - } - - // Helpers - - private async aesEncrypt(plainValue: ArrayBuffer, key: Domain.SymmetricCryptoKey): Promise { - const obj = new Domain.EncryptedObject(); - obj.key = await this.getKeyForEncryption(key); - const keyBuf = obj.key.getBuffers(); - - obj.iv = new Uint8Array(16); - Crypto.getRandomValues(obj.iv); - - const encKey = await Subtle.importKey('raw', keyBuf.encKey, AesAlgorithm, false, ['encrypt']); - const encValue = await Subtle.encrypt({ name: 'AES-CBC', iv: obj.iv }, encKey, plainValue); - obj.ct = new Uint8Array(encValue); - - if (keyBuf.macKey) { - const data = new Uint8Array(obj.iv.length + obj.ct.length); - data.set(obj.iv, 0); - data.set(obj.ct, obj.iv.length); - const mac = await this.computeMacWC(data.buffer, keyBuf.macKey); - obj.mac = new Uint8Array(mac); - } - - return obj; - } - - private async aesDecrypt(encType: Enums.EncryptionType, ctBytes: string, ivBytes: string, macBytes: string, - key: Domain.SymmetricCryptoKey): Promise { - const keyForEnc = await this.getKeyForEncryption(key); - const theKey = this.resolveLegacyKey(encType, keyForEnc); - - if (encType !== theKey.encType) { - // tslint:disable-next-line - console.error('encType unavailable.'); - return null; - } - - if (theKey.macKey != null && macBytes != null) { - const computedMacBytes = this.computeMac(ivBytes + ctBytes, theKey.macKey, false); - if (!this.macsEqual(theKey.macKey, computedMacBytes, macBytes)) { - // tslint:disable-next-line - console.error('MAC failed.'); - return null; - } - } - - const ctBuffer = (forge as any).util.createBuffer(ctBytes); - const decipher = (forge as any).cipher.createDecipher('AES-CBC', theKey.encKey); - decipher.start({ iv: ivBytes }); - decipher.update(ctBuffer); - decipher.finish(); - - return decipher; - } - - private async aesDecryptWC(encType: Enums.EncryptionType, ctBuf: ArrayBuffer, ivBuf: ArrayBuffer, - macBuf: ArrayBuffer, key: Domain.SymmetricCryptoKey): Promise { - const theKey = await this.getKeyForEncryption(key); - const keyBuf = theKey.getBuffers(); - const encKey = await Subtle.importKey('raw', keyBuf.encKey, AesAlgorithm, false, ['decrypt']); - if (!keyBuf.macKey || !macBuf) { - return null; - } - - const data = new Uint8Array(ivBuf.byteLength + ctBuf.byteLength); - data.set(new Uint8Array(ivBuf), 0); - data.set(new Uint8Array(ctBuf), ivBuf.byteLength); - const computedMacBuf = await this.computeMacWC(data.buffer, keyBuf.macKey); - if (computedMacBuf === null) { - return null; - } - - const macsMatch = await this.macsEqualWC(keyBuf.macKey, macBuf, computedMacBuf); - if (macsMatch === false) { - // tslint:disable-next-line - console.error('MAC failed.'); - return null; - } - - return await Subtle.decrypt({ name: 'AES-CBC', iv: ivBuf }, encKey, ctBuf); - } - - private computeMac(dataBytes: string, macKey: string, b64Output: boolean): string { - const hmac = (forge as any).hmac.create(); - hmac.start('sha256', macKey); - hmac.update(dataBytes); - const mac = hmac.digest(); - return b64Output ? forge.util.encode64(mac.getBytes()) : mac.getBytes(); - } - - private async computeMacWC(dataBuf: ArrayBuffer, macKeyBuf: ArrayBuffer): Promise { - const key = await Subtle.importKey('raw', macKeyBuf, SigningAlgorithm, false, ['sign']); - return await Subtle.sign(SigningAlgorithm, key, dataBuf); - } - - // Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification). - // ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/ - private macsEqual(macKey: string, mac1: string, mac2: string): boolean { - const hmac = (forge as any).hmac.create(); - - hmac.start('sha256', macKey); - hmac.update(mac1); - const mac1Bytes = hmac.digest().getBytes(); - - hmac.start(null, null); - hmac.update(mac2); - const mac2Bytes = hmac.digest().getBytes(); - - return mac1Bytes === mac2Bytes; - } - - private async macsEqualWC(macKeyBuf: ArrayBuffer, mac1Buf: ArrayBuffer, mac2Buf: ArrayBuffer): Promise { - const macKey = await Subtle.importKey('raw', macKeyBuf, SigningAlgorithm, false, ['sign']); - const mac1 = await Subtle.sign(SigningAlgorithm, macKey, mac1Buf); - const mac2 = await Subtle.sign(SigningAlgorithm, macKey, mac2Buf); - - if (mac1.byteLength !== mac2.byteLength) { - return false; - } - - const arr1 = new Uint8Array(mac1); - const arr2 = new Uint8Array(mac2); - - for (let i = 0; i < arr2.length; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } - } - - return true; - } - - private async getKeyForEncryption(key?: Domain.SymmetricCryptoKey): Promise { - if (key) { - return key; - } - - const encKey = await this.getEncKey(); - return encKey || (await this.getKey()); - } - - private resolveLegacyKey(encType: Enums.EncryptionType, key: Domain.SymmetricCryptoKey): Domain.SymmetricCryptoKey { - if (encType === Enums.EncryptionType.AesCbc128_HmacSha256_B64 && - key.encType === Enums.EncryptionType.AesCbc256_B64) { - // Old encrypt-then-mac scheme, make a new key - this.legacyEtmKey = this.legacyEtmKey || - new Domain.SymmetricCryptoKey(key.key, false, Enums.EncryptionType.AesCbc128_HmacSha256_B64); - return this.legacyEtmKey; - } - - return key; - } -} diff --git a/src/services/folder.service.ts b/src/services/folder.service.ts index 687e6ed339..887b4f0ae9 100644 --- a/src/services/folder.service.ts +++ b/src/services/folder.service.ts @@ -1,5 +1,4 @@ import ApiService from './api.service'; -import CryptoService from './crypto.service'; import UserService from './user.service'; import { Abstractions, Data, Domain, Request, Response } from '@bitwarden/jslib'; @@ -11,7 +10,7 @@ const Keys = { export default class FolderService { decryptedFolderCache: any[]; - constructor(private cryptoService: CryptoService, private userService: UserService, + constructor(private cryptoService: Abstractions.CryptoService, private userService: UserService, private i18nService: any, private apiService: ApiService, private storageService: Abstractions.StorageService) { } diff --git a/src/services/lock.service.ts b/src/services/lock.service.ts index 2fb47542c6..a77c97991d 100644 --- a/src/services/lock.service.ts +++ b/src/services/lock.service.ts @@ -1,14 +1,13 @@ import CipherService from './cipher.service'; import CollectionService from './collection.service'; import ConstantsService from './constants.service'; -import CryptoService from './crypto.service'; import FolderService from './folder.service'; import { Abstractions } from '@bitwarden/jslib'; export default class LockService { constructor(private cipherService: CipherService, private folderService: FolderService, - private collectionService: CollectionService, private cryptoService: CryptoService, + private collectionService: CollectionService, private cryptoService: Abstractions.CryptoService, private platformUtilsService: Abstractions.PlatformUtilsService, private storageService: Abstractions.StorageService, private setIcon: Function, private refreshBadgeAndMenu: Function) { diff --git a/src/services/passwordGeneration.service.ts b/src/services/passwordGeneration.service.ts index e953aa0e72..534faba1f0 100644 --- a/src/services/passwordGeneration.service.ts +++ b/src/services/passwordGeneration.service.ts @@ -1,5 +1,3 @@ -import CryptoService from './crypto.service'; - import { Abstractions, Domain, Services } from '@bitwarden/jslib'; const DefaultOptions = { @@ -143,7 +141,8 @@ export default class PasswordGenerationService { optionsCache: any; history: Domain.PasswordHistory[] = []; - constructor(private cryptoService: CryptoService, private storageService: Abstractions.StorageService) { + constructor(private cryptoService: Abstractions.CryptoService, + private storageService: Abstractions.StorageService) { storageService.get(Keys.history).then((encrypted) => { return this.decryptHistory(encrypted); }).then((history) => { diff --git a/src/services/sync.service.ts b/src/services/sync.service.ts index 1c65f524b6..417a89dde8 100644 --- a/src/services/sync.service.ts +++ b/src/services/sync.service.ts @@ -1,7 +1,6 @@ import ApiService from './api.service'; import CipherService from './cipher.service'; import CollectionService from './collection.service'; -import CryptoService from './crypto.service'; import FolderService from './folder.service'; import SettingsService from './settings.service'; import UserService from './user.service'; @@ -17,7 +16,7 @@ export default class SyncService { constructor(private userService: UserService, private apiService: ApiService, private settingsService: SettingsService, private folderService: FolderService, - private cipherService: CipherService, private cryptoService: CryptoService, + private cipherService: CipherService, private cryptoService: Abstractions.CryptoService, private collectionService: CollectionService, private storageService: Abstractions.StorageService, private messagingService: Abstractions.MessagingService, private logoutCallback: Function) { }