Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

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

Merged
merged 2 commits into from

2 participants

@tylerneylon

No description provided.

@tylerneylon tylerneylon Support old-style padding for public-en/priv.-de.
This adds support for the older RSA_PKCS1_PADDING style
for public encryption and private decryption. This makes ursa more
compatible with other crypto systems.
5aa7b5d
@danfuzz

Rather than do default logic at the low layer, I'd prefer to do it up at the JS level, a la:

padding = padding || ursaNative. RSA_PKCS1_OAEP_PADDING;

Pretty much, if something can be done in JS I'll aim to actually do it there, at least vaguely for reasons along the lines of consistency, code safety, and conciseness.

@danfuzz

See above.

@danfuzz

See above.

@danfuzz

Other than the one comment, LGTM.

@danfuzz

Looks great. Thanks!

@danfuzz danfuzz merged commit 33235e2 into quartzjer:master
@danfuzz

New version pushed to npm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 22, 2012
  1. @tylerneylon

    Support old-style padding for public-en/priv.-de.

    tylerneylon authored
    This adds support for the older RSA_PKCS1_PADDING style
    for public encryption and private decryption. This makes ursa more
    compatible with other crypto systems.
  2. @tylerneylon
This page is out of date. Refresh to see the latest.
View
30 README.md
@@ -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,7 +324,7 @@ 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
@@ -329,8 +332,9 @@ 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
48 lib/ursa.js
@@ -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
2  package.json
@@ -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
14 src/ursaNative.cc
@@ -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
10 test/fixture.js
@@ -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
55 test/native.js
@@ -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
2  test/test.js
@@ -531,4 +531,4 @@ test_matchingPublicKeys();
testSigner();
testVerifier();
-console.log("All tests pass!");
+console.log("All tests pass!");
Something went wrong with that request. Please try again.