[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/CombatCode/node/master/src/node_crypto.cc [Back]  [Original]

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "node_crypto.h"
#include "node_crypto_bio.h"
#include "node_crypto_groups.h"
#include "v8.h"

#include "node.h"
#include "node_buffer.h"
#include "string_bytes.h"
#include "node_root_certs.h"

#include 
#ifdef _MSC_VER
#define strcasecmp _stricmp
#endif

#include 
#include 

#if OPENSSL_VERSION_NUMBER >= 0x10000000L
# define OPENSSL_CONST const
#else
# define OPENSSL_CONST
#endif

#define ASSERT_IS_STRING_OR_BUFFER(val) do {                  \
    if (!Buffer::HasInstance(val) && !val->IsString()) {      \
      return ThrowTypeError("Not a string or buffer");        \
    }                                                         \
  } while (0)

#define ASSERT_IS_BUFFER(val) do {                            \
    if (!Buffer::HasInstance(val)) {                          \
      return ThrowTypeError("Not a buffer");                  \
    }                                                         \
  } while (0)

static const char PUBLIC_KEY_PFX[] =  "-----BEGIN PUBLIC KEY-----";
static const int PUBLIC_KEY_PFX_LEN = sizeof(PUBLIC_KEY_PFX) - 1;
static const char PUBRSA_KEY_PFX[] =  "-----BEGIN RSA PUBLIC KEY-----";
static const int PUBRSA_KEY_PFX_LEN = sizeof(PUBRSA_KEY_PFX) - 1;
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 {

using v8::Array;
using v8::Exception;
using v8::False;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Integer;
using v8::Local;
using v8::Null;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::ThrowException;


// Forcibly clear OpenSSL's error stack on return. This stops stale errors
// from popping up later in the lifecycle of crypto operations where they
// would cause spurious failures. It's a rather blunt method, though.
// ERR_clear_error() isn't necessarily cheap either.
struct ClearErrorOnReturn {
  ~ClearErrorOnReturn() { ERR_clear_error(); }
};

static Cached errno_symbol;
static Cached syscall_symbol;
static Cached subject_symbol;
static Cached subjectaltname_symbol;
static Cached modulus_symbol;
static Cached exponent_symbol;
static Cached issuer_symbol;
static Cached valid_from_symbol;
static Cached valid_to_symbol;
static Cached fingerprint_symbol;
static Cached name_symbol;
static Cached version_symbol;
static Cached ext_key_usage_symbol;
static Cached onhandshakestart_sym;
static Cached onhandshakedone_sym;
static Cached onclienthello_sym;
static Cached onnewsession_sym;
static Cached sessionid_sym;

static Persistent secure_context_constructor;

static uv_rwlock_t* locks;


static void crypto_threadid_cb(CRYPTO_THREADID* tid) {
  CRYPTO_THREADID_set_numeric(tid, uv_thread_self());
}


static void crypto_lock_init(void) {
  int i, n;

  n = CRYPTO_num_locks();
  locks = new uv_rwlock_t[n];

  for (i = 0; i < n; i++)
    if (uv_rwlock_init(locks + i))
      abort();
}


static void crypto_lock_cb(int mode, int n, const char* file, int line) {
  assert((mode & CRYPTO_LOCK) || (mode & CRYPTO_UNLOCK));
  assert((mode & CRYPTO_READ) || (mode & CRYPTO_WRITE));

  if (mode & CRYPTO_LOCK) {
    if (mode & CRYPTO_READ)
      uv_rwlock_rdlock(locks + n);
    else
      uv_rwlock_wrlock(locks + n);
  } else {
    if (mode & CRYPTO_READ)
      uv_rwlock_rdunlock(locks + n);
    else
      uv_rwlock_wrunlock(locks + n);
  }
}


void ThrowCryptoErrorHelper(unsigned long err, bool is_type_error) {
  HandleScope scope(node_isolate);
  char errmsg[128];
  ERR_error_string_n(err, errmsg, sizeof(errmsg));
  if (is_type_error)
    ThrowTypeError(errmsg);
  else
    ThrowError(errmsg);
}


void ThrowCryptoError(unsigned long err) {
  ThrowCryptoErrorHelper(err, false);
}


void ThrowCryptoTypeError(unsigned long err) {
  ThrowCryptoErrorHelper(err, true);
}


void SecureContext::Initialize(Handle target) {
  HandleScope scope(node_isolate);

  Local t = FunctionTemplate::New(SecureContext::New);
  secure_context_constructor.Reset(node_isolate, t);

  t->InstanceTemplate()->SetInternalFieldCount(1);
  t->SetClassName(String::NewSymbol("SecureContext"));

  NODE_SET_PROTOTYPE_METHOD(t, "init", SecureContext::Init);
  NODE_SET_PROTOTYPE_METHOD(t, "setKey", SecureContext::SetKey);
  NODE_SET_PROTOTYPE_METHOD(t, "setCert", SecureContext::SetCert);
  NODE_SET_PROTOTYPE_METHOD(t, "addCACert", SecureContext::AddCACert);
  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, "setSessionIdContext",
                               SecureContext::SetSessionIdContext);
  NODE_SET_PROTOTYPE_METHOD(t, "setSessionTimeout",
                               SecureContext::SetSessionTimeout);
  NODE_SET_PROTOTYPE_METHOD(t, "close", SecureContext::Close);
  NODE_SET_PROTOTYPE_METHOD(t, "loadPKCS12", SecureContext::LoadPKCS12);

  target->Set(String::NewSymbol("SecureContext"), t->GetFunction());
}


void SecureContext::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  SecureContext *p = new SecureContext();
  p->Wrap(args.This());
}


void SecureContext::Init(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  OPENSSL_CONST SSL_METHOD *method = SSLv23_method();

  if (args.Length() == 1 && args[0]->IsString()) {
    String::Utf8Value sslmethod(args[0]);

    if (strcmp(*sslmethod, "SSLv2_method") == 0) {
#ifndef OPENSSL_NO_SSL2
      method = SSLv2_method();
#else
      return ThrowError("SSLv2 methods disabled");
#endif
    } else if (strcmp(*sslmethod, "SSLv2_server_method") == 0) {
#ifndef OPENSSL_NO_SSL2
      method = SSLv2_server_method();
#else
      return ThrowError("SSLv2 methods disabled");
#endif
    } else if (strcmp(*sslmethod, "SSLv2_client_method") == 0) {
#ifndef OPENSSL_NO_SSL2
      method = SSLv2_client_method();
#else
      return ThrowError("SSLv2 methods disabled");
#endif
    } else if (strcmp(*sslmethod, "SSLv3_method") == 0) {
      method = SSLv3_method();
    } else if (strcmp(*sslmethod, "SSLv3_server_method") == 0) {
      method = SSLv3_server_method();
    } else if (strcmp(*sslmethod, "SSLv3_client_method") == 0) {
      method = SSLv3_client_method();
    } else if (strcmp(*sslmethod, "SSLv23_method") == 0) {
      method = SSLv23_method();
    } else if (strcmp(*sslmethod, "SSLv23_server_method") == 0) {
      method = SSLv23_server_method();
    } else if (strcmp(*sslmethod, "SSLv23_client_method") == 0) {
      method = SSLv23_client_method();
    } else if (strcmp(*sslmethod, "TLSv1_method") == 0) {
      method = TLSv1_method();
    } else if (strcmp(*sslmethod, "TLSv1_server_method") == 0) {
      method = TLSv1_server_method();
    } else if (strcmp(*sslmethod, "TLSv1_client_method") == 0) {
      method = TLSv1_client_method();
    } else {
      return ThrowError("Unknown method");
    }
  }

  sc->ctx_ = SSL_CTX_new(method);

  // SSL session cache configuration
  SSL_CTX_set_session_cache_mode(sc->ctx_,
                                 SSL_SESS_CACHE_SERVER |
                                 SSL_SESS_CACHE_NO_INTERNAL |
                                 SSL_SESS_CACHE_NO_AUTO_CLEAR);
  SSL_CTX_sess_set_get_cb(sc->ctx_, GetSessionCallback);
  SSL_CTX_sess_set_new_cb(sc->ctx_, NewSessionCallback);

  sc->ca_store_ = NULL;
}


SSL_SESSION* SecureContext::GetSessionCallback(SSL* s,
                                               unsigned char* key,
                                               int len,
                                               int* copy) {
  HandleScope scope(node_isolate);

  Connection* p = static_cast(SSL_get_app_data(s));

  *copy = 0;
  SSL_SESSION* sess = p->next_sess_;
  p->next_sess_ = NULL;

  return sess;
}


int SecureContext::NewSessionCallback(SSL* s, SSL_SESSION* sess) {
  HandleScope scope(node_isolate);

  Connection* p = static_cast(SSL_get_app_data(s));

  // Check if session is small enough to be stored
  int size = i2d_SSL_SESSION(sess, NULL);
  if (size > kMaxSessionSize) return 0;

  // Serialize session
  char* serialized = new char[size];
  unsigned char* pserialized = reinterpret_cast(serialized);
  memset(serialized, 0, size);
  i2d_SSL_SESSION(sess, &pserialized);

  Handle argv[2] = {
    Buffer::New(reinterpret_cast(sess->session_id),
                sess->session_id_length),
    Buffer::Use(serialized, size)
  };

  if (onnewsession_sym.IsEmpty()) {
    onnewsession_sym = String::New("onnewsession");
  }

  MakeCallback(p->handle(node_isolate),
               onnewsession_sym,
               ARRAY_SIZE(argv),
               argv);

  return 0;
}


// Takes a string or buffer and loads it into a BIO.
// Caller responsible for BIO_free_all-ing the returned object.
static BIO* LoadBIO (Handle v) {
  BIO *bio = BIO_new(NodeBIO::GetMethod());
  if (!bio) return NULL;

  HandleScope scope(node_isolate);

  int r = -1;

  if (v->IsString()) {
    String::Utf8Value s(v);
    r = BIO_write(bio, *s, s.length());
  } else if (Buffer::HasInstance(v)) {
    char* buffer_data = Buffer::Data(v);
    size_t buffer_length = Buffer::Length(v);
    r = BIO_write(bio, buffer_data, buffer_length);
  }

  if (r IsString()) {
    return ThrowTypeError("Bad parameter");
  }

  BIO *bio = LoadBIO(args[0]);
  if (!bio) return;

  String::Utf8Value passphrase(args[1]);

  EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, NULL,
                                          len == 1 ? NULL : *passphrase);

  if (!key) {
    BIO_free_all(bio);
    unsigned long err = ERR_get_error();
    if (!err) {
      return ThrowError("PEM_read_bio_PrivateKey");
    }
    return ThrowCryptoError(err);
  }

  SSL_CTX_use_PrivateKey(sc->ctx_, key);
  EVP_PKEY_free(key);
  BIO_free_all(bio);
}


// Read a file that contains our certificate in "PEM" format,
// possibly followed by a sequence of CA certificates that should be
// sent to the peer in the Certificate message.
//
// Taken from OpenSSL - editted for style.
int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, BIO *in) {
  int ret = 0;
  X509 *x = NULL;

  x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);

  if (x == NULL) {
    SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
    goto end;
  }

  ret = SSL_CTX_use_certificate(ctx, x);

  if (ERR_peek_error() != 0) {
    // Key/certificate mismatch doesn't imply ret==0 ...
    ret = 0;
  }

  if (ret) {
    // If we could set up our certificate, now proceed to
    // the CA certificates.
    X509 *ca;
    int r;
    unsigned long err;

    if (ctx->extra_certs != NULL) {
      sk_X509_pop_free(ctx->extra_certs, X509_free);
      ctx->extra_certs = NULL;
    }

    while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
      r = SSL_CTX_add_extra_chain_cert(ctx, ca);

      if (!r) {
        X509_free(ca);
        ret = 0;
        goto end;
      }
      // Note that we must not free r if it was successfully
      // added to the chain (while we must free the main
      // certificate, since its reference count is increased
      // by SSL_CTX_use_certificate).
    }

    // When the while loop ends, it's usually just EOF.
    err = ERR_peek_last_error();
    if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
        ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
      ERR_clear_error();
    } else  {
      // some real error
      ret = 0;
    }
  }

end:
  if (x != NULL) X509_free(x);
  return ret;
}


void SecureContext::SetCert(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1) {
    return ThrowTypeError("Bad parameter");
  }

  BIO* bio = LoadBIO(args[0]);
  if (!bio) return;

  int rv = SSL_CTX_use_certificate_chain(sc->ctx_, bio);

  BIO_free_all(bio);

  if (!rv) {
    unsigned long err = ERR_get_error();
    if (!err) {
      return ThrowError("SSL_CTX_use_certificate_chain");
    }
    return ThrowCryptoError(err);
  }
}


void SecureContext::AddCACert(const FunctionCallbackInfo& args) {
  bool newCAStore = false;
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1) {
    return ThrowTypeError("Bad parameter");
  }

  if (!sc->ca_store_) {
    sc->ca_store_ = X509_STORE_new();
    newCAStore = true;
  }

  X509* x509 = LoadX509(args[0]);
  if (!x509) return;

  X509_STORE_add_cert(sc->ca_store_, x509);
  SSL_CTX_add_client_CA(sc->ctx_, x509);

  X509_free(x509);

  if (newCAStore) {
    SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_);
  }
}


void SecureContext::AddCRL(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1) {
    return ThrowTypeError("Bad parameter");
  }

  ClearErrorOnReturn clear_error_on_return;
  (void) &clear_error_on_return;  // Silence compiler warning.

  BIO *bio = LoadBIO(args[0]);
  if (!bio) return;

  X509_CRL *x509 = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);

  if (x509 == NULL) {
    BIO_free_all(bio);
    return;
  }

  X509_STORE_add_crl(sc->ca_store_, x509);
  X509_STORE_set_flags(sc->ca_store_, X509_V_FLAG_CRL_CHECK |
                                      X509_V_FLAG_CRL_CHECK_ALL);
  BIO_free_all(bio);
  X509_CRL_free(x509);
}


void SecureContext::AddRootCerts(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  assert(sc->ca_store_ == NULL);

  if (!root_cert_store) {
    root_cert_store = X509_STORE_new();

    for (int i = 0; root_certs[i]; i++) {
      BIO *bp = BIO_new(NodeBIO::GetMethod());

      if (!BIO_write(bp, root_certs[i], strlen(root_certs[i]))) {
        BIO_free_all(bp);
        return;
      }

      X509 *x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);

      if (x509 == NULL) {
        BIO_free_all(bp);
        return;
      }

      X509_STORE_add_cert(root_cert_store, x509);

      BIO_free_all(bp);
      X509_free(x509);
    }
  }

  sc->ca_store_ = root_cert_store;
  SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_);
}


void SecureContext::SetCiphers(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1 || !args[0]->IsString()) {
    return ThrowTypeError("Bad parameter");
  }

  String::Utf8Value ciphers(args[0]);
  SSL_CTX_set_cipher_list(sc->ctx_, *ciphers);
}


void SecureContext::SetOptions(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1 || !args[0]->IntegerValue()) {
    return ThrowTypeError("Bad parameter");
  }

  SSL_CTX_set_options(sc->ctx_, args[0]->IntegerValue());
}


void SecureContext::SetSessionIdContext(
    const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1 || !args[0]->IsString()) {
    return ThrowTypeError("Bad parameter");
  }

  String::Utf8Value sessionIdContext(args[0]);
  const unsigned char* sid_ctx = (const unsigned char*) *sessionIdContext;
  unsigned int sid_ctx_len = sessionIdContext.length();

  int r = SSL_CTX_set_session_id_context(sc->ctx_, sid_ctx, sid_ctx_len);
  if (r == 1) return;

  BIO* bio;
  BUF_MEM* mem;
  Local message;

  bio = BIO_new(BIO_s_mem());
  if (bio == NULL) {
    message = String::New("SSL_CTX_set_session_id_context error");
  }
  else {
    ERR_print_errors(bio);
    BIO_get_mem_ptr(bio, &mem);
    message = String::New(mem->data, mem->length);
    BIO_free_all(bio);
  }

  ThrowException(Exception::TypeError(message));
}


void SecureContext::SetSessionTimeout(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() != 1 || !args[0]->IsInt32()) {
    return ThrowTypeError("Bad parameter");
  }

  int32_t sessionTimeout = args[0]->Int32Value();
  SSL_CTX_set_timeout(sc->ctx_, sessionTimeout);
}


void SecureContext::Close(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  SecureContext *sc = ObjectWrap::Unwrap(args.This());
  sc->FreeCTXMem();
}


//Takes .pfx or .p12 and password in string or buffer format
void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  BIO* in = NULL;
  PKCS12* p12 = NULL;
  EVP_PKEY* pkey = NULL;
  X509* cert = NULL;
  STACK_OF(X509)* extraCerts = NULL;
  char* pass = NULL;
  bool ret = false;

  SecureContext *sc = ObjectWrap::Unwrap(args.This());

  if (args.Length() < 1) {
    return ThrowTypeError("Bad parameter");
  }

  in = LoadBIO(args[0]);
  if (in == NULL) {
    return ThrowError("Unable to load BIO");
  }

  if (args.Length() >= 2) {
    ASSERT_IS_BUFFER(args[1]);

    int passlen = Buffer::Length(args[1]);
    if (passlen < 0) {
      BIO_free_all(in);
      return ThrowTypeError("Bad password");
    }
    pass = new char[passlen + 1];
    int pass_written = DecodeWrite(pass, passlen, args[1], BINARY);

    assert(pass_written == passlen);
    pass[passlen] = '\0';
  }

  if (d2i_PKCS12_bio(in, &p12) &&
      PKCS12_parse(p12, pass, &pkey, &cert, &extraCerts) &&
      SSL_CTX_use_certificate(sc->ctx_, cert) &&
      SSL_CTX_use_PrivateKey(sc->ctx_, pkey))
  {
    // set extra certs
    while (X509* x509 = sk_X509_pop(extraCerts)) {
      if (!sc->ca_store_) {
        sc->ca_store_ = X509_STORE_new();
        SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_);
      }

      X509_STORE_add_cert(sc->ca_store_, x509);
      SSL_CTX_add_client_CA(sc->ctx_, x509);
      X509_free(x509);
    }

    EVP_PKEY_free(pkey);
    X509_free(cert);
    sk_X509_free(extraCerts);

    ret = true;
  }

  PKCS12_free(p12);
  BIO_free_all(in);
  delete[] pass;

  if (!ret) {
    unsigned long err = ERR_get_error();
    const char* str = ERR_reason_error_string(err);
    return ThrowError(str);
  }
}


size_t ClientHelloParser::Write(const uint8_t* data, size_t len) {
  HandleScope scope(node_isolate);

  // Just accumulate data, everything will be pushed to BIO later
  if (state_ == kPaused) return 0;

  // Copy incoming data to the internal buffer
  // (which has a size of the biggest possible TLS frame)
  size_t available = sizeof(data_) - offset_;
  size_t copied = len < available ? len : available;
  memcpy(data_ + offset_, data, copied);
  offset_ += copied;

  // Vars for parsing hello
  bool is_clienthello = false;
  uint8_t session_size = -1;
  uint8_t* session_id = NULL;
  Local hello;
  Handle argv[1];

  switch (state_) {
   case kWaiting:
    // >= 5 bytes for header parsing
    if (offset_ < 5) break;

    if (data_[0] == kChangeCipherSpec || data_[0] == kAlert ||
        data_[0] == kHandshake || data_[0] == kApplicationData) {
      frame_len_ = (data_[3] = 5 + frame size bytes for frame parsing
    if (offset_ < body_offset_ + frame_len_) break;

    // Skip unsupported frames and gather some data from frame

    // TODO: Check protocol version
    if (data_[body_offset_] == kClientHello) {
      is_clienthello = true;
      uint8_t* body;
      size_t session_offset;

      if (state_ == kTLSHeader) {
        // Skip frame header, hello header, protocol version and random data
        session_offset = body_offset_ + 4 + 2 + 32;

        if (session_offset + 1 < offset_) {
          body = data_ + session_offset;
          session_size = *body;
          session_id = body + 1;
        }
      } else if (state_ == kSSLHeader) {
        // Skip header, version
        session_offset = body_offset_ + 3;

        if (session_offset + 4 < offset_) {
          body = data_ + session_offset;

          int ciphers_size = (body[0]  data_ + offset_) {
        Finish();
        return copied;
      }

      // TODO: Parse other things?
    }

    // Not client hello - let OpenSSL handle it
    if (!is_clienthello) {
      Finish();
      return copied;
    }

    // Parse frame, call javascript handler and
    // move parser into the paused state
    if (onclienthello_sym.IsEmpty()) {
      onclienthello_sym = String::New("onclienthello");
    }
    if (sessionid_sym.IsEmpty()) {
      sessionid_sym = String::New("sessionId");
    }

    state_ = kPaused;
    hello = Object::New();
    hello->Set(sessionid_sym,
               Buffer::New(reinterpret_cast(session_id),
                           session_size));

    argv[0] = hello;
    MakeCallback(conn_->handle(node_isolate),
                 onclienthello_sym,
                 ARRAY_SIZE(argv),
                 argv);
    break;
   case kEnded:
   default:
    break;
  }

  return copied;
}


void ClientHelloParser::Finish() {
  assert(state_ != kEnded);
  state_ = kEnded;

  // Write all accumulated data
  int r = BIO_write(conn_->bio_read_, reinterpret_cast(data_), offset_);
  conn_->HandleBIOError(conn_->bio_read_, "BIO_write", r);
  conn_->SetShutdownFlags();
}


#ifdef SSL_PRINT_DEBUG
# define DEBUG_PRINT(...) fprintf (stderr, __VA_ARGS__)
#else
# define DEBUG_PRINT(...)
#endif


int Connection::HandleBIOError(BIO *bio, const char* func, int rv) {
  if (rv >= 0) return rv;

  int retry = BIO_should_retry(bio);
  (void) retry; // unused if !defined(SSL_PRINT_DEBUG)

  if (BIO_should_write(bio)) {
    DEBUG_PRINT("[%p] BIO: %s want write. should retry %d\n", ssl_, func, retry);
    return 0;

  } else if (BIO_should_read(bio)) {
    DEBUG_PRINT("[%p] BIO: %s want read. should retry %d\n", ssl_, func, retry);
    return 0;

  } else {
    static char ssl_error_buf[512];
    ERR_error_string_n(rv, ssl_error_buf, sizeof(ssl_error_buf));

    HandleScope scope(node_isolate);
    Local e = Exception::Error(String::New(ssl_error_buf));
    handle(node_isolate)->Set(String::New("error"), e);

    DEBUG_PRINT("[%p] BIO: %s failed: (%d) %s\n", ssl_, func, rv, ssl_error_buf);

    return rv;
  }

  return 0;
}


int Connection::HandleSSLError(const char* func,
                               int rv,
                               ZeroStatus zs,
                               SyscallStatus ss) {
  ClearErrorOnReturn clear_error_on_return;
  (void) &clear_error_on_return;  // Silence unused variable warning.

  if (rv > 0) return rv;
  if ((rv == 0) && (zs == kZeroIsNotAnError)) return rv;

  int err = SSL_get_error(ssl_, rv);

  if (err == SSL_ERROR_NONE) {
    return 0;

  } else if (err == SSL_ERROR_WANT_WRITE) {
    DEBUG_PRINT("[%p] SSL: %s want write\n", ssl_, func);
    return 0;

  } else if (err == SSL_ERROR_WANT_READ) {
    DEBUG_PRINT("[%p] SSL: %s want read\n", ssl_, func);
    return 0;

  } else if (err == SSL_ERROR_ZERO_RETURN) {
    handle(node_isolate)->Set(String::New("error"),
                              Exception::Error(String::New("ZERO_RETURN")));
    return rv;

  } else if ((err == SSL_ERROR_SYSCALL) && (ss == kIgnoreSyscall)) {
    return 0;

  } else {
    HandleScope scope(node_isolate);
    BUF_MEM* mem;
    BIO *bio;

    assert(err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL);

    // 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 propagating these errors up
    // into JavaScript. There is no test which demonstrates this problem.
    // https://github.com/joyent/node/issues/1719
    if ((bio = BIO_new(BIO_s_mem()))) {
      ERR_print_errors(bio);
      BIO_get_mem_ptr(bio, &mem);
      Local e = Exception::Error(String::New(mem->data, mem->length));
      handle(node_isolate)->Set(String::New("error"), e);
      BIO_free_all(bio);
    }

    return rv;
  }

  return 0;
}


void Connection::ClearError() {
#ifndef NDEBUG
  HandleScope scope(node_isolate);

  // We should clear the error in JS-land
  assert(
      handle(node_isolate)->Get(String::New("error"))->BooleanValue() == false);
#endif // NDEBUG
}


void Connection::SetShutdownFlags() {
  HandleScope scope(node_isolate);

  int flags = SSL_get_shutdown(ssl_);

  if (flags & SSL_SENT_SHUTDOWN) {
    handle(node_isolate)->Set(String::New("sentShutdown"), True(node_isolate));
  }

  if (flags & SSL_RECEIVED_SHUTDOWN) {
    handle(node_isolate)->Set(String::New("receivedShutdown"),
                              True(node_isolate));
  }
}


void Connection::Initialize(Handle target) {
  HandleScope scope(node_isolate);

  Local t = FunctionTemplate::New(Connection::New);
  t->InstanceTemplate()->SetInternalFieldCount(1);
  t->SetClassName(String::NewSymbol("Connection"));

  NODE_SET_PROTOTYPE_METHOD(t, "encIn", Connection::EncIn);
  NODE_SET_PROTOTYPE_METHOD(t, "clearOut", Connection::ClearOut);
  NODE_SET_PROTOTYPE_METHOD(t, "clearIn", Connection::ClearIn);
  NODE_SET_PROTOTYPE_METHOD(t, "encOut", Connection::EncOut);
  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, "loadSession", Connection::LoadSession);
  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, "start", Connection::Start);
  NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Connection::Shutdown);
  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());
}


static int VerifyCallback(int preverify_ok, X509_STORE_CTX *ctx) {
  // Quoting SSL_set_verify(3ssl):
  //
  //   The VerifyCallback function is used to control the behaviour when
  //   the SSL_VERIFY_PEER flag is set. It must be supplied by the
  //   application and receives two arguments: preverify_ok indicates,
  //   whether the verification of the certificate in question was passed
  //   (preverify_ok=1) or not (preverify_ok=0). x509_ctx is a pointer to
  //   the complete context used for the certificate chain verification.
  //
  //   The certificate chain is checked starting with the deepest nesting
  //   level (the root CA certificate) and worked upward to the peer's
  //   certificate.  At each level signatures and issuer attributes are
  //   checked.  Whenever a verification error is found, the error number is
  //   stored in x509_ctx and VerifyCallback is called with preverify_ok=0.
  //   By applying X509_CTX_store_* functions VerifyCallback can locate the
  //   certificate in question and perform additional steps (see EXAMPLES).
  //   If no error is found for a certificate, VerifyCallback is called
  //   with preverify_ok=1 before advancing to the next level.
  //
  //   The return value of VerifyCallback controls the strategy of the
  //   further verification process. If VerifyCallback returns 0, the
  //   verification process is immediately stopped with "verification
  //   failed" state. If SSL_VERIFY_PEER is set, a verification failure
  //   alert is sent to the peer and the TLS/SSL handshake is terminated. If
  //   VerifyCallback returns 1, the verification process is continued. If
  //   VerifyCallback always returns 1, the TLS/SSL handshake will not be
  //   terminated with respect to verification failures and the connection
  //   will be established. The calling process can however retrieve the
  //   error code of the last verification error using
  //   SSL_get_verify_result(3) or by maintaining its own error storage
  //   managed by VerifyCallback.
  //
  //   If no VerifyCallback is specified, the default callback will be
  //   used.  Its return value is identical to preverify_ok, so that any
  //   verification failure will lead to a termination of the TLS/SSL
  //   handshake with an alert message, if SSL_VERIFY_PEER is set.
  //
  // Since we cannot perform I/O quickly enough in this callback, we ignore
  // all preverify_ok errors and let the handshake continue. It is
  // imparative that the user use Connection::VerifyError after the
  // 'secure' callback has been made.
  return 1;
}

#ifdef OPENSSL_NPN_NEGOTIATED

int Connection::AdvertiseNextProtoCallback_(SSL *s,
                                            const unsigned char** data,
                                            unsigned int *len,
                                            void *arg) {

  Connection *p = static_cast(SSL_get_app_data(s));

  if (p->npnProtos_.IsEmpty()) {
    // No initialization - no NPN protocols
    *data = reinterpret_cast("");
    *len = 0;
  } else {
    Local obj = PersistentToLocal(p->npnProtos_);
    *data = reinterpret_cast(Buffer::Data(obj));
    *len = Buffer::Length(obj);
  }

  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 SSL_get_app_data(s);

  // Release old protocol handler if present
  p->selectedNPNProto_.Dispose();

  if (p->npnProtos_.IsEmpty()) {
    // We should at least select one protocol
    // If server is using NPN
    *out = reinterpret_cast(const_cast("http/1.1"));
    *outlen = 8;

    // set status unsupported
    p->selectedNPNProto_.Reset(node_isolate, False(node_isolate));

    return SSL_TLSEXT_ERR_OK;
  }

  Local obj = PersistentToLocal(p->npnProtos_);
  const unsigned char* npnProtos =
      reinterpret_cast(Buffer::Data(obj));

  int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtos,
                                     Buffer::Length(obj));

  switch (status) {
    case OPENSSL_NPN_UNSUPPORTED:
      p->selectedNPNProto_.Reset(node_isolate, Null(node_isolate));
      break;
    case OPENSSL_NPN_NEGOTIATED:
      p->selectedNPNProto_.Reset(
          node_isolate,
          String::New(reinterpret_cast(*out), *outlen));
      break;
    case OPENSSL_NPN_NO_OVERLAP:
      p->selectedNPNProto_.Reset(node_isolate, False(node_isolate));
      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(node_isolate);

  Connection *p = static_cast SSL_get_app_data(s);

  const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);

  if (servername) {
    p->servername_.Reset(node_isolate, String::New(servername));

    // Call the SNI callback and use its return value as context
    if (!p->sniObject_.IsEmpty()) {
      p->sniContext_.Dispose();

      Local arg = PersistentToLocal(p->servername_);
      Local ret = MakeCallback(p->sniObject_, "onselect", 1, &arg);

      // If ret is SecureContext
      if (HasInstance(secure_context_constructor, ret)) {
        p->sniContext_.Reset(node_isolate, ret);
        SecureContext* sc = ObjectWrap::Unwrap(ret.As());
        SSL_set_SSL_CTX(s, sc->ctx_);
      } else {
        return SSL_TLSEXT_ERR_NOACK;
      }
    }
  }

  return SSL_TLSEXT_ERR_OK;
}
#endif

void Connection::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *p = new Connection();
  p->Wrap(args.This());

  if (args.Length() < 1 || !args[0]->IsObject()) {
    return ThrowError("First argument must be a crypto module Credentials");
  }

  SecureContext *sc = ObjectWrap::Unwrap(args[0]->ToObject());

  bool is_server = args[1]->BooleanValue();

  p->ssl_ = SSL_new(sc->ctx_);
  p->bio_read_ = BIO_new(NodeBIO::GetMethod());
  p->bio_write_ = BIO_new(NodeBIO::GetMethod());

  SSL_set_app_data(p->ssl_, p);

  if (is_server) SSL_set_info_callback(p->ssl_, SSLInfoCallback);

#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]);
    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
  long mode = SSL_get_mode(p->ssl_);
  SSL_set_mode(p->ssl_, mode | SSL_MODE_RELEASE_BUFFERS);
#endif


  int verify_mode;
  if (is_server) {
    bool request_cert = args[2]->BooleanValue();
    if (!request_cert) {
      // Note reject_unauthorized ignored.
      verify_mode = SSL_VERIFY_NONE;
    } else {
      bool reject_unauthorized = args[3]->BooleanValue();
      verify_mode = SSL_VERIFY_PEER;
      if (reject_unauthorized) verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
    }
  } else {
    // Note request_cert and reject_unauthorized are ignored for clients.
    verify_mode = SSL_VERIFY_NONE;
  }


  // Always allow a connection. We'll reject in javascript.
  SSL_set_verify(p->ssl_, verify_mode, VerifyCallback);

  if ((p->is_server_ = is_server)) {
    SSL_set_accept_state(p->ssl_);
  } else {
    SSL_set_connect_state(p->ssl_);
  }
}


void Connection::SSLInfoCallback(const SSL *ssl_, int where, int ret) {
  // Be compatible with older versions of OpenSSL. SSL_get_app_data() wants
  // a non-const SSL* in OpenSSL handle(node_isolate), onhandshakestart_sym, 0, NULL);
  }
  if (where & SSL_CB_HANDSHAKE_DONE) {
    HandleScope scope(node_isolate);
    Connection* c = static_cast(SSL_get_app_data(ssl));
    if (onhandshakedone_sym.IsEmpty()) {
      onhandshakedone_sym = String::New("onhandshakedone");
    }
    MakeCallback(c->handle(node_isolate), onhandshakedone_sym, 0, NULL);
  }
}


void Connection::EncIn(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 3) {
    return ThrowTypeError("Takes 3 parameters");
  }

  if (!Buffer::HasInstance(args[0])) {
    return ThrowTypeError("Second argument should be a buffer");
  }

  char* buffer_data = Buffer::Data(args[0]);
  size_t buffer_length = Buffer::Length(args[0]);

  size_t off = args[1]->Int32Value();
  size_t len = args[2]->Int32Value();
  if (off + len > buffer_length) {
    return ThrowError("off + len > buffer.length");
  }

  int bytes_written;
  char* data = buffer_data + off;

  if (ss->is_server_ && !ss->hello_parser_.ended()) {
    bytes_written = ss->hello_parser_.Write(reinterpret_cast(data),
                                            len);
  } else {
    bytes_written = BIO_write(ss->bio_read_, data, len);
    ss->HandleBIOError(ss->bio_read_, "BIO_write", bytes_written);
    ss->SetShutdownFlags();
  }

  args.GetReturnValue().Set(bytes_written);
}


void Connection::ClearOut(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 3) {
    return ThrowTypeError("Takes 3 parameters");
  }

  if (!Buffer::HasInstance(args[0])) {
    return ThrowTypeError("Second argument should be a buffer");
  }

  char* buffer_data = Buffer::Data(args[0]);
  size_t buffer_length = Buffer::Length(args[0]);

  size_t off = args[1]->Int32Value();
  size_t len = args[2]->Int32Value();
  if (off + len > buffer_length) {
    return ThrowError("off + len > buffer.length");
  }

  if (!SSL_is_init_finished(ss->ssl_)) {
    int rv;

    if (ss->is_server_) {
      rv = SSL_accept(ss->ssl_);
      ss->HandleSSLError("SSL_accept:ClearOut",
                         rv,
                         kZeroIsAnError,
                         kSyscallError);
    } else {
      rv = SSL_connect(ss->ssl_);
      ss->HandleSSLError("SSL_connect:ClearOut",
                         rv,
                         kZeroIsAnError,
                         kSyscallError);
    }

    if (rv < 0) {
      return args.GetReturnValue().Set(rv);
    }
  }

  int bytes_read = SSL_read(ss->ssl_, buffer_data + off, len);
  ss->HandleSSLError("SSL_read:ClearOut",
                     bytes_read,
                     kZeroIsNotAnError,
                     kSyscallError);
  ss->SetShutdownFlags();

  args.GetReturnValue().Set(bytes_read);
}


void Connection::ClearPending(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Connection *ss = Connection::Unwrap(args);
  int bytes_pending = BIO_pending(ss->bio_read_);
  args.GetReturnValue().Set(bytes_pending);
}


void Connection::EncPending(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Connection *ss = Connection::Unwrap(args);
  int bytes_pending = BIO_pending(ss->bio_write_);
  args.GetReturnValue().Set(bytes_pending);
}


void Connection::EncOut(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 3) {
    return ThrowTypeError("Takes 3 parameters");
  }

  if (!Buffer::HasInstance(args[0])) {
    return ThrowTypeError("Second argument should be a buffer");
  }

  char* buffer_data = Buffer::Data(args[0]);
  size_t buffer_length = Buffer::Length(args[0]);

  size_t off = args[1]->Int32Value();
  size_t len = args[2]->Int32Value();
  if (off + len > buffer_length) {
    return ThrowError("off + len > buffer.length");
  }

  int bytes_read = BIO_read(ss->bio_write_, buffer_data + off, len);

  ss->HandleBIOError(ss->bio_write_, "BIO_read:EncOut", bytes_read);
  ss->SetShutdownFlags();

  args.GetReturnValue().Set(bytes_read);
}


void Connection::ClearIn(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 3) {
    return ThrowTypeError("Takes 3 parameters");
  }

  if (!Buffer::HasInstance(args[0])) {
    return ThrowTypeError("Second argument should be a buffer");
  }

  char* buffer_data = Buffer::Data(args[0]);
  size_t buffer_length = Buffer::Length(args[0]);

  size_t off = args[1]->Int32Value();
  size_t len = args[2]->Int32Value();
  if (off + len > buffer_length) {
    return ThrowError("off + len > buffer.length");
  }

  if (!SSL_is_init_finished(ss->ssl_)) {
    int rv;
    if (ss->is_server_) {
      rv = SSL_accept(ss->ssl_);
      ss->HandleSSLError("SSL_accept:ClearIn",
                         rv,
                         kZeroIsAnError,
                         kSyscallError);
    } else {
      rv = SSL_connect(ss->ssl_);
      ss->HandleSSLError("SSL_connect:ClearIn",
                         rv,
                         kZeroIsAnError,
                         kSyscallError);
    }

    if (rv < 0) {
      return args.GetReturnValue().Set(rv);
    }
  }

  int bytes_written = SSL_write(ss->ssl_, buffer_data + off, len);

  ss->HandleSSLError("SSL_write:ClearIn",
                     bytes_written,
                     len == 0 ? kZeroIsNotAnError : kZeroIsAnError,
                     kSyscallError);
  ss->SetShutdownFlags();

  args.GetReturnValue().Set(bytes_written);
}


void Connection::GetPeerCertificate(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (ss->ssl_ == NULL) return;
  Local info = Object::New();
  X509* peer_cert = SSL_get_peer_certificate(ss->ssl_);
  if (peer_cert != NULL) {
    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));
    }
    (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));
    }
    (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);
    }

    if (pkey != NULL) {
      EVP_PKEY_free(pkey);
      pkey = NULL;
    }
    if (rsa != NULL) {
      RSA_free(rsa);
      rsa = NULL;
    }

    ASN1_TIME_print(bio, X509_get_notBefore(peer_cert));
    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));
    BIO_get_mem_ptr(bio, &mem);
    info->Set(valid_to_symbol, String::New(mem->data, mem->length));
    BIO_free_all(bio);

    unsigned int md_size, i;
    unsigned char md[EVP_MAX_MD_SIZE];
    if (X509_digest(peer_cert, EVP_sha1(), md, &md_size)) {
      const char hex[] = "0123456789ABCDEF";
      char fingerprint[EVP_MAX_MD_SIZE * 3];

      for (i = 0; i> 4];
        fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)];
        fingerprint[(3*i)+2] = ':';
      }

      if (md_size > 0) {
        fingerprint[(3*(md_size-1))+2] = '\0';
      }
      else {
        fingerprint[0] = '\0';
      }

      info->Set(fingerprint_symbol, String::New(fingerprint));
    }

    STACK_OF(ASN1_OBJECT) *eku = (STACK_OF(ASN1_OBJECT) *)X509_get_ext_d2i(
        peer_cert, NID_ext_key_usage, NULL, NULL);
    if (eku != NULL) {
      Local ext_key_usage = Array::New();
      char buf[256];

      for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
        memset(buf, 0, sizeof(buf));
        OBJ_obj2txt(buf, sizeof(buf) - 1, sk_ASN1_OBJECT_value(eku, i), 1);
        ext_key_usage->Set(Integer::New(i, node_isolate), String::New(buf));
      }

      sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
      info->Set(ext_key_usage_symbol, ext_key_usage);
    }

    X509_free(peer_cert);
  }

  args.GetReturnValue().Set(info);
}


void Connection::GetSession(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (ss->ssl_ == NULL) return;

  SSL_SESSION* sess = SSL_get_session(ss->ssl_);
  if (!sess) return;

  int slen = i2d_SSL_SESSION(sess, NULL);
  assert(slen > 0);

  unsigned char* sbuf = new unsigned char[slen];
  unsigned char* p = sbuf;
  i2d_SSL_SESSION(sess, &p);
  args.GetReturnValue().Set(Encode(sbuf, slen, BINARY));
  delete[] sbuf;
}


void Connection::SetSession(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 1 ||
      (!args[0]->IsString() && !Buffer::HasInstance(args[0]))) {
    return ThrowTypeError("Bad argument");
  }

  ASSERT_IS_BUFFER(args[0]);
  ssize_t slen = Buffer::Length(args[0]);

  if (slen < 0) {
    return ThrowTypeError("Bad argument");
  }

  char* sbuf = new char[slen];

  ssize_t wlen = DecodeWrite(sbuf, slen, args[0], BINARY);
  assert(wlen == slen);

  const unsigned char* p = reinterpret_cast(sbuf);
  SSL_SESSION* sess = d2i_SSL_SESSION(NULL, &p, wlen);

  delete [] sbuf;

  if (!sess) return;

  int r = SSL_set_session(ss->ssl_, sess);
  SSL_SESSION_free(sess);

  if (!r) {
    return ThrowError("SSL_set_session error");
  }
}


void Connection::LoadSession(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() >= 1 && Buffer::HasInstance(args[0])) {
    ssize_t slen = Buffer::Length(args[0].As());
    char* sbuf = Buffer::Data(args[0].As());

    const unsigned char* p = reinterpret_cast(sbuf);
    SSL_SESSION* sess = d2i_SSL_SESSION(NULL, &p, slen);

    // Setup next session and move hello to the BIO buffer
    if (ss->next_sess_ != NULL) {
      SSL_SESSION_free(ss->next_sess_);
    }
    ss->next_sess_ = sess;
  }

  ss->hello_parser_.Finish();
}


void Connection::IsSessionReused(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Connection *ss = Connection::Unwrap(args);
  bool yes = ss->ssl_ && SSL_session_reused(ss->ssl_);
  args.GetReturnValue().Set(yes);
}


void Connection::Start(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  int rv = 0;
  if (!SSL_is_init_finished(ss->ssl_)) {
    if (ss->is_server_) {
      rv = SSL_accept(ss->ssl_);
      ss->HandleSSLError("SSL_accept:Start", rv, kZeroIsAnError, kSyscallError);
    } else {
      rv = SSL_connect(ss->ssl_);
      ss->HandleSSLError("SSL_connect:Start",
                         rv,
                         kZeroIsAnError,
                         kSyscallError);
    }
  }
  args.GetReturnValue().Set(rv);
}


void Connection::Shutdown(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (ss->ssl_ == NULL) {
    return args.GetReturnValue().Set(false);
  }

  int rv = SSL_shutdown(ss->ssl_);
  ss->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError, kIgnoreSyscall);
  ss->SetShutdownFlags();
  args.GetReturnValue().Set(rv);
}


void Connection::ReceivedShutdown(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Connection *ss = Connection::Unwrap(args);
  bool yes = ss->ssl_ && SSL_get_shutdown(ss->ssl_) == SSL_RECEIVED_SHUTDOWN;
  args.GetReturnValue().Set(yes);
}


void Connection::IsInitFinished(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Connection *ss = Connection::Unwrap(args);
  bool yes = ss->ssl_ && SSL_is_init_finished(ss->ssl_);
  args.GetReturnValue().Set(yes);
}


void Connection::VerifyError(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (ss->ssl_ == NULL) {
    return args.GetReturnValue().SetNull();
  }

  // XXX Do this check in JS land?
  X509* peer_cert = SSL_get_peer_certificate(ss->ssl_);
  if (peer_cert == NULL) {
    // We requested a certificate and they did not send us one.
    // Definitely an error.
    // XXX is this the right error message?
    return args.GetReturnValue().Set(
        Exception::Error(String::New("UNABLE_TO_GET_ISSUER_CERT")));
  }
  X509_free(peer_cert);


  long x509_verify_error = SSL_get_verify_result(ss->ssl_);

  Local s;

  switch (x509_verify_error) {
    case X509_V_OK:
      break;

    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
      s = String::New("UNABLE_TO_GET_ISSUER_CERT");
      break;

    case X509_V_ERR_UNABLE_TO_GET_CRL:
      s = String::New("UNABLE_TO_GET_CRL");
      break;

    case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
      s = String::New("UNABLE_TO_DECRYPT_CERT_SIGNATURE");
      break;

    case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
      s = String::New("UNABLE_TO_DECRYPT_CRL_SIGNATURE");
      break;

    case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
      s = String::New("UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY");
      break;

    case X509_V_ERR_CERT_SIGNATURE_FAILURE:
      s = String::New("CERT_SIGNATURE_FAILURE");
      break;

    case X509_V_ERR_CRL_SIGNATURE_FAILURE:
      s = String::New("CRL_SIGNATURE_FAILURE");
      break;

    case X509_V_ERR_CERT_NOT_YET_VALID:
      s = String::New("CERT_NOT_YET_VALID");
      break;

    case X509_V_ERR_CERT_HAS_EXPIRED:
      s = String::New("CERT_HAS_EXPIRED");
      break;

    case X509_V_ERR_CRL_NOT_YET_VALID:
      s = String::New("CRL_NOT_YET_VALID");
      break;

    case X509_V_ERR_CRL_HAS_EXPIRED:
      s = String::New("CRL_HAS_EXPIRED");
      break;

    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
      s = String::New("ERROR_IN_CERT_NOT_BEFORE_FIELD");
      break;

    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
      s = String::New("ERROR_IN_CERT_NOT_AFTER_FIELD");
      break;

    case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
      s = String::New("ERROR_IN_CRL_LAST_UPDATE_FIELD");
      break;

    case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
      s = String::New("ERROR_IN_CRL_NEXT_UPDATE_FIELD");
      break;

    case X509_V_ERR_OUT_OF_MEM:
      s = String::New("OUT_OF_MEM");
      break;

    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
      s = String::New("DEPTH_ZERO_SELF_SIGNED_CERT");
      break;

    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
      s = String::New("SELF_SIGNED_CERT_IN_CHAIN");
      break;

    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
      s = String::New("UNABLE_TO_GET_ISSUER_CERT_LOCALLY");
      break;

    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
      s = String::New("UNABLE_TO_VERIFY_LEAF_SIGNATURE");
      break;

    case X509_V_ERR_CERT_CHAIN_TOO_LONG:
      s = String::New("CERT_CHAIN_TOO_LONG");
      break;

    case X509_V_ERR_CERT_REVOKED:
      s = String::New("CERT_REVOKED");
      break;

    case X509_V_ERR_INVALID_CA:
      s = String::New("INVALID_CA");
      break;

    case X509_V_ERR_PATH_LENGTH_EXCEEDED:
      s = String::New("PATH_LENGTH_EXCEEDED");
      break;

    case X509_V_ERR_INVALID_PURPOSE:
      s = String::New("INVALID_PURPOSE");
      break;

    case X509_V_ERR_CERT_UNTRUSTED:
      s = String::New("CERT_UNTRUSTED");
      break;

    case X509_V_ERR_CERT_REJECTED:
      s = String::New("CERT_REJECTED");
      break;

    default:
      s = String::New(X509_verify_cert_error_string(x509_verify_error));
      break;
  }

  if (s.IsEmpty())
    args.GetReturnValue().SetNull();
  else
    args.GetReturnValue().Set(Exception::Error(s));
}


void Connection::GetCurrentCipher(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  OPENSSL_CONST SSL_CIPHER *c;

  if (ss->ssl_ == NULL) return;
  c = SSL_get_current_cipher(ss->ssl_);
  if (c == NULL) return;
  Local info = Object::New();
  const char* cipher_name = SSL_CIPHER_get_name(c);
  info->Set(name_symbol, String::New(cipher_name));
  const char* cipher_version = SSL_CIPHER_get_version(c);
  info->Set(version_symbol, String::New(cipher_version));
  args.GetReturnValue().Set(info);
}


void Connection::Close(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (ss->ssl_ != NULL) {
    SSL_free(ss->ssl_);
    ss->ssl_ = NULL;
  }
}


#ifdef OPENSSL_NPN_NEGOTIATED
void Connection::GetNegotiatedProto(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  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 args.GetReturnValue().Set(false);
    }

    args.GetReturnValue().Set(
        String::New(reinterpret_cast(npn_proto), npn_proto_len));
  } else {
    args.GetReturnValue().Set(ss->selectedNPNProto_);
  }
}


void Connection::SetNPNProtocols(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 1 || !Buffer::HasInstance(args[0])) {
    return ThrowError("Must give a Buffer as first argument");
  }

  ss->npnProtos_.Reset(node_isolate, args[0].As());
}
#endif


#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
void Connection::GetServername(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (ss->is_server_ && !ss->servername_.IsEmpty()) {
    args.GetReturnValue().Set(ss->servername_);
  } else {
    args.GetReturnValue().Set(false);
  }
}


void Connection::SetSNICallback(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Connection *ss = Connection::Unwrap(args);

  if (args.Length() < 1 || !args[0]->IsFunction()) {
    return ThrowError("Must give a Function as first argument");
  }

  Local obj = Object::New();
  obj->Set(String::New("onselect"), args[0]);
  ss->sniObject_.Reset(node_isolate, obj);
}
#endif


void CipherBase::Initialize(Handle target) {
  HandleScope scope(node_isolate);

  Local t = FunctionTemplate::New(New);

  t->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(t, "init", Init);
  NODE_SET_PROTOTYPE_METHOD(t, "initiv", InitIv);
  NODE_SET_PROTOTYPE_METHOD(t, "update", Update);
  NODE_SET_PROTOTYPE_METHOD(t, "final", Final);
  NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);

  target->Set(String::NewSymbol("CipherBase"), t->GetFunction());
}


void CipherBase::New(const FunctionCallbackInfo& args) {
  assert(args.IsConstructCall() == true);
  HandleScope scope(node_isolate);
  CipherBase* cipher = new CipherBase(args[0]->IsTrue() ? kCipher : kDecipher);
  cipher->Wrap(args.This());
}


void CipherBase::Init(char* cipher_type, char* key_buf, int key_buf_len) {
  HandleScope scope(node_isolate);

  assert(cipher_ == NULL);
  cipher_ = EVP_get_cipherbyname(cipher_type);
  if (cipher_ == NULL) {
    return ThrowError("Unknown cipher");
  }

  unsigned char key[EVP_MAX_KEY_LENGTH];
  unsigned char iv[EVP_MAX_IV_LENGTH];

  int key_len = EVP_BytesToKey(cipher_,
                               EVP_md5(),
                               NULL,
                               reinterpret_cast(key_buf),
                               key_buf_len,
                               1,
                               key,
                               iv);

  EVP_CIPHER_CTX_init(&ctx_);
  EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
  if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
    EVP_CIPHER_CTX_cleanup(&ctx_);
    return ThrowError("Invalid key length");
  }

  EVP_CipherInit_ex(&ctx_,
                    NULL,
                    NULL,
                    reinterpret_cast(key),
                    reinterpret_cast(iv),
                    kind_ == kCipher);
  initialised_ = true;
}


void CipherBase::Init(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  CipherBase* cipher = ObjectWrap::Unwrap(args.This());

  if (args.Length() < 2 ||
      !(args[0]->IsString() && Buffer::HasInstance(args[1]))) {
    return ThrowError("Must give cipher-type, key");
  }

  String::Utf8Value cipher_type(args[0]);
  char* key_buf = Buffer::Data(args[1]);
  ssize_t key_buf_len = Buffer::Length(args[1]);
  cipher->Init(*cipher_type, key_buf, key_buf_len);
}


void CipherBase::InitIv(char* cipher_type,
                        char* key,
                        int key_len,
                        char* iv,
                        int iv_len) {
  HandleScope scope(node_isolate);

  cipher_ = EVP_get_cipherbyname(cipher_type);
  if (cipher_ == NULL) {
    return ThrowError("Unknown cipher");
  }

  /* OpenSSL versions up to 0.9.8l failed to return the correct
     iv_length (0) for ECB ciphers */
  if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
      !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
    return ThrowError("Invalid IV length");
  }
  EVP_CIPHER_CTX_init(&ctx_);
  EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
  if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
    EVP_CIPHER_CTX_cleanup(&ctx_);
    return ThrowError("Invalid key length");
  }

  EVP_CipherInit_ex(&ctx_,
                    NULL,
                    NULL,
                    reinterpret_cast(key),
                    reinterpret_cast(iv),
                    kind_ == kCipher);
  initialised_ = true;
}


void CipherBase::InitIv(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  CipherBase* cipher = ObjectWrap::Unwrap(args.This());

  if (args.Length() < 3 || !args[0]->IsString()) {
    return ThrowError("Must give cipher-type, key, and iv as argument");
  }

  ASSERT_IS_BUFFER(args[1]);
  ASSERT_IS_BUFFER(args[2]);

  String::Utf8Value cipher_type(args[0]);
  ssize_t key_len = Buffer::Length(args[1]);
  char* key_buf = Buffer::Data(args[1]);
  ssize_t iv_len = Buffer::Length(args[2]);
  char* iv_buf = Buffer::Data(args[2]);
  cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len);
}


bool CipherBase::Update(char* data,
                        int len,
                        unsigned char** out,
                        int* out_len) {
  if (!initialised_) return 0;
  *out_len = len + EVP_CIPHER_CTX_block_size(&ctx_);
  *out = new unsigned char[*out_len];
  return EVP_CipherUpdate(&ctx_,
                          *out,
                          out_len,
                          reinterpret_cast(data),
                          len);
}


void CipherBase::Update(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  CipherBase* cipher = ObjectWrap::Unwrap(args.This());

  ASSERT_IS_STRING_OR_BUFFER(args[0]);

  unsigned char* out = NULL;
  bool r;
  int out_len = 0;

  // Only copy the data if we have to, because it's a string
  if (args[0]->IsString()) {
    enum encoding encoding = ParseEncoding(args[1], BINARY);
    size_t buflen = StringBytes::StorageSize(args[0], encoding);
    char* buf = new char[buflen];
    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
    r = cipher->Update(buf, written, &out, &out_len);
    delete[] buf;
  } else {
    char* buf = Buffer::Data(args[0]);
    size_t buflen = Buffer::Length(args[0]);
    r = cipher->Update(buf, buflen, &out, &out_len);
  }

  if (!r) {
    delete[] out;
    return ThrowCryptoTypeError(ERR_get_error());
  }

  Local buf = Buffer::New(reinterpret_cast(out), out_len);
  if (out) delete[] out;

  args.GetReturnValue().Set(buf);
}


bool CipherBase::SetAutoPadding(bool auto_padding) {
  if (!initialised_) return false;
  return EVP_CIPHER_CTX_set_padding(&ctx_, auto_padding);
}


void CipherBase::SetAutoPadding(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  CipherBase* cipher = ObjectWrap::Unwrap(args.This());
  cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
}


bool CipherBase::Final(unsigned char** out, int *out_len) {
  if (!initialised_) return false;

  *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx_)];
  bool r = EVP_CipherFinal_ex(&ctx_, *out, out_len);
  EVP_CIPHER_CTX_cleanup(&ctx_);
  initialised_ = false;

  return r;
}


void CipherBase::Final(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  CipherBase* cipher = ObjectWrap::Unwrap(args.This());

  unsigned char* out_value = NULL;
  int out_len = -1;
  Local outString;

  bool r = cipher->Final(&out_value, &out_len);

  if (out_len InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
  NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
  NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);

  target->Set(String::NewSymbol("Hmac"), t->GetFunction());
}


void Hmac::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Hmac* hmac = new Hmac();
  hmac->Wrap(args.This());
}


void Hmac::HmacInit(char* hashType, char* key, int key_len) {
  HandleScope scope(node_isolate);

  assert(md_ == NULL);
  md_ = EVP_get_digestbyname(hashType);
  if (md_ == NULL) {
    return ThrowError("Unknown message digest");
  }
  HMAC_CTX_init(&ctx_);
  if (key_len == 0) {
    HMAC_Init(&ctx_, "", 0, md_);
  } else {
    HMAC_Init(&ctx_, key, key_len, md_);
  }
  initialised_ = true;
}


void Hmac::HmacInit(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Hmac* hmac = ObjectWrap::Unwrap(args.This());

  if (args.Length() < 2 || !args[0]->IsString()) {
    return ThrowError("Must give hashtype string, key as arguments");
  }

  ASSERT_IS_BUFFER(args[1]);

  String::Utf8Value hashType(args[0]);

  char* buffer_data = Buffer::Data(args[1]);
  size_t buffer_length = Buffer::Length(args[1]);
  hmac->HmacInit(*hashType, buffer_data, buffer_length);
}


bool Hmac::HmacUpdate(char* data, int len) {
  if (!initialised_) return false;
  HMAC_Update(&ctx_, reinterpret_cast(data), len);
  return true;
}


void Hmac::HmacUpdate(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Hmac* hmac = ObjectWrap::Unwrap(args.This());

  ASSERT_IS_STRING_OR_BUFFER(args[0]);

  // Only copy the data if we have to, because it's a string
  bool r;
  if (args[0]->IsString()) {
    enum encoding encoding = ParseEncoding(args[1], BINARY);
    size_t buflen = StringBytes::StorageSize(args[0], encoding);
    char* buf = new char[buflen];
    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
    r = hmac->HmacUpdate(buf, written);
    delete[] buf;
  } else {
    char* buf = Buffer::Data(args[0]);
    size_t buflen = Buffer::Length(args[0]);
    r = hmac->HmacUpdate(buf, buflen);
  }

  if (!r) {
    return ThrowTypeError("HmacUpdate fail");
  }
}


bool Hmac::HmacDigest(unsigned char** md_value, unsigned int* md_len) {
  if (!initialised_) return false;
  *md_value = new unsigned char[EVP_MAX_MD_SIZE];
  HMAC_Final(&ctx_, *md_value, md_len);
  HMAC_CTX_cleanup(&ctx_);
  initialised_ = false;
  return true;
}


void Hmac::HmacDigest(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Hmac* hmac = ObjectWrap::Unwrap(args.This());

  enum encoding encoding = BUFFER;
  if (args.Length() >= 1) {
    encoding = ParseEncoding(args[0]->ToString(), BUFFER);
  }

  unsigned char* md_value = NULL;
  unsigned int md_len = 0;

  bool r = hmac->HmacDigest(&md_value, &md_len);
  if (!r) {
    md_value = NULL;
    md_len = 0;
  }

  Local rc = StringBytes::Encode(
        reinterpret_cast(md_value), md_len, encoding);
  delete[] md_value;
  args.GetReturnValue().Set(rc);
}


void Hash::Initialize(v8::Handle target) {
  HandleScope scope(node_isolate);

  Local t = FunctionTemplate::New(New);

  t->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
  NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);

  target->Set(String::NewSymbol("Hash"), t->GetFunction());
}


void Hash::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  if (args.Length() == 0 || !args[0]->IsString()) {
    return ThrowError("Must give hashtype string as argument");
  }

  String::Utf8Value hashType(args[0]);

  Hash* hash = new Hash();
  if (!hash->HashInit(*hashType)) {
    delete hash;
    return ThrowError("Digest method not supported");
  }

  hash->Wrap(args.This());
}


bool Hash::HashInit(const char* hashType) {
  assert(md_ == NULL);
  md_ = EVP_get_digestbyname(hashType);
  if (md_ == NULL) return false;
  EVP_MD_CTX_init(&mdctx_);
  EVP_DigestInit_ex(&mdctx_, md_, NULL);
  initialised_ = true;
  return true;
}


bool Hash::HashUpdate(char* data, int len) {
  if (!initialised_) return false;
  EVP_DigestUpdate(&mdctx_, data, len);
  return true;
}


void Hash::HashUpdate(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Hash* hash = ObjectWrap::Unwrap(args.This());

  ASSERT_IS_STRING_OR_BUFFER(args[0]);

  // Only copy the data if we have to, because it's a string
  bool r;
  if (args[0]->IsString()) {
    enum encoding encoding = ParseEncoding(args[1], BINARY);
    size_t buflen = StringBytes::StorageSize(args[0], encoding);
    char* buf = new char[buflen];
    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
    r = hash->HashUpdate(buf, written);
    delete[] buf;
  } else {
    char* buf = Buffer::Data(args[0]);
    size_t buflen = Buffer::Length(args[0]);
    r = hash->HashUpdate(buf, buflen);
  }

  if (!r) {
    return ThrowTypeError("HashUpdate fail");
  }
}


void Hash::HashDigest(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Hash* hash = ObjectWrap::Unwrap(args.This());

  if (!hash->initialised_) {
    return ThrowError("Not initialized");
  }

  enum encoding encoding = BUFFER;
  if (args.Length() >= 1) {
    encoding = ParseEncoding(args[0]->ToString(), BUFFER);
  }

  unsigned char md_value[EVP_MAX_MD_SIZE];
  unsigned int md_len;

  EVP_DigestFinal_ex(&hash->mdctx_, md_value, &md_len);
  EVP_MD_CTX_cleanup(&hash->mdctx_);
  hash->initialised_ = false;

  Local rc = StringBytes::Encode(
      reinterpret_cast(md_value), md_len, encoding);
  args.GetReturnValue().Set(rc);
}


void Sign::Initialize(v8::Handle target) {
  HandleScope scope(node_isolate);

  Local t = FunctionTemplate::New(New);

  t->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
  NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
  NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);

  target->Set(String::NewSymbol("Sign"), t->GetFunction());
}


void Sign::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Sign* sign = new Sign();
  sign->Wrap(args.This());
}


void Sign::SignInit(const char* sign_type) {
  HandleScope scope(node_isolate);

  assert(md_ == NULL);
  md_ = EVP_get_digestbyname(sign_type);
  if (!md_) {
    return ThrowError("Uknown message digest");
  }
  EVP_MD_CTX_init(&mdctx_);
  EVP_SignInit_ex(&mdctx_, md_, NULL);
  initialised_ = true;
}


void Sign::SignInit(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Sign* sign = ObjectWrap::Unwrap(args.This());

  if (args.Length() == 0 || !args[0]->IsString()) {
    return ThrowError("Must give signtype string as argument");
  }

  String::Utf8Value sign_type(args[0]);
  sign->SignInit(*sign_type);
}


bool Sign::SignUpdate(char* data, int len) {
  if (!initialised_) return false;
  EVP_SignUpdate(&mdctx_, data, len);
  return true;
}


void Sign::SignUpdate(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Sign* sign = ObjectWrap::Unwrap(args.This());

  ASSERT_IS_STRING_OR_BUFFER(args[0]);

  // Only copy the data if we have to, because it's a string
  int r;
  if (args[0]->IsString()) {
    enum encoding encoding = ParseEncoding(args[1], BINARY);
    size_t buflen = StringBytes::StorageSize(args[0], encoding);
    char* buf = new char[buflen];
    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
    r = sign->SignUpdate(buf, written);
    delete[] buf;
  } else {
    char* buf = Buffer::Data(args[0]);
    size_t buflen = Buffer::Length(args[0]);
    r = sign->SignUpdate(buf, buflen);
  }

  if (!r) {
    return ThrowTypeError("SignUpdate fail");
  }
}


bool Sign::SignFinal(unsigned char** md_value,
                     unsigned int *md_len,
                     char* key_pem,
                     int key_pem_len) {
  if (!initialised_) return false;

  BIO* bp = NULL;
  EVP_PKEY* pkey = NULL;
  bp = BIO_new(BIO_s_mem());
  if (!BIO_write(bp, key_pem, key_pem_len)) return false;

  pkey = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL);
  if (pkey == NULL) return 0;

  EVP_SignFinal(&mdctx_, *md_value, md_len, pkey);
  EVP_MD_CTX_cleanup(&mdctx_);
  initialised_ = false;
  EVP_PKEY_free(pkey);
  BIO_free_all(bp);
  return true;
}


void Sign::SignFinal(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Sign* sign = ObjectWrap::Unwrap(args.This());

  unsigned char* md_value;
  unsigned int md_len;

  enum encoding encoding = BUFFER;
  if (args.Length() >= 2) {
    encoding = ParseEncoding(args[1]->ToString(), BUFFER);
  }

  ASSERT_IS_BUFFER(args[0]);
  ssize_t len = Buffer::Length(args[0]);
  char* buf = Buffer::Data(args[0]);

  md_len = 8192; // Maximum key size is 8192 bits
  md_value = new unsigned char[md_len];

  bool r = sign->SignFinal(&md_value, &md_len, buf, len);
  if (!r) {
    delete[] md_value;
    md_value = NULL;
    md_len = 0;
  }

  Local rc = StringBytes::Encode(
      reinterpret_cast(md_value), md_len, encoding);
  delete[] md_value;
  args.GetReturnValue().Set(rc);
}


void Verify::Initialize(v8::Handle target) {
  HandleScope scope(node_isolate);

  Local t = FunctionTemplate::New(New);

  t->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
  NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
  NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);

  target->Set(String::NewSymbol("Verify"), t->GetFunction());
}


void Verify::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Verify* verify = new Verify();
  verify->Wrap(args.This());
}


void Verify::VerifyInit(const char* verify_type) {
  HandleScope scope(node_isolate);

  assert(md_ == NULL);
  md_ = EVP_get_digestbyname(verify_type);
  if (md_ == NULL) {
    return ThrowError("Unknown message digest");
  }

  EVP_MD_CTX_init(&mdctx_);
  EVP_VerifyInit_ex(&mdctx_, md_, NULL);
  initialised_ = true;
}


void Verify::VerifyInit(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Verify* verify = ObjectWrap::Unwrap(args.This());

  if (args.Length() == 0 || !args[0]->IsString()) {
    return ThrowError("Must give verifytype string as argument");
  }

  String::Utf8Value verify_type(args[0]);
  verify->VerifyInit(*verify_type);
}


bool Verify::VerifyUpdate(char* data, int len) {
  if (!initialised_) return false;
  EVP_VerifyUpdate(&mdctx_, data, len);
  return true;
}


void Verify::VerifyUpdate(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Verify* verify = ObjectWrap::Unwrap(args.This());

  ASSERT_IS_STRING_OR_BUFFER(args[0]);

  // Only copy the data if we have to, because it's a string
  bool r;
  if (args[0]->IsString()) {
    enum encoding encoding = ParseEncoding(args[1], BINARY);
    size_t buflen = StringBytes::StorageSize(args[0], encoding);
    char* buf = new char[buflen];
    size_t written = StringBytes::Write(buf, buflen, args[0], encoding);
    r = verify->VerifyUpdate(buf, written);
    delete[] buf;
  } else {
    char* buf = Buffer::Data(args[0]);
    size_t buflen = Buffer::Length(args[0]);
    r = verify->VerifyUpdate(buf, buflen);
  }

  if (!r) {
    return ThrowTypeError("VerifyUpdate fail");
  }
}


bool Verify::VerifyFinal(char* key_pem,
                         int key_pem_len,
                         unsigned char* sig,
                         int siglen) {
  HandleScope scope(node_isolate);

  if (!initialised_) {
    ThrowError("Verify not initalised");
    return false;
  }

  EVP_PKEY* pkey = NULL;
  BIO* bp = NULL;
  X509* x509 = NULL;
  bool fatal = true;
  int r = 0;

  bp = BIO_new(BIO_s_mem());
  if (bp == NULL)
    goto exit;

  if (!BIO_write(bp, key_pem, key_pem_len))
    goto exit;

  // Check if this is a PKCS#8 or RSA public key before trying as X.509.
  // Split this out into a separate function once we have more than one
  // consumer of public keys.
  if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
    pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL);
    if (pkey == NULL)
      goto exit;
  } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
    RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
    if (rsa) {
      pkey = EVP_PKEY_new();
      if (pkey) EVP_PKEY_set1_RSA(pkey, rsa);
      RSA_free(rsa);
    }
    if (pkey == NULL)
      goto exit;
  } else {
    // X.509 fallback
    x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL);
    if (x509 == NULL)
      goto exit;

    pkey = X509_get_pubkey(x509);
    if (pkey == NULL)
      goto exit;
  }

  fatal = false;
  r = EVP_VerifyFinal(&mdctx_, sig, siglen, pkey);

exit:
  if (pkey != NULL)
    EVP_PKEY_free(pkey);
  if (bp != NULL)
    BIO_free_all(bp);
  if (x509 != NULL)
    X509_free(x509);

  EVP_MD_CTX_cleanup(&mdctx_);
  initialised_ = false;

  if (fatal) {
    unsigned long err = ERR_get_error();
    ThrowCryptoError(err);
    return false;
  }

  return r == 1;
}


void Verify::VerifyFinal(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  Verify* verify = ObjectWrap::Unwrap(args.This());

  ASSERT_IS_BUFFER(args[0]);
  char* kbuf = Buffer::Data(args[0]);
  ssize_t klen = Buffer::Length(args[0]);

  ASSERT_IS_STRING_OR_BUFFER(args[1]);
  // BINARY works for both buffers and binary strings.
  enum encoding encoding = BINARY;
  if (args.Length() >= 3) {
    encoding = ParseEncoding(args[2]->ToString(), BINARY);
  }

  ssize_t hlen = StringBytes::Size(args[1], encoding);

  // only copy if we need to, because it's a string.
  unsigned char* hbuf;
  if (args[1]->IsString()) {
    hbuf = new unsigned char[hlen];
    ssize_t hwritten = StringBytes::Write(
        reinterpret_cast(hbuf), hlen, args[1], encoding);
    assert(hwritten == hlen);
  } else {
    hbuf = reinterpret_cast(Buffer::Data(args[1]));
  }

  bool rc = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
  if (args[1]->IsString()) {
    delete[] hbuf;
  }
  args.GetReturnValue().Set(rc);
}


void DiffieHellman::Initialize(v8::Handle target) {
  HandleScope scope(node_isolate);

  Local 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());

  Local t2 = FunctionTemplate::New(DiffieHellmanGroup);
  t2->InstanceTemplate()->SetInternalFieldCount(1);

  NODE_SET_PROTOTYPE_METHOD(t2, "generateKeys", GenerateKeys);
  NODE_SET_PROTOTYPE_METHOD(t2, "computeSecret", ComputeSecret);
  NODE_SET_PROTOTYPE_METHOD(t2, "getPrime", GetPrime);
  NODE_SET_PROTOTYPE_METHOD(t2, "getGenerator", GetGenerator);
  NODE_SET_PROTOTYPE_METHOD(t2, "getPublicKey", GetPublicKey);
  NODE_SET_PROTOTYPE_METHOD(t2, "getPrivateKey", GetPrivateKey);

  target->Set(String::NewSymbol("DiffieHellmanGroup"), t2->GetFunction());
}


bool DiffieHellman::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 DiffieHellman::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;
}


bool DiffieHellman::Init(unsigned char* p,
                         int p_len,
                         unsigned char* g,
                         int g_len) {
  dh = DH_new();
  dh->p = BN_bin2bn(p, p_len, 0);
  dh->g = BN_bin2bn(g, g_len, 0);
  initialised_ = true;
  return true;
}


void DiffieHellman::DiffieHellmanGroup(
    const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman = new DiffieHellman();

  if (args.Length() != 1 || !args[0]->IsString()) {
    return ThrowError("No group name given");
  }

  String::Utf8Value group_name(args[0]);

  modp_group* it = modp_groups;

  while(it->name != NULL) {
    if (!strcasecmp(*group_name, it->name))
      break;
    it++;
  }

  if (it->name != NULL) {
    diffieHellman->Init(it->prime,
                        it->prime_size,
                        it->gen,
                        it->gen_size);
  } else {
    return ThrowError("Unknown group");
  }

  diffieHellman->Wrap(args.This());
}


void DiffieHellman::New(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman = new DiffieHellman();
  bool initialized = false;

  if (args.Length() > 0) {
    if (args[0]->IsInt32()) {
      initialized = diffieHellman->Init(args[0]->Int32Value());
    } else {
      initialized = diffieHellman->Init(
              reinterpret_cast(Buffer::Data(args[0])),
              Buffer::Length(args[0]));
    }
  }

  if (!initialized) {
    return ThrowError("Initialization failed");
  }

  diffieHellman->Wrap(args.This());
}


void DiffieHellman::GenerateKeys(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  if (!DH_generate_key(diffieHellman->dh)) {
    return ThrowError("Key generation failed");
  }

  int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
  char* data = new char[dataSize];
  BN_bn2bin(diffieHellman->dh->pub_key,
            reinterpret_cast(data));

  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
  delete[] data;
}


void DiffieHellman::GetPrime(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  int dataSize = BN_num_bytes(diffieHellman->dh->p);
  char* data = new char[dataSize];
  BN_bn2bin(diffieHellman->dh->p, reinterpret_cast(data));

  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
  delete[] data;
}


void DiffieHellman::GetGenerator(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  int dataSize = BN_num_bytes(diffieHellman->dh->g);
  char* data = new char[dataSize];
  BN_bn2bin(diffieHellman->dh->g, reinterpret_cast(data));

  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
  delete[] data;
}


void DiffieHellman::GetPublicKey(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  if (diffieHellman->dh->pub_key == NULL) {
    return ThrowError("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(data));

  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
  delete[] data;
}


void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  if (diffieHellman->dh->priv_key == NULL) {
    return ThrowError("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(data));

  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
  delete[] data;
}


void DiffieHellman::ComputeSecret(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  ClearErrorOnReturn clear_error_on_return;
  (void) &clear_error_on_return;  // Silence compiler warning.
  BIGNUM* key = NULL;

  if (args.Length() == 0) {
    return ThrowError("First argument must be other party's public key");
  } else {
    ASSERT_IS_BUFFER(args[0]);
    key = BN_bin2bn(
        reinterpret_cast(Buffer::Data(args[0])),
        Buffer::Length(args[0]),
        0);
  }

  int dataSize = DH_size(diffieHellman->dh);
  char* data = new char[dataSize];

  int size = DH_compute_key(reinterpret_cast(data),
                            key,
                            diffieHellman->dh);

  if (size == -1) {
    int checkResult;
    int checked;

    checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
    BN_free(key);
    delete[] data;

    if (!checked) {
      return ThrowError("Invalid key");
    } else if (checkResult) {
      if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
        return ThrowError("Supplied key is too small");
      } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
        return ThrowError("Supplied key is too large");
      } else {
        return ThrowError("Invalid key");
      }
    } else {
      return ThrowError("Invalid key");
    }
  }

  BN_free(key);
  assert(size >= 0);

  // DH_size returns number of bytes in a prime number
  // DH_compute_key returns number of bytes in a remainder of exponent, which
  // may have less bytes than a prime number. Therefore add 0-padding to the
  // allocated buffer.
  if (size != dataSize) {
    assert(dataSize > size);
    memmove(data + dataSize - size, data, size);
    memset(data, 0, dataSize - size);
  }

  args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
  delete[] data;
}


void DiffieHellman::SetPublicKey(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  if (args.Length() == 0) {
    return ThrowError("First argument must be public key");
  } else {
    ASSERT_IS_BUFFER(args[0]);
    diffieHellman->dh->pub_key = BN_bin2bn(
        reinterpret_cast(Buffer::Data(args[0])),
        Buffer::Length(args[0]), 0);
  }
}


void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  DiffieHellman* diffieHellman =
      ObjectWrap::Unwrap(args.This());

  if (!diffieHellman->initialised_) {
    return ThrowError("Not initialized");
  }

  if (args.Length() == 0) {
    return ThrowError("First argument must be private key");
  } else {
    ASSERT_IS_BUFFER(args[0]);
    diffieHellman->dh->priv_key = BN_bin2bn(
        reinterpret_cast(Buffer::Data(args[0])),
        Buffer::Length(args[0]),
        0);
  }
}


bool DiffieHellman::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;
}


struct pbkdf2_req {
  uv_work_t work_req;
  int err;
  char* pass;
  size_t passlen;
  char* salt;
  size_t saltlen;
  size_t iter;
  char* key;
  size_t keylen;
  Persistent obj;
};


void EIO_PBKDF2(pbkdf2_req* req) {
  req->err = PKCS5_PBKDF2_HMAC_SHA1(
    req->pass,
    req->passlen,
    (unsigned char*)req->salt,
    req->saltlen,
    req->iter,
    req->keylen,
    (unsigned char*)req->key);
  memset(req->pass, 0, req->passlen);
  memset(req->salt, 0, req->saltlen);
}


void EIO_PBKDF2(uv_work_t* work_req) {
  pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req);
  EIO_PBKDF2(req);
}


void EIO_PBKDF2After(pbkdf2_req* req, Local argv[2]) {
  if (req->err) {
    argv[0] = Undefined(node_isolate);
    argv[1] = Encode(req->key, req->keylen, BUFFER);
    memset(req->key, 0, req->keylen);
  } else {
    argv[0] = Exception::Error(String::New("PBKDF2 error"));
    argv[1] = Undefined(node_isolate);
  }

  delete[] req->pass;
  delete[] req->salt;
  delete[] req->key;
  delete req;
}


void EIO_PBKDF2After(uv_work_t* work_req, int status) {
  assert(status == 0);
  pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req);
  HandleScope scope(node_isolate);
  Local obj = PersistentToLocal(req->obj);
  req->obj.Dispose();
  Local argv[2];
  EIO_PBKDF2After(req, argv);
  MakeCallback(obj, "ondone", ARRAY_SIZE(argv), argv);
}


void PBKDF2(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  const char* type_error = NULL;
  char* pass = NULL;
  char* salt = NULL;
  ssize_t passlen = -1;
  ssize_t saltlen = -1;
  ssize_t keylen = -1;
  ssize_t pass_written = -1;
  ssize_t salt_written = -1;
  ssize_t iter = -1;
  pbkdf2_req* req = NULL;

  if (args.Length() != 4 && args.Length() != 5) {
    type_error = "Bad parameter";
    goto err;
  }

  ASSERT_IS_BUFFER(args[0]);
  passlen = Buffer::Length(args[0]);
  if (passlen < 0) {
    type_error = "Bad password";
    goto err;
  }

  pass = new char[passlen];
  pass_written = DecodeWrite(pass, passlen, args[0], BINARY);
  assert(pass_written == passlen);

  ASSERT_IS_BUFFER(args[1]);
  saltlen = Buffer::Length(args[1]);
  if (saltlen < 0) {
    type_error = "Bad salt";
    goto err;
  }

  salt = new char[saltlen];
  salt_written = DecodeWrite(salt, saltlen, args[1], BINARY);
  assert(salt_written == saltlen);

  if (!args[2]->IsNumber()) {
    type_error = "Iterations not a number";
    goto err;
  }

  iter = args[2]->Int32Value();
  if (iter < 0) {
    type_error = "Bad iterations";
    goto err;
  }

  if (!args[3]->IsNumber()) {
    type_error = "Key length not a number";
    goto err;
  }

  keylen = args[3]->Int32Value();
  if (keylen < 0) {
    type_error = "Bad key length";
    goto err;
  }

  req = new pbkdf2_req;
  req->err = 0;
  req->pass = pass;
  req->passlen = passlen;
  req->salt = salt;
  req->saltlen = saltlen;
  req->iter = iter;
  req->key = new char[keylen];
  req->keylen = keylen;

  if (args[4]->IsFunction()) {
    Local obj = Object::New();
    obj->Set(String::New("ondone"), args[4]);
    req->obj.Reset(node_isolate, obj);
    uv_queue_work(uv_default_loop(),
                  &req->work_req,
                  EIO_PBKDF2,
                  EIO_PBKDF2After);
  } else {
    Local argv[2];
    EIO_PBKDF2(req);
    EIO_PBKDF2After(req, argv);
    if (argv[0]->IsObject())
      ThrowException(argv[0]);
    else
      args.GetReturnValue().Set(argv[1]);
  }
  return;

err:
  delete[] salt;
  delete[] pass;
  return ThrowTypeError(type_error);
}


struct RandomBytesRequest {
  ~RandomBytesRequest();
  Persistent obj_;
  unsigned long error_; // openssl error code or zero
  uv_work_t work_req_;
  size_t size_;
  char* data_;
};


RandomBytesRequest::~RandomBytesRequest() {
  obj_.Dispose();
}


template 
void RandomBytesWork(uv_work_t* work_req) {
  RandomBytesRequest* req = container_of(work_req,
                                         RandomBytesRequest,
                                         work_req_);
  int r;

  if (pseudoRandom == true) {
    r = RAND_pseudo_bytes(reinterpret_cast(req->data_),
                          req->size_);
  } else {
    r = RAND_bytes(reinterpret_cast(req->data_), req->size_);
  }

  // RAND_bytes() returns 0 on error. RAND_pseudo_bytes() returns 0 when the
  // result is not cryptographically strong - but that's not an error.
  if (r == 0 && pseudoRandom == false) {
    req->error_ = ERR_get_error();
  } else if (r == -1) {
    req->error_ = static_cast(-1);
  }
}


// don't call this function without a valid HandleScope
void RandomBytesCheck(RandomBytesRequest* req, Local argv[2]) {
  if (req->error_) {
    char errmsg[256] = "Operation not supported";

    if (req->error_ != (unsigned long) -1)
      ERR_error_string_n(req->error_, errmsg, sizeof errmsg);

    argv[0] = Exception::Error(String::New(errmsg));
    argv[1] = Null(node_isolate);
  }
  else {
    argv[0] = Null(node_isolate);
    argv[1] = Buffer::Use(req->data_, req->size_);
  }
}


void RandomBytesAfter(uv_work_t* work_req, int status) {
  assert(status == 0);
  RandomBytesRequest* req = container_of(work_req,
                                         RandomBytesRequest,
                                         work_req_);
  HandleScope scope(node_isolate);
  Local argv[2];
  RandomBytesCheck(req, argv);
  MakeCallback(req->obj_, "ondone", ARRAY_SIZE(argv), argv);
  delete req;
}


template 
void RandomBytes(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  // maybe allow a buffer to write to? cuts down on object creation
  // when generating random data in a loop
  if (!args[0]->IsUint32()) {
    return ThrowTypeError("Argument #1 must be number > 0");
  }

  const uint32_t size = args[0]->Uint32Value();
  if (size > Buffer::kMaxLength) {
    return ThrowTypeError("size > Buffer::kMaxLength");
  }

  RandomBytesRequest* req = new RandomBytesRequest();
  req->error_ = 0;
  req->data_ = new char[size];
  req->size_ = size;

  if (args[1]->IsFunction()) {
    Local obj = Object::New();
    obj->Set(String::New("ondone"), args[1]);
    req->obj_.Reset(node_isolate, obj);

    uv_queue_work(uv_default_loop(),
                  &req->work_req_,
                  RandomBytesWork,
                  RandomBytesAfter);
    args.GetReturnValue().Set(obj);
  }
  else {
    Local argv[2];
    RandomBytesWork(&req->work_req_);
    RandomBytesCheck(req, argv);
    delete req;

    if (!argv[0]->IsNull())
      ThrowException(argv[0]);
    else
      args.GetReturnValue().Set(argv[1]);
  }
}


void GetSSLCiphers(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);

  SSL_CTX* ctx = SSL_CTX_new(TLSv1_server_method());
  if (ctx == NULL) {
    return ThrowError("SSL_CTX_new() failed.");
  }

  SSL* ssl = SSL_new(ctx);
  if (ssl == NULL) {
    SSL_CTX_free(ctx);
    return ThrowError("SSL_new() failed.");
  }

  Local arr = Array::New();
  STACK_OF(SSL_CIPHER)* ciphers = SSL_get_ciphers(ssl);

  for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) {
    SSL_CIPHER* cipher = sk_SSL_CIPHER_value(ciphers, i);
    arr->Set(i, String::New(SSL_CIPHER_get_name(cipher)));
  }

  SSL_free(ssl);
  SSL_CTX_free(ctx);

  args.GetReturnValue().Set(arr);
}


template 
static void array_push_back(const TypeName* md,
                            const char* from,
                            const char* to,
                            void* arg) {
  Local& arr = *static_cast(arg);
  arr->Set(arr->Length(), String::New(from));
}


void GetCiphers(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Local arr = Array::New();
  EVP_CIPHER_do_all_sorted(array_push_back, &arr);
  args.GetReturnValue().Set(arr);
}


void GetHashes(const FunctionCallbackInfo& args) {
  HandleScope scope(node_isolate);
  Local arr = Array::New();
  EVP_MD_do_all_sorted(array_push_back, &arr);
  args.GetReturnValue().Set(arr);
}


void InitCrypto(Handle target) {
  HandleScope scope(node_isolate);

  SSL_library_init();
  OpenSSL_add_all_algorithms();
  OpenSSL_add_all_digests();
  SSL_load_error_strings();
  ERR_load_crypto_strings();

  crypto_lock_init();
  CRYPTO_set_locking_callback(crypto_lock_cb);
  CRYPTO_THREADID_set_callback(crypto_threadid_cb);

  // Turn off compression. Saves memory - do it in userland.
#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

  SecureContext::Initialize(target);
  Connection::Initialize(target);
  CipherBase::Initialize(target);
  DiffieHellman::Initialize(target);
  Hmac::Initialize(target);
  Hash::Initialize(target);
  Sign::Initialize(target);
  Verify::Initialize(target);

  NODE_SET_METHOD(target, "PBKDF2", PBKDF2);
  NODE_SET_METHOD(target, "randomBytes", RandomBytes);
  NODE_SET_METHOD(target, "pseudoRandomBytes", RandomBytes);
  NODE_SET_METHOD(target, "getSSLCiphers", GetSSLCiphers);
  NODE_SET_METHOD(target, "getCiphers", GetCiphers);
  NODE_SET_METHOD(target, "getHashes", GetHashes);

  subject_symbol    = String::New("subject");
  issuer_symbol     = String::New("issuer");
  valid_from_symbol = String::New("valid_from");
  valid_to_symbol   = String::New("valid_to");
  subjectaltname_symbol = String::New("subjectaltname");
  modulus_symbol        = String::New("modulus");
  exponent_symbol       = String::New("exponent");
  fingerprint_symbol   = String::New("fingerprint");
  name_symbol       = String::New("name");
  version_symbol    = String::New("version");
  ext_key_usage_symbol = String::New("ext_key_usage");
}

}  // namespace crypto
}  // namespace node

NODE_MODULE(node_crypto, node::crypto::InitCrypto)

Web Proxy Viewer  |  New URL  |  Original Page