diff --git a/spec/node/services/nodeCryptoFunction.service.spec.ts b/spec/node/services/nodeCryptoFunction.service.spec.ts index 3964d39ae5..d4e33b4e0c 100644 --- a/spec/node/services/nodeCryptoFunction.service.spec.ts +++ b/spec/node/services/nodeCryptoFunction.service.spec.ts @@ -1,6 +1,7 @@ import { NodeCryptoFunctionService } from '../../../src/services/nodeCryptoFunction.service'; import { Utils } from '../../../src/misc/utils'; +import { SymmetricCryptoKey } from '../../../src/models/domain/symmetricCryptoKey'; const RsaPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP' + '4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP' + @@ -92,19 +93,20 @@ describe('NodeCrypto Function Service', () => { const value = 'EncryptMe!'; const data = Utils.fromUtf8ToArray(value); const encValue = await nodeCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); - const decValue = await nodeCryptoFunctionService.aesDecryptSmall(encValue, iv.buffer, key.buffer); + const decValue = await nodeCryptoFunctionService.aesDecryptLarge(encValue, iv.buffer, key.buffer); expect(Utils.fromBufferToUtf8(decValue)).toBe(value); }); }); - describe('aesDecryptSmall', () => { + describe('aesDecryptFast', () => { it('should successfully decrypt data', async () => { const nodeCryptoFunctionService = new NodeCryptoFunctionService(); - const iv = makeStaticByteArray(16); - const key = makeStaticByteArray(32); - const data = Utils.fromB64ToArray('ByUF8vhyX4ddU9gcooznwA=='); - const decValue = await nodeCryptoFunctionService.aesDecryptSmall(data.buffer, iv.buffer, key.buffer); - expect(Utils.fromBufferToUtf8(decValue)).toBe('EncryptMe!'); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const data = 'ByUF8vhyX4ddU9gcooznwA=='; + const params = nodeCryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); + const decValue = await nodeCryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe('EncryptMe!'); }); }); diff --git a/spec/web/services/webCryptoFunction.service.spec.ts b/spec/web/services/webCryptoFunction.service.spec.ts index 386a56aebd..de3f2af126 100644 --- a/spec/web/services/webCryptoFunction.service.spec.ts +++ b/spec/web/services/webCryptoFunction.service.spec.ts @@ -7,6 +7,7 @@ import { PlatformUtilsService } from '../../../src/abstractions/platformUtils.se import { WebCryptoFunctionService } from '../../../src/services/webCryptoFunction.service'; import { Utils } from '../../../src/misc/utils'; +import { SymmetricCryptoKey } from '../../../src/models/domain'; const RsaPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP' + '4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP' + @@ -109,8 +110,12 @@ describe('WebCrypto Function Service', () => { const value = 'EncryptMe!'; const data = Utils.fromUtf8ToArray(value); const encValue = await webCryptoFunctionService.aesEncrypt(data.buffer, iv.buffer, key.buffer); - const decValue = await webCryptoFunctionService.aesDecryptSmall(encValue, iv.buffer, key.buffer); - expect(Utils.fromBufferToUtf8(decValue)).toBe(value); + const encData = Utils.fromBufferToB64(encValue); + const b64Iv = Utils.fromBufferToB64(iv.buffer); + const symKey = new SymmetricCryptoKey(key.buffer); + const params = webCryptoFunctionService.aesDecryptFastParameters(encData, b64Iv, null, symKey); + const decValue = await webCryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe(value); }); it('should successfully encrypt and then decrypt large data', async () => { @@ -125,14 +130,15 @@ describe('WebCrypto Function Service', () => { }); }); - describe('aesDecryptSmall', () => { + describe('aesDecryptFast', () => { it('should successfully decrypt data', async () => { const webCryptoFunctionService = getWebCryptoFunctionService(); - const iv = makeStaticByteArray(16); - const key = makeStaticByteArray(32); - const data = Utils.fromB64ToArray('ByUF8vhyX4ddU9gcooznwA=='); - const decValue = await webCryptoFunctionService.aesDecryptSmall(data.buffer, iv.buffer, key.buffer); - expect(Utils.fromBufferToUtf8(decValue)).toBe('EncryptMe!'); + const iv = Utils.fromBufferToB64(makeStaticByteArray(16).buffer); + const symKey = new SymmetricCryptoKey(makeStaticByteArray(32).buffer); + const data = 'ByUF8vhyX4ddU9gcooznwA=='; + const params = webCryptoFunctionService.aesDecryptFastParameters(data, iv, null, symKey); + const decValue = await webCryptoFunctionService.aesDecryptFast(params); + expect(decValue).toBe('EncryptMe!'); }); }); diff --git a/src/abstractions/crypto.service.ts b/src/abstractions/crypto.service.ts index d2bb86d1eb..4220a36b90 100644 --- a/src/abstractions/crypto.service.ts +++ b/src/abstractions/crypto.service.ts @@ -27,7 +27,6 @@ export abstract class CryptoService { makeEncKey: (key: SymmetricCryptoKey) => Promise; encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise; encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise; - decrypt: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise; decryptToUtf8: (cipherString: CipherString, key?: SymmetricCryptoKey) => Promise; decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise; randomNumber: (min: number, max: number) => Promise; diff --git a/src/abstractions/cryptoFunction.service.ts b/src/abstractions/cryptoFunction.service.ts index c8f5bdf5bd..8935d32aa7 100644 --- a/src/abstractions/cryptoFunction.service.ts +++ b/src/abstractions/cryptoFunction.service.ts @@ -1,10 +1,19 @@ +import { DecryptParameters } from '../models/domain/decryptParameters'; +import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey'; + export abstract class CryptoFunctionService { pbkdf2: (password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512', iterations: number) => Promise; hash: (value: string | ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise; hmac: (value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512') => Promise; + timeSafeEqual: (a: ArrayBuffer, b: ArrayBuffer) => Promise; + hmacFast: (value: ArrayBuffer | string, key: ArrayBuffer | string, algorithm: 'sha1' | 'sha256' | 'sha512') => + Promise; + timeSafeEqualFast: (a: ArrayBuffer | string, b: ArrayBuffer | string) => Promise; aesEncrypt: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; - aesDecryptSmall: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; + aesDecryptFastParameters: (data: string, iv: string, mac: string, key: SymmetricCryptoKey) => + DecryptParameters; + aesDecryptFast: (parameters: DecryptParameters) => Promise; aesDecryptLarge: (data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer) => Promise; rsaEncrypt: (data: ArrayBuffer, publicKey: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise; rsaDecrypt: (data: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256') => Promise; diff --git a/src/models/domain/decryptParameters.ts b/src/models/domain/decryptParameters.ts new file mode 100644 index 0000000000..1274e88d4f --- /dev/null +++ b/src/models/domain/decryptParameters.ts @@ -0,0 +1,8 @@ +export class DecryptParameters { + encKey: T; + data: T; + iv: T; + macKey: T; + mac: T; + macData: T; +} diff --git a/src/models/domain/symmetricCryptoKey.ts b/src/models/domain/symmetricCryptoKey.ts index dd7a6800d0..f2b593d402 100644 --- a/src/models/domain/symmetricCryptoKey.ts +++ b/src/models/domain/symmetricCryptoKey.ts @@ -9,6 +9,8 @@ export class SymmetricCryptoKey { encType: EncryptionType; keyB64: string; + encKeyB64: string; + macKeyB64: string; constructor(key: ArrayBuffer, encType?: EncryptionType) { if (key == null) { @@ -26,7 +28,6 @@ export class SymmetricCryptoKey { } this.key = key; - this.keyB64 = Utils.fromBufferToB64(key); this.encType = encType; if (encType === EncryptionType.AesCbc256_B64 && key.byteLength === 32) { @@ -41,5 +42,15 @@ export class SymmetricCryptoKey { } else { throw new Error('Unsupported encType/key length.'); } + + if (this.key != null) { + this.keyB64 = Utils.fromBufferToB64(this.key); + } + if (this.encKey != null) { + this.encKeyB64 = Utils.fromBufferToB64(this.encKey); + } + if (this.macKey != null) { + this.macKeyB64 = Utils.fromBufferToB64(this.macKey); + } } } diff --git a/src/services/crypto.service.ts b/src/services/crypto.service.ts index 3055953df3..7d5a56a754 100644 --- a/src/services/crypto.service.ts +++ b/src/services/crypto.service.ts @@ -304,7 +304,7 @@ export class CryptoService implements CryptoServiceAbstraction { const iv = Utils.fromB64ToArray(cipherString.initializationVector).buffer; const ct = Utils.fromB64ToArray(cipherString.cipherText).buffer; const mac = cipherString.mac ? Utils.fromB64ToArray(cipherString.mac).buffer : null; - const decipher = await this.aesDecrypt(cipherString.encryptionType, ct, iv, mac, key); + const decipher = await this.aesDecryptToBytes(cipherString.encryptionType, ct, iv, mac, key); if (decipher == null) { return null; } @@ -313,8 +313,8 @@ export class CryptoService implements CryptoServiceAbstraction { } async decryptToUtf8(cipherString: CipherString, key?: SymmetricCryptoKey): Promise { - const decipher = await this.decrypt(cipherString, key); - return Utils.fromBufferToUtf8(decipher); + return await this.aesDecryptToUtf8(cipherString.encryptionType, cipherString.cipherText, + cipherString.initializationVector, cipherString.mac, key); } async decryptFromBytes(encBuf: ArrayBuffer, key: SymmetricCryptoKey): Promise { @@ -351,7 +351,7 @@ export class CryptoService implements CryptoServiceAbstraction { return null; } - return await this.aesDecryptLarge(encType, ctBytes.buffer, ivBytes.buffer, + return await this.aesDecryptToBytes(encType, ctBytes.buffer, ivBytes.buffer, macBytes != null ? macBytes.buffer : null, key); } @@ -409,8 +409,8 @@ export class CryptoService implements CryptoServiceAbstraction { return obj; } - private async aesDecrypt(encType: EncryptionType, ct: ArrayBuffer, iv: ArrayBuffer, mac: ArrayBuffer, - key: SymmetricCryptoKey): Promise { + private async aesDecryptToUtf8(encType: EncryptionType, ct: string, iv: string, mac: string, + key: SymmetricCryptoKey): Promise { const keyForEnc = await this.getKeyForEncryption(key); const theKey = this.resolveLegacyKey(encType, keyForEnc); @@ -420,51 +420,57 @@ export class CryptoService implements CryptoServiceAbstraction { return null; } - if (encType !== theKey.encType) { + if (theKey.encType !== encType) { // tslint:disable-next-line console.error('encType unavailable.'); return null; } + const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(ct, iv, mac, theKey); + if (fastParams.macKey != null && fastParams.mac != null) { + const computedMac = await this.cryptoFunctionService.hmacFast(fastParams.macData, + fastParams.macKey, 'sha256'); + const macsEqual = await this.cryptoFunctionService.timeSafeEqualFast(fastParams.mac, computedMac); + if (!macsEqual) { + // tslint:disable-next-line + console.error('mac failed.'); + return null; + } + } + + return this.cryptoFunctionService.aesDecryptFast(fastParams); + } + + private async aesDecryptToBytes(encType: EncryptionType, ct: ArrayBuffer, iv: ArrayBuffer, + mac: ArrayBuffer, key: SymmetricCryptoKey): Promise { + const keyForEnc = await this.getKeyForEncryption(key); + const theKey = this.resolveLegacyKey(encType, keyForEnc); + + if (theKey.macKey != null && mac == null) { + return null; + } + + if (theKey.encType !== encType) { + return null; + } + if (theKey.macKey != null && mac != null) { const macData = new Uint8Array(iv.byteLength + ct.byteLength); macData.set(new Uint8Array(iv), 0); macData.set(new Uint8Array(ct), iv.byteLength); - const computedMac = await this.cryptoFunctionService.hmac(new Uint8Array(iv).buffer, - theKey.macKey, 'sha256'); - if (!this.macsEqual(computedMac, mac)) { + const computedMac = await this.cryptoFunctionService.hmac(macData.buffer, theKey.macKey, 'sha256'); + if (computedMac === null) { + return null; + } + + const macsMatch = await this.cryptoFunctionService.timeSafeEqual(mac, computedMac); + if (!macsMatch) { // tslint:disable-next-line console.error('mac failed.'); return null; } } - return this.cryptoFunctionService.aesDecryptSmall(ct, iv, theKey.encKey); - } - - private async aesDecryptLarge(encType: EncryptionType, ct: ArrayBuffer, iv: ArrayBuffer, - mac: ArrayBuffer, key: SymmetricCryptoKey): Promise { - const theKey = await this.getKeyForEncryption(key); - if (theKey.macKey == null || mac == null) { - return null; - } - - const macData = new Uint8Array(iv.byteLength + ct.byteLength); - macData.set(new Uint8Array(iv), 0); - macData.set(new Uint8Array(ct), iv.byteLength); - const computedMac = await this.cryptoFunctionService.hmac(new Uint8Array(iv).buffer, - theKey.macKey, 'sha256'); - if (computedMac === null) { - return null; - } - - const macsMatch = await this.macsEqual(mac, computedMac); - if (macsMatch === false) { - // tslint:disable-next-line - console.error('mac failed.'); - return null; - } - return await this.cryptoFunctionService.aesDecryptLarge(ct, iv, theKey.encKey); } @@ -509,7 +515,7 @@ export class CryptoService implements CryptoServiceAbstraction { if (key != null && key.macKey != null && encPieces.length > 1) { const mac = Utils.fromB64ToArray(encPieces[1]).buffer; const computedMac = await this.cryptoFunctionService.hmac(ct, key.macKey, 'sha256'); - const macsEqual = await this.macsEqual(mac, computedMac); + const macsEqual = await this.cryptoFunctionService.timeSafeEqual(mac, computedMac); if (!macsEqual) { throw new Error('MAC failed.'); } @@ -536,28 +542,6 @@ export class CryptoService implements CryptoServiceAbstraction { return this.cryptoFunctionService.rsaDecrypt(ct, privateKey, alg); } - // 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/ - // ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy - private async macsEqual(mac1: ArrayBuffer, mac2: ArrayBuffer): Promise { - const key = await this.cryptoFunctionService.randomBytes(32); - const newMac1 = await this.cryptoFunctionService.hmac(mac1, key, 'sha256'); - const newMac2 = await this.cryptoFunctionService.hmac(mac2, key, 'sha256'); - if (newMac1.byteLength !== newMac2.byteLength) { - return false; - } - - const arr1 = new Uint8Array(newMac1); - const arr2 = new Uint8Array(newMac2); - for (let i = 0; i < arr2.length; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } - } - - return true; - } - private async getKeyForEncryption(key?: SymmetricCryptoKey): Promise { if (key != null) { return key; diff --git a/src/services/nodeCryptoFunction.service.ts b/src/services/nodeCryptoFunction.service.ts index 1739df9987..c4fdc89ccd 100644 --- a/src/services/nodeCryptoFunction.service.ts +++ b/src/services/nodeCryptoFunction.service.ts @@ -6,6 +6,9 @@ import { CryptoFunctionService } from '../abstractions/cryptoFunction.service'; import { Utils } from '../misc/utils'; +import { DecryptParameters } from '../models/domain/decryptParameters'; +import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey'; + export class NodeCryptoFunctionService implements CryptoFunctionService { pbkdf2(password: string | ArrayBuffer, salt: string | ArrayBuffer, algorithm: 'sha256' | 'sha512', iterations: number): Promise { @@ -38,6 +41,33 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(this.toArrayBuffer(hmac.digest())); } + async timeSafeEqual(a: ArrayBuffer, b: ArrayBuffer): Promise { + const key = await this.randomBytes(32); + const mac1 = await this.hmac(a, key, 'sha256'); + const mac2 = await this.hmac(b, key, 'sha256'); + 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; + } + + hmacFast(value: ArrayBuffer, key: ArrayBuffer, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise { + return this.hmac(value, key, algorithm); + } + + timeSafeEqualFast(a: ArrayBuffer, b: ArrayBuffer): Promise { + return this.timeSafeEqual(a, b); + } + aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { const nodeData = this.toNodeBuffer(data); const nodeIv = this.toNodeBuffer(iv); @@ -47,8 +77,31 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(this.toArrayBuffer(encBuf)); } - aesDecryptSmall(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { - return this.aesDecryptLarge(data, iv, key); + aesDecryptFastParameters(data: string, iv: string, mac: string, key: SymmetricCryptoKey): + DecryptParameters { + const p = new DecryptParameters(); + p.encKey = key.encKey; + p.data = Utils.fromB64ToArray(data).buffer; + p.iv = Utils.fromB64ToArray(iv).buffer; + + const macData = new Uint8Array(p.iv.byteLength + p.data.byteLength); + macData.set(new Uint8Array(p.iv), 0); + macData.set(new Uint8Array(p.data), p.iv.byteLength); + p.macData = macData.buffer; + + if (key.macKey != null) { + p.macKey = key.macKey; + } + if (mac != null) { + p.mac = Utils.fromB64ToArray(mac).buffer; + } + + return p; + } + + async aesDecryptFast(parameters: DecryptParameters): Promise { + const decBuf = await this.aesDecryptLarge(parameters.data, parameters.iv, parameters.encKey); + return Utils.fromBufferToUtf8(decBuf); } aesDecryptLarge(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { diff --git a/src/services/webCryptoFunction.service.ts b/src/services/webCryptoFunction.service.ts index dd4a33c62b..da1c18cdce 100644 --- a/src/services/webCryptoFunction.service.ts +++ b/src/services/webCryptoFunction.service.ts @@ -5,6 +5,9 @@ import { PlatformUtilsService } from '../abstractions/platformUtils.service'; import { Utils } from '../misc/utils'; +import { SymmetricCryptoKey } from '../models/domain'; +import { DecryptParameters } from '../models/domain/decryptParameters'; + export class WebCryptoFunctionService implements CryptoFunctionService { private crypto: Crypto; private subtle: SubtleCrypto; @@ -80,21 +83,93 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return await this.subtle.sign(signingAlgorithm, impKey, value); } + // Safely compare two values 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/ + // ref: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy + async timeSafeEqual(a: ArrayBuffer, b: ArrayBuffer): Promise { + const macKey = await this.randomBytes(32); + const signingAlgorithm = { + name: 'HMAC', + hash: { name: 'SHA-256' }, + }; + const impKey = await this.subtle.importKey('raw', macKey, signingAlgorithm, false, ['sign']); + const mac1 = await this.subtle.sign(signingAlgorithm, impKey, a); + const mac2 = await this.subtle.sign(signingAlgorithm, impKey, b); + + 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; + } + + hmacFast(value: string, key: string, algorithm: 'sha1' | 'sha256' | 'sha512'): Promise { + const hmac = (forge as any).hmac.create(); + hmac.start(algorithm, key); + hmac.update(value); + const bytes = hmac.digest().getBytes(); + return Promise.resolve(bytes); + } + + async timeSafeEqualFast(a: string, b: string): Promise { + const rand = await this.randomBytes(32); + const bytes = new Uint32Array(rand); + const buffer = forge.util.createBuffer(); + for (let i = 0; i < bytes.length; i++) { + buffer.putInt32(bytes[i]); + } + const macKey = buffer.getBytes(); + + const hmac = (forge as any).hmac.create(); + hmac.start('sha256', macKey); + hmac.update(a); + const mac1 = hmac.digest().getBytes(); + + hmac.start(null, null); + hmac.update(b); + const mac2 = hmac.digest().getBytes(); + + const equals = mac1 === mac2; + return equals; + } + async aesEncrypt(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { const impKey = await this.subtle.importKey('raw', key, { name: 'AES-CBC' }, false, ['encrypt']); return await this.subtle.encrypt({ name: 'AES-CBC', iv: iv }, impKey, data); } - async aesDecryptSmall(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise { - const dataBytes = this.toByteString(data); - const ivBytes = this.toByteString(iv); - const keyBytes = this.toByteString(key); - const dataBuffer = (forge as any).util.createBuffer(dataBytes); - const decipher = (forge as any).cipher.createDecipher('AES-CBC', keyBytes); - decipher.start({ iv: ivBytes }); + aesDecryptFastParameters(data: string, iv: string, mac: string, key: SymmetricCryptoKey): + DecryptParameters { + const p = new DecryptParameters(); + p.encKey = forge.util.decode64(key.encKeyB64); + p.data = forge.util.decode64(data); + p.iv = forge.util.decode64(iv); + p.macData = p.iv + p.data; + if (key.macKeyB64 != null) { + p.macKey = forge.util.decode64(key.macKeyB64); + } + if (mac != null) { + p.mac = forge.util.decode64(mac); + } + return p; + } + + aesDecryptFast(parameters: DecryptParameters): Promise { + const dataBuffer = (forge as any).util.createBuffer(parameters.data); + const decipher = (forge as any).cipher.createDecipher('AES-CBC', parameters.encKey); + decipher.start({ iv: parameters.iv }); decipher.update(dataBuffer); decipher.finish(); - return Utils.fromByteStringToArray(decipher.output.getBytes()).buffer; + const val = decipher.output.toString('utf8'); + return Promise.resolve(val); } async aesDecryptLarge(data: ArrayBuffer, iv: ArrayBuffer, key: ArrayBuffer): Promise {