diff --git a/README.md b/README.md index ec6e711..c5339e3 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 +* Generating keys * Supports long messages for encrypt/decrypt +* Signing and verifying ## Installing @@ -23,6 +25,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(); @@ -58,8 +67,8 @@ key.loadFromPEM(pem_string); ### Export keys ```js -key.toPrivatePEM(); -key.toPublicPEM(); +key.getPrivatePEM(); +key.getPublicPEM(); ``` ### Test key @@ -71,18 +80,34 @@ key.isPublic([strict]); ### Encrypting/decrypting ```js -key.encrypt(buffer, [source_encoding], [output_encoding]); +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.
-**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*. +**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.
```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. +**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 buffer. 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 diff --git a/package.json b/package.json index b9ec3bf..f12a53e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-rsa", - "version": "0.1.32", + "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 7ee0943..b883009 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'); @@ -16,16 +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); } } @@ -41,6 +47,7 @@ module.exports = (function() { exp = 65537; this.keyPair.generate(bits, exp.toString(16)); + this.$recalculateCache(); return this; }; @@ -55,6 +62,8 @@ module.exports = (function() { this.loadFromPublicPEM(pem, 'base64'); } else throw Error('Invalid PEM format'); + + this.$recalculateCache(); }; /** @@ -106,9 +115,157 @@ module.exports = (function() { }; /** + * Check if keypair contains private key + */ + NodeRSA.prototype.isPrivate = function() { + return this.keyPair.n && this.keyPair.e && this.keyPair.d || false; + }; + + /** + * Check if keypair contains public key + * @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) || false; + }; + + /** + * 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. + * @returns {string|Buffer} + */ + NodeRSA.prototype.encrypt = function(buffer, encoding, source_encoding) { + var res = this.keyPair.encrypt(this.$getDataForEcrypt(buffer, source_encoding)); + + if (encoding == 'buffer' || !encoding) { + return res; + } else { + return res.toString(encoding); + } + }; + + /** + * Decrypting data method + * + * @param buffer {Buffer} - buffer for decrypting + * @param encoding - encoding for result string, can also take 'json' or 'buffer' for the automatic conversion of this type + * @returns {Buffer|object|string} + */ + NodeRSA.prototype.decrypt = function(buffer, encoding) { + buffer = _.isString(buffer) ? new Buffer(buffer, 'base64') : buffer; + return this.$getDecryptedData(this.keyPair.decrypt(buffer), encoding); + }; + + /** + * Signing data + * + * @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, source_encoding) { + if (!this.isPrivate()) { + throw Error("It is not private key"); + } + + encoding = (!encoding || encoding == 'buffer' ? null : encoding); + var signer = crypt.createSign(this.options.signingAlgorithm); + signer.update(this.$getDataForEcrypt(buffer, source_encoding)); + return signer.sign(this.getPrivatePEM(), encoding); + }; + + /** + * Verifying signed data + * + * @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, source_encoding, signature_encoding) { + signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding); + var verifier = crypt.createVerify(this.options.signingAlgorithm); + verifier.update(this.$getDataForEcrypt(buffer, source_encoding)); + return verifier.verify(this.getPublicPEM(), signature, signature_encoding); + }; + + NodeRSA.prototype.getPrivatePEM = function () { + if (!this.isPrivate()) { + throw Error("It is not private key"); + } + + return this.$cache.privatePEM; + }; + + NodeRSA.prototype.getPublicPEM = function () { + 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 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 + */ + NodeRSA.prototype.$recalculateCache = function() { + this.$cache.privatePEM = this.$makePrivatePEM(); + this.$cache.publicPEM = this.$makePublicPEM(); + }; + + /** + * private * @returns {string} private PEM string */ - NodeRSA.prototype.toPrivatePEM = function() { + 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(); @@ -138,9 +295,14 @@ module.exports = (function() { }; /** + * private * @returns {string} public PEM string */ - NodeRSA.prototype.toPublicPEM = function() { + NodeRSA.prototype.$makePublicPEM = function() { + if (!this.isPublic()) { + return null; + } + var n = this.keyPair.n.toBuffer(); var length = n.length + 512; // magic @@ -161,75 +323,10 @@ module.exports = (function() { 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 - */ - NodeRSA.prototype.isPrivate = function() { - return this.keyPair.n && this.keyPair.e && this.keyPair.d; - }; - - /** - * Check if keypair contains public key - * @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); - }; - - /** - * Encrypting data method - * - * @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 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; - - 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') { - return res; - } else { - return res.toString(output_encoding || 'base64'); - } - }; - - /** - * Decrypting data method - * - * @param buffer {Buffer} - buffer for decrypting - * @param encoding - encoding for result string, can also take 'json' or 'buffer' for the automatic conversion of this type - * @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 NodeRSA; + return NodeRSA; })(); \ No newline at end of file diff --git a/test/tests.js b/test/tests.js index 34a151c..dbb3027 100644 --- a/test/tests.js +++ b/test/tests.js @@ -2,165 +2,239 @@ * TODO: tests for compatibility with other rsa libraries */ -var assert = require('chai').assert; -var _ = require('lodash'); -var NodeRSA = (require('../src/NodeRSA')); - -describe('NodeRSA', function(){ - var nodeRSA = null; +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 + ]; + + 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; - describe('Work with keys', function(){ - it('.generateKeyPair() should make key pair', function(){ - nodeRSA = new NodeRSA({b: 1024}); - assert.instanceOf(nodeRSA.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(){ - 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()); - }); + describe("Work with keys", function(){ - it('.toPrivatePEM() should return private PEM string', function(){ - assert.equal(privateNodeRSA.toPrivatePEM(), privateKeyPEM); - }); + 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); + }); + } + }); - it('.toPublicPEM() from public key should return public PEM string', function(){ - assert.equal(publicNodeRSA.toPublicPEM(), publicKeyPEM); + 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-----"; + + 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(".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(".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.toPublicPEM(), 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"); + }); }); }); }); - - 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, null, 'buffer'); - assert(Buffer.isBuffer(encryptedBuffer)); + describe("Encrypting & decrypting", function(){ + 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); + }); + + 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('.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}==)$/); - }); - - it('.encrypt() should return encrypted Buffer for long message', function(){ - encryptedLong = nodeRSA.encrypt(longDataForEncrypt, null, 'buffer'); - assert(Buffer.isBuffer(encryptedLong)); - }); + 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('.encrypt() for js object. Should return Buffer object', function(){ - encryptedJSON = nodeRSA.encrypt(JSONForEncrypt, null, 'buffer'); - assert(Buffer.isBuffer(encryptedJSON)); + it("incorrect key for decrypting", function(){ + var encrypted = generatedKeys[0].encrypt('data'); + assert.notEqual('data', generatedKeys[1].decrypt(encrypted)); + }); }); }); - describe('Decrypting', function(){ - it('.decrypt() should return decrypted Buffer', function(){ - decrypted = nodeRSA.decrypt(encryptedBuffer, 'buffer'); - assert(Buffer.isBuffer(decrypted)); - }); - - it('.decrypt() should return decrypted string', function(){ - decrypted = nodeRSA.decrypt(new Buffer(encrypted, 'base64')); - assert.isString(decrypted); - }); - - it('.decrypt() should return decrypted string for long message', function(){ - decryptedLong = nodeRSA.decrypt(encryptedLong); - assert.isString(decryptedLong); + 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 verify "+i, 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)); + }); }); - it('.decrypt() for js object. Should return decrypted js object', function(){ - decryptedJSON = nodeRSA.decrypt(encryptedJSON, 'json'); - assert.isObject(decryptedJSON); - }); + describe("Bad cases", function () { + it("incorrect data for verifying", function(){ + var signed = generatedKeys[0].sign('data1'); + assert(! generatedKeys[0].verify('data2', signed)); + }); - it('source and decrypted should be the same', function(){ - assert.equal(decrypted, dataForEncrypt); - }); + it("incorrect key for signing", function(){ + var key = new NodeRSA(generatedKeys[0].getPublicPEM()); + assert.throw(function(){ key.sign('data'); }, Error, "It is not private key"); + }); - it('long source and decrypted should be the same', function(){ - assert.equal(decryptedLong, longDataForEncrypt); - }); + it("incorrect key for verifying", function(){ + var signed = generatedKeys[0].sign('data'); + assert(! generatedKeys[1].verify('data', signed)); + }); - it('source JSON and decrypted JSON should be the same', function(){ - assert(_.isEqual(decryptedJSON, JSONForEncrypt)); + 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