summaryrefslogtreecommitdiff
path: root/src/node_crypto.cc
diff options
context:
space:
mode:
authorseishun <vvnicholas@gmail.com>2014-05-05 17:35:28 +0300
committerFedor Indutny <fedor@indutny.com>2014-08-11 22:00:34 +0400
commit42bda05af8d628db90f3ee64fa2dd89973cb1e03 (patch)
treef322c4605779c7055e407221e3d1ab25f5dd1015 /src/node_crypto.cc
parent93f3b640d06df5e123e7ede90e40df06258d9d47 (diff)
downloadnode-new-42bda05af8d628db90f3ee64fa2dd89973cb1e03.tar.gz
crypto: add RSA encryption
Reviewed-By: Fedor Indutny <fedor@indutny.com>
Diffstat (limited to 'src/node_crypto.cc')
-rw-r--r--src/node_crypto.cc146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index 4abc513dce..03c2d25b25 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -65,6 +65,9 @@ static const char PUBLIC_KEY_PFX[] = "-----BEGIN PUBLIC KEY-----";
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
static const char PUBRSA_KEY_PFX[] = "-----BEGIN RSA PUBLIC KEY-----";
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
+static const char CERTIFICATE_PFX[] = "-----BEGIN CERTIFICATE-----";
+static const int CERTIFICATE_PFX_LEN = sizeof(CERTIFICATE_PFX) - 1;
+
static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
| ASN1_STRFLGS_ESC_MSB
| XN_FLAG_SEP_MULTILINE
@@ -3544,6 +3547,139 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
}
+template <PublicKeyCipher::Operation operation,
+ PublicKeyCipher::EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
+ PublicKeyCipher::EVP_PKEY_cipher_t EVP_PKEY_cipher>
+bool PublicKeyCipher::Cipher(const char* key_pem,
+ int key_pem_len,
+ const char* passphrase,
+ const unsigned char* data,
+ int len,
+ unsigned char** out,
+ size_t* out_len) {
+ EVP_PKEY* pkey = NULL;
+ EVP_PKEY_CTX* ctx = NULL;
+ BIO* bp = NULL;
+ X509* x509 = NULL;
+ bool fatal = true;
+
+ bp = BIO_new(BIO_s_mem());
+ if (bp == NULL)
+ goto exit;
+
+ if (!BIO_write(bp, key_pem, key_pem_len))
+ goto exit;
+
+ // Check if this is a PKCS#8 or RSA public key before trying as X.509 and
+ // private key.
+ if (operation == kEncrypt &&
+ strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
+ pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
+ if (pkey == NULL)
+ goto exit;
+ } else if (operation == kEncrypt &&
+ strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
+ RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
+ if (rsa) {
+ pkey = EVP_PKEY_new();
+ if (pkey)
+ EVP_PKEY_set1_RSA(pkey, rsa);
+ RSA_free(rsa);
+ }
+ if (pkey == NULL)
+ goto exit;
+ } else if (operation == kEncrypt &&
+ strncmp(key_pem, CERTIFICATE_PFX, CERTIFICATE_PFX_LEN) == 0) {
+ x509 = PEM_read_bio_X509(bp, NULL, CryptoPemCallback, NULL);
+ if (x509 == NULL)
+ goto exit;
+
+ pkey = X509_get_pubkey(x509);
+ if (pkey == NULL)
+ goto exit;
+ } else {
+ pkey = PEM_read_bio_PrivateKey(bp,
+ NULL,
+ CryptoPemCallback,
+ const_cast<char*>(passphrase));
+ if (pkey == NULL)
+ goto exit;
+ }
+
+ ctx = EVP_PKEY_CTX_new(pkey, NULL);
+ if (!ctx)
+ goto exit;
+ if (EVP_PKEY_cipher_init(ctx) <= 0)
+ goto exit;
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+ goto exit;
+ if (EVP_PKEY_cipher(ctx, NULL, out_len, data, len) <= 0)
+ goto exit;
+
+ *out = new unsigned char[*out_len];
+
+ if (EVP_PKEY_cipher(ctx, *out, out_len, data, len) <= 0)
+ goto exit;
+
+ fatal = false;
+
+ exit:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (bp != NULL)
+ BIO_free_all(bp);
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+
+ return !fatal;
+}
+
+
+template <PublicKeyCipher::Operation operation,
+ PublicKeyCipher::EVP_PKEY_cipher_init_t EVP_PKEY_cipher_init,
+ PublicKeyCipher::EVP_PKEY_cipher_t EVP_PKEY_cipher>
+void PublicKeyCipher::Cipher(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ HandleScope scope(env->isolate());
+
+ ASSERT_IS_BUFFER(args[0]);
+ char* kbuf = Buffer::Data(args[0]);
+ ssize_t klen = Buffer::Length(args[0]);
+
+ ASSERT_IS_BUFFER(args[1]);
+ char* buf = Buffer::Data(args[1]);
+ ssize_t len = Buffer::Length(args[1]);
+
+ String::Utf8Value passphrase(args[2]);
+
+ unsigned char* out_value = NULL;
+ size_t out_len = -1;
+
+ bool r = Cipher<operation, EVP_PKEY_cipher_init, EVP_PKEY_cipher>(
+ kbuf,
+ klen,
+ args.Length() >= 3 && !args[2]->IsNull() ? *passphrase : NULL,
+ reinterpret_cast<const unsigned char*>(buf),
+ len,
+ &out_value,
+ &out_len);
+
+ if (out_len <= 0 || !r) {
+ delete[] out_value;
+ out_value = NULL;
+ out_len = 0;
+ if (!r) {
+ return ThrowCryptoError(env,
+ ERR_get_error());
+ }
+ }
+
+ args.GetReturnValue().Set(
+ Buffer::New(env, reinterpret_cast<char*>(out_value), out_len));
+ delete[] out_value;
+}
+
+
void DiffieHellman::Initialize(Environment* env, Handle<Object> target) {
Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate(), New);
@@ -4730,6 +4866,16 @@ void InitCrypto(Handle<Object> target,
NODE_SET_METHOD(target, "getSSLCiphers", GetSSLCiphers);
NODE_SET_METHOD(target, "getCiphers", GetCiphers);
NODE_SET_METHOD(target, "getHashes", GetHashes);
+ NODE_SET_METHOD(target,
+ "publicEncrypt",
+ PublicKeyCipher::Cipher<PublicKeyCipher::kEncrypt,
+ EVP_PKEY_encrypt_init,
+ EVP_PKEY_encrypt>);
+ NODE_SET_METHOD(target,
+ "privateDecrypt",
+ PublicKeyCipher::Cipher<PublicKeyCipher::kDecrypt,
+ EVP_PKEY_decrypt_init,
+ EVP_PKEY_decrypt>);
}
} // namespace crypto