Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

TLSv1.{1,2} #4317

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions lib/tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
135 changes: 124 additions & 11 deletions src/node_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -201,13 +205,27 @@ 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")));
}
}

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 |
Expand All @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
15 changes: 15 additions & 0 deletions src/node_crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -106,6 +120,7 @@ class SecureContext : ObjectWrap {

~SecureContext() {
FreeCTXMem();
_ClearECDHECurves();
}

private:
Expand Down