Merge remote branch 'origin/v0.4'
authorRyan Dahl <ry@tinyclouds.org>
Thu, 15 Sep 2011 22:08:38 +0000 (15:08 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Thu, 15 Sep 2011 22:08:38 +0000 (15:08 -0700)
1  2 
src/node_crypto.cc

diff --combined src/node_crypto.cc
  #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 {
  
@@@ -63,9 -53,6 +63,9 @@@ using namespace v8
  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;
@@@ -74,14 -61,11 +74,14 @@@ static Persistent<String> name_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"));
  
@@@ -92,7 -76,6 +92,7 @@@
    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());
@@@ -458,21 -441,6 +458,21 @@@ Handle<Value> SecureContext::SetCiphers
    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;
@@@ -536,11 -504,22 +536,22 @@@ int Connection::HandleSSLError(const ch
      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;
    }
@@@ -588,9 -567,6 +599,9 @@@ void Connection::Initialize(Handle<Obje
    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());
  }
  
@@@ -659,126 -624,6 +670,126 @@@ static int VerifyCallback(int preverify
    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
@@@ -1077,63 -895,27 +1088,63 @@@ Handle<Value> Connection::GetPeerCertif
    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;
  
@@@ -1490,81 -1194,6 +1501,81 @@@ Handle<Value> Connection::Close(const A
    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,
@@@ -2631,25 -2260,15 +2642,25 @@@ class Hmac : public ObjectWrap 
        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")));
@@@ -3370,634 -2989,6 +3381,634 @@@ class Verify : public ObjectWrap 
  
  };
  
 +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");