Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

TLSv1.{1,2} #4317

Closed
wants to merge 4 commits into from

4 participants

@ksdlck

No description provided.

@bnoordhuis

The commit log should not only explain what changed but also why. If you think it's a bug (which I assume you do), then please add a regression test.

As you can see, the only change here is the addition of the EVP_PKEY_RSA == EVP_PKEY_type(pkey->type) clause to the if conditional. This is to work around what I believe to be a bug in OpenSSL, or at least a divergence from their docs. Quoting from http://www.openssl.org/docs/crypto/EVP_PKEY_set1_RSA.html:

EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and EVP_PKEY_get1_EC_KEY() return the referenced key in pkey or NULL if the key is not of the correct type.

But the behavior presented was, for non-RSA keys, that the library would simply throw an error for EVP_PKEY_get1_RSA when passed an EC key. I don't really have time to push this upstream right now or write a test for it, but it is effectively a one-line patch if you'd like to try it out.

@ksdlck ksdlck referenced this pull request
Closed

Add ECDHE support to TLS #4315

@Nodejs-Jenkins

Can one of the admins verify this patch?

@ErikDubbelboer

I think this pull request can probably be closed after the merger of #5854.

@bnoordhuis

Closing.

@bnoordhuis bnoordhuis closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 26, 2012
  1. @ksdlck

    add tlsv1.x methods

    ksdlck authored
  2. @ksdlck
Commits on Nov 28, 2012
  1. @ksdlck

    add ECDHE support

    ksdlck authored
Commits on Jan 16, 2013
  1. @ksdlck
This page is out of date. Refresh to see the latest.
Showing with 144 additions and 11 deletions.
  1. +3 −0  lib/crypto.js
  2. +2 −0  lib/tls.js
  3. +124 −11 src/node_crypto.cc
  4. +15 −0 src/node_crypto.h
View
3  lib/crypto.js
@@ -93,6 +93,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);
View
2  lib/tls.js
@@ -1127,6 +1127,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,
@@ -1231,6 +1232,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;
View
135 src/node_crypto.cc
@@ -131,6 +131,10 @@ void SecureContext::Initialize(Handle<Object> 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);
@@ -201,6 +205,18 @@ Handle<Value> 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")));
}
@@ -208,6 +224,8 @@ Handle<Value> 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 |
@@ -220,6 +238,100 @@ Handle<Value> SecureContext::Init(const Arguments& args) {
return True(node_isolate);
}
+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<Value> SecureContext::SetECDHECurves(const Arguments& args) {
+ HandleScope scope;
+
+ SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
+ sc->_ClearECDHECurves();
+
+ char err[80];
+ int i = 0;
+
+ sc->ecdhe_keys_len = args.Length();
+ sc->ecdhe_keys = new BitLenKey<EC_KEY>[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<Value> SecureContext::ClearECDHECurves(const Arguments& args) {
+ HandleScope scope;
+
+ SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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,
@@ -1520,17 +1632,18 @@ Handle<Value> 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);
}
if (pkey != NULL) {
View
15 src/node_crypto.h
@@ -27,6 +27,7 @@
#include "node_object_wrap.h"
#include "v8.h"
+#include <openssl/objects.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
@@ -52,6 +53,11 @@ static X509_STORE* root_cert_store;
// Forward declaration
class Connection;
+template <typename KeyType> struct BitLenKey {
+ int bit_len;
+ KeyType *key;
+};
+
class SecureContext : ObjectWrap {
public:
static void Initialize(v8::Handle<v8::Object> 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<EC_KEY> *ecdhe_keys;
+
protected:
static const int kMaxSessionSize = 10 * 1024;
static v8::Handle<v8::Value> New(const v8::Arguments& args);
static v8::Handle<v8::Value> Init(const v8::Arguments& args);
+ static v8::Handle<v8::Value> SetECDHECurves(const v8::Arguments& args);
+ static v8::Handle<v8::Value> ClearECDHECurves(const v8::Arguments& args);
static v8::Handle<v8::Value> SetKey(const v8::Arguments& args);
static v8::Handle<v8::Value> SetCert(const v8::Arguments& args);
static v8::Handle<v8::Value> 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:
Something went wrong with that request. Please try again.