#include <node_root_certs.h>
#include <string.h>
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#endif
+
#include <stdlib.h>
#include <errno.h>
static const char *PUBLIC_KEY_PFX = "-----BEGIN PUBLIC KEY-----";
static const int PUBLIC_KEY_PFX_LEN = strlen(PUBLIC_KEY_PFX);
+static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL
+ | ASN1_STRFLGS_ESC_MSB
+ | XN_FLAG_SEP_MULTILINE
+ | XN_FLAG_FN_SN;
+
namespace node {
namespace crypto {
static Persistent<String> errno_symbol;
static Persistent<String> syscall_symbol;
static Persistent<String> subject_symbol;
+static Persistent<String> subjectaltname_symbol;
+static Persistent<String> modulus_symbol;
+static Persistent<String> exponent_symbol;
static Persistent<String> issuer_symbol;
static Persistent<String> valid_from_symbol;
static Persistent<String> valid_to_symbol;
static Persistent<String> version_symbol;
static Persistent<String> ext_key_usage_symbol;
+static Persistent<FunctionTemplate> secure_context_constructor;
void SecureContext::Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(SecureContext::New);
+ secure_context_constructor = Persistent<FunctionTemplate>::New(t);
+
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("SecureContext"));
NODE_SET_PROTOTYPE_METHOD(t, "addCRL", SecureContext::AddCRL);
NODE_SET_PROTOTYPE_METHOD(t, "addRootCerts", SecureContext::AddRootCerts);
NODE_SET_PROTOTYPE_METHOD(t, "setCiphers", SecureContext::SetCiphers);
+ NODE_SET_PROTOTYPE_METHOD(t, "setOptions", SecureContext::SetOptions);
NODE_SET_PROTOTYPE_METHOD(t, "close", SecureContext::Close);
target->Set(String::NewSymbol("SecureContext"), t->GetFunction());
return True();
}
+Handle<Value> SecureContext::SetOptions(const Arguments& args) {
+ HandleScope scope;
+
+ SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
+
+ if (args.Length() != 1 || !args[0]->IsUint32()) {
+ return ThrowException(Exception::TypeError(String::New("Bad parameter")));
+ }
+
+ unsigned int opts = args[0]->Uint32Value();
+
+ SSL_CTX_set_options(sc->ctx_, opts);
+
+ return True();
+}
Handle<Value> SecureContext::Close(const Arguments& args) {
HandleScope scope;
static char ssl_error_buf[512];
ERR_error_string_n(err, ssl_error_buf, sizeof(ssl_error_buf));
+ // XXX We need to drain the error queue for this thread or else OpenSSL
+ // has the possibility of blocking connections? This problem is not well
+ // understood. And we should be somehow propigating these errors up
+ // into JavaScript. There is no test which demonstrates this problem.
+ // https://github.com/joyent/node/issues/1719
+ while ((err = ERR_get_error()) != 0) {
+ ERR_error_string_n(err, ssl_error_buf, sizeof(ssl_error_buf));
+ fprintf(stderr, "(node SSL) %s\n", ssl_error_buf);
+ }
+
HandleScope scope;
Local<Value> e = Exception::Error(String::New(ssl_error_buf));
handle_->Set(String::New("error"), e);
- DEBUG_PRINT("[%p] SSL: %s failed: (%d:%d) %s\n", ssl_, func, err, rv, ssl_error_buf);
+ DEBUG_PRINT("[%p] SSL: %s failed: (%d:%d) %s\n", ssl_, func, err, rv,
+ ssl_error_buf);
return rv;
}
NODE_SET_PROTOTYPE_METHOD(t, "clearPending", Connection::ClearPending);
NODE_SET_PROTOTYPE_METHOD(t, "encPending", Connection::EncPending);
NODE_SET_PROTOTYPE_METHOD(t, "getPeerCertificate", Connection::GetPeerCertificate);
+ NODE_SET_PROTOTYPE_METHOD(t, "getSession", Connection::GetSession);
+ NODE_SET_PROTOTYPE_METHOD(t, "setSession", Connection::SetSession);
+ NODE_SET_PROTOTYPE_METHOD(t, "isSessionReused", Connection::IsSessionReused);
NODE_SET_PROTOTYPE_METHOD(t, "isInitFinished", Connection::IsInitFinished);
NODE_SET_PROTOTYPE_METHOD(t, "verifyError", Connection::VerifyError);
NODE_SET_PROTOTYPE_METHOD(t, "getCurrentCipher", Connection::GetCurrentCipher);
NODE_SET_PROTOTYPE_METHOD(t, "receivedShutdown", Connection::ReceivedShutdown);
NODE_SET_PROTOTYPE_METHOD(t, "close", Connection::Close);
+#ifdef OPENSSL_NPN_NEGOTIATED
+ NODE_SET_PROTOTYPE_METHOD(t, "getNegotiatedProtocol", Connection::GetNegotiatedProto);
+ NODE_SET_PROTOTYPE_METHOD(t, "setNPNProtocols", Connection::SetNPNProtocols);
+#endif
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+ NODE_SET_PROTOTYPE_METHOD(t, "getServername", Connection::GetServername);
+ NODE_SET_PROTOTYPE_METHOD(t, "setSNICallback", Connection::SetSNICallback);
+#endif
+
target->Set(String::NewSymbol("Connection"), t->GetFunction());
}
return 1;
}
+#ifdef OPENSSL_NPN_NEGOTIATED
+
+int Connection::AdvertiseNextProtoCallback_(SSL *s,
+ const unsigned char **data,
+ unsigned int *len,
+ void *arg) {
+
+ Connection *p = static_cast<Connection*>(SSL_get_app_data(s));
+
+ if (p->npnProtos_.IsEmpty()) {
+ // No initialization - no NPN protocols
+ *data = reinterpret_cast<const unsigned char*>("");
+ *len = 0;
+ } else {
+ *data = reinterpret_cast<const unsigned char*>(Buffer::Data(p->npnProtos_));
+ *len = Buffer::Length(p->npnProtos_);
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+int Connection::SelectNextProtoCallback_(SSL *s,
+ unsigned char **out, unsigned char *outlen,
+ const unsigned char* in,
+ unsigned int inlen, void *arg) {
+ Connection *p = static_cast<Connection*> SSL_get_app_data(s);
+
+ // Release old protocol handler if present
+ if (!p->selectedNPNProto_.IsEmpty()) {
+ p->selectedNPNProto_.Dispose();
+ }
+
+ if (p->npnProtos_.IsEmpty()) {
+ // We should at least select one protocol
+ // If server is using NPN
+ *out = reinterpret_cast<unsigned char*>(const_cast<char*>("http/1.1"));
+ *outlen = 8;
+
+ // set status unsupported
+ p->selectedNPNProto_ = Persistent<Value>::New(False());
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ const unsigned char* npnProtos =
+ reinterpret_cast<const unsigned char*>(Buffer::Data(p->npnProtos_));
+
+ int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtos,
+ Buffer::Length(p->npnProtos_));
+
+ switch (status) {
+ case OPENSSL_NPN_UNSUPPORTED:
+ p->selectedNPNProto_ = Persistent<Value>::New(Null());
+ break;
+ case OPENSSL_NPN_NEGOTIATED:
+ p->selectedNPNProto_ = Persistent<Value>::New(String::New(
+ reinterpret_cast<const char*>(*out), *outlen
+ ));
+ break;
+ case OPENSSL_NPN_NO_OVERLAP:
+ p->selectedNPNProto_ = Persistent<Value>::New(False());
+ break;
+ default:
+ break;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
+ HandleScope scope;
+
+ Connection *p = static_cast<Connection*> SSL_get_app_data(s);
+
+ const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+
+ if (servername) {
+ if (!p->servername_.IsEmpty()) {
+ p->servername_.Dispose();
+ }
+ p->servername_ = Persistent<String>::New(String::New(servername));
+
+ // Call sniCallback_ and use it's return value as context
+ if (!p->sniCallback_.IsEmpty()) {
+ if (!p->sniContext_.IsEmpty()) {
+ p->sniContext_.Dispose();
+ }
+
+ // Get callback init args
+ Local<Value> argv[1] = {*p->servername_};
+ Local<Function> callback = *p->sniCallback_;
+
+ TryCatch try_catch;
+
+ // Call it
+ Local<Value> ret = callback->Call(Context::GetCurrent()->Global(),
+ 1,
+ argv);
+
+ if (try_catch.HasCaught()) {
+ FatalException(try_catch);
+ }
+
+ // If ret is SecureContext
+ if (secure_context_constructor->HasInstance(ret)) {
+ p->sniContext_ = Persistent<Value>::New(ret);
+ SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(
+ Local<Object>::Cast(ret));
+ SSL_set_SSL_CTX(s, sc->ctx_);
+ } else {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ }
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
Handle<Value> Connection::New(const Arguments& args) {
HandleScope scope;
p->ssl_ = SSL_new(sc->ctx_);
p->bio_read_ = BIO_new(BIO_s_mem());
p->bio_write_ = BIO_new(BIO_s_mem());
+
+ SSL_set_app_data(p->ssl_, p);
+
+#ifdef OPENSSL_NPN_NEGOTIATED
+ if (is_server) {
+ // Server should advertise NPN protocols
+ SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
+ AdvertiseNextProtoCallback_,
+ NULL);
+ } else {
+ // Client should select protocol from advertised
+ // If server supports NPN
+ SSL_CTX_set_next_proto_select_cb(sc->ctx_,
+ SelectNextProtoCallback_,
+ NULL);
+ }
+#endif
+
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+ if (is_server) {
+ SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_);
+ } else {
+ String::Utf8Value servername(args[2]->ToString());
+ SSL_set_tlsext_host_name(p->ssl_, *servername);
+ }
+#endif
+
SSL_set_bio(p->ssl_, p->bio_read_, p->bio_write_);
#ifdef SSL_MODE_RELEASE_BUFFERS
Local<Object> info = Object::New();
X509* peer_cert = SSL_get_peer_certificate(ss->ssl_);
if (peer_cert != NULL) {
- char* subject = X509_NAME_oneline(X509_get_subject_name(peer_cert), 0, 0);
- if (subject != NULL) {
- info->Set(subject_symbol, String::New(subject));
- OPENSSL_free(subject);
+ BIO* bio = BIO_new(BIO_s_mem());
+ BUF_MEM* mem;
+ if (X509_NAME_print_ex(bio, X509_get_subject_name(peer_cert), 0,
+ X509_NAME_FLAGS) > 0) {
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(subject_symbol, String::New(mem->data, mem->length));
}
- char* issuer = X509_NAME_oneline(X509_get_issuer_name(peer_cert), 0, 0);
- if (subject != NULL) {
- info->Set(issuer_symbol, String::New(issuer));
- OPENSSL_free(issuer);
+ (void) BIO_reset(bio);
+
+ if (X509_NAME_print_ex(bio, X509_get_issuer_name(peer_cert), 0,
+ X509_NAME_FLAGS) > 0) {
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(issuer_symbol, String::New(mem->data, mem->length));
}
- char buf[256];
- BIO* bio = BIO_new(BIO_s_mem());
+ (void) BIO_reset(bio);
+
+ int index = X509_get_ext_by_NID(peer_cert, NID_subject_alt_name, -1);
+ if (index >= 0) {
+ X509_EXTENSION* ext;
+ int rv;
+
+ ext = X509_get_ext(peer_cert, index);
+ assert(ext != NULL);
+
+ rv = X509V3_EXT_print(bio, ext, 0, 0);
+ assert(rv == 1);
+
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(subjectaltname_symbol, String::New(mem->data, mem->length));
+
+ (void) BIO_reset(bio);
+ }
+
+ 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);
+ }
+
ASN1_TIME_print(bio, X509_get_notBefore(peer_cert));
- memset(buf, 0, sizeof(buf));
- BIO_read(bio, buf, sizeof(buf) - 1);
- info->Set(valid_from_symbol, String::New(buf));
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(valid_from_symbol, String::New(mem->data, mem->length));
+ (void) BIO_reset(bio);
+
ASN1_TIME_print(bio, X509_get_notAfter(peer_cert));
- memset(buf, 0, sizeof(buf));
- BIO_read(bio, buf, sizeof(buf) - 1);
+ BIO_get_mem_ptr(bio, &mem);
+ info->Set(valid_to_symbol, String::New(mem->data, mem->length));
BIO_free(bio);
- info->Set(valid_to_symbol, String::New(buf));
unsigned int md_size, i;
unsigned char md[EVP_MAX_MD_SIZE];
peer_cert, NID_ext_key_usage, NULL, NULL);
if (eku != NULL) {
Local<Array> ext_key_usage = Array::New();
+ char buf[256];
for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
memset(buf, 0, sizeof(buf));
return scope.Close(info);
}
+Handle<Value> Connection::GetSession(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (ss->ssl_ == NULL) return Undefined();
+
+ SSL_SESSION* sess = SSL_get_session(ss->ssl_);
+ if (!sess) return Undefined();
+
+ int slen = i2d_SSL_SESSION(sess, NULL);
+ assert(slen > 0);
+
+ if (slen > 0) {
+ unsigned char* sbuf = new unsigned char[slen];
+ unsigned char* p = sbuf;
+ i2d_SSL_SESSION(sess, &p);
+ Local<Value> s = Encode(sbuf, slen, BINARY);
+ delete[] sbuf;
+ return scope.Close(s);
+ }
+
+ return Null();
+}
+
+Handle<Value> Connection::SetSession(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (args.Length() < 1 || !args[0]->IsString()) {
+ Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
+ return ThrowException(exception);
+ }
+
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ ssize_t slen = DecodeBytes(args[0], BINARY);
+
+ if (slen < 0) {
+ Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
+ return ThrowException(exception);
+ }
+
+ char* sbuf = new char[slen];
+
+ ssize_t wlen = DecodeWrite(sbuf, slen, args[0], BINARY);
+ assert(wlen == slen);
+
+ const unsigned char* p = (unsigned char*) sbuf;
+ SSL_SESSION* sess = d2i_SSL_SESSION(NULL, &p, wlen);
+
+ delete [] sbuf;
+
+ if (!sess)
+ return Undefined();
+
+ int r = SSL_set_session(ss->ssl_, sess);
+ SSL_SESSION_free(sess);
+
+ if (!r) {
+ Local<String> eStr = String::New("SSL_set_session error");
+ return ThrowException(Exception::Error(eStr));
+ }
+
+ return True();
+}
+
+Handle<Value> Connection::IsSessionReused(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (ss->ssl_ == NULL) return False();
+ return SSL_session_reused(ss->ssl_) ? True() : False();
+}
+
+
Handle<Value> Connection::Start(const Arguments& args) {
HandleScope scope;
return True();
}
+#ifdef OPENSSL_NPN_NEGOTIATED
+Handle<Value> Connection::GetNegotiatedProto(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (ss->is_server_) {
+ const unsigned char *npn_proto;
+ unsigned int npn_proto_len;
+
+ SSL_get0_next_proto_negotiated(ss->ssl_, &npn_proto, &npn_proto_len);
+
+ if (!npn_proto) {
+ return False();
+ }
+
+ return String::New((const char*) npn_proto, npn_proto_len);
+ } else {
+ return ss->selectedNPNProto_;
+ }
+}
+
+Handle<Value> Connection::SetNPNProtocols(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (args.Length() < 1 || !Buffer::HasInstance(args[0])) {
+ return ThrowException(Exception::Error(String::New(
+ "Must give a Buffer as first argument")));
+ }
+
+ // Release old handle
+ if (!ss->npnProtos_.IsEmpty()) {
+ ss->npnProtos_.Dispose();
+ }
+ ss->npnProtos_ = Persistent<Object>::New(args[0]->ToObject());
+
+ return True();
+};
+#endif
+
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+Handle<Value> Connection::GetServername(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (ss->is_server_ && !ss->servername_.IsEmpty()) {
+ return ss->servername_;
+ } else {
+ return False();
+ }
+}
+
+Handle<Value> Connection::SetSNICallback(const Arguments& args) {
+ HandleScope scope;
+
+ Connection *ss = Connection::Unwrap(args);
+
+ if (args.Length() < 1 || !args[0]->IsFunction()) {
+ return ThrowException(Exception::Error(String::New(
+ "Must give a Function as first argument")));
+ }
+
+ // Release old handle
+ if (!ss->sniCallback_.IsEmpty()) {
+ ss->sniCallback_.Dispose();
+ }
+ ss->sniCallback_ = Persistent<Function>::New(
+ Local<Function>::Cast(args[0]));
+
+ return True();
+}
+#endif
static void HexEncode(unsigned char *md_value,
int md_len,
return ThrowException(exception);
}
- char* buf = new char[len];
- ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
- assert(written == len);
-
String::Utf8Value hashType(args[0]->ToString());
- bool r = hmac->HmacInit(*hashType, buf, len);
+ bool r;
- delete [] buf;
+ if( Buffer::HasInstance(args[1])) {
+ Local<Object> buffer_obj = args[1]->ToObject();
+ char* buffer_data = Buffer::Data(buffer_obj);
+ size_t buffer_length = Buffer::Length(buffer_obj);
+
+ r = hmac->HmacInit(*hashType, buffer_data, buffer_length);
+ } else {
+ char* buf = new char[len];
+ ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
+ assert(written == len);
+
+ r = hmac->HmacInit(*hashType, buf, len);
+
+ delete [] buf;
+ }
if (!r) {
return ThrowException(Exception::Error(String::New("hmac error")));
};
+class DiffieHellman : public ObjectWrap {
+ public:
+ static void Initialize(v8::Handle<v8::Object> target) {
+ HandleScope scope;
+
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
+
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+
+ NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
+ NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
+ NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
+ NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
+ NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
+ NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
+ NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
+ NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
+
+ target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
+ }
+
+ bool Init(int primeLength) {
+ dh = DH_new();
+ DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
+ bool result = VerifyContext();
+ if (!result) return false;
+ initialised_ = true;
+ return true;
+ }
+
+ bool Init(unsigned char* p, int p_len) {
+ dh = DH_new();
+ dh->p = BN_bin2bn(p, p_len, 0);
+ dh->g = BN_new();
+ if (!BN_set_word(dh->g, 2)) return false;
+ bool result = VerifyContext();
+ if (!result) return false;
+ initialised_ = true;
+ return true;
+ }
+
+ protected:
+ static Handle<Value> New(const Arguments& args) {
+ HandleScope scope;
+
+ DiffieHellman* diffieHellman = new DiffieHellman();
+ bool initialized = false;
+
+ if (args.Length() > 0) {
+ if (args[0]->IsInt32()) {
+ diffieHellman->Init(args[0]->Int32Value());
+ initialized = true;
+ } else {
+ if (args[0]->IsString()) {
+ char* buf;
+ int len;
+ if (args.Length() > 1 && args[1]->IsString()) {
+ len = DecodeWithEncoding(args[0], args[1], &buf);
+ } else {
+ len = DecodeBinary(args[0], &buf);
+ }
+
+ if (len == -1) {
+ delete[] buf;
+ return ThrowException(Exception::Error(
+ String::New("Invalid argument")));
+ } else {
+ diffieHellman->Init(reinterpret_cast<unsigned char*>(buf), len);
+ delete[] buf;
+ initialized = true;
+ }
+ } else if (Buffer::HasInstance(args[0])) {
+ Local<Object> buffer = args[0]->ToObject();
+ diffieHellman->Init(
+ reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
+ Buffer::Length(buffer));
+ initialized = true;
+ }
+ }
+ }
+
+ if (!initialized) {
+ return ThrowException(Exception::Error(
+ String::New("Initialization failed")));
+ }
+
+ diffieHellman->Wrap(args.This());
+
+ return args.This();
+ }
+
+ static Handle<Value> GenerateKeys(const Arguments& args) {
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ HandleScope scope;
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(
+ String::New("Not initialized")));
+ }
+
+ if (!DH_generate_key(diffieHellman->dh)) {
+ return ThrowException(Exception::Error(
+ String::New("Key generation failed")));
+ }
+
+ Local<Value> outString;
+
+ int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->pub_key,
+ reinterpret_cast<unsigned char*>(data));
+
+ if (args.Length() > 0 && args[0]->IsString()) {
+ outString = EncodeWithEncoding(args[0], data, dataSize);
+ } else {
+ outString = Encode(data, dataSize, BINARY);
+ }
+ delete[] data;
+
+ return scope.Close(outString);
+ }
+
+ static Handle<Value> GetPrime(const Arguments& args) {
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ HandleScope scope;
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(String::New("Not initialized")));
+ }
+
+ int dataSize = BN_num_bytes(diffieHellman->dh->p);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
+
+ Local<Value> outString;
+
+ if (args.Length() > 0 && args[0]->IsString()) {
+ outString = EncodeWithEncoding(args[0], data, dataSize);
+ } else {
+ outString = Encode(data, dataSize, BINARY);
+ }
+
+ delete[] data;
+
+ return scope.Close(outString);
+ }
+
+ static Handle<Value> GetGenerator(const Arguments& args) {
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ HandleScope scope;
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(String::New("Not initialized")));
+ }
+
+ int dataSize = BN_num_bytes(diffieHellman->dh->g);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
+
+ Local<Value> outString;
+
+ if (args.Length() > 0 && args[0]->IsString()) {
+ outString = EncodeWithEncoding(args[0], data, dataSize);
+ } else {
+ outString = Encode(data, dataSize, BINARY);
+ }
+
+ delete[] data;
+
+ return scope.Close(outString);
+ }
+
+ static Handle<Value> GetPublicKey(const Arguments& args) {
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ HandleScope scope;
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(String::New("Not initialized")));
+ }
+
+ if (diffieHellman->dh->pub_key == NULL) {
+ return ThrowException(Exception::Error(
+ String::New("No public key - did you forget to generate one?")));
+ }
+
+ int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->pub_key,
+ reinterpret_cast<unsigned char*>(data));
+
+ Local<Value> outString;
+
+ if (args.Length() > 0 && args[0]->IsString()) {
+ outString = EncodeWithEncoding(args[0], data, dataSize);
+ } else {
+ outString = Encode(data, dataSize, BINARY);
+ }
+
+ delete[] data;
+
+ return scope.Close(outString);
+ }
+
+ static Handle<Value> GetPrivateKey(const Arguments& args) {
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ HandleScope scope;
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(String::New("Not initialized")));
+ }
+
+ if (diffieHellman->dh->priv_key == NULL) {
+ return ThrowException(Exception::Error(
+ String::New("No private key - did you forget to generate one?")));
+ }
+
+ int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->priv_key,
+ reinterpret_cast<unsigned char*>(data));
+
+ Local<Value> outString;
+
+ if (args.Length() > 0 && args[0]->IsString()) {
+ outString = EncodeWithEncoding(args[0], data, dataSize);
+ } else {
+ outString = Encode(data, dataSize, BINARY);
+ }
+
+ delete[] data;
+
+ return scope.Close(outString);
+ }
+
+ static Handle<Value> ComputeSecret(const Arguments& args) {
+ HandleScope scope;
+
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(String::New("Not initialized")));
+ }
+
+ BIGNUM* key = 0;
+
+ if (args.Length() == 0) {
+ return ThrowException(Exception::Error(
+ String::New("First argument must be other party's public key")));
+ } else {
+ if (args[0]->IsString()) {
+ char* buf;
+ int len;
+ if (args.Length() > 1) {
+ len = DecodeWithEncoding(args[0], args[1], &buf);
+ } else {
+ len = DecodeBinary(args[0], &buf);
+ }
+ if (len == -1) {
+ delete[] buf;
+ return ThrowException(Exception::Error(
+ String::New("Invalid argument")));
+ }
+ key = BN_bin2bn(reinterpret_cast<unsigned char*>(buf), len, 0);
+ delete[] buf;
+ } else if (Buffer::HasInstance(args[0])) {
+ Local<Object> buffer = args[0]->ToObject();
+ key = BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
+ Buffer::Length(buffer), 0);
+ } else {
+ return ThrowException(Exception::Error(
+ String::New("First argument must be other party's public key")));
+ }
+ }
+
+ int dataSize = DH_size(diffieHellman->dh);
+ char* data = new char[dataSize];
+
+ int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
+ key, diffieHellman->dh);
+ BN_free(key);
+
+ Local<Value> outString;
+
+ if (size == -1) {
+ int checkResult;
+ if (!DH_check_pub_key(diffieHellman->dh, key, &checkResult)) {
+ return ThrowException(Exception::Error(String::New("Invalid key")));
+ } else if (checkResult) {
+ if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
+ return ThrowException(Exception::Error(
+ String::New("Supplied key is too small")));
+ } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
+ return ThrowException(Exception::Error(
+ String::New("Supplied key is too large")));
+ } else {
+ return ThrowException(Exception::Error(String::New("Invalid key")));
+ }
+ } else {
+ return ThrowException(Exception::Error(String::New("Invalid key")));
+ }
+ } else {
+ if (args.Length() > 2 && args[2]->IsString()) {
+ outString = EncodeWithEncoding(args[2], data, dataSize);
+ } else if (args.Length() > 1 && args[1]->IsString()) {
+ outString = EncodeWithEncoding(args[1], data, dataSize);
+ } else {
+ outString = Encode(data, dataSize, BINARY);
+ }
+ }
+
+ delete[] data;
+ return scope.Close(outString);
+ }
+
+ static Handle<Value> SetPublicKey(const Arguments& args) {
+ HandleScope scope;
+
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(String::New("Not initialized")));
+ }
+
+ if (args.Length() == 0) {
+ return ThrowException(Exception::Error(
+ String::New("First argument must be public key")));
+ } else {
+ if (args[0]->IsString()) {
+ char* buf;
+ int len;
+ if (args.Length() > 1) {
+ len = DecodeWithEncoding(args[0], args[1], &buf);
+ } else {
+ len = DecodeBinary(args[0], &buf);
+ }
+ if (len == -1) {
+ delete[] buf;
+ return ThrowException(Exception::Error(
+ String::New("Invalid argument")));
+ }
+ diffieHellman->dh->pub_key =
+ BN_bin2bn(reinterpret_cast<unsigned char*>(buf), len, 0);
+ delete[] buf;
+ } else if (Buffer::HasInstance(args[0])) {
+ Local<Object> buffer = args[0]->ToObject();
+ diffieHellman->dh->pub_key =
+ BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
+ Buffer::Length(buffer), 0);
+ } else {
+ return ThrowException(Exception::Error(
+ String::New("First argument must be public key")));
+ }
+ }
+
+ return args.This();
+ }
+
+ static Handle<Value> SetPrivateKey(const Arguments& args) {
+ HandleScope scope;
+
+ DiffieHellman* diffieHellman =
+ ObjectWrap::Unwrap<DiffieHellman>(args.This());
+
+ if (!diffieHellman->initialised_) {
+ return ThrowException(Exception::Error(
+ String::New("Not initialized")));
+ }
+
+ if (args.Length() == 0) {
+ return ThrowException(Exception::Error(
+ String::New("First argument must be private key")));
+ } else {
+ if (args[0]->IsString()) {
+ char* buf;
+ int len;
+ if (args.Length() > 1) {
+ len = DecodeWithEncoding(args[0], args[1], &buf);
+ } else {
+ len = DecodeBinary(args[0], &buf);
+ }
+ if (len == -1) {
+ delete[] buf;
+ return ThrowException(Exception::Error(
+ String::New("Invalid argument")));
+ }
+ diffieHellman->dh->priv_key =
+ BN_bin2bn(reinterpret_cast<unsigned char*>(buf), len, 0);
+ delete[] buf;
+ } else if (Buffer::HasInstance(args[0])) {
+ Local<Object> buffer = args[0]->ToObject();
+ diffieHellman->dh->priv_key =
+ BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
+ Buffer::Length(buffer), 0);
+ } else {
+ return ThrowException(Exception::Error(
+ String::New("First argument must be private key")));
+ }
+ }
+
+ return args.This();
+ }
+
+ DiffieHellman() : ObjectWrap() {
+ initialised_ = false;
+ dh = NULL;
+ }
+
+ ~DiffieHellman() {
+ if (dh != NULL) {
+ DH_free(dh);
+ }
+ }
+
+ private:
+ bool VerifyContext() {
+ int codes;
+ if (!DH_check(dh, &codes)) return false;
+ if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
+ if (codes & DH_CHECK_P_NOT_PRIME) return false;
+ if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
+ if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
+ return true;
+ }
+
+ static int DecodeBinary(Handle<Value> str, char** buf) {
+ int len = DecodeBytes(str);
+ *buf = new char[len];
+ int written = DecodeWrite(*buf, len, str, BINARY);
+ if (written != len) {
+ return -1;
+ }
+ return len;
+ }
+
+ static int DecodeWithEncoding(Handle<Value> str, Handle<Value> enc,
+ char** buf) {
+ int len = DecodeBinary(str, buf);
+ if (len == -1) {
+ return len;
+ }
+ String::Utf8Value encoding(enc->ToString());
+ char* retbuf = 0;
+ int retlen;
+
+ if (strcasecmp(*encoding, "hex") == 0) {
+ HexDecode((unsigned char*)*buf, len, &retbuf, &retlen);
+
+ } else if (strcasecmp(*encoding, "base64") == 0) {
+ unbase64((unsigned char*)*buf, len, &retbuf, &retlen);
+
+ } else if (strcasecmp(*encoding, "binary") == 0) {
+ // Binary - do nothing
+ } else {
+ fprintf(stderr, "node-crypto : Diffie-Hellman parameter encoding "
+ "can be binary, hex or base64\n");
+ }
+
+ if (retbuf != 0) {
+ delete [] *buf;
+ *buf = retbuf;
+ len = retlen;
+ }
+
+ return len;
+ }
+
+ static Local<Value> EncodeWithEncoding(Handle<Value> enc, char* buf,
+ int len) {
+ HandleScope scope;
+
+ Local<Value> outString;
+ String::Utf8Value encoding(enc->ToString());
+ char* retbuf;
+ int retlen;
+ if (strcasecmp(*encoding, "hex") == 0) {
+ // Hex encoding
+ HexEncode(reinterpret_cast<unsigned char*>(buf), len, &retbuf, &retlen);
+ outString = Encode(retbuf, retlen, BINARY);
+ delete [] retbuf;
+ } else if (strcasecmp(*encoding, "base64") == 0) {
+ base64(reinterpret_cast<unsigned char*>(buf), len, &retbuf, &retlen);
+ outString = Encode(retbuf, retlen, BINARY);
+ delete [] retbuf;
+ } else if (strcasecmp(*encoding, "binary") == 0) {
+ outString = Encode(buf, len, BINARY);
+ } else {
+ fprintf(stderr, "node-crypto : Diffie-Hellman parameter encoding "
+ "can be binary, hex or base64\n");
+ }
+
+ return scope.Close(outString);
+ }
+
+ bool initialised_;
+ DH* dh;
+};
+
+struct pbkdf2_req {
+ int err;
+ char* pass;
+ size_t passlen;
+ char* salt;
+ size_t saltlen;
+ size_t iter;
+ char* key;
+ size_t keylen;
+ Persistent<Function> callback;
+};
+
+void
+EIO_PBKDF2(uv_work_t* req) {
+ pbkdf2_req* request = (pbkdf2_req*)req->data;
+ request->err = PKCS5_PBKDF2_HMAC_SHA1(
+ request->pass,
+ request->passlen,
+ (unsigned char*)request->salt,
+ request->saltlen,
+ request->iter,
+ request->keylen,
+ (unsigned char*)request->key);
+ memset(request->pass, 0, request->passlen);
+ memset(request->salt, 0, request->saltlen);
+}
+
+void
+EIO_PBKDF2After(uv_work_t* req) {
+ HandleScope scope;
+
+ pbkdf2_req* request = (pbkdf2_req*)req->data;
+ delete req;
+
+ Handle<Value> argv[2];
+ if (request->err) {
+ argv[0] = Undefined();
+ argv[1] = Encode(request->key, request->keylen, BINARY);
+ memset(request->key, 0, request->keylen);
+ } else {
+ argv[0] = Exception::Error(String::New("PBKDF2 error"));
+ argv[1] = Undefined();
+ }
+
+ TryCatch try_catch;
+
+ request->callback->Call(Context::GetCurrent()->Global(), 2, argv);
+
+ if (try_catch.HasCaught())
+ FatalException(try_catch);
+
+ delete[] request->pass;
+ delete[] request->salt;
+ delete[] request->key;
+ request->callback.Dispose();
+
+ delete request;
+}
+
+Handle<Value>
+PBKDF2(const Arguments& args) {
+ HandleScope scope;
+
+ if (args.Length() != 5)
+ return ThrowException(Exception::TypeError(String::New("Bad parameter")));
+
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ ssize_t passlen = DecodeBytes(args[0], BINARY);
+ if (passlen < 0)
+ return ThrowException(Exception::TypeError(String::New("Bad password")));
+ char* pass = new char[passlen];
+ ssize_t pass_written = DecodeWrite(pass, passlen, args[0], BINARY);
+ assert(pass_written == passlen);
+
+ ASSERT_IS_STRING_OR_BUFFER(args[1]);
+ ssize_t saltlen = DecodeBytes(args[1], BINARY);
+ if (saltlen < 0)
+ return ThrowException(Exception::TypeError(String::New("Bad salt")));
+ char* salt = new char[saltlen];
+ ssize_t salt_written = DecodeWrite(salt, saltlen, args[1], BINARY);
+ assert(salt_written == saltlen);
+
+ if (!args[2]->IsNumber())
+ return ThrowException(Exception::TypeError(String::New("Iterations not a number")));
+ ssize_t iter = args[2]->Int32Value();
+ if (iter < 0)
+ return ThrowException(Exception::TypeError(String::New("Bad iterations")));
+
+ if (!args[3]->IsNumber())
+ return ThrowException(Exception::TypeError(String::New("Key length not a number")));
+ ssize_t keylen = args[3]->Int32Value();
+ if (keylen < 0)
+ return ThrowException(Exception::TypeError(String::New("Bad key length")));
+ char* key = new char[keylen];
+
+ if (!args[4]->IsFunction())
+ return ThrowException(Exception::TypeError(String::New("Callback not a function")));
+ Local<Function> callback = Local<Function>::Cast(args[4]);
+
+ pbkdf2_req* request = new pbkdf2_req;
+ request->err = 0;
+ request->pass = pass;
+ request->passlen = passlen;
+ request->salt = salt;
+ request->saltlen = saltlen;
+ request->iter = iter;
+ request->key = key;
+ request->keylen = keylen;
+ request->callback = Persistent<Function>::New(callback);
+
+ uv_work_t* req = new uv_work_t();
+ req->data = request;
+ uv_queue_work(uv_default_loop(), req, EIO_PBKDF2, EIO_PBKDF2After);
+
+ return Undefined();
+}
void InitCrypto(Handle<Object> target) {
HandleScope scope;
ERR_load_crypto_strings();
// Turn off compression. Saves memory - do it in userland.
-#ifdef SSL_COMP_get_compression_methods
- // Before OpenSSL 0.9.8 this was not possible.
- STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_methods();
+#if !defined(OPENSSL_NO_COMP)
+ STACK_OF(SSL_COMP)* comp_methods =
+#if OPENSSL_VERSION_NUMBER < 0x00908000L
+ SSL_COMP_get_compression_method()
+#else
+ SSL_COMP_get_compression_methods()
+#endif
+ ;
sk_SSL_COMP_zero(comp_methods);
assert(sk_SSL_COMP_num(comp_methods) == 0);
#endif
Connection::Initialize(target);
Cipher::Initialize(target);
Decipher::Initialize(target);
+ DiffieHellman::Initialize(target);
Hmac::Initialize(target);
Hash::Initialize(target);
Sign::Initialize(target);
Verify::Initialize(target);
+ NODE_SET_METHOD(target, "PBKDF2", PBKDF2);
+
subject_symbol = NODE_PSYMBOL("subject");
issuer_symbol = NODE_PSYMBOL("issuer");
valid_from_symbol = NODE_PSYMBOL("valid_from");
valid_to_symbol = NODE_PSYMBOL("valid_to");
+ subjectaltname_symbol = NODE_PSYMBOL("subjectaltname");
+ modulus_symbol = NODE_PSYMBOL("modulus");
+ exponent_symbol = NODE_PSYMBOL("exponent");
fingerprint_symbol = NODE_PSYMBOL("fingerprint");
name_symbol = NODE_PSYMBOL("name");
version_symbol = NODE_PSYMBOL("version");