Skip to content

Commit 458900c

Browse files
committed
privateEncrypt padding fix
1 parent a838ee0 commit 458900c

File tree

7 files changed

+251
-185
lines changed

7 files changed

+251
-185
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,12 @@ Questions, comments, bug reports, and pull requests are all welcome.
210210
## Changelog
211211

212212
### 0.2.22
213-
* Native support for `encryptPrivate` and `decryptPublic` in io.js caused error in linux and was removed.
213+
* `encryptPrivate` and `decryptPublic` now using only pkcs1 (type 1) padding.
214214

215215
### 0.2.20
216216
* Added `.encryptPrivate()` and `.decryptPublic()` methods.
217217
* Encrypt/decrypt methods in nodejs 0.12.x and io.js using native implementation (> 40x speed boost).
218218
* Fixed some regex issue causing catastrophic backtracking.
219-
* *KNOWN ISSUE*: `encryptPrivate` and `decryptPublic` don't have native implementation in nodejs and can't be use in native implementation with pkcs1_oaep padding in io.js.
220219

221220
### 0.2.10
222221
* **Methods `.exportPrivate()` and `.exportPublic()` was replaced by `.exportKey([format])`.**

src/encryptEngines/encryptEngines.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@ module.exports = {
55
var engine = require('./js.js');
66
if (options.environment === 'node') {
77
if (typeof crypt.publicEncrypt === 'function' && typeof crypt.privateDecrypt === 'function') {
8-
engine = require('./node12.js');
9-
10-
/*
11-
12-
io.js privateEncrypt/publicDecrypt with different environments causing error in linux
13-
148
if (typeof crypt.privateEncrypt === 'function' && typeof crypt.publicDecrypt === 'function') {
159
engine = require('./io.js');
1610
} else {
1711
engine = require('./node12.js');
18-
}*/
12+
}
1913
}
2014
}
2115
return engine(keyPair, options);

src/encryptEngines/io.js

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,21 @@ var crypto = require('crypto');
22
var constants = require('constants');
33

44
module.exports = function (keyPair, options) {
5-
var jsEngine = require('./js.js')(keyPair);
5+
var jsEngine = require('./js.js')(keyPair, options);
66

77
return {
88
encrypt: function (buffer, usePrivate) {
9-
var padding = constants.RSA_PKCS1_OAEP_PADDING;
10-
if (options.encryptionScheme === 'pkcs1') {
11-
padding = constants.RSA_PKCS1_PADDING;
12-
}
139
if (usePrivate) {
14-
// openssl don't support oaep padding for private encrypt
15-
if (padding === constants.RSA_PKCS1_OAEP_PADDING) {
16-
return jsEngine.encrypt(buffer, usePrivate);
17-
}
1810
return crypto.privateEncrypt({
1911
key: options.rsaUtils.exportKey('private'),
20-
padding: padding
12+
padding: constants.RSA_PKCS1_PADDING
2113
}, buffer);
2214
} else {
15+
var padding = constants.RSA_PKCS1_OAEP_PADDING;
16+
if (options.encryptionScheme === 'pkcs1') {
17+
padding = constants.RSA_PKCS1_PADDING;
18+
}
19+
2320
return crypto.publicEncrypt({
2421
key: options.rsaUtils.exportKey('public'),
2522
padding: padding
@@ -28,20 +25,17 @@ module.exports = function (keyPair, options) {
2825
},
2926

3027
decrypt: function (buffer, usePublic) {
31-
var padding = constants.RSA_PKCS1_OAEP_PADDING;
32-
if (options.encryptionScheme === 'pkcs1') {
33-
padding = constants.RSA_PKCS1_PADDING;
34-
}
3528
if (usePublic) {
36-
// openssl don't support oaep padding for public decrypt
37-
if (padding === constants.RSA_PKCS1_OAEP_PADDING) {
38-
return jsEngine.decrypt(buffer, usePublic);
39-
}
4029
return crypto.publicDecrypt({
4130
key: options.rsaUtils.exportKey('public'),
42-
padding: padding
31+
padding: constants.RSA_PKCS1_PADDING
4332
}, buffer);
4433
} else {
34+
var padding = constants.RSA_PKCS1_OAEP_PADDING;
35+
if (options.encryptionScheme === 'pkcs1') {
36+
padding = constants.RSA_PKCS1_PADDING;
37+
}
38+
4539
return crypto.privateDecrypt({
4640
key: options.rsaUtils.exportKey('private'),
4741
padding: padding

src/encryptEngines/js.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
var BigInteger = require('../libs/jsbn.js');
2+
var schemes = require('../schemes/schemes.js');
23

34
module.exports = function (keyPair, options) {
5+
var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options);
6+
47
return {
58
encrypt: function (buffer, usePrivate) {
6-
var m = new BigInteger(keyPair.encryptionScheme.encPad(buffer));
7-
var c = usePrivate ? keyPair.$doPrivate(m) : keyPair.$doPublic(m);
9+
if (usePrivate) {
10+
var m = new BigInteger(pkcs1Scheme.encPad(buffer, {type: 1}));
11+
var c = keyPair.$doPrivate(m);
12+
} else {
13+
var m = new BigInteger(keyPair.encryptionScheme.encPad(buffer));
14+
var c = keyPair.$doPublic(m);
15+
}
816
return c.toBuffer(keyPair.encryptedDataLength);
917
},
1018

1119
decrypt: function (buffer, usePublic) {
1220
var c = new BigInteger(buffer);
13-
var m = usePublic ? keyPair.$doPublic(c) : keyPair.$doPrivate(c);
14-
return keyPair.encryptionScheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength));
21+
22+
if (usePublic) {
23+
var m = keyPair.$doPublic(c);
24+
return pkcs1Scheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength), {type: 1});
25+
} else {
26+
var m = keyPair.$doPrivate(c);
27+
return keyPair.encryptionScheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength));
28+
}
1529
}
1630
};
1731
};

src/encryptEngines/node12.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ var crypto = require('crypto');
22
var constants = require('constants');
33

44
module.exports = function (keyPair, options) {
5-
var jsEngine = require('./js.js')(keyPair);
5+
var jsEngine = require('./js.js')(keyPair, options);
66

77
return {
88
encrypt: function (buffer, usePrivate) {

src/schemes/pkcs1.js

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,32 +39,55 @@ module.exports.makeScheme = function (key, options) {
3939

4040
/**
4141
* Pad input Buffer to encryptedDataLength bytes, and return new Buffer
42-
* alg: PKCS#1 (type 2, random)
42+
* alg: PKCS#1
4343
* @param buffer
4444
* @returns {Buffer}
4545
*/
46-
Scheme.prototype.encPad = function (buffer) {
46+
Scheme.prototype.encPad = function (buffer, options) {
47+
options = options || {};
4748
if (buffer.length > this.key.maxMessageLength) {
4849
throw new Error("Message too long for RSA (n=" + this.key.encryptedDataLength + ", l=" + buffer.length + ")");
4950
}
5051

51-
// TODO: make n-length buffer
52-
var ba = Array.prototype.slice.call(buffer, 0);
52+
if (options.type === 1) {
53+
var filled = new Buffer(this.key.encryptedDataLength - buffer.length - 1);
54+
filled.fill(0xff, 0, filled.length - 1);
55+
filled[0] = 1;
56+
filled[filled.length - 1] = 0;
5357

54-
// random padding
55-
ba.unshift(0);
56-
var rand = crypt.randomBytes(this.key.encryptedDataLength - ba.length - 2);
57-
for (var i = 0; i < rand.length; i++) {
58-
var r = rand[i];
59-
while (r === 0) { // non-zero only
60-
r = crypt.randomBytes(1)[0];
58+
return Buffer.concat([filled, buffer]);
59+
} else {
60+
/*// random padding
61+
// TODO: make n-length buffer
62+
var ba = Array.prototype.slice.call(buffer, 0);
63+
ba.unshift(0);
64+
var rand = crypt.randomBytes(this.key.encryptedDataLength - ba.length - 2);
65+
for (var i = 0; i < rand.length; i++) {
66+
var r = rand[i];
67+
while (r === 0) { // non-zero only
68+
r = crypt.randomBytes(1)[0];
69+
}
70+
ba.unshift(r);
71+
}
72+
ba.unshift(2);
73+
ba.unshift(0);
74+
// return ba;
75+
*/
76+
77+
var filled = new Buffer(this.key.encryptedDataLength - buffer.length);
78+
filled[0] = 0;
79+
filled[1] = 2;
80+
var rand = crypt.randomBytes(filled.length - 3);
81+
for (var i = 0; i < rand.length; i++) {
82+
var r = rand[i];
83+
while (r === 0) { // non-zero only
84+
r = crypt.randomBytes(1)[0];
85+
}
86+
filled[i + 2] = r;
6187
}
62-
ba.unshift(r);
88+
filled[filled.length - 1] = 0;
89+
return Buffer.concat([filled, buffer]);
6390
}
64-
ba.unshift(2);
65-
ba.unshift(0);
66-
67-
return ba;
6891
};
6992

7093
/**
@@ -73,10 +96,38 @@ module.exports.makeScheme = function (key, options) {
7396
* @param buffer
7497
* @returns {Buffer}
7598
*/
76-
Scheme.prototype.encUnPad = function (buffer) {
77-
//var buffer = buffer.toByteArray();
99+
Scheme.prototype.encUnPad = function (buffer, options) {
100+
options = options || {};
78101
var i = 0;
79102

103+
if (buffer.length < 4) {
104+
return null;
105+
}
106+
107+
if (options.type === 1) {
108+
if (buffer[0] !== 0 && buffer[1] !== 1) {
109+
return null;
110+
}
111+
i = 3;
112+
while (buffer[i] !== 0) {
113+
if (buffer[i] != 0xFF || ++i >= buffer.length) {
114+
return null;
115+
}
116+
}
117+
} else {
118+
if (buffer[0] !== 0 && buffer[1] !== 2) {
119+
return null;
120+
}
121+
i = 3;
122+
while (buffer[i] !== 0) {
123+
if (++i >= buffer.length) {
124+
return null;
125+
}
126+
}
127+
}
128+
return buffer.slice(i + 1, buffer.length);
129+
/*var i = 0;
130+
80131
while (i < buffer.length && buffer[i] === 0) {
81132
++i;
82133
}
@@ -96,7 +147,7 @@ module.exports.makeScheme = function (key, options) {
96147
var res = new Buffer(buffer.length - i - 1);
97148
while (++i < buffer.length) {
98149
res[c++] = buffer[i] & 255;
99-
}
150+
}*/
100151

101152
return res;
102153
};

0 commit comments

Comments
 (0)