crypto: allow padding in RSA methods
authorFedor Indutny <fedor@indutny.com>
Sat, 23 Aug 2014 13:38:32 +0000 (17:38 +0400)
committerFedor Indutny <fedor@indutny.com>
Tue, 26 Aug 2014 20:24:57 +0000 (00:24 +0400)
Reviewed-By: Trevor Norris <trevnorris@gmail.com>
doc/api/crypto.markdown
lib/crypto.js
src/node_constants.cc
src/node_crypto.cc
src/node_crypto.h
test/simple/test-crypto.js

index d1ed683..cc13aca 100644 (file)
@@ -597,17 +597,36 @@ Exports the encoded challenge associated with the SPKAC.
 
 Encrypts `buffer` with `public_key`. Only RSA is currently supported.
 
+`public_key` can be an object or a string. If `public_key` is a string, it is
+treated as the key with no passphrase and will use `RSA_PKCS1_OAEP_PADDING`.
+
+`public_key`:
+
+* `key` : A string holding the PEM encoded private key
+* `padding` : An optional padding value, one of the following:
+  * `constants.RSA_NO_PADDING`
+  * `constants.RSA_PKCS1_PADDING`
+  * `constants.RSA_PKCS1_OAEP_PADDING`
+
+NOTE: All paddings are defined in `constants` module.
+
 ## crypto.privateDecrypt(private_key, buffer)
 
 Decrypts `buffer` with `private_key`.
 
 `private_key` can be an object or a string. If `private_key` is a string, it is
-treated as the key with no passphrase.
+treated as the key with no passphrase and will use `RSA_PKCS1_OAEP_PADDING`.
 
 `private_key`:
 
 * `key` : A string holding the PEM encoded private key
-* `passphrase` : A string of passphrase for the private key
+* `passphrase` : An optional string of passphrase for the private key
+* `padding` : An optional padding value, one of the following:
+  * `constants.RSA_NO_PADDING`
+  * `constants.RSA_PKCS1_PADDING`
+  * `constants.RSA_PKCS1_OAEP_PADDING`
+
+NOTE: All paddings are defined in `constants` module.
 
 ## crypto.DEFAULT_ENCODING
 
index 16a3e38..828c0f4 100644 (file)
@@ -355,14 +355,17 @@ Verify.prototype.verify = function(object, signature, sigEncoding) {
   return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));
 };
 
-exports.publicEncrypt = function(object, buffer) {
-  return binding.publicEncrypt(toBuf(object), buffer);
+exports.publicEncrypt = function(options, buffer) {
+  var key = options.key || options;
+  var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING;
+  return binding.publicEncrypt(toBuf(key), buffer, padding);
 };
 
 exports.privateDecrypt = function(options, buffer) {
   var key = options.key || options;
   var passphrase = options.passphrase || null;
-  return binding.privateDecrypt(toBuf(key), buffer, passphrase);
+  var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING;
+  return binding.privateDecrypt(toBuf(key), buffer, padding, passphrase);
 };
 
 
index dc7dc80..430a09c 100644 (file)
@@ -950,6 +950,30 @@ void DefineOpenSSLConstants(Handle<Object> target) {
 #define NPN_ENABLED 1
     NODE_DEFINE_CONSTANT(target, NPN_ENABLED);
 #endif
+
+#ifdef RSA_PKCS1_PADDING
+    NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PADDING);
+#endif
+
+#ifdef RSA_SSLV23_PADDING
+    NODE_DEFINE_CONSTANT(target, RSA_SSLV23_PADDING);
+#endif
+
+#ifdef RSA_NO_PADDING
+    NODE_DEFINE_CONSTANT(target, RSA_NO_PADDING);
+#endif
+
+#ifdef RSA_PKCS1_OAEP_PADDING
+    NODE_DEFINE_CONSTANT(target, RSA_PKCS1_OAEP_PADDING);
+#endif
+
+#ifdef RSA_X931_PADDING
+    NODE_DEFINE_CONSTANT(target, RSA_X931_PADDING);
+#endif
+
+#ifdef RSA_PKCS1_PSS_PADDING
+    NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING);
+#endif
 }
 
 void DefineSystemConstants(Handle<Object> target) {
index 9fa1e2c..6085a18 100644 (file)
@@ -3552,6 +3552,7 @@ template <PublicKeyCipher::Operation operation,
 bool PublicKeyCipher::Cipher(const char* key_pem,
                              int key_pem_len,
                              const char* passphrase,
+                             int padding,
                              const unsigned char* data,
                              int len,
                              unsigned char** out,
@@ -3610,8 +3611,9 @@ bool PublicKeyCipher::Cipher(const char* key_pem,
     goto exit;
   if (EVP_PKEY_cipher_init(ctx) <= 0)
     goto exit;
-  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+  if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)
     goto exit;
+
   if (EVP_PKEY_cipher(ctx, NULL, out_len, data, len) <= 0)
     goto exit;
 
@@ -3649,7 +3651,9 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
   char* buf = Buffer::Data(args[1]);
   ssize_t len = Buffer::Length(args[1]);
 
-  String::Utf8Value passphrase(args[2]);
+  int padding = args[2]->Uint32Value();
+
+  String::Utf8Value passphrase(args[3]);
 
   unsigned char* out_value = NULL;
   size_t out_len = -1;
@@ -3658,6 +3662,7 @@ void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
       kbuf,
       klen,
       args.Length() >= 3 && !args[2]->IsNull() ? *passphrase : NULL,
+      padding,
       reinterpret_cast<const unsigned char*>(buf),
       len,
       &out_value,
index 9531df0..2a02c89 100644 (file)
@@ -577,6 +577,7 @@ class PublicKeyCipher {
   static bool Cipher(const char* key_pem,
                      int key_pem_len,
                      const char* passphrase,
+                     int padding,
                      const unsigned char* data,
                      int len,
                      unsigned char** out,
index fd98966..74baaa7 100644 (file)
@@ -857,6 +857,30 @@ assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR);
   });
 })();
 
+function test_rsa(padding) {
+  var input = new Buffer(padding === 'RSA_NO_PADDING' ? 1024 / 8 : 32);
+  for (var i = 0; i < input.length; i++)
+    input[i] = (i * 7 + 11) & 0xff;
+  var bufferToEncrypt = new Buffer(input);
+
+  padding = constants[padding];
+
+  var encryptedBuffer = crypto.publicEncrypt({
+    key: rsaPubPem,
+    padding: padding
+  }, bufferToEncrypt);
+
+  var decryptedBuffer = crypto.privateDecrypt({
+    key: rsaKeyPem,
+    padding: padding
+  }, encryptedBuffer);
+  assert.equal(input, decryptedBuffer.toString());
+}
+
+test_rsa('RSA_NO_PADDING');
+test_rsa('RSA_PKCS1_PADDING');
+test_rsa('RSA_PKCS1_OAEP_PADDING');
+
 // Test RSA key signing/verification
 var rsaSign = crypto.createSign('RSA-SHA1');
 var rsaVerify = crypto.createVerify('RSA-SHA1');