Add the option to use old-style cipher padding on public-encryption / private-decryption. #2

Merged
merged 2 commits into from Aug 23, 2012
Jump to file or symbol
Failed to load files and symbols.
+109 −52
Diff settings

Always

Just for now

View
@@ -56,11 +56,13 @@ The library knows how to read and output PEM format files for both
public and private keys, and it can generate new private keys (aka
keypairs).
-The usual public-encryption / private-decryption operations are always
-done using padding mode `RSA_PKCS1_OAEP_PADDING`, which is the recommended
+The usual public-encryption / private-decryption operations by default
+use padding mode `RSA_PKCS1_OAEP_PADDING`, which is the recommended
mode for all new applications (as of this writing). Note that this mode
builds-in a random element into every encryption operation, making it
unnecessary to waste time or effort adding randomness in at a higher layer.
+This default may be overridden to use the older mode `RSA_PKCS1_PADDING`
+if needed.
The less well-understood private-encryption / public-decryption operations
(used for building signature mechanisms) are always done using padding
@@ -220,7 +222,7 @@ These are all the methods available on public keys. These methods are
*also* available on private keys (since private keys have all the
underlying data necessary to perform the public-side operations).
-### encrypt(buf, bufEncoding, outEncoding)
+### encrypt(buf, bufEncoding, outEncoding, padding)
This performs the "public encrypt" operation on the given buffer. The
result is always a byte sequence that is the same size as the key
@@ -230,8 +232,9 @@ then the result of this operation will be 2048 bits, aka 256 bytes.)
The input buffer is limited to be no larger than the key size
minus 41 bytes.
-This operation is always performed using padding mode
-`RSA_PKCS1_OAEP_PADDING`.
+If no padding mode is specified, the default, and recommended, mode
+is `ursa.RSA_PKCS1_OAEP_PADDING`. The mode
+`ursa.RSA_PKCS1_PADDING` is also supported.
### getExponent(encoding)
@@ -321,16 +324,17 @@ Private Key Methods
These are the methods available on private keys, above and beyond
what is available for public keys.
-### decrypt(buf, bufEncoding, outEncoding)
+### decrypt(buf, bufEncoding, outEncoding, padding)
This performs the "private decrypt" operation on the given buffer. The
result is always a byte sequence that is no more than the size of the
key associated with the instance. (For example, if the key is 2048
bits, then the result of this operation will be no more than 2048
bits, aka 256 bytes.)
-This operation is always performed using padding mode
-`RSA_PKCS1_OAEP_PADDING`.
+If no padding mode is specified, the default, and recommended, mode
+is `ursa.RSA_PKCS1_OAEP_PADDING`. The mode
+`ursa.RSA_PKCS1_PADDING` is also supported.
### hashAndSign(algorithm, buf, bufEncoding, outEncoding)
@@ -411,6 +415,16 @@ structurally) yet doesn't match. This throws an exception in all
other cases.
+Constants
+---------
+
+Allowed padding modes for public encryption and
+private decryption:
+
+* `ursa.RSA_PKCS1_PADDING`
+* `ursa.RSA_PKCS1_OAEP_PADDING`
+
+
Contributing
------------
View
@@ -229,9 +229,10 @@ function PublicKey(rsa) {
return sshFingerprint(createSshPublicKey(rsa), undefined, encoding);
}
- function encrypt(buf, bufEncoding, outEncoding) {
+ function encrypt(buf, bufEncoding, outEncoding, padding) {
buf = decodeString(buf, bufEncoding);
- return encodeBuffer(rsa.publicEncrypt(buf), outEncoding);
+ padding = padding || ursaNative.RSA_PKCS1_OAEP_PADDING;
+ return encodeBuffer(rsa.publicEncrypt(buf, padding), outEncoding);
}
function publicDecrypt(buf, bufEncoding, outEncoding) {
@@ -284,9 +285,10 @@ function PrivateKey(rsa) {
return encodeBuffer(rsa.getPrivateKeyPem(), encoding);
}
- function decrypt(buf, bufEncoding, outEncoding) {
+ function decrypt(buf, bufEncoding, outEncoding, padding) {
buf = decodeString(buf, bufEncoding);
- return encodeBuffer(rsa.privateDecrypt(buf), outEncoding);
+ padding = padding || ursaNative.RSA_PKCS1_OAEP_PADDING;
+ return encodeBuffer(rsa.privateDecrypt(buf, padding), outEncoding);
}
function privateEncrypt(buf, bufEncoding, outEncoding) {
@@ -612,22 +614,24 @@ function createVerifier(algorithm) {
*/
module.exports = {
- assertKey: assertKey,
- assertPrivateKey: assertPrivateKey,
- assertPublicKey: assertPublicKey,
- coerceKey: coerceKey,
- coercePrivateKey: coercePrivateKey,
- coercePublicKey: coercePublicKey,
- createKey: createKey,
- createPrivateKey: createPrivateKey,
- createPublicKey: createPublicKey,
- createSigner: createSigner,
- createVerifier: createVerifier,
- equalKeys: equalKeys,
- generatePrivateKey: generatePrivateKey,
- isKey: isKey,
- isPrivateKey: isPrivateKey,
- isPublicKey: isPublicKey,
- matchingPublicKeys: matchingPublicKeys,
- sshFingerprint: sshFingerprint
+ assertKey: assertKey,
+ assertPrivateKey: assertPrivateKey,
+ assertPublicKey: assertPublicKey,
+ coerceKey: coerceKey,
+ coercePrivateKey: coercePrivateKey,
+ coercePublicKey: coercePublicKey,
+ createKey: createKey,
+ createPrivateKey: createPrivateKey,
+ createPublicKey: createPublicKey,
+ createSigner: createSigner,
+ createVerifier: createVerifier,
+ equalKeys: equalKeys,
+ generatePrivateKey: generatePrivateKey,
+ isKey: isKey,
+ isPrivateKey: isPrivateKey,
+ isPublicKey: isPublicKey,
+ matchingPublicKeys: matchingPublicKeys,
+ sshFingerprint: sshFingerprint,
+ RSA_PKCS1_PADDING: ursaNative.RSA_PKCS1_PADDING,
+ RSA_PKCS1_OAEP_PADDING: ursaNative.RSA_PKCS1_OAEP_PADDING,
};
View
@@ -1,6 +1,6 @@
{
"name": "ursa",
- "version": "0.6.8",
+ "version": "0.6.9",
"keywords": [
"crypto", "key", "openssl", "private", "public", "rsa", "sign",
"signature", "verify", "verification", "hash", "digest"
View
@@ -27,6 +27,8 @@ using namespace v8;
* Top-level initialization function.
*/
void init(Handle<Object> target) {
+ NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PADDING);
+ NODE_DEFINE_CONSTANT(target, RSA_PKCS1_OAEP_PADDING);
BIND(target, textToNid, TextToNid);
RsaWrap::InitClass(target);
}
@@ -245,7 +247,7 @@ static char *getArgString(const Arguments& args, int index) {
str->WriteUtf8(result, length + 1);
if (result[length] != '\0') {
- char *message = "String conversion failed.";
+ const char *message = "String conversion failed.";
ThrowException(Exception::Error(String::New(message)));
free(result);
return NULL;
@@ -560,8 +562,11 @@ Handle<Value> RsaWrap::PrivateDecrypt(const Arguments& args) {
int rsaLength = RSA_size(obj->rsa);
unsigned char buf[rsaLength];
+ int padding;
+ if (!getArgInt(args, 1, &padding)) { return Undefined(); }
+
int bufLength = RSA_private_decrypt(length, (unsigned char *) data,
- buf, obj->rsa, RSA_PKCS1_OAEP_PADDING);
+ buf, obj->rsa, padding);
if (bufLength < 0) {
scheduleSslException();
@@ -673,9 +678,12 @@ Handle<Value> RsaWrap::PublicEncrypt(const Arguments& args) {
return Undefined();
}
+ int padding;
+ if (!getArgInt(args, 1, &padding)) { return Undefined(); }
+
int ret = RSA_public_encrypt(length, (unsigned char *) data,
(unsigned char *) node::Buffer::Data(result),
- obj->rsa, RSA_PKCS1_OAEP_PADDING);
+ obj->rsa, padding);
if (ret < 0) {
// TODO: Will this leak the result buffer? Is it going to be gc'ed?
View
@@ -57,6 +57,15 @@ var PRIVATE_CIPHERTEXT_HEX =
"a0f0969804d4968fb2e6220db5cf02e2c2200ff9d0a5a5037ac859a55c005ecc" +
"52ce194a6a9624c71547c96cf90d911caa4097f9cdfded71d23c9f8f5551188c" +
"8326357d54224ab25b9f29c1efdbc960a0968e4c9027cd507ffadd8dff93256c";
+var PRIVATE_OLD_PAD_CIPHER_HEX =
+ "69d1c385929fc00f89aa98ae9cd8529afe884b581505acdcd4ceaa10bfda9adc" +
+ "79c472dd7e35bcc94f1146459c6a8d96e572116c7a62f1da5dd18cdb8f81e72b" +
+ "4a4649f40470e88c11b04fdf72e48c6adb44c41edc0c4c56074a041c03017f72" +
+ "f66a000066a4dbe888119c83f79e7cb8f667f0af1af41cf4adf21320fada9355" +
+ "6d056a2fdb1f5a9f5708e096a7408a115efa14f0e2f94feaa32322aa4af9c97a" +
+ "438d205f62317020e657c5057227a3d7e60a6a6658781cf41b0820988a4f9e8e" +
+ "b947c424248d231c3e43c711b0c4a4342a0fa484d0e3ded231a695250f4dafcf" +
+ "f9e94d02e3f74d4c509cfae24b8615e619805c9cdc9e85faed7d706dd6891383";
var PUBLIC_CIPHERTEXT_HEX =
"16b5e95a02db09e95bb5419998b3c5f450571578be271602828740242236e6aa" +
"0bce325d6b9a681038c864e0877a3e68e20329a3602829128385f182a20f06c7" +
@@ -116,6 +125,7 @@ module.exports = {
PLAINTEXT_SHA256: PLAINTEXT_SHA256,
PLAINTEXT_SHA256_SIGNATURE: PLAINTEXT_SHA256_SIGNATURE,
PRIVATE_CIPHERTEXT_HEX: PRIVATE_CIPHERTEXT_HEX,
+ PRIVATE_OLD_PAD_CIPHER_HEX: PRIVATE_OLD_PAD_CIPHER_HEX,
PRIVATE_KEY: PRIVATE_KEY,
PRIVATE_KEY_2: PRIVATE_KEY_2,
PUBLIC_CIPHERTEXT_HEX: PUBLIC_CIPHERTEXT_HEX,
View
@@ -12,9 +12,10 @@
var assert = require("assert");
-var fixture = require("./fixture");
-var RsaWrap = fixture.RsaWrap;
-var textToNid = fixture.ursaNative.textToNid;
+var fixture = require("./fixture");
+var RsaWrap = fixture.RsaWrap;
+var ursaNative = fixture.ursaNative;
+var textToNid = ursaNative.textToNid;
/*
@@ -198,7 +199,11 @@ function test_privateDecrypt() {
rsa.setPrivateKeyPem(fixture.PRIVATE_KEY);
var encoded = new Buffer(fixture.PRIVATE_CIPHERTEXT_HEX, fixture.HEX);
- var decoded = rsa.privateDecrypt(encoded).toString(fixture.UTF8);
+ var decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8);
+ assert.equal(decoded, fixture.PLAINTEXT);
+
+ var encoded = new Buffer(fixture.PRIVATE_OLD_PAD_CIPHER_HEX, fixture.HEX);
+ var decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_PADDING).toString(fixture.UTF8);
assert.equal(decoded, fixture.PLAINTEXT);
}
@@ -217,14 +222,19 @@ function test_fail_privateDecrypt() {
rsa.setPrivateKeyPem(fixture.PRIVATE_KEY);
function f2() {
- rsa.privateDecrypt("x");
+ rsa.privateDecrypt("x", ursaNative.RSA_PKCS1_OAEP_PADDING);
}
assert.throws(f2, /Expected a Buffer in args\[0]\./);
function f3() {
- rsa.privateDecrypt(new Buffer("x"));
+ rsa.privateDecrypt(new Buffer("x"), ursaNative.RSA_PKCS1_OAEP_PADDING);
}
assert.throws(f3, /decoding error/);
+
+ function f4() {
+ rsa.privateDecrypt(new Buffer("x"), "str");
+ }
+ assert.throws(f4, /Expected a 32-bit integer/);
}
function test_publicEncrypt() {
@@ -235,12 +245,18 @@ function test_publicEncrypt() {
var rsa = new RsaWrap();
rsa.setPublicKeyPem(fixture.PUBLIC_KEY);
- var encoded = rsa.publicEncrypt(plainBuf);
- var decoded = priv.privateDecrypt(encoded).toString(fixture.UTF8);
+ var encoded = rsa.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING);
+ var decoded = priv.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8);
assert.equal(decoded, fixture.PLAINTEXT);
- encoded = priv.publicEncrypt(plainBuf);
- decoded = priv.privateDecrypt(encoded).toString(fixture.UTF8);
+ encoded = priv.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING);
+ decoded = priv.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8);
+ assert.equal(decoded, fixture.PLAINTEXT);
+
+ // Test with old-style padding.
+ var encoded = rsa.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_PADDING);
+ var decoded = priv.privateDecrypt(encoded, ursaNative.RSA_PKCS1_PADDING);
+ decoded = decoded.toString(fixture.UTF8);
assert.equal(decoded, fixture.PLAINTEXT);
}
@@ -257,14 +273,19 @@ function test_fail_publicEncrypt() {
rsa.setPublicKeyPem(fixture.PUBLIC_KEY);
function f2() {
- rsa.publicEncrypt("x");
+ rsa.publicEncrypt("x", ursaNative.RSA_PKCS1_OAEP_PADDING);
}
assert.throws(f2, /Expected a Buffer in args\[0]\./);
function f3() {
- rsa.publicEncrypt(new Buffer(2048));
+ rsa.publicEncrypt(new Buffer(2048), ursaNative.RSA_PKCS1_OAEP_PADDING);
}
assert.throws(f3, /too large/);
+
+ function f4() {
+ rsa.publicEncrypt(new Buffer("x"), "str");
+ }
+ assert.throws(f4, /Expected a 32-bit integer/);
}
function test_privateEncrypt() {
@@ -345,21 +366,21 @@ function test_generatePrivateKey() {
// Do a round trip check.
var plainBuf = new Buffer(fixture.PLAINTEXT, fixture.UTF8);
- var encoded = rsa.publicEncrypt(plainBuf);
- var decoded = rsa.privateDecrypt(encoded).toString(fixture.UTF8);
+ var encoded = rsa.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING);
+ var decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8);
assert.equal(decoded, fixture.PLAINTEXT);
// Extract the public key, and try using it for a round trip.
var pubKey = new RsaWrap();
pubKey.setPublicKeyPem(rsa.getPublicKeyPem());
- encoded = pubKey.publicEncrypt(plainBuf);
- decoded = rsa.privateDecrypt(encoded).toString(fixture.UTF8);
+ encoded = pubKey.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING);
+ decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8);
assert.equal(decoded, fixture.PLAINTEXT);
// Similarly, try decoding with an extracted private key.
var privKey = new RsaWrap();
privKey.setPrivateKeyPem(rsa.getPrivateKeyPem());
- decoded = privKey.privateDecrypt(encoded).toString(fixture.UTF8);
+ decoded = privKey.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8);
assert.equal(decoded, fixture.PLAINTEXT);
}
View
@@ -531,4 +531,4 @@ test_matchingPublicKeys();
testSigner();
testVerifier();
-console.log("All tests pass!");
+console.log("All tests pass!");