Don't use asmcrypto's streaming API when not necessary

Optimization for Firefox
This commit is contained in:
Daniel Huigens 2018-06-18 12:54:57 +02:00
parent e1a8b17753
commit eb72d4dd63
5 changed files with 37 additions and 23 deletions

View File

@ -43,10 +43,14 @@ function hashjs_hash(hash) {
function asmcrypto_hash(hash) {
return function(data) {
if (util.isStream(data)) {
const hashInstance = new hash();
return stream.transform(data, value => {
hashInstance.process(value);
}, () => hashInstance.finish().result);
} else {
return hash.bytes(data);
}
};
}

View File

@ -99,7 +99,7 @@ Message.prototype.getSigningKeyIds = function() {
* @returns {Promise<Message>} new message with decrypted content
* @async
*/
Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys) {
Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys, asStream) {
const keyObjs = sessionKeys || await this.decryptSessionKeys(privateKeys, passwords);
const symEncryptedPacketlist = this.packets.filterByTag(
@ -120,7 +120,7 @@ Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys)
}
try {
await symEncryptedPacket.decrypt(keyObjs[i].algorithm, keyObjs[i].data);
await symEncryptedPacket.decrypt(keyObjs[i].algorithm, keyObjs[i].data, asStream);
break;
} catch (e) {
util.print_debug_error(e);
@ -260,7 +260,7 @@ Message.prototype.getText = function() {
* @returns {Promise<Message>} new message with encrypted content
* @async
*/
Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard=false, date=new Date(), userId={}) {
Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard=false, date=new Date(), userId={}, asStream) {
let symAlgo;
let aeadAlgo;
let symEncryptedPacket;
@ -300,7 +300,7 @@ Message.prototype.encrypt = async function(keys, passwords, sessionKey, wildcard
}
symEncryptedPacket.packets = this.packets;
await symEncryptedPacket.encrypt(symAlgo, sessionKey);
await symEncryptedPacket.encrypt(symAlgo, sessionKey, asStream);
msg.packets.push(symEncryptedPacket);
symEncryptedPacket.packets = new packet.List(); // remove packets after encryption
@ -536,7 +536,7 @@ export async function createSignaturePackets(literalDataPacket, privateKeys, sig
* @returns {Promise<Array<({keyid: module:type/keyid, valid: Boolean})>>} list of signer's keyid and validity of signature
* @async
*/
Message.prototype.verify = async function(keys, date=new Date()) {
Message.prototype.verify = async function(keys, date=new Date(), asStream) {
const msg = this.unwrapCompressed();
const literalDataList = msg.packets.filterByTag(enums.packet.literal);
if (literalDataList.length !== 1) {
@ -548,7 +548,7 @@ Message.prototype.verify = async function(keys, date=new Date()) {
onePassSig.signatureData = stream.fromAsync(() => new Promise(resolve => {
onePassSig.signatureDataResolve = resolve;
}));
onePassSig.hashed = onePassSig.hash(literalDataList[0]);
onePassSig.hashed = onePassSig.hash(literalDataList[0], undefined, asStream);
});
return stream.transform(msg.packets.stream, signature => {
const onePassSig = onePassSigList.pop();

View File

@ -323,7 +323,7 @@ export function encrypt({ data, dataType, publicKeys, privateKeys, passwords, se
}
}
message = message.compress(compression);
return message.encrypt(publicKeys, passwords, sessionKey, wildcard, date, toUserId);
return message.encrypt(publicKeys, passwords, sessionKey, wildcard, date, toUserId, asStream);
}).then(async encrypted => {
if (armor) {
@ -364,13 +364,13 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe
return asyncProxy.delegate('decrypt', { message, privateKeys, passwords, sessionKeys, publicKeys, format, asStream, signature, date });
}
return message.decrypt(privateKeys, passwords, sessionKeys).then(async function(decrypted) {
return message.decrypt(privateKeys, passwords, sessionKeys, asStream).then(async function(decrypted) {
if (!publicKeys) {
publicKeys = [];
}
const result = {};
result.signatures = signature ? await decrypted.verifyDetached(signature, publicKeys, date) : await decrypted.verify(publicKeys, date);
result.signatures = signature ? await decrypted.verifyDetached(signature, publicKeys, date) : await decrypted.verify(publicKeys, date, asStream);
result.data = format === 'binary' ? decrypted.getLiteralData() : decrypted.getText();
result.data = await convertStream(result.data, asStream);
result.signatures = await convertStreamArray(result.signatures, asStream);
@ -462,7 +462,7 @@ export function verify({ message, publicKeys, asStream, signature=null, date=new
return Promise.resolve().then(async function() {
const result = {};
result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date) : await message.verify(publicKeys, date);
result.signatures = signature ? await message.verifyDetached(signature, publicKeys, date) : await message.verify(publicKeys, date, asStream);
result.data = message instanceof CleartextMessage ? message.getText() : message.getLiteralData();
result.data = await convertStream(result.data, asStream);
result.signatures = await convertStreamArray(result.signatures, asStream);

View File

@ -658,9 +658,13 @@ Signature.prototype.toHash = function(data) {
}
};
Signature.prototype.hash = function(data, toHash) {
Signature.prototype.hash = function(data, toHash, asStream=true) {
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
return crypto.hash.digest(hashAlgorithm, toHash || this.toHash(data));
if (!toHash) toHash = this.toHash(data);
if (!asStream && util.isStream(toHash)) {
return stream.fromAsync(async () => this.hash(data, await stream.readToEnd(toHash)));
}
return crypto.hash.digest(hashAlgorithm, toHash);
};

View File

@ -24,7 +24,7 @@
* @requires util
*/
import { AES_CFB_Decrypt, AES_CFB_Encrypt } from 'asmcrypto.js/src/aes/cfb/exports';
import { AES_CFB, AES_CFB_Decrypt, AES_CFB_Encrypt } from 'asmcrypto.js/src/aes/cfb/exports';
import config from '../config';
import crypto from '../crypto';
@ -90,8 +90,9 @@ SymEncryptedIntegrityProtected.prototype.write = function () {
* @returns {Promise<Boolean>}
* @async
*/
SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key) {
const bytes = this.packets.write();
SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key, asStream) {
let bytes = this.packets.write();
if (!asStream) bytes = await stream.readToEnd(bytes);
const prefixrandom = await crypto.getPrefixRandom(sessionKeyAlgorithm);
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
const prefix = util.concat([prefixrandom, repeat]);
@ -118,12 +119,13 @@ SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlg
* @returns {Promise<Boolean>}
* @async
*/
SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key, asStream) {
if (!asStream) this.encrypted = await stream.readToEnd(this.encrypted);
const encrypted = stream.clone(this.encrypted);
const encryptedClone = stream.passiveClone(encrypted);
let decrypted;
if (sessionKeyAlgorithm.substr(0, 3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
decrypted = aesDecrypt(sessionKeyAlgorithm, encrypted, key);
decrypted = aesDecrypt(sessionKeyAlgorithm, encrypted, key, asStream);
} else {
decrypted = crypto.cfb.decrypt(sessionKeyAlgorithm, key, await stream.readToEnd(encrypted), false);
}
@ -176,8 +178,12 @@ function aesDecrypt(algo, ct, key) {
if (nodeCrypto) { // Node crypto library.
pt = nodeDecrypt(algo, ct, key);
} else { // asm.js fallback
if (util.isStream(ct)) {
const cfb = new AES_CFB_Decrypt(key);
pt = stream.transform(ct, value => cfb.process(value).result, () => cfb.finish().result);
} else {
pt = AES_CFB.decrypt(ct, key);
}
}
return stream.slice(pt, crypto.cipher[algo].blockSize + 2); // Remove random prefix
}