Skip to content

Commit e248773

Browse files
committed
encrypt/decrypt methods now using native implementation if possible
1 parent 55a095a commit e248773

File tree

9 files changed

+166
-42
lines changed

9 files changed

+166
-42
lines changed

README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ console.log('decrypted: ', decrypted);
2929
```shell
3030
npm install node-rsa
3131
```
32-
*Requires nodejs >= 0.10.x*
32+
*Requires nodejs >= 0.10.x or io.js >= 1.x*
3333

3434
### Testing
3535

@@ -58,10 +58,8 @@ var key = new NodeRSA([keyData, [format]], [options]);
5858
You can specify some options by second/third constructor argument, or over `key.setOptions()` method.
5959

6060
* environment — working environment (default autodetect):
61-
* `'browser'`,
62-
* `'node10'` for `nodejs 0.10.x` — provide native sign/verify methods.
63-
* `'node'` for `nodejs 0.12.x` — provide also native publicEncrypt/privateDecrypt methods.
64-
* `'iojs'` for `io.js 1.x` — provide also native publicDecrypt/privateEncrypt methods.
61+
* `'browser'` — will run pure js implementation of RSA algorithms.
62+
* `'node'` for `nodejs >= 0.10.x or io.js >= 1.x` — provide some native methods like sign/verify and encrypt/decrypt.
6563
* encryptionScheme — padding scheme for encrypt/decrypt. Can be `'pkcs1_oaep'` or `'pkcs1'`. Default `'pkcs1_oaep'`.
6664
* signingScheme — scheme used for signing and verifying. Can be `'pkcs1'` or `'pss'` or 'scheme-hash' format string (eg `'pss-sha1'`). Default `'pkcs1-sha256'`, or, if chosen pss: `'pss-sha1'`.
6765

@@ -187,6 +185,7 @@ Return max data size for encrypt in bytes.
187185

188186
```javascript
189187
key.encrypt(buffer, [encoding], [source_encoding]);
188+
key.encryptPrivate(buffer, [encoding], [source_encoding]); // using private key for encryption
190189
```
191190
Return encrypted data.<br/>
192191

@@ -196,6 +195,7 @@ Return encrypted data.<br/>
196195

197196
```javascript
198197
key.decrypt(buffer, [encoding]);
198+
key.decryptPublic(buffer, [encoding]); // using public key for decryption
199199
```
200200
Return decrypted data.<br/>
201201

@@ -224,6 +224,13 @@ Questions, comments, bug reports, and pull requests are all welcome.
224224

225225
## Changelog
226226

227+
### 0.2.20
228+
* Added `.encryptPrivate()` and `.decryptPublic()` methods
229+
* Encrypt/decrypt methods in nodejs 0.12.x and io.js using native implementation (40x speed boost)
230+
* **KNOWN ISSUES**:
231+
* `encryptPrivate` and `decryptPublic` don't have native implementation in nodejs
232+
* `encryptPrivate` and `decryptPublic` with pkcs1_oaep padding scheme don't work in io.js and using js implementation
233+
227234
### 0.2.10
228235
* **Methods `.exportPrivate()` and `.exportPublic()` was replaced by `.exportKey([format])`.**
229236
* By default `.exportKey()` returns private key as `.exportPrivate()`, if you need public key from `.exportPublic()` you must specify format as `'public'` or `'pkcs8-public-pem'`.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-rsa",
3-
"version": "0.2.13",
3+
"version": "0.2.20",
44
"description": "Node.js RSA library",
55
"main": "src/NodeRSA.js",
66
"scripts": {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
var crypt = require('crypto');
2+
3+
module.exports = {
4+
getEngine: function (keyPair, options) {
5+
var engine;
6+
if (options.environment === 'browser') {
7+
engine = require('./js.js');
8+
} else {
9+
if (typeof crypt.publicEncrypt === 'function' && typeof crypt.privateDecrypt === 'function') {
10+
if (typeof crypt.privateEncrypt === 'function' && typeof crypt.publicDecrypt === 'function') {
11+
engine = require('./io.js');
12+
} else {
13+
engine = require('./node12.js');
14+
}
15+
}
16+
}
17+
return engine(keyPair, options);
18+
}
19+
};

src/encryptEngines/io.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
var crypto = require('crypto');
2+
var constants = require('constants');
3+
4+
module.exports = function (keyPair, options) {
5+
var jsEngine = require('./js.js')(keyPair);
6+
7+
return {
8+
encrypt: function (buffer, usePrivate) {
9+
var padding = constants.RSA_PKCS1_OAEP_PADDING;
10+
if (options.encryptionScheme === 'pkcs1') {
11+
padding = constants.RSA_PKCS1_PADDING;
12+
}
13+
14+
/* io.js 1.3.x bug */
15+
if (padding === constants.RSA_PKCS1_OAEP_PADDING) {
16+
return jsEngine.encrypt(buffer, usePrivate);
17+
}
18+
19+
if (usePrivate) {
20+
return crypto.privateEncrypt({
21+
key: options.rsaUtils.exportKey('private'),
22+
padding: padding
23+
}, buffer);
24+
} else {
25+
return crypto.publicEncrypt({
26+
key: options.rsaUtils.exportKey('public'),
27+
padding: padding
28+
}, buffer);
29+
}
30+
},
31+
32+
decrypt: function (buffer, usePublic) {
33+
var padding = constants.RSA_PKCS1_OAEP_PADDING;
34+
if (options.encryptionScheme === 'pkcs1') {
35+
padding = constants.RSA_PKCS1_PADDING;
36+
}
37+
38+
/* io.js 1.3.x bug */
39+
if (padding === constants.RSA_PKCS1_OAEP_PADDING) {
40+
return jsEngine.decrypt(buffer, usePrivate);
41+
}
42+
43+
if (usePublic) {
44+
return crypto.publicDecrypt({
45+
key: options.rsaUtils.exportKey('public'),
46+
padding: padding
47+
}, buffer);
48+
} else {
49+
return crypto.privateDecrypt({
50+
key: options.rsaUtils.exportKey('private'),
51+
padding: padding
52+
}, buffer);
53+
}
54+
}
55+
};
56+
};

src/encryptEngines/js.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
var BigInteger = require('../libs/jsbn.js');
2+
3+
module.exports = function (keyPair, options) {
4+
return {
5+
encrypt: function (buffer, usePrivate) {
6+
var m = new BigInteger(keyPair.encryptionScheme.encPad(buffer));
7+
var c = usePrivate ? keyPair.$doPrivate(m) : keyPair.$doPublic(m);
8+
return c.toBuffer(keyPair.encryptedDataLength);
9+
},
10+
11+
decrypt: function (buffer, usePublic) {
12+
var c = new BigInteger(buffer);
13+
var m = usePublic ? keyPair.$doPublic(c) : keyPair.$doPrivate(c);
14+
return keyPair.encryptionScheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength));
15+
}
16+
};
17+
};

src/encryptEngines/node12.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
var crypto = require('crypto');
2+
var constants = require('constants');
3+
4+
module.exports = function (keyPair, options) {
5+
var jsEngine = require('./js.js')(keyPair);
6+
7+
return {
8+
encrypt: function (buffer, usePrivate) {
9+
if (usePrivate) {
10+
return jsEngine.encrypt(buffer, usePrivate);
11+
}
12+
var padding = constants.RSA_PKCS1_OAEP_PADDING;
13+
if (options.encryptionScheme === 'pkcs1') {
14+
padding = constants.RSA_PKCS1_PADDING;
15+
}
16+
17+
return crypto.publicEncrypt({
18+
key: options.rsaUtils.exportKey('public'),
19+
padding: padding
20+
}, buffer);
21+
},
22+
23+
decrypt: function (buffer, usePublic) {
24+
if (usePublic) {
25+
return jsEngine.decrypt(buffer, usePublic);
26+
}
27+
var padding = constants.RSA_PKCS1_OAEP_PADDING;
28+
if (options.encryptionScheme === 'pkcs1') {
29+
padding = constants.RSA_PKCS1_PADDING;
30+
}
31+
32+
return crypto.privateDecrypt({
33+
key: options.rsaUtils.exportKey('private'),
34+
padding: padding
35+
}, buffer);
36+
}
37+
};
38+
};

src/libs/rsa.js

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ var crypt = require('crypto');
4444
var BigInteger = require('./jsbn.js');
4545
var utils = require('../utils.js');
4646
var schemes = require('../schemes/schemes.js');
47+
var encryptEngines = require('../encryptEngines/encryptEngines.js');
4748

4849
exports.BigInteger = BigInteger;
49-
module.exports.Key = (function() {
50+
module.exports.Key = (function () {
5051
/**
5152
* RSA key constructor
5253
*
@@ -80,6 +81,8 @@ module.exports.Key = (function() {
8081
this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options);
8182
this.signingScheme = signingSchemeProvider.makeScheme(this, options);
8283
}
84+
85+
this.encryptEngine = encryptEngines.getEngine(this, options);
8386
};
8487

8588
/**
@@ -91,13 +94,13 @@ module.exports.Key = (function() {
9194
var qs = B >> 1;
9295
this.e = parseInt(E, 16);
9396
var ee = new BigInteger(E, 16);
94-
for (; ;) {
95-
for (; ;) {
97+
while (true) {
98+
while (true) {
9699
this.p = new BigInteger(B - qs, 1);
97100
if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) === 0 && this.p.isProbablePrime(10))
98101
break;
99102
}
100-
for (; ;) {
103+
while (true) {
101104
this.q = new BigInteger(qs, 1);
102105
if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) === 0 && this.q.isProbablePrime(10))
103106
break;
@@ -213,26 +216,16 @@ module.exports.Key = (function() {
213216
var buffersCount = Math.ceil(bufferSize / this.maxMessageLength) || 1; // total buffers count for encrypt
214217
var dividedSize = Math.ceil(bufferSize / buffersCount || 1); // each buffer size
215218

216-
if ( buffersCount == 1) {
219+
if (buffersCount == 1) {
217220
buffers.push(buffer);
218221
} else {
219222
for (var bufNum = 0; bufNum < buffersCount; bufNum++) {
220-
buffers.push(buffer.slice(bufNum * dividedSize, (bufNum+1) * dividedSize));
223+
buffers.push(buffer.slice(bufNum * dividedSize, (bufNum + 1) * dividedSize));
221224
}
222225
}
223226

224-
for(var i = 0; i < buffers.length; i++) {
225-
var buf = buffers[i];
226-
227-
var m = new BigInteger(this.encryptionScheme.encPad(buf));
228-
var c = usePrivate ? this.$doPrivate(m) : this.$doPublic(m);
229-
230-
if (c === null) {
231-
return null;
232-
}
233-
234-
var encryptedBuffer = c.toBuffer(this.encryptedDataLength);
235-
results.push(encryptedBuffer);
227+
for (var i = 0; i < buffers.length; i++) {
228+
results.push(this.encryptEngine.encrypt(buffers[i], usePrivate));
236229
}
237230

238231
return Buffer.concat(results);
@@ -256,11 +249,7 @@ module.exports.Key = (function() {
256249
for (var i = 0; i < buffersCount; i++) {
257250
offset = i * this.encryptedDataLength;
258251
length = offset + this.encryptedDataLength;
259-
260-
var c = new BigInteger(buffer.slice(offset, Math.min(length, buffer.length)));
261-
var m = usePublic ? this.$doPublic(c) : this.$doPrivate(c);
262-
263-
result.push(this.encryptionScheme.encUnPad(m.toBuffer(this.encryptedDataLength)));
252+
result.push(this.encryptEngine.decrypt(buffer.slice(offset, Math.min(length, buffer.length)), usePublic));
264253
}
265254

266255
return Buffer.concat(result);
@@ -290,15 +279,21 @@ module.exports.Key = (function() {
290279
};
291280

292281
Object.defineProperty(RSAKey.prototype, 'keySize', {
293-
get: function() { return this.cache.keyBitLength; }
282+
get: function () {
283+
return this.cache.keyBitLength;
284+
}
294285
});
295286

296287
Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', {
297-
get: function() { return this.cache.keyByteLength; }
288+
get: function () {
289+
return this.cache.keyByteLength;
290+
}
298291
});
299292

300293
Object.defineProperty(RSAKey.prototype, 'maxMessageLength', {
301-
get: function() { return this.encryptionScheme.maxMessageLength(); }
294+
get: function () {
295+
return this.encryptionScheme.maxMessageLength();
296+
}
302297
});
303298

304299
/**

src/utils.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,7 @@ module.exports.linebrk = function (str, maxLen) {
2323

2424
module.exports.detectEnvironment = function () {
2525
if (process && process.title != 'browser') {
26-
if (typeof crypt.publicEncrypt === 'function' && typeof crypt.privateDecrypt === 'function') {
27-
if (typeof crypt.publicDecrypt === 'function' && typeof crypt.privateEncrypt === 'function') {
28-
return 'iojs';
29-
}
30-
return 'node';
31-
}
32-
return 'node10';
26+
return 'node';
3327
} else if (window) {
3428
return 'browser';
3529
}

test/tests.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@ describe("NodeRSA", function(){
1717
{b: 1024} // 'e' should be 65537
1818
];
1919

20-
var environments = ['browser', 'node10', 'node', 'iojs'];
20+
var environments = ['browser', 'node'];
2121
var encryptSchemes = ['pkcs1', 'pkcs1_oaep'];
2222
var signingSchemes = ['pkcs1', 'pss'];
2323
var signHashAlgorithms = {
2424
'node': ['MD4', 'MD5', 'RIPEMD160', 'SHA', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512'],
25-
'node10': ['MD4', 'MD5', 'RIPEMD160', 'SHA', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512'],
26-
'iojs': ['MD4', 'MD5', 'RIPEMD160', 'SHA', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512'],
2725
'browser': ['MD5', 'RIPEMD160', 'SHA1', 'SHA256', 'SHA512']
2826
};
2927

0 commit comments

Comments
 (0)