diff --git a/src/libs/jsbn.js b/src/libs/jsbn.js index 75b50d9..eee75a2 100644 --- a/src/libs/jsbn.js +++ b/src/libs/jsbn.js @@ -31,6 +31,11 @@ * and disclaimer. */ +/* + * Added Node.js Buffers support + * 2014 rzcoder + */ + var crypt = require('crypto'); // Bits per digit @@ -176,19 +181,25 @@ function nbv(i) { // (protected) set from string and radix function bnpFromString(data, radix, unsigned) { var k; - if (radix == 16) k = 4; - else if (radix == 8) k = 3; - else if (radix == 256) k = 8; // byte array - else if (radix == 2) k = 1; - else if (radix == 32) k = 5; - else if (radix == 4) k = 2; - else { - this.fromRadix(data, radix); - return; + switch(radix) { + case 2: k=1; break; + case 4: k=2; break; + case 8: k=3; break; + case 16: k=4; break; + case 32: k=5; break; + case 256: k=8; break; + default: + this.fromRadix(data, radix); + return; } + this.t = 0; this.s = 0; - var i = data.length, mi = false, sh = 0; + + var i = data.length; + var mi = false; + var sh = 0; + while (--i >= 0) { var x = (k == 8) ? data[i] & 0xff : intAt(data, i); if (x < 0) { @@ -207,7 +218,7 @@ function bnpFromString(data, radix, unsigned) { sh += k; if (sh >= this.DB) sh -= this.DB; } - if (k == 8 && (data[0] & 0x80) != 0) { + if ((!unsigned) && k == 8 && (data[0] & 0x80) != 0) { this.s = -1; if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; } @@ -215,8 +226,8 @@ function bnpFromString(data, radix, unsigned) { if (mi) BigInteger.ZERO.subTo(this, this); } -function bnpFromByteArray(a) { - this.fromString(a, 256) +function bnpFromByteArray(a, unsigned) { + this.fromString(a, 256, unsigned) } function bnpFromBuffer(a) { diff --git a/src/libs/rsa.js b/src/libs/rsa.js index b41c6f7..d033727 100644 --- a/src/libs/rsa.js +++ b/src/libs/rsa.js @@ -38,7 +38,7 @@ var crypt = require('crypto'); var BigInteger = require("./jsbn.js"); -var utils = require('../utils.js') +var utils = require('../utils.js'); exports.BigInteger = BigInteger; module.exports.Key = (function() { @@ -129,6 +129,8 @@ module.exports.Key = (function() { this.dmp1 = new BigInteger(DP); this.dmq1 = new BigInteger(DQ); this.coeff = new BigInteger(C); + } else { + // TODO: re-calculate any missing CRT params } this.$$recalculateCache(); } else @@ -201,34 +203,28 @@ module.exports.Key = (function() { } } - this.debug = {} - this.debug.s = {} - this.debug.s.b = []; - this.debug.s.m = []; - this.debug.s.c = []; - this.debug.s.resbuf = []; for(var i in buffers) { var buf = buffers[i]; - this.debug.s.b[i] = buf; var m = this.$$pkcs1pad2(buf, this.encryptedDataLength); - this.debug.s.m[i] = m; if (m === null) { return null; } var c = this.$doPublic(m); - this.debug.s.c[i] = c; + if (c === null) { return null; } - this.debug.s.resbuf[i] = c.toBuffer(true) - while (this.debug.s.resbuf[i].length < this.encryptedDataLength) - this.debug.s.resbuf[i] = Buffer.concat(new Buffer([0]), this.debug.s.resbuf[i]); + var encryptedBuffer = c.toBuffer(true); + + while (encryptedBuffer.length < this.encryptedDataLength) { + encryptedBuffer = Buffer.concat([new Buffer([0]), encryptedBuffer]); + } - results.push( this.debug.s.resbuf[i]); + results.push(encryptedBuffer); } return Buffer.concat(results); @@ -240,47 +236,31 @@ module.exports.Key = (function() { * @returns {Buffer} */ RSAKey.prototype.decrypt = function (buffer) { - // if (buffer.length % this.encryptedDataLength > 0) - // throw Error('Incorrect data or key'); + if (buffer.length % this.encryptedDataLength > 0) + throw Error('Incorrect data or key'); var result = []; - var s = ''; var offset = 0; var length = 0; - this.debug.o = {} - this.debug.o.b = []; - this.debug.o.m = []; - this.debug.o.c = []; - this.debug.o.resbuf = []; - for (var i = 0; ; i++) { - offset = length; - length = offset + this.encryptedDataLength// + (buffer[offset] === 0 ? 1 : 0); - - this.debug.o.resbuf[i] = buffer.slice(offset, Math.min(length, buffer.length)) - var c = new BigInteger(this.debug.o.resbuf[i]); - this.debug.o.c[i] = c; + var buffersCount = buffer.length / this.encryptedDataLength; + + + for (var i = 0; i < buffersCount; i++) { + offset = i * this.encryptedDataLength; + length = offset + this.encryptedDataLength; + + var c = new BigInteger(buffer.slice(offset, Math.min(length, buffer.length))); var m = this.$doPrivate(c); - this.debug.o.m[i] = m; if (m === null) { return null; } - this.debug.o.b[i] = this.$$pkcs1unpad2(m, this.encryptedDataLength) - result.push(this.debug.o.b[i]); - - if (length >= buffer.length) - break; + result.push(this.$$pkcs1unpad2(m, this.encryptedDataLength)); } - try{ - a = Buffer.concat(result); - } catch (e) { - console.log(e) - throw e; - } - return a; + return Buffer.concat(result); }; Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', { @@ -299,7 +279,7 @@ module.exports.Key = (function() { // Bit & byte length this.cache.keyBitLength = this.n.bitLength(); this.cache.keyByteLength = (this.cache.keyBitLength + 6) >> 3; - } + }; /** * PKCS#1 (type 2, random) pad input buffer to n bytes, and return a bigint @@ -328,7 +308,7 @@ module.exports.Key = (function() { ba.unshift(0); return new BigInteger(ba); - } + }; /** * Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext @@ -360,7 +340,7 @@ module.exports.Key = (function() { res[c++] = b[i] & 255; } return res; - } + }; return RSAKey; })(); diff --git a/test/tests.js b/test/tests.js index d116735..6e6fb43 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1,7 +1,12 @@ +/** + * TODO: test for compatibility with other rsa libraries + */ + var assert = require('chai').assert; var _ = require('lodash'); var NodeRSA = (require('../src/NodeRSA')); + for(var i=0;i<100;i++) describe('NodeRSA', function(){ var nodeRSA = null; @@ -60,7 +65,7 @@ describe('NodeRSA', function(){ 'KY4kQIIx8JEBsAYzgyP2iy0CAwEAAQ==\n'+ '-----END PUBLIC KEY-----'; - false && it('.loadFromPrivatePEM() should load private key from PEM string', function(){ + it('.loadFromPrivatePEM() should load private key from PEM string', function(){ privateNodeRSA = new NodeRSA(privateKeyPEM); assert.instanceOf(privateNodeRSA.keyPair, Object); assert(privateNodeRSA.isPrivate()); @@ -68,7 +73,7 @@ describe('NodeRSA', function(){ assert(!privateNodeRSA.isPublic(true)); }); - false && it('.loadFromPublicPEM() should load public key from PEM string', function(){ + it('.loadFromPublicPEM() should load public key from PEM string', function(){ publicNodeRSA = new NodeRSA(publicKeyPEM); assert.instanceOf(privateNodeRSA.keyPair, Object); assert(publicNodeRSA.isPublic()); @@ -76,15 +81,15 @@ describe('NodeRSA', function(){ assert(!publicNodeRSA.isPrivate()); }); - false && it('.toPrivatePEM() should return private PEM string', function(){ + it('.toPrivatePEM() should return private PEM string', function(){ assert.equal(privateNodeRSA.toPrivatePEM(), privateKeyPEM); }); - false && it('.toPublicPEM() from public key should return public PEM string', function(){ + it('.toPublicPEM() from public key should return public PEM string', function(){ assert.equal(publicNodeRSA.toPublicPEM(), publicKeyPEM); }); - false && it('.toPublicPEM() from private key should return public PEM string', function(){ + it('.toPublicPEM() from private key should return public PEM string', function(){ assert.equal(privateNodeRSA.toPublicPEM(), publicKeyPEM); }); }); @@ -105,12 +110,12 @@ describe('NodeRSA', function(){ var decryptedJSON = null; describe('Encrypting', function(){ - false && it('.encrypt() should return Buffer object', function(){ + it('.encrypt() should return Buffer object', function(){ encryptedBuffer = nodeRSA.encrypt(dataForEncrypt, null, 'buffer'); assert(Buffer.isBuffer(encryptedBuffer)); }); - false && it('.encrypt() should return base64 encrypted string', function(){ + it('.encrypt() should return base64 encrypted string', function(){ encrypted = nodeRSA.encrypt(dataForEncrypt); assert.isString(encrypted); assert.match(encrypted, /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/); @@ -121,19 +126,19 @@ describe('NodeRSA', function(){ assert(Buffer.isBuffer(encryptedLong)); }); - false && it('.encrypt() for js object. Should return Buffer object', function(){ + it('.encrypt() for js object. Should return Buffer object', function(){ encryptedJSON = nodeRSA.encrypt(JSONForEncrypt, null, 'buffer'); assert(Buffer.isBuffer(encryptedJSON)); }); }); describe('Decrypting', function(){ - false && it('.decrypt() should return decrypted Buffer', function(){ + it('.decrypt() should return decrypted Buffer', function(){ decrypted = nodeRSA.decrypt(encryptedBuffer, 'buffer'); assert(Buffer.isBuffer(decrypted)); }); - false && it('.decrypt() should return decrypted string', function(){ + it('.decrypt() should return decrypted string', function(){ decrypted = nodeRSA.decrypt(new Buffer(encrypted, 'base64')); assert.isString(decrypted); }); @@ -143,12 +148,12 @@ describe('NodeRSA', function(){ assert.isString(decryptedLong); }); - false && it('.decrypt() for js object. Should return decrypted js object', function(){ + it('.decrypt() for js object. Should return decrypted js object', function(){ decryptedJSON = nodeRSA.decrypt(encryptedJSON, 'json'); assert.isObject(decryptedJSON); }); - false && it('source and decrypted should be the same', function(){ + it('source and decrypted should be the same', function(){ assert.equal(decrypted, dataForEncrypt); }); @@ -156,7 +161,7 @@ describe('NodeRSA', function(){ assert.equal(decryptedLong, longDataForEncrypt); }); - false && it('source JSON and decrypted JSON should be the same', function(){ + it('source JSON and decrypted JSON should be the same', function(){ assert(_.isEqual(decryptedJSON, JSONForEncrypt)); }); });