From 273957f4732ff38d347075efcc905e2e36220279 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Sun, 30 Mar 2014 22:06:11 +0600 Subject: [PATCH 01/12] wip start signing implementation start tests refactoring --- README.md | 4 +- package.json | 2 +- src/NodeRSA.js | 179 +++++++++++++++++++++++++++++++------------------ test/tests.js | 36 ++++++++-- 4 files changed, 149 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index ec6e711..57ca24f 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ key.loadFromPEM(pem_string); ### Export keys ```js -key.toPrivatePEM(); -key.toPublicPEM(); +key.getPrivatePEM(); +key.getPublicPEM(); ``` ### Test key diff --git a/package.json b/package.json index b9ec3bf..43ffb67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-rsa", - "version": "0.1.32", + "version": "0.1.33", "description": "Node.js RSA library", "main": "src/NodeRSA.js", "scripts": { diff --git a/src/NodeRSA.js b/src/NodeRSA.js index 7ee0943..00ab87b 100644 --- a/src/NodeRSA.js +++ b/src/NodeRSA.js @@ -8,6 +8,7 @@ */ var rsa = require('./libs/rsa.js'); +var crypt = require('crypto'); var ber = require('asn1').Ber; var _ = require('lodash'); var utils = require('./utils'); @@ -21,6 +22,7 @@ module.exports = (function() { */ function NodeRSA(arg) { this.keyPair = new rsa.Key(); + this.$cache = {} if (_.isObject(arg)) { this.generateKeyPair(arg.b, arg.e); @@ -41,6 +43,7 @@ module.exports = (function() { exp = 65537; this.keyPair.generate(bits, exp.toString(16)); + this.$recalculateCache(); return this; }; @@ -55,6 +58,8 @@ module.exports = (function() { this.loadFromPublicPEM(pem, 'base64'); } else throw Error('Invalid PEM format'); + + this.$recalculateCache(); }; /** @@ -105,69 +110,6 @@ module.exports = (function() { ); }; - /** - * @returns {string} private PEM string - */ - NodeRSA.prototype.toPrivatePEM = function() { - var n = this.keyPair.n.toBuffer(); - var d = this.keyPair.d.toBuffer(); - var p = this.keyPair.p.toBuffer(); - var q = this.keyPair.q.toBuffer(); - var dmp1 = this.keyPair.dmp1.toBuffer(); - var dmq1 = this.keyPair.dmq1.toBuffer(); - var coeff = this.keyPair.coeff.toBuffer(); - - var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic - var writer = new ber.Writer({size: length}); - - writer.startSequence(); - writer.writeInt(0); - writer.writeBuffer(n, 2); - writer.writeInt(this.keyPair.e); - writer.writeBuffer(d, 2); - writer.writeBuffer(p, 2); - writer.writeBuffer(q, 2); - writer.writeBuffer(dmp1, 2); - writer.writeBuffer(dmq1, 2); - writer.writeBuffer(coeff, 2); - writer.endSequence(); - - return '-----BEGIN RSA PRIVATE KEY-----\n' + - utils.linebrk(writer.buffer.toString('base64'), 64) + - '\n-----END RSA PRIVATE KEY-----'; - }; - - /** - * @returns {string} public PEM string - */ - NodeRSA.prototype.toPublicPEM = function() { - var n = this.keyPair.n.toBuffer(); - var length = n.length + 512; // magic - - var bodyWriter = new ber.Writer({size: length}); - bodyWriter.writeByte(0); - bodyWriter.startSequence(); - bodyWriter.writeBuffer(n, 2); - bodyWriter.writeInt(this.keyPair.e); - bodyWriter.endSequence(); - var body = bodyWriter.buffer; - - var writer = new ber.Writer({size: length}); - writer.startSequence(); - writer.startSequence(); - writer.writeOID(PUBLIC_RSA_OID); - writer.writeNull(); - writer.endSequence(); - writer.writeBuffer(body, 3); - writer.endSequence(); - - n = writer.buffer.toString('hex'); - - return '-----BEGIN PUBLIC KEY-----\n' + - utils.linebrk(writer.buffer.toString('base64'), 64) + - '\n-----END PUBLIC KEY-----'; - }; - /** * Check if keypair contains private key */ @@ -231,5 +173,114 @@ module.exports = (function() { } }; - return NodeRSA; + /** + * Signing data + * + * @param buffer - data for signing + * @param encoding - output encoding. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. + * @returns {*} + */ + NodeRSA.prototype.sign = function(buffer, encoding) { + encoding = (!encoding || encoding == 'buffer' ? null : encoding) + var signer = crypt.createSign('RSA-SHA256'); + signer.update(buffer); + return signer.sign(this.getPrivatePEM(), encoding); + } + + /** + * Verifying signed data + * + * @param buffer - signed data + * @param signature + * @param signature_encoding - encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. + * @returns {*} + */ + NodeRSA.prototype.verify = function(buffer, signature, signature_encoding) { + signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding) + var verifier = crypt.createVerify('RSA-SHA256'); + verifier.update(buffer); + return verifier.verify(this.getPublicPEM(), signature, signature_encoding); + } + + NodeRSA.prototype.getPrivatePEM = function () { + return this.$cache.privatePEM + } + + NodeRSA.prototype.getPublicPEM = function () { + return this.$cache.publicPEM + } + + /** + * private + * Recalculating properties + */ + NodeRSA.prototype.$recalculateCache = function() { + this.$cache.privatePEM = this.$makePrivatePEM() + this.$cache.publicPEM = this.$makePublicPEM() + } + + /** + * private + * @returns {string} private PEM string + */ + NodeRSA.prototype.$makePrivatePEM = function() { + var n = this.keyPair.n.toBuffer(); + var d = this.keyPair.d.toBuffer(); + var p = this.keyPair.p.toBuffer(); + var q = this.keyPair.q.toBuffer(); + var dmp1 = this.keyPair.dmp1.toBuffer(); + var dmq1 = this.keyPair.dmq1.toBuffer(); + var coeff = this.keyPair.coeff.toBuffer(); + + var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic + var writer = new ber.Writer({size: length}); + + writer.startSequence(); + writer.writeInt(0); + writer.writeBuffer(n, 2); + writer.writeInt(this.keyPair.e); + writer.writeBuffer(d, 2); + writer.writeBuffer(p, 2); + writer.writeBuffer(q, 2); + writer.writeBuffer(dmp1, 2); + writer.writeBuffer(dmq1, 2); + writer.writeBuffer(coeff, 2); + writer.endSequence(); + + return '-----BEGIN RSA PRIVATE KEY-----\n' + + utils.linebrk(writer.buffer.toString('base64'), 64) + + '\n-----END RSA PRIVATE KEY-----'; + }; + + /** + * private + * @returns {string} public PEM string + */ + NodeRSA.prototype.$makePublicPEM = function() { + var n = this.keyPair.n.toBuffer(); + var length = n.length + 512; // magic + + var bodyWriter = new ber.Writer({size: length}); + bodyWriter.writeByte(0); + bodyWriter.startSequence(); + bodyWriter.writeBuffer(n, 2); + bodyWriter.writeInt(this.keyPair.e); + bodyWriter.endSequence(); + var body = bodyWriter.buffer; + + var writer = new ber.Writer({size: length}); + writer.startSequence(); + writer.startSequence(); + writer.writeOID(PUBLIC_RSA_OID); + writer.writeNull(); + writer.endSequence(); + writer.writeBuffer(body, 3); + writer.endSequence(); + + return '-----BEGIN PUBLIC KEY-----\n' + + utils.linebrk(writer.buffer.toString('base64'), 64) + + '\n-----END PUBLIC KEY-----'; + }; + + return NodeRSA; })(); \ No newline at end of file diff --git a/test/tests.js b/test/tests.js index 34a151c..5679c27 100644 --- a/test/tests.js +++ b/test/tests.js @@ -4,16 +4,25 @@ var assert = require('chai').assert; var _ = require('lodash'); -var NodeRSA = (require('../src/NodeRSA')); +var NodeRSA = require('../src/NodeRSA'); describe('NodeRSA', function(){ var nodeRSA = null; var privateNodeRSA = null; var publicNodeRSA = null; + var dataBundle = { + "string": "ascii + юникод スラ ⑨", + "empty string": "", + "long string": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "buffer": new Buffer("ascii + юникод スラ ⑨"), + "json object": {str: "string", arr: ["a","r","r", "a", "y", true, '⑨'], int: 42, nested: {key: {key: 1}}}, + "json array": [1,2,3,4,5,6,7,8,9,[10,11,12,[13],14,15,[16,17,[18]]]] + }; + describe('Work with keys', function(){ it('.generateKeyPair() should make key pair', function(){ - nodeRSA = new NodeRSA({b: 1024}); + nodeRSA = new NodeRSA({b: 512}); assert.instanceOf(nodeRSA.keyPair, Object); }); @@ -80,15 +89,15 @@ describe('NodeRSA', function(){ }); it('.toPrivatePEM() should return private PEM string', function(){ - assert.equal(privateNodeRSA.toPrivatePEM(), privateKeyPEM); + assert.equal(privateNodeRSA.getPrivatePEM(), privateKeyPEM); }); it('.toPublicPEM() from public key should return public PEM string', function(){ - assert.equal(publicNodeRSA.toPublicPEM(), publicKeyPEM); + assert.equal(publicNodeRSA.getPublicPEM(), publicKeyPEM); }); it('.toPublicPEM() from private key should return public PEM string', function(){ - assert.equal(privateNodeRSA.toPublicPEM(), publicKeyPEM); + assert.equal(privateNodeRSA.getPublicPEM(), publicKeyPEM); }); }); }); @@ -163,4 +172,21 @@ describe('NodeRSA', function(){ assert(_.isEqual(decryptedJSON, JSONForEncrypt)); }); }); + + describe('Signing & verifying', function () { + + + var signed = {}; + + for(var i in dataForSign) { + var sign = dataForSign[i]; + var signature = null; + + it('should signed '+i, function(){ + signature = nodeRSA.sign(sign, 'hex'); + console.log(signature) + }); + + } + }); }); \ No newline at end of file From 9ce6449d052929ca8ec5e325a92b01607bc7cb77 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Mon, 31 Mar 2014 00:51:34 +0600 Subject: [PATCH 02/12] signing wip --- README.md | 9 ++--- src/NodeRSA.js | 89 +++++++++++++++++++++++++++++++++----------------- test/tests.js | 18 +++++----- 3 files changed, 73 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 57ca24f..ec06733 100644 --- a/README.md +++ b/README.md @@ -71,18 +71,19 @@ key.isPublic([strict]); ### Encrypting/decrypting ```js -key.encrypt(buffer, [source_encoding], [output_encoding]); +key.encrypt(buffer, [encoding], [source_encoding]); ``` **buffer** - data for encrypting, may be string, Buffer, or any object/array. Arrays and objects will encoded to JSON string first.
+**encoding** - encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default *buffer*. **source_encoding** - source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). *Utf8* by default.
-**output_encoding** - encoding for output result, can also take 'buffer' to return Buffer object. Default *base64*. ```js key.decrypt(buffer, [encoding]); ``` - **buffer** - data for decrypting. Takes Buffer object or base64 encoded string.
-**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. +**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. Default 'buffer'. + +### Signing/Verifying ## Contributing diff --git a/src/NodeRSA.js b/src/NodeRSA.js index 00ab87b..a5d0963 100644 --- a/src/NodeRSA.js +++ b/src/NodeRSA.js @@ -129,25 +129,17 @@ module.exports = (function() { * Encrypting data method * * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. + * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. * @param source_encoding {string} - optional. Encoding for given string. Default utf8. - * @param output_encoding {string} - optional. Encoding for output result, can also take 'buffer' to return Buffer object. Default base64. * @returns {string|Buffer} */ - NodeRSA.prototype.encrypt = function(buffer, source_encoding, output_encoding) { - var res = null; + NodeRSA.prototype.encrypt = function(buffer, encoding, source_encoding) { + var res = this.keyPair.encrypt(this.$getDataForEcrypt(buffer, source_encoding)); - if (_.isString(buffer) || _.isNumber(buffer)) { - res = this.keyPair.encrypt(new Buffer('' + buffer, source_encoding || 'utf8')); - } else if (Buffer.isBuffer(buffer)) { - res = this.keyPair.encrypt(buffer); - } else if (_.isObject(buffer)) { - res = this.keyPair.encrypt(new Buffer(JSON.stringify(buffer))); - } - - if (output_encoding == 'buffer') { + if (encoding == 'buffer' || !encoding) { return res; } else { - return res.toString(output_encoding || 'base64'); + return res.toString(encoding); } }; @@ -159,31 +151,22 @@ module.exports = (function() { * @returns {Buffer|object|string} */ NodeRSA.prototype.decrypt = function(buffer, encoding) { - encoding = encoding || 'utf8'; - buffer = _.isString(buffer) ? new Buffer(buffer, 'base64') : buffer; - var res = this.keyPair.decrypt(buffer); - - if (encoding == 'buffer') { - return res; - } else if (encoding == 'json') { - return JSON.parse(res.toString()); - } else { - return res.toString(encoding); - } + return this.$getDecryptedData(this.keyPair.decrypt(buffer), encoding); }; /** * Signing data * - * @param buffer - data for signing - * @param encoding - output encoding. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. - * @returns {*} + * @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string. + * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. + * @param source_encoding {string} - optional. Encoding for given string. Default utf8. + * @returns {string|Buffer} */ - NodeRSA.prototype.sign = function(buffer, encoding) { + NodeRSA.prototype.sign = function(buffer, encoding, source_encoding) { encoding = (!encoding || encoding == 'buffer' ? null : encoding) var signer = crypt.createSign('RSA-SHA256'); - signer.update(buffer); + signer.update(this.$getDataForEcrypt(buffer, source_encoding)); return signer.sign(this.getPrivatePEM(), encoding); } @@ -192,7 +175,7 @@ module.exports = (function() { * * @param buffer - signed data * @param signature - * @param signature_encoding - encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. + * @param signature_encoding - optional. Encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. * @returns {*} */ NodeRSA.prototype.verify = function(buffer, signature, signature_encoding) { @@ -210,6 +193,44 @@ module.exports = (function() { return this.$cache.publicPEM } + /** + * Preparing given data for encrypting/signing. Just make new/return Buffer object. + * + * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. + * @param source_encoding {string} - optional. Encoding for given string. Default utf8. + * @returns {Buffer} + */ + NodeRSA.prototype.$getDataForEcrypt = function(buffer, encoding) { + if (_.isString(buffer) || _.isNumber(buffer)) { + return new Buffer('' + buffer, encoding || 'utf8'); + } else if (Buffer.isBuffer(buffer)) { + return buffer; + } else if (_.isObject(buffer)) { + return new Buffer(JSON.stringify(buffer)); + } else { + throw Error("Unexpected data type") + } + } + + /** + * + * @param buffer {Buffer} - decrypted data. + * @param encoding - optional. Encoding for result output. May be 'buffer', 'json' or any of Node.js Buffer supported encoding. + * @returns {*} + */ + NodeRSA.prototype.$getDecryptedData = function(buffer, encoding) { + encoding = encoding || 'buffer'; + + if (encoding == 'buffer') { + return buffer; + } else if (encoding == 'json') { + return JSON.parse(buffer.toString()); + } else { + return buffer.toString(encoding); + } + }; + + /** * private * Recalculating properties @@ -224,6 +245,10 @@ module.exports = (function() { * @returns {string} private PEM string */ NodeRSA.prototype.$makePrivatePEM = function() { + if (!this.isPrivate()) { + return null; + } + var n = this.keyPair.n.toBuffer(); var d = this.keyPair.d.toBuffer(); var p = this.keyPair.p.toBuffer(); @@ -257,6 +282,10 @@ module.exports = (function() { * @returns {string} public PEM string */ NodeRSA.prototype.$makePublicPEM = function() { + if (!this.isPublic()) { + return null; + } + var n = this.keyPair.n.toBuffer(); var length = n.length + 512; // magic diff --git a/test/tests.js b/test/tests.js index 5679c27..1b59ac7 100644 --- a/test/tests.js +++ b/test/tests.js @@ -118,40 +118,40 @@ describe('NodeRSA', function(){ describe('Encrypting', function(){ it('.encrypt() should return Buffer object', function(){ - encryptedBuffer = nodeRSA.encrypt(dataForEncrypt, null, 'buffer'); + encryptedBuffer = nodeRSA.encrypt(dataForEncrypt, 'buffer'); assert(Buffer.isBuffer(encryptedBuffer)); }); it('.encrypt() should return base64 encrypted string', function(){ - encrypted = nodeRSA.encrypt(dataForEncrypt); + encrypted = nodeRSA.encrypt(dataForEncrypt, 'base64'); 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}==)$/); }); it('.encrypt() should return encrypted Buffer for long message', function(){ - encryptedLong = nodeRSA.encrypt(longDataForEncrypt, null, 'buffer'); + encryptedLong = nodeRSA.encrypt(longDataForEncrypt, 'buffer'); assert(Buffer.isBuffer(encryptedLong)); }); it('.encrypt() for js object. Should return Buffer object', function(){ - encryptedJSON = nodeRSA.encrypt(JSONForEncrypt, null, 'buffer'); + encryptedJSON = nodeRSA.encrypt(JSONForEncrypt, 'buffer'); assert(Buffer.isBuffer(encryptedJSON)); }); }); describe('Decrypting', function(){ it('.decrypt() should return decrypted Buffer', function(){ - decrypted = nodeRSA.decrypt(encryptedBuffer, 'buffer'); + decrypted = nodeRSA.decrypt(encryptedBuffer); assert(Buffer.isBuffer(decrypted)); }); it('.decrypt() should return decrypted string', function(){ - decrypted = nodeRSA.decrypt(new Buffer(encrypted, 'base64')); + decrypted = nodeRSA.decrypt(new Buffer(encrypted, 'base64'), 'utf8'); assert.isString(decrypted); }); it('.decrypt() should return decrypted string for long message', function(){ - decryptedLong = nodeRSA.decrypt(encryptedLong); + decryptedLong = nodeRSA.decrypt(encryptedLong, 'utf8'); assert.isString(decryptedLong); }); @@ -178,8 +178,8 @@ describe('NodeRSA', function(){ var signed = {}; - for(var i in dataForSign) { - var sign = dataForSign[i]; + for(var i in dataBundle) { + var sign = dataBundle[i]; var signature = null; it('should signed '+i, function(){ From 7c3c3b5479c55dad2370409b676077ca26499189 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Mon, 31 Mar 2014 00:59:55 +0600 Subject: [PATCH 03/12] signing wip --- test/tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.js b/test/tests.js index 1b59ac7..aaf30af 100644 --- a/test/tests.js +++ b/test/tests.js @@ -182,7 +182,7 @@ describe('NodeRSA', function(){ var sign = dataBundle[i]; var signature = null; - it('should signed '+i, function(){ + it('should sign '+i, function(){ signature = nodeRSA.sign(sign, 'hex'); console.log(signature) }); From 2835ca50d32d1a9ed5284f111ed6c5d538196fcb Mon Sep 17 00:00:00 2001 From: rzcoder Date: Tue, 1 Apr 2014 00:14:18 +0600 Subject: [PATCH 04/12] tests refactoring (wip) --- src/NodeRSA.js | 5 +- test/tests.js | 267 +++++++++++++++++++++++-------------------------- 2 files changed, 127 insertions(+), 145 deletions(-) diff --git a/src/NodeRSA.js b/src/NodeRSA.js index a5d0963..1c53352 100644 --- a/src/NodeRSA.js +++ b/src/NodeRSA.js @@ -175,13 +175,14 @@ module.exports = (function() { * * @param buffer - signed data * @param signature + * @param source_encoding {string} - optional. Encoding for given string. Default utf8. * @param signature_encoding - optional. Encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'. * @returns {*} */ - NodeRSA.prototype.verify = function(buffer, signature, signature_encoding) { + NodeRSA.prototype.verify = function(buffer, signature, source_encoding, signature_encoding) { signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding) var verifier = crypt.createVerify('RSA-SHA256'); - verifier.update(buffer); + verifier.update(this.$getDataForEcrypt(buffer, source_encoding)); return verifier.verify(this.getPublicPEM(), signature, signature_encoding); } diff --git a/test/tests.js b/test/tests.js index aaf30af..324e007 100644 --- a/test/tests.js +++ b/test/tests.js @@ -2,77 +2,109 @@ * TODO: tests for compatibility with other rsa libraries */ -var assert = require('chai').assert; -var _ = require('lodash'); -var NodeRSA = require('../src/NodeRSA'); +var assert = require("chai").assert; +var _ = require("lodash"); +var NodeRSA = require("../src/NodeRSA"); + +describe("NodeRSA", function(){ + var keySizes = [ + {b: 512, e: 3}, + {b: 512, e: 5}, + {b: 512, e: 257}, + {b: 512, e: 65537}, + {b: 768}, // 'e' should be 65537 + {b: 1024} // 'e' should be 65537 + ]; -describe('NodeRSA', function(){ - var nodeRSA = null; + var dataBundle = { + "string": { + data: "ascii + юникод スラ ⑨", + encoding: "utf8" + }, + "empty string": { + data: "", + encoding: ["utf8", "ascii", "hex", "base64"] + }, + "long string": { + data: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + encoding: ["utf8", "ascii"] + }, + "buffer": { + data: new Buffer("ascii + юникод スラ ⑨"), + encoding: "buffer" + }, + "json object": { + data: {str: "string", arr: ["a","r","r", "a", "y", true, "⑨"], int: 42, nested: {key: {key: 1}}}, + encoding: "json" + }, + "json array": { + data: [1,2,3,4,5,6,7,8,9,[10,11,12,[13],14,15,[16,17,[18]]]], + encoding: "json" + } + }; + + var generatedKeys = []; var privateNodeRSA = null; var publicNodeRSA = null; - var dataBundle = { - "string": "ascii + юникод スラ ⑨", - "empty string": "", - "long string": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - "buffer": new Buffer("ascii + юникод スラ ⑨"), - "json object": {str: "string", arr: ["a","r","r", "a", "y", true, '⑨'], int: 42, nested: {key: {key: 1}}}, - "json array": [1,2,3,4,5,6,7,8,9,[10,11,12,[13],14,15,[16,17,[18]]]] - }; + describe("Work with keys", function(){ - describe('Work with keys', function(){ - it('.generateKeyPair() should make key pair', function(){ - nodeRSA = new NodeRSA({b: 512}); - assert.instanceOf(nodeRSA.keyPair, Object); + describe("Generating keys", function() { + for (var i in keySizes) { + it("should make key pair " + keySizes[i].b + "-bit length and public exponent is " + (keySizes[i].e || 65537), function () { + generatedKeys.push(new NodeRSA({b: keySizes[i].b, e: keySizes[i].e})); + assert.instanceOf(generatedKeys[generatedKeys.length - 1].keyPair, Object); + }); + } }); - describe('PEM', function(){ - var privateKeyPEM = '-----BEGIN RSA PRIVATE KEY-----\n'+ - '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\n'+ - '-----END RSA PRIVATE KEY-----'; - - var publicKeyPEM = '-----BEGIN PUBLIC KEY-----\n'+ - 'MIIBYjANBgkqhkiG9w0BAQEFAAOCAU8AMIIBSgKCAUEAsE1edyfToZRv6cFOkB0t\n'+ - 'AJ5qJor4YF5CccJAL0fS/o1Yk10VSXH4Xx4peSJgYQKkO0HqO1hAz6k9dFQB4U1C\n'+ - 'nWtRjtNEcIfycqrZrhu6you5syb6ScV3Zu/9bm7/DyaLlx/gJhUPR1OxOzaqsEvl\n'+ - 'u7hbDhNLIYo1zKFb/aUBbD6+UcaGxH2BfFNdzVAtVSVpc/s2Y3sboMN7rByUj793\n'+ - '7iQlaMINvVjyasynYuzHNw6ZRP9JP9fwxrCyaxnTPWxVl0qvVaQO2+TtFMtDXH2O\n'+ - 'VZtWWeLHAL8cildw0G+u2qVqTqIGEwNyJlsAHykaPFAMW0xLueumrSlB+JUJPrRv\n'+ - 'vw4nBCd4GOrNSlPCE/xlk1Cb8JaICTLvDUcYc3ZqL3jqAueBhkpw2uCz8xVJeOA1\n'+ - 'KY4kQIIx8JEBsAYzgyP2iy0CAwEAAQ==\n'+ - '-----END PUBLIC KEY-----'; - - it('.loadFromPrivatePEM() should load private key from PEM string', function(){ + describe("PEM", function(){ + var privateKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n"+ + "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\n"+ + "-----END RSA PRIVATE KEY-----"; + + var publicKeyPEM = "-----BEGIN PUBLIC KEY-----\n"+ + "MIIBYjANBgkqhkiG9w0BAQEFAAOCAU8AMIIBSgKCAUEAsE1edyfToZRv6cFOkB0t\n"+ + "AJ5qJor4YF5CccJAL0fS/o1Yk10VSXH4Xx4peSJgYQKkO0HqO1hAz6k9dFQB4U1C\n"+ + "nWtRjtNEcIfycqrZrhu6you5syb6ScV3Zu/9bm7/DyaLlx/gJhUPR1OxOzaqsEvl\n"+ + "u7hbDhNLIYo1zKFb/aUBbD6+UcaGxH2BfFNdzVAtVSVpc/s2Y3sboMN7rByUj793\n"+ + "7iQlaMINvVjyasynYuzHNw6ZRP9JP9fwxrCyaxnTPWxVl0qvVaQO2+TtFMtDXH2O\n"+ + "VZtWWeLHAL8cildw0G+u2qVqTqIGEwNyJlsAHykaPFAMW0xLueumrSlB+JUJPrRv\n"+ + "vw4nBCd4GOrNSlPCE/xlk1Cb8JaICTLvDUcYc3ZqL3jqAueBhkpw2uCz8xVJeOA1\n"+ + "KY4kQIIx8JEBsAYzgyP2iy0CAwEAAQ==\n"+ + "-----END PUBLIC KEY-----"; + + it(".loadFromPrivatePEM() should load private key from PEM string", function(){ privateNodeRSA = new NodeRSA(privateKeyPEM); assert.instanceOf(privateNodeRSA.keyPair, Object); assert(privateNodeRSA.isPrivate()); @@ -80,7 +112,7 @@ describe('NodeRSA', function(){ assert(!privateNodeRSA.isPublic(true)); }); - 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()); @@ -88,105 +120,54 @@ describe('NodeRSA', function(){ assert(!publicNodeRSA.isPrivate()); }); - it('.toPrivatePEM() should return private PEM string', function(){ + it(".toPrivatePEM() should return private PEM string", function(){ assert.equal(privateNodeRSA.getPrivatePEM(), privateKeyPEM); }); - 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.getPublicPEM(), publicKeyPEM); }); - 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.getPublicPEM(), publicKeyPEM); }); }); }); + describe("Encrypting & decrypting", function(){ + var encrypted = {}; + var decrypted = {}; - var dataForEncrypt = "ascii + юникод スラ ⑨"; - var longDataForEncrypt = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - var JSONForEncrypt = {str: "string", arr: ["a","r","r", "a", "y", true, '⑨'], int: 42, nested: {key: {key: 1}}} - - var encrypted = null; - var encryptedLong = null; - var encryptedBuffer = null; - var encryptedJSON = null; - - var decrypted = null; - var decryptedLong = null; - var decryptedJSON = null; - - describe('Encrypting', function(){ - it('.encrypt() should return Buffer object', function(){ - encryptedBuffer = nodeRSA.encrypt(dataForEncrypt, 'buffer'); - assert(Buffer.isBuffer(encryptedBuffer)); - }); - - it('.encrypt() should return base64 encrypted string', function(){ - encrypted = nodeRSA.encrypt(dataForEncrypt, 'base64'); - 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}==)$/); - }); - - it('.encrypt() should return encrypted Buffer for long message', function(){ - encryptedLong = nodeRSA.encrypt(longDataForEncrypt, 'buffer'); - assert(Buffer.isBuffer(encryptedLong)); - }); - - it('.encrypt() for js object. Should return Buffer object', function(){ - encryptedJSON = nodeRSA.encrypt(JSONForEncrypt, 'buffer'); - assert(Buffer.isBuffer(encryptedJSON)); - }); - }); - - describe('Decrypting', function(){ - it('.decrypt() should return decrypted Buffer', function(){ - decrypted = nodeRSA.decrypt(encryptedBuffer); - assert(Buffer.isBuffer(decrypted)); - }); - - it('.decrypt() should return decrypted string', function(){ - decrypted = nodeRSA.decrypt(new Buffer(encrypted, 'base64'), 'utf8'); - assert.isString(decrypted); - }); - - it('.decrypt() should return decrypted string for long message', function(){ - decryptedLong = nodeRSA.decrypt(encryptedLong, 'utf8'); - assert.isString(decryptedLong); - }); - - it('.decrypt() for js object. Should return decrypted js object', function(){ - decryptedJSON = nodeRSA.decrypt(encryptedJSON, 'json'); - assert.isObject(decryptedJSON); - }); - - it('source and decrypted should be the same', function(){ - assert.equal(decrypted, dataForEncrypt); - }); + for(var i in dataBundle) { + var suit = dataBundle[i]; - it('long source and decrypted should be the same', function(){ - assert.equal(decryptedLong, longDataForEncrypt); - }); + it("should encrypt "+i, function(){ + encrypted[i] = generatedKeys[0].encrypt(suit.data); + assert(Buffer.isBuffer(encrypted[i])); + assert(encrypted[i].length > 0); + }); - it('source JSON and decrypted JSON should be the same', function(){ - assert(_.isEqual(decryptedJSON, JSONForEncrypt)); - }); + it("should decrypt "+i, function(){ + decrypted[i] = generatedKeys[0].decrypt(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding); + assert(_.isEqual(suit.data, decrypted[i])); + }); + } }); - describe('Signing & verifying', function () { - - + describe("Signing & verifying", function () { var signed = {}; for(var i in dataBundle) { - var sign = dataBundle[i]; - var signature = null; - - it('should sign '+i, function(){ - signature = nodeRSA.sign(sign, 'hex'); - console.log(signature) + var suit = dataBundle[i]; + it("should sign "+i, function(){ + signed[i] = generatedKeys[0].sign(suit.data); + assert(Buffer.isBuffer(signed[i])); + assert(signed[i].length > 0); }); + it("should verify "+i, function(){ + assert(generatedKeys[0].verify(suit.data, signed[i])); + }); } }); }); \ No newline at end of file From 9850d4cea3a3aacf3ca94be59fda86b888c1df51 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Thu, 3 Apr 2014 11:16:57 +0600 Subject: [PATCH 05/12] tests refactoring (wip) --- src/NodeRSA.js | 8 +++-- test/tests.js | 83 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/NodeRSA.js b/src/NodeRSA.js index 1c53352..650a563 100644 --- a/src/NodeRSA.js +++ b/src/NodeRSA.js @@ -114,7 +114,7 @@ module.exports = (function() { * Check if keypair contains private key */ NodeRSA.prototype.isPrivate = function() { - return this.keyPair.n && this.keyPair.e && this.keyPair.d; + return this.keyPair.n && this.keyPair.e && this.keyPair.d || false; }; /** @@ -122,7 +122,7 @@ module.exports = (function() { * @param strict {boolean} - public key only, return false if have private exponent */ NodeRSA.prototype.isPublic = function(strict) { - return this.keyPair.n && this.keyPair.e && !(strict && this.keyPair.d); + return this.keyPair.n && this.keyPair.e && !(strict && this.keyPair.d) || false; }; /** @@ -164,6 +164,10 @@ module.exports = (function() { * @returns {string|Buffer} */ NodeRSA.prototype.sign = function(buffer, encoding, source_encoding) { + if (!this.isPrivate()) { + throw Error("isn't private key") + } + encoding = (!encoding || encoding == 'buffer' ? null : encoding) var signer = crypt.createSign('RSA-SHA256'); signer.update(this.$getDataForEcrypt(buffer, source_encoding)); diff --git a/test/tests.js b/test/tests.js index 324e007..28da964 100644 --- a/test/tests.js +++ b/test/tests.js @@ -11,9 +11,9 @@ describe("NodeRSA", function(){ {b: 512, e: 3}, {b: 512, e: 5}, {b: 512, e: 257}, - {b: 512, e: 65537}, - {b: 768}, // 'e' should be 65537 - {b: 1024} // 'e' should be 65537 + //{b: 512, e: 65537}, + //{b: 768}, // 'e' should be 65537 + //{b: 1024} // 'e' should be 65537 ]; var dataBundle = { @@ -135,39 +135,64 @@ describe("NodeRSA", function(){ }); describe("Encrypting & decrypting", function(){ - var encrypted = {}; - var decrypted = {}; + describe("Good cases", function () { + var encrypted = {}; + var decrypted = {}; + + for(var i in dataBundle) { + var suit = dataBundle[i]; + var key = null; + + it("should encrypt "+i, function(){ + key = generatedKeys[Math.round(Math.random()*1000) % generatedKeys.length]; + encrypted[i] = key.encrypt(suit.data); + assert(Buffer.isBuffer(encrypted[i])); + assert(encrypted[i].length > 0); + }); - for(var i in dataBundle) { - var suit = dataBundle[i]; + it("should decrypt "+i, function(){ + decrypted[i] = key.decrypt(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding); + assert(_.isEqual(suit.data, decrypted[i])); + }); + } + }); + }); - it("should encrypt "+i, function(){ - encrypted[i] = generatedKeys[0].encrypt(suit.data); - assert(Buffer.isBuffer(encrypted[i])); - assert(encrypted[i].length > 0); - }); + describe("Signing & verifying", function () { + describe("Good cases", function () { + var signed = {}; + var key = null; + + for(var i in dataBundle) { + var suit = dataBundle[i]; + it("should sign "+i, function(){ + key = generatedKeys[Math.round(Math.random()*1000) % generatedKeys.length]; + signed[i] = key.sign(suit.data); + assert(Buffer.isBuffer(signed[i])); + assert(signed[i].length > 0); + }); - it("should decrypt "+i, function(){ - decrypted[i] = generatedKeys[0].decrypt(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding); - assert(_.isEqual(suit.data, decrypted[i])); + it("should verify "+i, function(){ + assert(key.verify(suit.data, signed[i])); + }); + } + }); + + describe("Bad cases", function () { + it("incorrect information", function(){ + var signed = generatedKeys[0].sign('data1'); + assert(! generatedKeys[0].verify('data2', signed)); }); - } - }); - describe("Signing & verifying", function () { - var signed = {}; - - for(var i in dataBundle) { - var suit = dataBundle[i]; - it("should sign "+i, function(){ - signed[i] = generatedKeys[0].sign(suit.data); - assert(Buffer.isBuffer(signed[i])); - assert(signed[i].length > 0); + it("incorrect key for signing", function(){ + var key = new NodeRSA(generatedKeys[0].getPublicPEM()); + key.sign('data'); }); - it("should verify "+i, function(){ - assert(generatedKeys[0].verify(suit.data, signed[i])); + it("incorrect key for verifying", function(){ + var signed = generatedKeys[0].sign('data'); + assert(! generatedKeys[1].verify('data', signed)); }); - } + }); }); }); \ No newline at end of file From b3e89952e774968285d1bb1a06ef65812ce6dd61 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 00:12:22 +0600 Subject: [PATCH 06/12] tests refactoring (wip) --- package.json | 6 ++-- src/NodeRSA.js | 40 +++++++++++++++---------- test/tests.js | 81 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 83 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 43ffb67..f12a53e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-rsa", - "version": "0.1.33", + "version": "0.1.40", "description": "Node.js RSA library", "main": "src/NodeRSA.js", "scripts": { @@ -16,7 +16,9 @@ "crypto", "assymetric", "encryption", - "decryption" + "decryption", + "sign", + "verify" ], "author": "rzcoder", "license": "BSD", diff --git a/src/NodeRSA.js b/src/NodeRSA.js index 650a563..d53d17e 100644 --- a/src/NodeRSA.js +++ b/src/NodeRSA.js @@ -22,7 +22,7 @@ module.exports = (function() { */ function NodeRSA(arg) { this.keyPair = new rsa.Key(); - this.$cache = {} + this.$cache = {}; if (_.isObject(arg)) { this.generateKeyPair(arg.b, arg.e); @@ -165,14 +165,14 @@ module.exports = (function() { */ NodeRSA.prototype.sign = function(buffer, encoding, source_encoding) { if (!this.isPrivate()) { - throw Error("isn't private key") + throw Error("It is not private key"); } - encoding = (!encoding || encoding == 'buffer' ? null : encoding) + encoding = (!encoding || encoding == 'buffer' ? null : encoding); var signer = crypt.createSign('RSA-SHA256'); signer.update(this.$getDataForEcrypt(buffer, source_encoding)); return signer.sign(this.getPrivatePEM(), encoding); - } + }; /** * Verifying signed data @@ -184,25 +184,33 @@ module.exports = (function() { * @returns {*} */ NodeRSA.prototype.verify = function(buffer, signature, source_encoding, signature_encoding) { - signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding) + signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding); var verifier = crypt.createVerify('RSA-SHA256'); verifier.update(this.$getDataForEcrypt(buffer, source_encoding)); return verifier.verify(this.getPublicPEM(), signature, signature_encoding); - } + }; NodeRSA.prototype.getPrivatePEM = function () { - return this.$cache.privatePEM - } + if (!this.isPrivate()) { + throw Error("It is not private key"); + } + + return this.$cache.privatePEM; + }; NodeRSA.prototype.getPublicPEM = function () { - return this.$cache.publicPEM - } + if (!this.isPublic()) { + throw Error("It is not public key"); + } + + return this.$cache.publicPEM; + }; /** * Preparing given data for encrypting/signing. Just make new/return Buffer object. * * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string. - * @param source_encoding {string} - optional. Encoding for given string. Default utf8. + * @param encoding {string} - optional. Encoding for given string. Default utf8. * @returns {Buffer} */ NodeRSA.prototype.$getDataForEcrypt = function(buffer, encoding) { @@ -213,9 +221,9 @@ module.exports = (function() { } else if (_.isObject(buffer)) { return new Buffer(JSON.stringify(buffer)); } else { - throw Error("Unexpected data type") + throw Error("Unexpected data type"); } - } + }; /** * @@ -241,9 +249,9 @@ module.exports = (function() { * Recalculating properties */ NodeRSA.prototype.$recalculateCache = function() { - this.$cache.privatePEM = this.$makePrivatePEM() - this.$cache.publicPEM = this.$makePublicPEM() - } + this.$cache.privatePEM = this.$makePrivatePEM(); + this.$cache.publicPEM = this.$makePublicPEM(); + }; /** * private diff --git a/test/tests.js b/test/tests.js index 28da964..21afbe4 100644 --- a/test/tests.js +++ b/test/tests.js @@ -11,9 +11,9 @@ describe("NodeRSA", function(){ {b: 512, e: 3}, {b: 512, e: 5}, {b: 512, e: 257}, - //{b: 512, e: 65537}, - //{b: 768}, // 'e' should be 65537 - //{b: 1024} // 'e' should be 65537 + {b: 512, e: 65537}, + {b: 768}, // 'e' should be 65537 + {b: 1024} // 'e' should be 65537 ]; var dataBundle = { @@ -104,32 +104,48 @@ describe("NodeRSA", function(){ "KY4kQIIx8JEBsAYzgyP2iy0CAwEAAQ==\n"+ "-----END PUBLIC KEY-----"; - it(".loadFromPrivatePEM() should load private key from PEM string", function(){ - privateNodeRSA = new NodeRSA(privateKeyPEM); - assert.instanceOf(privateNodeRSA.keyPair, Object); - assert(privateNodeRSA.isPrivate()); - assert(privateNodeRSA.isPublic()); - assert(!privateNodeRSA.isPublic(true)); - }); + describe("Good cases", function () { + it(".loadFromPrivatePEM() should load private key from PEM string", function(){ + privateNodeRSA = new NodeRSA(privateKeyPEM); + assert.instanceOf(privateNodeRSA.keyPair, Object); + assert(privateNodeRSA.isPrivate()); + assert(privateNodeRSA.isPublic()); + assert(!privateNodeRSA.isPublic(true)); + }); - it(".loadFromPublicPEM() should load public key from PEM string", function(){ - publicNodeRSA = new NodeRSA(publicKeyPEM); - assert.instanceOf(privateNodeRSA.keyPair, Object); - assert(publicNodeRSA.isPublic()); - assert(publicNodeRSA.isPublic(true)); - assert(!publicNodeRSA.isPrivate()); - }); + it(".loadFromPublicPEM() should load public key from PEM string", function(){ + publicNodeRSA = new NodeRSA(publicKeyPEM); + assert.instanceOf(privateNodeRSA.keyPair, Object); + assert(publicNodeRSA.isPublic()); + assert(publicNodeRSA.isPublic(true)); + assert(!publicNodeRSA.isPrivate()); + }); - it(".toPrivatePEM() should return private PEM string", function(){ - assert.equal(privateNodeRSA.getPrivatePEM(), privateKeyPEM); - }); + it(".getPrivatePEM() should return private PEM string", function(){ + assert.equal(privateNodeRSA.getPrivatePEM(), privateKeyPEM); + }); + + it(".getPublicPEM() from public key should return public PEM string", function(){ + assert.equal(publicNodeRSA.getPublicPEM(), publicKeyPEM); + }); - it(".toPublicPEM() from public key should return public PEM string", function(){ - assert.equal(publicNodeRSA.getPublicPEM(), publicKeyPEM); + it(".getPublicPEM() from private key should return public PEM string", function(){ + assert.equal(privateNodeRSA.getPublicPEM(), publicKeyPEM); + }); }); - it(".toPublicPEM() from private key should return public PEM string", function(){ - assert.equal(privateNodeRSA.getPublicPEM(), publicKeyPEM); + describe("Bad cases", function () { + it("not public key", function(){ + var key = new NodeRSA(); + assert.throw(function(){ key.getPrivatePEM(); }, Error, "It is not private key"); + assert.throw(function(){ key.getPublicPEM(); }, Error, "It is not public key"); + }); + + it("not private key", function(){ + var key = new NodeRSA(publicKeyPEM); + assert.throw(function(){ key.getPrivatePEM(); }, Error, "It is not private key"); + assert.doesNotThrow(function(){ key.getPublicPEM(); }, Error, "It is not public key"); + }); }); }); }); @@ -156,6 +172,19 @@ describe("NodeRSA", function(){ }); } }); + + describe("Bad cases", function () { + it("unsupported data types", function(){ + assert.throw(function(){ generatedKeys[0].encrypt(null); }, Error, "Unexpected data type"); + assert.throw(function(){ generatedKeys[0].encrypt(undefined); }, Error, "Unexpected data type"); + assert.throw(function(){ generatedKeys[0].encrypt(true); }, Error, "Unexpected data type"); + }); + + it("incorrect key for decrypting", function(){ + var encrypted = generatedKeys[0].encrypt('data'); + assert.notEqual('data', generatedKeys[1].decrypt(encrypted)); + }); + }); }); describe("Signing & verifying", function () { @@ -179,14 +208,14 @@ describe("NodeRSA", function(){ }); describe("Bad cases", function () { - it("incorrect information", function(){ + it("incorrect data for verifying", function(){ var signed = generatedKeys[0].sign('data1'); assert(! generatedKeys[0].verify('data2', signed)); }); it("incorrect key for signing", function(){ var key = new NodeRSA(generatedKeys[0].getPublicPEM()); - key.sign('data'); + assert.throw(function(){ key.sign('data'); }, Error, "It is not private key"); }); it("incorrect key for verifying", function(){ From 3168a934509782971e5851a63827a662ac7e9b05 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 00:47:21 +0600 Subject: [PATCH 07/12] added algorithm options for signing --- README.md | 7 +++++++ src/NodeRSA.js | 20 ++++++++++++-------- test/tests.js | 13 +++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ec06733..1c1f63b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,13 @@ npm test ## Usage ### Create instance +```js +var key = new NodeRSA([key], [options]); +``` +**key** - parameters of a generated key or the key in PEM format. +**options** - additional settings
+ * **signingAlgorithm** - algorithm used for signing and verifying. Default _'RSA-SHA256'_ + #### "Empty" key ```js var key = new NodeRSA(); diff --git a/src/NodeRSA.js b/src/NodeRSA.js index d53d17e..b883009 100644 --- a/src/NodeRSA.js +++ b/src/NodeRSA.js @@ -17,17 +17,21 @@ var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1'; module.exports = (function() { /** - * @param arg {string|object} Key in PEM format, or data for generate key {b: bits, e: exponent} + * @param key {string|object} Key in PEM format, or data for generate key {b: bits, e: exponent} * @constructor */ - function NodeRSA(arg) { + function NodeRSA(key, options) { this.keyPair = new rsa.Key(); this.$cache = {}; - if (_.isObject(arg)) { - this.generateKeyPair(arg.b, arg.e); - } else if (_.isString(arg)) { - this.loadFromPEM(arg); + this.options = _.merge({ + signingAlgorithm: 'RSA-SHA256' + }, options || {}); + + if (_.isObject(key)) { + this.generateKeyPair(key.b, key.e); + } else if (_.isString(key)) { + this.loadFromPEM(key); } } @@ -169,7 +173,7 @@ module.exports = (function() { } encoding = (!encoding || encoding == 'buffer' ? null : encoding); - var signer = crypt.createSign('RSA-SHA256'); + var signer = crypt.createSign(this.options.signingAlgorithm); signer.update(this.$getDataForEcrypt(buffer, source_encoding)); return signer.sign(this.getPrivatePEM(), encoding); }; @@ -185,7 +189,7 @@ module.exports = (function() { */ NodeRSA.prototype.verify = function(buffer, signature, source_encoding, signature_encoding) { signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding); - var verifier = crypt.createVerify('RSA-SHA256'); + var verifier = crypt.createVerify(this.options.signingAlgorithm); verifier.update(this.$getDataForEcrypt(buffer, source_encoding)); return verifier.verify(this.getPublicPEM(), signature, signature_encoding); }; diff --git a/test/tests.js b/test/tests.js index 21afbe4..dbb3027 100644 --- a/test/tests.js +++ b/test/tests.js @@ -205,6 +205,12 @@ describe("NodeRSA", function(){ assert(key.verify(suit.data, signed[i])); }); } + + it("signing with custom algorithm", function(){ + var key = new NodeRSA(generatedKeys[0].getPrivatePEM(), {signingAlgorithm: 'RSA-MD5'}); + var signed = key.sign('data'); + assert(key.verify('data', signed)); + }); }); describe("Bad cases", function () { @@ -222,6 +228,13 @@ describe("NodeRSA", function(){ var signed = generatedKeys[0].sign('data'); assert(! generatedKeys[1].verify('data', signed)); }); + + it("different algorithms", function(){ + var singKey = new NodeRSA(generatedKeys[0].getPrivatePEM(), {signingAlgorithm: 'RSA-MD5'}); + var verifyKey = new NodeRSA(generatedKeys[0].getPrivatePEM(), {signingAlgorithm: 'RSA-SHA1'}); + var signed = singKey.sign('data'); + assert(! verifyKey.verify('data', signed)); + }); }); }); }); \ No newline at end of file From 27db976a99a787b1eef6742b13740b513ef27c6e Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 00:48:25 +0600 Subject: [PATCH 08/12] updated readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1c1f63b..19b23c4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ Based on jsbn library from Tom Wu http://www-cs-students.stanford.edu/~tjw/jsbn/ * Pure JavaScript * No needed OpenSSL +* Generate keys * Supports long messages for encrypt/decrypt +* Signing/verifying ## Installing From 78116e45fd8459febce4fe39a728654074bf4f8d Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 00:49:14 +0600 Subject: [PATCH 09/12] updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 19b23c4..fe5f878 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Based on jsbn library from Tom Wu http://www-cs-students.stanford.edu/~tjw/jsbn/ * Pure JavaScript * No needed OpenSSL -* Generate keys +* Generating keys * Supports long messages for encrypt/decrypt -* Signing/verifying +* Signing and verifying ## Installing From 12f5101e81afe988d22ea804a8adb93616e9f984 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 00:49:53 +0600 Subject: [PATCH 10/12] updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe5f878..c93fe5f 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,8 @@ npm test ```js var key = new NodeRSA([key], [options]); ``` -**key** - parameters of a generated key or the key in PEM format. -**options** - additional settings
+**key** - parameters of a generated key or the key in PEM format.
+**options** - additional settings * **signingAlgorithm** - algorithm used for signing and verifying. Default _'RSA-SHA256'_ #### "Empty" key From 48d69706d2e593130e71293fd57a80aeba31415f Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 01:00:15 +0600 Subject: [PATCH 11/12] updated readme --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c93fe5f..39d70d6 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ var key = new NodeRSA([key], [options]); ``` **key** - parameters of a generated key or the key in PEM format.
**options** - additional settings - * **signingAlgorithm** - algorithm used for signing and verifying. Default _'RSA-SHA256'_ + * **signingAlgorithm** - algorithm used for signing and verifying. Default *'RSA-SHA256'* #### "Empty" key ```js @@ -82,17 +82,32 @@ key.isPublic([strict]); ```js key.encrypt(buffer, [encoding], [source_encoding]); ``` +Return encrypted data.
**buffer** - data for encrypting, may be string, Buffer, or any object/array. Arrays and objects will encoded to JSON string first.
**encoding** - encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default *buffer*. -**source_encoding** - source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). *Utf8* by default.
+**source_encoding** - source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). *'utf8'* by default.
```js key.decrypt(buffer, [encoding]); ``` +Return decrypted data.
**buffer** - data for decrypting. Takes Buffer object or base64 encoded string.
-**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. Default 'buffer'. +**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. Default *'buffer*. ### Signing/Verifying +```js +key.sign(buffer, [encoding], [source_encoding]); +``` +Return signature for data. All the arguments are the same as for `encrypt` method. + +```js +key.verify(buffer, signature, [source_encoding], [signature_encoding]) +``` +Return result of check, _true_ or _false_.
+**buffer** - data for check, same as `encrypt` method.
+**signature** - signature for check, result of `sign` method.
+**source_encoding** - same as for`encrypt` method.
+**signature_encoding** - encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default *'buffer'*. ## Contributing From 6c6b8ccc256abb5385895a1bde660c500d1a73d8 Mon Sep 17 00:00:00 2001 From: rzcoder Date: Fri, 4 Apr 2014 01:01:46 +0600 Subject: [PATCH 12/12] updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 39d70d6..c5339e3 100644 --- a/README.md +++ b/README.md @@ -92,13 +92,13 @@ key.decrypt(buffer, [encoding]); ``` Return decrypted data.
**buffer** - data for decrypting. Takes Buffer object or base64 encoded string.
-**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. Default *'buffer*. +**encoding** - encoding for result string. Can also take 'buffer' for raw Buffer object, or 'json' for automatic JSON.parse result. Default *'buffer'*. ### Signing/Verifying ```js key.sign(buffer, [encoding], [source_encoding]); ``` -Return signature for data. All the arguments are the same as for `encrypt` method. +Return signature for buffer. All the arguments are the same as for `encrypt` method. ```js key.verify(buffer, signature, [source_encoding], [signature_encoding])