From a891e0b4eab0e0514bc530b42b767bf768e22521 Mon Sep 17 00:00:00 2001 From: Daniel Huigens <d.huigens@protonmail.com> Date: Thu, 20 Dec 2018 11:05:04 -0500 Subject: [PATCH] Web Crypto CFB encryption --- .../sym_encrypted_integrity_protected.js | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 4362ee5d..6594823c 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -32,6 +32,7 @@ import crypto from '../crypto'; import enums from '../enums'; import util from '../util'; +const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); const Buffer = util.getNodeBuffer(); @@ -104,7 +105,7 @@ SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlg tohash = util.concat([tohash, hash]); if (sessionKeyAlgorithm.substr(0, 3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser. - this.encrypted = aesEncrypt(sessionKeyAlgorithm, util.concat([prefix, tohash]), key); + this.encrypted = await aesEncrypt(sessionKeyAlgorithm, util.concat([prefix, tohash]), key); } else { tohash = await stream.readToEnd(tohash); this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false); @@ -168,6 +169,14 @@ export default SymEncryptedIntegrityProtected; function aesEncrypt(algo, pt, key) { + if ( + util.getWebCrypto() && + key.length !== 24 && // Chrome doesn't support 192 bit keys, see https://www.chromium.org/blink/webcrypto#TOC-AES-support + !util.isStream(pt) && + pt.length >= 3000 * config.min_bytes_for_web_crypto // Default to a 3MB minimum. Chrome is pretty slow for small messages, see: https://bugs.chromium.org/p/chromium/issues/detail?id=701188#c2 + ) { // Web Crypto + return webEncrypt(algo, pt, key); + } if (nodeCrypto) { // Node crypto library. return nodeEncrypt(algo, pt, key); } // asm.js fallback @@ -190,6 +199,22 @@ function aesDecrypt(algo, ct, key) { return stream.slice(pt, crypto.cipher[algo].blockSize + 2); // Remove random prefix } +function xorMut(a, b) { + for (let i = 0; i < a.length; i++) { + a[i] = a[i] ^ b[i]; + } +} + +async function webEncrypt(algo, pt, key) { + const ALGO = 'AES-CBC'; + const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt']); + const { blockSize } = crypto.cipher[algo]; + const cbc_pt = util.concatUint8Array([new Uint8Array(blockSize), pt]); + const ct = new Uint8Array(await webCrypto.encrypt({ name: ALGO, iv: new Uint8Array(blockSize) }, _key, cbc_pt)).subarray(0, pt.length); + xorMut(ct, pt); + return ct; +} + function nodeEncrypt(algo, pt, key) { key = new Buffer(key); const iv = new Buffer(new Uint8Array(crypto.cipher[algo].blockSize));