From 97fece003d01b73271b44bde2cd25b95332f0768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Sedl=C3=A0=C4=8Dek?= Date: Mon, 26 Nov 2012 18:03:28 +0100 Subject: [PATCH 1/3] add tlsv1.x methods --- src/node_crypto.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index f41b2d5a8c6..d7deee88ee9 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -201,6 +201,18 @@ Handle SecureContext::Init(const Arguments& args) { method = TLSv1_server_method(); } else if (strcmp(*sslmethod, "TLSv1_client_method") == 0) { method = TLSv1_client_method(); + } else if (strcmp(*sslmethod, "TLSv1_1_method") == 0) { + method = TLSv1_1_method(); + } else if (strcmp(*sslmethod, "TLSv1_1_server_method") == 0) { + method = TLSv1_1_server_method(); + } else if (strcmp(*sslmethod, "TLSv1_1_client_method") == 0) { + method = TLSv1_1_client_method(); + } else if (strcmp(*sslmethod, "TLSv1_2_method") == 0) { + method = TLSv1_2_method(); + } else if (strcmp(*sslmethod, "TLSv1_2_server_method") == 0) { + method = TLSv1_2_server_method(); + } else if (strcmp(*sslmethod, "TLSv1_2_client_method") == 0) { + method = TLSv1_2_client_method(); } else { return ThrowException(Exception::Error(String::New("Unknown method"))); } From d5ab2a7576130d97d1d361e5b50dcb95a52f1f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Sedl=C3=A0=C4=8Dek?= Date: Mon, 26 Nov 2012 20:03:28 +0100 Subject: [PATCH 2/3] don't get RSA pubkey params for non-RSA --- src/node_crypto.cc | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index d7deee88ee9..a2ecfdb8ee9 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1531,17 +1531,18 @@ Handle Connection::GetPeerCertificate(const Arguments& args) { EVP_PKEY *pkey = NULL; RSA *rsa = NULL; - if( NULL != (pkey = X509_get_pubkey(peer_cert)) - && NULL != (rsa = EVP_PKEY_get1_RSA(pkey)) ) { - BN_print(bio, rsa->n); - BIO_get_mem_ptr(bio, &mem); - info->Set(modulus_symbol, String::New(mem->data, mem->length) ); - (void) BIO_reset(bio); - - BN_print(bio, rsa->e); - BIO_get_mem_ptr(bio, &mem); - info->Set(exponent_symbol, String::New(mem->data, mem->length) ); - (void) BIO_reset(bio); + if (NULL != X509_get_pubkey(peer_cert) && + EVP_PKEY_RSA == EVP_PKEY_type(pkey->type) && + NULL != (rsa = EVP_PKEY_get1_RSA(pkey)) ) { + BN_print(bio, rsa->n); + BIO_get_mem_ptr(bio, &mem); + info->Set(modulus_symbol, String::New(mem->data, mem->length) ); + (void) BIO_reset(bio); + + BN_print(bio, rsa->e); + BIO_get_mem_ptr(bio, &mem); + info->Set(exponent_symbol, String::New(mem->data, mem->length) ); + (void) BIO_reset(bio); } ASN1_TIME_print(bio, X509_get_notBefore(peer_cert)); From 2b9a558fb1b9c5a5011778461d3bb3cd3e85b095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Sedl=C3=A0=C4=8Dek?= Date: Wed, 28 Nov 2012 21:35:17 +0100 Subject: [PATCH 3/3] add ECDHE support --- lib/crypto.js | 3 ++ lib/tls.js | 2 + src/node_crypto.cc | 100 +++++++++++++++++++++++++++++++++++++++++++++ src/node_crypto.h | 15 +++++++ 4 files changed, 120 insertions(+) diff --git a/lib/crypto.js b/lib/crypto.js index a787e09c348..2dcbc1ea7a5 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -90,6 +90,9 @@ exports.createCredentials = function(options, context) { if (context) return c; + if (options.ecdheCurves) + c.context.setECDHECurves.apply(c.context, options.ecdheCurves); + if (options.key) { if (options.passphrase) { c.context.setKey(options.key, options.passphrase); diff --git a/lib/tls.js b/lib/tls.js index 7894c27e20b..0a743e1be09 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -1109,6 +1109,7 @@ function Server(/* [options], listener */) { cert: self.cert, ca: self.ca, ciphers: self.ciphers || DEFAULT_CIPHERS, + ecdheCurves: self.ecdheCurves, secureProtocol: self.secureProtocol, secureOptions: self.secureOptions, crl: self.crl, @@ -1197,6 +1198,7 @@ Server.prototype.setOptions = function(options) { if (options.secureProtocol) this.secureProtocol = options.secureProtocol; if (options.crl) this.crl = options.crl; if (options.ciphers) this.ciphers = options.ciphers; + if (options.ecdheCurves) this.ecdheCurves = options.ecdheCurves; var secureOptions = options.secureOptions || 0; if (options.honorCipherOrder) { secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index a2ecfdb8ee9..144903e065b 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -131,6 +131,10 @@ void SecureContext::Initialize(Handle target) { t->SetClassName(String::NewSymbol("SecureContext")); NODE_SET_PROTOTYPE_METHOD(t, "init", SecureContext::Init); + NODE_SET_PROTOTYPE_METHOD(t, "setECDHECurves", + SecureContext::SetECDHECurves); + NODE_SET_PROTOTYPE_METHOD(t, "clearECDHECurves", + SecureContext::ClearECDHECurves); NODE_SET_PROTOTYPE_METHOD(t, "setKey", SecureContext::SetKey); NODE_SET_PROTOTYPE_METHOD(t, "setCert", SecureContext::SetCert); NODE_SET_PROTOTYPE_METHOD(t, "addCACert", SecureContext::AddCACert); @@ -220,6 +224,8 @@ Handle SecureContext::Init(const Arguments& args) { sc->ctx_ = SSL_CTX_new(method); + SSL_CTX_set_app_data(sc->ctx_, sc); + // SSL session cache configuration SSL_CTX_set_session_cache_mode(sc->ctx_, SSL_SESS_CACHE_SERVER | @@ -232,6 +238,100 @@ Handle SecureContext::Init(const Arguments& args) { return True(); } +static EC_KEY *SecureContext_ecdhe_key_cb(SSL *ssl, int is_ex, int bit_len) { + SecureContext *sc = (SecureContext *)SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)); + + assert(NULL != sc->ecdhe_keys); + + for (int i = 0; i < sc->ecdhe_keys_len; i++) + if (sc->ecdhe_keys[i].bit_len >= bit_len) + return sc->ecdhe_keys[i].key; + + if (0 < sc->ecdhe_keys_len) + return sc->ecdhe_keys[sc->ecdhe_keys_len - 1].key; + + return NULL; +} + +Handle SecureContext::SetECDHECurves(const Arguments& args) { + HandleScope scope; + + SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); + sc->_ClearECDHECurves(); + + char err[80]; + int i = 0; + + sc->ecdhe_keys_len = args.Length(); + sc->ecdhe_keys = new BitLenKey[sc->ecdhe_keys_len]; + + for (; i < args.Length(); i++) { + String::Utf8Value _curvesn(args[i]); + const char *curvesn = *_curvesn; + + int curvenid = OBJ_sn2nid(curvesn); + if (NID_undef == curvenid) { + snprintf(err, 80, "Invalid curve: %s", curvesn); + goto err_obj_sn2nid; + } + + EC_KEY *key = EC_KEY_new_by_curve_name(curvenid); + if (NULL == key) { + snprintf(err, 80, "Failed to generate key for curve: %s", curvesn); + goto err_ec_key_new; + } + + const EC_GROUP *grp = EC_KEY_get0_group(key); + if (NULL == grp) { + snprintf(err, 80, "Failed to get group for curve: %s", curvesn); + goto err_ec_key_get_group; + } + + sc->ecdhe_keys[i].bit_len = EC_GROUP_get_degree(grp); + sc->ecdhe_keys[i].key = key; + } + + SSL_CTX_set_tmp_ecdh_callback(sc->ctx_, SecureContext_ecdhe_key_cb); + + return True(); + +err_obj_sn2nid: +err_ec_key_new: + i--; + +err_ec_key_get_group: + for (; i >= 0; i--) + EC_KEY_free(sc->ecdhe_keys[i].key); + + delete sc->ecdhe_keys; + sc->ecdhe_keys = NULL; + + return ThrowException(Exception::Error(String::New(err))); +} + +Handle SecureContext::ClearECDHECurves(const Arguments& args) { + HandleScope scope; + + SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); + + sc->_ClearECDHECurves(); + + return True(); +} + +void SecureContext::_ClearECDHECurves(void) { + if (NULL == ecdhe_keys) + return; + + for (int i = 0; i < ecdhe_keys_len; i++) + EC_KEY_free(ecdhe_keys[i].key); + + delete ecdhe_keys; + ecdhe_keys = NULL; + + SSL_CTX_set_tmp_ecdh_callback(ctx_, NULL); +} + SSL_SESSION* SecureContext::GetSessionCallback(SSL* s, unsigned char* key, diff --git a/src/node_crypto.h b/src/node_crypto.h index ee3cf93ba00..5bdeb6f16e9 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -27,6 +27,7 @@ #include "node_object_wrap.h" #include "v8.h" +#include #include #include #include @@ -52,6 +53,11 @@ static X509_STORE* root_cert_store; // Forward declaration class Connection; +template struct BitLenKey { + int bit_len; + KeyType *key; +}; + class SecureContext : ObjectWrap { public: static void Initialize(v8::Handle target); @@ -60,11 +66,16 @@ class SecureContext : ObjectWrap { // TODO: ca_store_ should probably be removed, it's not used anywhere. X509_STORE *ca_store_; + int ecdhe_keys_len; + BitLenKey *ecdhe_keys; + protected: static const int kMaxSessionSize = 10 * 1024; static v8::Handle New(const v8::Arguments& args); static v8::Handle Init(const v8::Arguments& args); + static v8::Handle SetECDHECurves(const v8::Arguments& args); + static v8::Handle ClearECDHECurves(const v8::Arguments& args); static v8::Handle SetKey(const v8::Arguments& args); static v8::Handle SetCert(const v8::Arguments& args); static v8::Handle AddCACert(const v8::Arguments& args); @@ -85,8 +96,11 @@ class SecureContext : ObjectWrap { SecureContext() : ObjectWrap() { ctx_ = NULL; ca_store_ = NULL; + ecdhe_keys = NULL; } + void _ClearECDHECurves(void); + void FreeCTXMem() { if (ctx_) { if (ctx_->cert_store == root_cert_store) { @@ -106,6 +120,7 @@ class SecureContext : ObjectWrap { ~SecureContext() { FreeCTXMem(); + _ClearECDHECurves(); } private: