crypto: implement privateEncrypt/publicDecrypt
authorFedor Indutny <fedor@indutny.com>
Tue, 27 Jan 2015 19:58:14 +0000 (22:58 +0300)
committerFedor Indutny <fedor@indutny.com>
Tue, 27 Jan 2015 23:02:52 +0000 (02:02 +0300)
PR-URL: https://github.com/iojs/io.js/pull/625
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Fix iojs/io.js#477

doc/api/crypto.markdown
lib/crypto.js
src/node_crypto.cc
src/node_crypto.h
test/parallel/test-crypto.js

index 84cd27a..b6834d0 100644 (file)
@@ -707,6 +707,14 @@ treated as the key with no passphrase and will use `RSA_PKCS1_OAEP_PADDING`.
 
 NOTE: All paddings are defined in `constants` module.
 
+## crypto.privateEncrypt(private_key, buffer)
+
+See above for details. Has the same API as `crypto.privateDecrypt`.
+
+## crypto.publicDecrypt(public_key, buffer)
+
+See above for details. Has the same API as `crypto.publicEncrypt`.
+
 ## crypto.DEFAULT_ENCODING
 
 The default encoding to use for functions that can take either strings
index 8e3a843..2d08433 100644 (file)
@@ -337,19 +337,31 @@ Verify.prototype.verify = function(object, signature, sigEncoding) {
   return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));
 };
 
-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);
-};
+function rsaPublic(method, defaultPadding) {
+  return function(options, buffer) {
+    var key = options.key || options;
+    var padding = options.padding || defaultPadding;
+    return method(toBuf(key), buffer, padding);
+  };
+}
 
-exports.privateDecrypt = function(options, buffer) {
-  var key = options.key || options;
-  var passphrase = options.passphrase || null;
-  var padding = options.padding || constants.RSA_PKCS1_OAEP_PADDING;
-  return binding.privateDecrypt(toBuf(key), buffer, padding, passphrase);
-};
+function rsaPrivate(method, defaultPadding) {
+  return function(options, buffer) {
+    var key = options.key || options;
+    var passphrase = options.passphrase || null;
+    var padding = options.padding || defaultPadding;
+    return method(toBuf(key), buffer, padding, passphrase);
+  };
+}
 
+exports.publicEncrypt = rsaPublic(binding.publicEncrypt,
+                                  constants.RSA_PKCS1_OAEP_PADDING);
+exports.publicDecrypt = rsaPublic(binding.publicDecrypt,
+                                  constants.RSA_PKCS1_PADDING);
+exports.privateEncrypt = rsaPrivate(binding.privateEncrypt,
+                                    constants.RSA_PKCS1_PADDING);
+exports.privateDecrypt = rsaPrivate(binding.privateDecrypt,
+                                    constants.RSA_PKCS1_OAEP_PADDING);
 
 
 exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman;
index b76a1a6..3ad4af2 100644 (file)
@@ -3561,12 +3561,12 @@ bool PublicKeyCipher::Cipher(const char* key_pem,
 
   // Check if this is a PKCS#8 or RSA public key before trying as X.509 and
   // private key.
-  if (operation == kEncrypt &&
+  if (operation == kPublic &&
       strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
     pkey = PEM_read_bio_PUBKEY(bp, nullptr, nullptr, nullptr);
     if (pkey == nullptr)
       goto exit;
-  } else if (operation == kEncrypt &&
+  } else if (operation == kPublic &&
              strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
     RSA* rsa = PEM_read_bio_RSAPublicKey(bp, nullptr, nullptr, nullptr);
     if (rsa) {
@@ -3577,7 +3577,7 @@ bool PublicKeyCipher::Cipher(const char* key_pem,
     }
     if (pkey == nullptr)
       goto exit;
-  } else if (operation == kEncrypt &&
+  } else if (operation == kPublic &&
              strncmp(key_pem, CERTIFICATE_PFX, CERTIFICATE_PFX_LEN) == 0) {
     x509 = PEM_read_bio_X509(bp, nullptr, CryptoPemCallback, nullptr);
     if (x509 == nullptr)
@@ -5038,13 +5038,21 @@ void InitCrypto(Handle<Object> target,
   env->SetMethod(target, "getCiphers", GetCiphers);
   env->SetMethod(target, "getHashes", GetHashes);
   env->SetMethod(target, "publicEncrypt",
-                 PublicKeyCipher::Cipher<PublicKeyCipher::kEncrypt,
+                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                          EVP_PKEY_encrypt_init,
                                          EVP_PKEY_encrypt>);
   env->SetMethod(target, "privateDecrypt",
-                 PublicKeyCipher::Cipher<PublicKeyCipher::kDecrypt,
+                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                          EVP_PKEY_decrypt_init,
                                          EVP_PKEY_decrypt>);
+  env->SetMethod(target, "privateEncrypt",
+                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
+                                         EVP_PKEY_sign_init,
+                                         EVP_PKEY_sign>);
+  env->SetMethod(target, "publicDecrypt",
+                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
+                                         EVP_PKEY_verify_recover_init,
+                                         EVP_PKEY_verify_recover>);
 }
 
 }  // namespace crypto
index 05ab073..75ffe4f 100644 (file)
@@ -553,8 +553,8 @@ class PublicKeyCipher {
                                    const unsigned char *in, size_t inlen);
 
   enum Operation {
-    kEncrypt,
-    kDecrypt
+    kPublic,
+    kPrivate
   };
 
   template <Operation operation,
index 970bee0..8198a6c 100644 (file)
@@ -841,6 +841,11 @@ assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR);
   decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer);
   assert.equal(input, decryptedBuffer.toString());
 
+  encryptedBuffer = crypto.privateEncrypt(keyPem, bufferToEncrypt);
+
+  decryptedBuffer = crypto.publicDecrypt(keyPem, encryptedBuffer);
+  assert.equal(input, decryptedBuffer.toString());
+
   assert.throws(function() {
     crypto.privateDecrypt({
       key: rsaKeyPemEncrypted,