Merge pull request #329 from 1and1/master
Optimized memory footprint for encrypting and decrypting
This commit is contained in:
commit
58cac452db
|
@ -178,7 +178,7 @@ module.exports = {
|
||||||
var iblock = new Uint8Array(block_size);
|
var iblock = new Uint8Array(block_size);
|
||||||
var ablock = new Uint8Array(block_size);
|
var ablock = new Uint8Array(block_size);
|
||||||
var i, n = '';
|
var i, n = '';
|
||||||
var text = '';
|
var text = [];
|
||||||
|
|
||||||
// initialisation vector
|
// initialisation vector
|
||||||
for (i = 0; i < block_size; i++) {
|
for (i = 0; i < block_size; i++) {
|
||||||
|
@ -200,11 +200,11 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RFC4880: Tag 18 and Resync:
|
/* RFC4880: Tag 18 and Resync:
|
||||||
* [...] Unlike the Symmetrically Encrypted Data Packet, no
|
* [...] Unlike the Symmetrically Encrypted Data Packet, no
|
||||||
* special CFB resynchronization is done after encrypting this prefix
|
* special CFB resynchronization is done after encrypting this prefix
|
||||||
* data. See "OpenPGP CFB Mode" below for more details.
|
* data. See "OpenPGP CFB Mode" below for more details.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (resync) {
|
if (resync) {
|
||||||
for (i = 0; i < block_size; i++) {
|
for (i = 0; i < block_size; i++) {
|
||||||
|
@ -215,7 +215,7 @@ module.exports = {
|
||||||
|
|
||||||
for (i = 0; i < block_size && i + n < ciphertext.length; i++) {
|
for (i = 0; i < block_size && i + n < ciphertext.length; i++) {
|
||||||
iblock[i] = ciphertext.charCodeAt(n + i);
|
iblock[i] = ciphertext.charCodeAt(n + i);
|
||||||
text += String.fromCharCode(ablock[i] ^ iblock[i]);
|
text.push(String.fromCharCode(ablock[i] ^ iblock[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -226,19 +226,18 @@ module.exports = {
|
||||||
ablock = cipherfn.encrypt(iblock);
|
ablock = cipherfn.encrypt(iblock);
|
||||||
for (i = 0; i < block_size && i + n < ciphertext.length; i++) {
|
for (i = 0; i < block_size && i + n < ciphertext.length; i++) {
|
||||||
iblock[i] = ciphertext.charCodeAt(n + i);
|
iblock[i] = ciphertext.charCodeAt(n + i);
|
||||||
text += String.fromCharCode(ablock[i] ^ iblock[i]);
|
text.push(String.fromCharCode(ablock[i] ^ iblock[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!resync)
|
||||||
n = resync ? 0 : 2;
|
{
|
||||||
|
text.splice(0, 2);
|
||||||
text = text.substring(n, ciphertext.length - block_size - 2 + n);
|
}
|
||||||
|
text.splice(ciphertext.length - block_size - 2);
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
normalEncrypt: function(cipherfn, key, plaintext, iv) {
|
normalEncrypt: function(cipherfn, key, plaintext, iv) {
|
||||||
cipherfn = new cipher[cipherfn](key);
|
cipherfn = new cipher[cipherfn](key);
|
||||||
var block_size = cipherfn.blockSize;
|
var block_size = cipherfn.blockSize;
|
||||||
|
|
|
@ -350,56 +350,56 @@ function dearmor(text) {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function armor(messagetype, body, partindex, parttotal) {
|
function armor(messagetype, body, partindex, parttotal) {
|
||||||
var result = "";
|
var result = [];
|
||||||
switch (messagetype) {
|
switch (messagetype) {
|
||||||
case enums.armor.multipart_section:
|
case enums.armor.multipart_section:
|
||||||
result += "-----BEGIN PGP MESSAGE, PART " + partindex + "/" + parttotal + "-----\r\n";
|
result.push("-----BEGIN PGP MESSAGE, PART " + partindex + "/" + parttotal + "-----\r\n");
|
||||||
result += addheader();
|
result.push(addheader());
|
||||||
result += base64.encode(body);
|
result.push(base64.encode(body));
|
||||||
result += "\r\n=" + getCheckSum(body) + "\r\n";
|
result.push("\r\n=" + getCheckSum(body) + "\r\n");
|
||||||
result += "-----END PGP MESSAGE, PART " + partindex + "/" + parttotal + "-----\r\n";
|
result.push("-----END PGP MESSAGE, PART " + partindex + "/" + parttotal + "-----\r\n");
|
||||||
break;
|
break;
|
||||||
case enums.armor.multipart_last:
|
case enums.armor.multipart_last:
|
||||||
result += "-----BEGIN PGP MESSAGE, PART " + partindex + "-----\r\n";
|
result.push("-----BEGIN PGP MESSAGE, PART " + partindex + "-----\r\n");
|
||||||
result += addheader();
|
result.push(addheader());
|
||||||
result += base64.encode(body);
|
result.push(base64.encode(body));
|
||||||
result += "\r\n=" + getCheckSum(body) + "\r\n";
|
result.push("\r\n=" + getCheckSum(body) + "\r\n");
|
||||||
result += "-----END PGP MESSAGE, PART " + partindex + "-----\r\n";
|
result.push("-----END PGP MESSAGE, PART " + partindex + "-----\r\n");
|
||||||
break;
|
break;
|
||||||
case enums.armor.signed:
|
case enums.armor.signed:
|
||||||
result += "\r\n-----BEGIN PGP SIGNED MESSAGE-----\r\n";
|
result.push("\r\n-----BEGIN PGP SIGNED MESSAGE-----\r\n");
|
||||||
result += "Hash: " + body.hash + "\r\n\r\n";
|
result.push("Hash: " + body.hash + "\r\n\r\n");
|
||||||
result += body.text.replace(/\n-/g, "\n- -");
|
result.push(body.text.replace(/\n-/g, "\n- -"));
|
||||||
result += "\r\n-----BEGIN PGP SIGNATURE-----\r\n";
|
result.push("\r\n-----BEGIN PGP SIGNATURE-----\r\n");
|
||||||
result += addheader();
|
result.push(addheader());
|
||||||
result += base64.encode(body.data);
|
result.push(base64.encode(body.data));
|
||||||
result += "\r\n=" + getCheckSum(body.data) + "\r\n";
|
result.push("\r\n=" + getCheckSum(body.data) + "\r\n");
|
||||||
result += "-----END PGP SIGNATURE-----\r\n";
|
result.push("-----END PGP SIGNATURE-----\r\n");
|
||||||
break;
|
break;
|
||||||
case enums.armor.message:
|
case enums.armor.message:
|
||||||
result += "-----BEGIN PGP MESSAGE-----\r\n";
|
result.push("-----BEGIN PGP MESSAGE-----\r\n");
|
||||||
result += addheader();
|
result.push(addheader());
|
||||||
result += base64.encode(body);
|
result.push(base64.encode(body));
|
||||||
result += "\r\n=" + getCheckSum(body) + "\r\n";
|
result.push("\r\n=" + getCheckSum(body) + "\r\n");
|
||||||
result += "-----END PGP MESSAGE-----\r\n";
|
result.push("-----END PGP MESSAGE-----\r\n");
|
||||||
break;
|
break;
|
||||||
case enums.armor.public_key:
|
case enums.armor.public_key:
|
||||||
result += "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n";
|
result.push("-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n");
|
||||||
result += addheader();
|
result.push(addheader());
|
||||||
result += base64.encode(body);
|
result.push(base64.encode(body));
|
||||||
result += "\r\n=" + getCheckSum(body) + "\r\n";
|
result.push("\r\n=" + getCheckSum(body) + "\r\n");
|
||||||
result += "-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n";
|
result.push("-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n");
|
||||||
break;
|
break;
|
||||||
case enums.armor.private_key:
|
case enums.armor.private_key:
|
||||||
result += "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n";
|
result.push("-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n");
|
||||||
result += addheader();
|
result.push(addheader());
|
||||||
result += base64.encode(body);
|
result.push(base64.encode(body));
|
||||||
result += "\r\n=" + getCheckSum(body) + "\r\n";
|
result.push("\r\n=" + getCheckSum(body) + "\r\n");
|
||||||
result += "-----END PGP PRIVATE KEY BLOCK-----\r\n";
|
result.push("-----END PGP PRIVATE KEY BLOCK-----\r\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -23,51 +23,55 @@ var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
* @returns {string} radix-64 version of input string
|
* @returns {string} radix-64 version of input string
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function s2r(t) {
|
function s2r(t, o) {
|
||||||
|
// TODO check btoa alternative
|
||||||
var a, c, n;
|
var a, c, n;
|
||||||
var r = '',
|
var r = o ? o : [],
|
||||||
l = 0,
|
l = 0,
|
||||||
s = 0;
|
s = 0;
|
||||||
var tl = t.length;
|
var tl = t.length;
|
||||||
|
|
||||||
for (n = 0; n < tl; n++) {
|
for (n = 0; n < tl; n++) {
|
||||||
c = t.charCodeAt(n);
|
c = t.charCodeAt(n);
|
||||||
if (s === 0) {
|
if (s === 0) {
|
||||||
r += b64s.charAt((c >> 2) & 63);
|
r.push(b64s.charAt((c >> 2) & 63));
|
||||||
a = (c & 3) << 4;
|
a = (c & 3) << 4;
|
||||||
} else if (s == 1) {
|
} else if (s == 1) {
|
||||||
r += b64s.charAt((a | (c >> 4) & 15));
|
r.push(b64s.charAt((a | (c >> 4) & 15)));
|
||||||
a = (c & 15) << 2;
|
a = (c & 15) << 2;
|
||||||
} else if (s == 2) {
|
} else if (s == 2) {
|
||||||
r += b64s.charAt(a | ((c >> 6) & 3));
|
r.push(b64s.charAt(a | ((c >> 6) & 3)));
|
||||||
l += 1;
|
l += 1;
|
||||||
if ((l % 60) === 0)
|
if ((l % 60) === 0)
|
||||||
r += "\n";
|
r.push("\n");
|
||||||
r += b64s.charAt(c & 63);
|
r.push(b64s.charAt(c & 63));
|
||||||
}
|
}
|
||||||
l += 1;
|
l += 1;
|
||||||
if ((l % 60) === 0)
|
if ((l % 60) === 0)
|
||||||
r += "\n";
|
r.push("\n");
|
||||||
|
|
||||||
s += 1;
|
s += 1;
|
||||||
if (s == 3)
|
if (s == 3)
|
||||||
s = 0;
|
s = 0;
|
||||||
}
|
}
|
||||||
if (s > 0) {
|
if (s > 0) {
|
||||||
r += b64s.charAt(a);
|
r.push(b64s.charAt(a));
|
||||||
l += 1;
|
l += 1;
|
||||||
if ((l % 60) === 0)
|
if ((l % 60) === 0)
|
||||||
r += "\n";
|
r.push("\n");
|
||||||
r += '=';
|
r.push('=');
|
||||||
l += 1;
|
l += 1;
|
||||||
}
|
}
|
||||||
if (s == 1) {
|
if (s == 1) {
|
||||||
if ((l % 60) === 0)
|
if ((l % 60) === 0)
|
||||||
r += "\n";
|
r.push("\n");
|
||||||
r += '=';
|
r.push('=');
|
||||||
}
|
}
|
||||||
|
if (o)
|
||||||
return r;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return r.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,22 +81,23 @@ function s2r(t) {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function r2s(t) {
|
function r2s(t) {
|
||||||
|
// TODO check atob alternative
|
||||||
var c, n;
|
var c, n;
|
||||||
var r = '',
|
var r = [],
|
||||||
s = 0,
|
s = 0,
|
||||||
a = 0;
|
a = 0;
|
||||||
var tl = t.length;
|
var tl = t.length;
|
||||||
|
|
||||||
for (n = 0; n < tl; n++) {
|
for (n = 0; n < tl; n++) {
|
||||||
c = b64s.indexOf(t.charAt(n));
|
c = b64s.indexOf(t.charAt(n));
|
||||||
if (c >= 0) {
|
if (c >= 0) {
|
||||||
if (s)
|
if (s)
|
||||||
r += String.fromCharCode(a | (c >> (6 - s)) & 255);
|
r.push(String.fromCharCode(a | (c >> (6 - s)) & 255));
|
||||||
s = (s + 2) & 7;
|
s = (s + 2) & 7;
|
||||||
a = (c << s) & 255;
|
a = (c << s) & 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
return r.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -93,8 +93,12 @@ SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm
|
||||||
|
|
||||||
|
|
||||||
this.encrypted = crypto.cfb.encrypt(prefixrandom,
|
this.encrypted = crypto.cfb.encrypt(prefixrandom,
|
||||||
sessionKeyAlgorithm, tohash, key, false).substring(0,
|
sessionKeyAlgorithm, tohash, key, false);
|
||||||
prefix.length + tohash.length);
|
|
||||||
|
if (prefix.length + tohash.length != this.encrypted.length)
|
||||||
|
{
|
||||||
|
this.encrypted = this.encrypted.substring(0, prefix.length + tohash.length);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,17 +114,20 @@ SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm
|
||||||
var decrypted = crypto.cfb.decrypt(
|
var decrypted = crypto.cfb.decrypt(
|
||||||
sessionKeyAlgorithm, key, this.encrypted, false);
|
sessionKeyAlgorithm, key, this.encrypted, false);
|
||||||
|
|
||||||
|
var mdc = decrypted.slice(decrypted.length - 20, decrypted.length).join('');
|
||||||
|
|
||||||
|
decrypted.splice(decrypted.length - 20);
|
||||||
|
|
||||||
// there must be a modification detection code packet as the
|
// there must be a modification detection code packet as the
|
||||||
// last packet and everything gets hashed except the hash itself
|
// last packet and everything gets hashed except the hash itself
|
||||||
this.hash = crypto.hash.sha1(
|
this.hash = crypto.hash.sha1(
|
||||||
crypto.cfb.mdc(sessionKeyAlgorithm, key, this.encrypted) + decrypted.substring(0, decrypted.length - 20));
|
crypto.cfb.mdc(sessionKeyAlgorithm, key, this.encrypted) + decrypted.join(''));
|
||||||
|
|
||||||
|
|
||||||
var mdc = decrypted.substr(decrypted.length - 20, 20);
|
|
||||||
|
|
||||||
if (this.hash != mdc) {
|
if (this.hash != mdc) {
|
||||||
throw new Error('Modification detected.');
|
throw new Error('Modification detected.');
|
||||||
} else
|
} else {
|
||||||
this.packets.read(decrypted.substr(0, decrypted.length - 22));
|
decrypted.splice(decrypted.length - 2);
|
||||||
|
this.packets.read(decrypted.join(''));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,6 +119,7 @@ SymEncryptedSessionKey.prototype.decrypt = function(passphrase) {
|
||||||
} else {
|
} else {
|
||||||
var decrypted = crypto.cfb.decrypt(
|
var decrypted = crypto.cfb.decrypt(
|
||||||
this.sessionKeyEncryptionAlgorithm, key, this.encrypted, true);
|
this.sessionKeyEncryptionAlgorithm, key, this.encrypted, true);
|
||||||
|
decrypted = decrypted.join('');
|
||||||
|
|
||||||
this.sessionKeyAlgorithm = enums.read(enums.symmetric,
|
this.sessionKeyAlgorithm = enums.read(enums.symmetric,
|
||||||
decrypted[0].keyCodeAt());
|
decrypted[0].keyCodeAt());
|
||||||
|
|
|
@ -65,7 +65,7 @@ SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) {
|
||||||
var decrypted = crypto.cfb.decrypt(
|
var decrypted = crypto.cfb.decrypt(
|
||||||
sessionKeyAlgorithm, key, this.encrypted, true);
|
sessionKeyAlgorithm, key, this.encrypted, true);
|
||||||
|
|
||||||
this.packets.read(decrypted);
|
this.packets.read(decrypted.join(''))
|
||||||
};
|
};
|
||||||
|
|
||||||
SymmetricallyEncrypted.prototype.encrypt = function (algo, key) {
|
SymmetricallyEncrypted.prototype.encrypt = function (algo, key) {
|
||||||
|
|
|
@ -202,11 +202,11 @@ module.exports = {
|
||||||
* @return {String} String representation of the array
|
* @return {String} String representation of the array
|
||||||
*/
|
*/
|
||||||
Uint8Array2str: function (bin) {
|
Uint8Array2str: function (bin) {
|
||||||
var result = '';
|
var result = [];
|
||||||
for (var i = 0; i < bin.length; i++) {
|
for (var i = 0; i < bin.length; i++) {
|
||||||
result += String.fromCharCode(bin[i]);
|
result[i] = String.fromCharCode(bin[i]);
|
||||||
}
|
}
|
||||||
return result;
|
return result.join('');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -261,7 +261,7 @@ describe('API functional testing', function() {
|
||||||
symmAlgos.forEach(function(algo) {
|
symmAlgos.forEach(function(algo) {
|
||||||
var symmKey = openpgp.crypto.generateSessionKey(algo);
|
var symmKey = openpgp.crypto.generateSessionKey(algo);
|
||||||
var symmencData = openpgp.crypto.cfb.encrypt(openpgp.crypto.getPrefixRandom(algo), algo, plaintext, symmKey, resync);
|
var symmencData = openpgp.crypto.cfb.encrypt(openpgp.crypto.getPrefixRandom(algo), algo, plaintext, symmKey, resync);
|
||||||
var text = openpgp.crypto.cfb.decrypt(algo, symmKey, symmencData, resync);
|
var text = openpgp.crypto.cfb.decrypt(algo, symmKey, symmencData, resync).join('');
|
||||||
expect(text).to.equal(plaintext);
|
expect(text).to.equal(plaintext);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user