// 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_groups.h" #include "v8.h" #include "node.h" #include "node_buffer.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_BUFFER(val) \ if (!Buffer::HasInstance(val)) { \ return ThrowException(Exception::TypeError(String::New("Not a buffer"))); \ } 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 namespace v8; static Persistent errno_symbol; static Persistent syscall_symbol; static Persistent subject_symbol; static Persistent subjectaltname_symbol; static Persistent modulus_symbol; static Persistent exponent_symbol; static Persistent issuer_symbol; static Persistent valid_from_symbol; static Persistent valid_to_symbol; static Persistent fingerprint_symbol; static Persistent name_symbol; static Persistent version_symbol; static Persistent ext_key_usage_symbol; static Persistent onhandshakestart_sym; static Persistent onhandshakedone_sym; static Persistent onclienthello_sym; static Persistent onnewsession_sym; static Persistent 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); } } Handle ThrowCryptoErrorHelper(unsigned long err, bool is_type_error) { HandleScope scope; char errmsg[128]; ERR_error_string_n(err, errmsg, sizeof(errmsg)); return is_type_error ? ThrowTypeError(errmsg) : ThrowError(errmsg); } Handle ThrowCryptoError(unsigned long err) { return ThrowCryptoErrorHelper(err, false); } Handle ThrowCryptoTypeError(unsigned long err) { return ThrowCryptoErrorHelper(err, true); } void SecureContext::Initialize(Handle target) { HandleScope scope; Local t = FunctionTemplate::New(SecureContext::New); secure_context_constructor = Persistent::New(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, "close", SecureContext::Close); NODE_SET_PROTOTYPE_METHOD(t, "loadPKCS12", SecureContext::LoadPKCS12); target->Set(String::NewSymbol("SecureContext"), t->GetFunction()); } Handle SecureContext::New(const Arguments& args) { HandleScope scope; SecureContext *p = new SecureContext(); p->Wrap(args.Holder()); return args.This(); } Handle SecureContext::Init(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); 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 ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); #endif } else if (strcmp(*sslmethod, "SSLv2_server_method") == 0) { #ifndef OPENSSL_NO_SSL2 method = SSLv2_server_method(); #else return ThrowException(Exception::Error(String::New("SSLv2 methods disabled"))); #endif } else if (strcmp(*sslmethod, "SSLv2_client_method") == 0) { #ifndef OPENSSL_NO_SSL2 method = SSLv2_client_method(); #else return ThrowException(Exception::Error(String::New("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 ThrowException(Exception::Error(String::New("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; return True(); } SSL_SESSION* SecureContext::GetSessionCallback(SSL* s, unsigned char* key, int len, int* copy) { HandleScope scope; Connection* p = static_cast(SSL_get_app_data(s)); *copy = 0; SSL_SESSION* sess = p->next_sess_; p->next_sess_ = NULL; return sess; } void SessionDataFree(char* data, void* hint) { delete[] data; } int SecureContext::NewSessionCallback(SSL* s, SSL_SESSION* sess) { HandleScope scope; 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)->handle_, Buffer::New(serialized, size, SessionDataFree, NULL)->handle_ }; if (onnewsession_sym.IsEmpty()) { onnewsession_sym = NODE_PSYMBOL("onnewsession"); } MakeCallback(p->handle_, onnewsession_sym, ARRAY_SIZE(argv), argv); return 0; } // Takes a string or buffer and loads it into a BIO. // Caller responsible for BIO_free-ing the returned object. static BIO* LoadBIO (Handle v) { BIO *bio = BIO_new(BIO_s_mem()); if (!bio) return NULL; HandleScope scope; 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 <= 0) { BIO_free(bio); return NULL; } return bio; } // Takes a string or buffer and loads it into an X509 // Caller responsible for X509_free-ing the returned object. static X509* LoadX509 (Handle v) { HandleScope scope; // necessary? BIO *bio = LoadBIO(v); if (!bio) return NULL; X509 * x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); if (!x509) { BIO_free(bio); return NULL; } BIO_free(bio); return x509; } Handle SecureContext::SetKey(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); unsigned int len = args.Length(); if (len != 1 && len != 2) { return ThrowException(Exception::TypeError(String::New("Bad parameter"))); } if (len == 2 && !args[1]->IsString()) { return ThrowException(Exception::TypeError(String::New("Bad parameter"))); } BIO *bio = LoadBIO(args[0]); if (!bio) return False(); String::Utf8Value passphrase(args[1]); EVP_PKEY* key = PEM_read_bio_PrivateKey(bio, NULL, NULL, len == 1 ? NULL : *passphrase); if (!key) { BIO_free(bio); unsigned long err = ERR_get_error(); if (!err) { return ThrowException(Exception::Error( String::New("PEM_read_bio_PrivateKey"))); } return ThrowCryptoError(err); } SSL_CTX_use_PrivateKey(sc->ctx_, key); EVP_PKEY_free(key); BIO_free(bio); return True(); } // 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; } Handle SecureContext::SetCert(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); if (args.Length() != 1) { return ThrowException(Exception::TypeError( String::New("Bad parameter"))); } BIO* bio = LoadBIO(args[0]); if (!bio) return False(); int rv = SSL_CTX_use_certificate_chain(sc->ctx_, bio); BIO_free(bio); if (!rv) { unsigned long err = ERR_get_error(); if (!err) { return ThrowException(Exception::Error( String::New("SSL_CTX_use_certificate_chain"))); } return ThrowCryptoError(err); } return True(); } Handle SecureContext::AddCACert(const Arguments& args) { bool newCAStore = false; HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); if (args.Length() != 1) { return ThrowException(Exception::TypeError(String::New("Bad parameter"))); } if (!sc->ca_store_) { sc->ca_store_ = X509_STORE_new(); newCAStore = true; } X509* x509 = LoadX509(args[0]); if (!x509) return False(); 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_); } return True(); } Handle SecureContext::AddCRL(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); if (args.Length() != 1) { return ThrowException(Exception::TypeError(String::New("Bad parameter"))); } BIO *bio = LoadBIO(args[0]); if (!bio) return False(); X509_CRL *x509 = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); if (x509 == NULL) { BIO_free(bio); return False(); } 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(bio); X509_CRL_free(x509); return True(); } Handle SecureContext::AddRootCerts(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); 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(BIO_s_mem()); if (!BIO_write(bp, root_certs[i], strlen(root_certs[i]))) { BIO_free(bp); return False(); } X509 *x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL); if (x509 == NULL) { BIO_free(bp); return False(); } X509_STORE_add_cert(root_cert_store, x509); BIO_free(bp); X509_free(x509); } } sc->ca_store_ = root_cert_store; SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_); return True(); } Handle SecureContext::SetCiphers(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); if (args.Length() != 1 || !args[0]->IsString()) { return ThrowException(Exception::TypeError(String::New("Bad parameter"))); } String::Utf8Value ciphers(args[0]); SSL_CTX_set_cipher_list(sc->ctx_, *ciphers); return True(); } Handle SecureContext::SetOptions(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); if (args.Length() != 1 || !args[0]->IntegerValue()) { return ThrowException(Exception::TypeError(String::New("Bad parameter"))); } SSL_CTX_set_options(sc->ctx_, args[0]->IntegerValue()); return True(); } Handle SecureContext::SetSessionIdContext(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); if (args.Length() != 1 || !args[0]->IsString()) { return ThrowException(Exception::TypeError(String::New("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) { Local message; BIO* bio; BUF_MEM* mem; if ((bio = BIO_new(BIO_s_mem()))) { ERR_print_errors(bio); BIO_get_mem_ptr(bio, &mem); message = String::New(mem->data, mem->length); BIO_free(bio); } else { message = String::New("SSL_CTX_set_session_id_context error"); } return ThrowException(Exception::TypeError(message)); } return True(); } Handle SecureContext::Close(const Arguments& args) { HandleScope scope; SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); sc->FreeCTXMem(); return False(); } //Takes .pfx or .p12 and password in string or buffer format Handle SecureContext::LoadPKCS12(const Arguments& args) { HandleScope scope; 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.Holder()); if (args.Length() < 1) { return ThrowException(Exception::TypeError( String::New("Bad parameter"))); } in = LoadBIO(args[0]); if (in == NULL) { return ThrowException(Exception::Error( String::New("Unable to load BIO"))); } if (args.Length() >= 2) { ASSERT_IS_BUFFER(args[1]); int passlen = Buffer::Length(args[1]); if (passlen < 0) { BIO_free(in); return ThrowException(Exception::TypeError( String::New("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); } EVP_PKEY_free(pkey); X509_free(cert); sk_X509_free(extraCerts); ret = true; } PKCS12_free(p12); BIO_free(in); delete[] pass; if (!ret) { unsigned long err = ERR_get_error(); const char* str = ERR_reason_error_string(err); return ThrowException(Exception::Error(String::New(str))); } return True(); } size_t ClientHelloParser::Write(const uint8_t* data, size_t len) { HandleScope scope; // 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] << 8) + data_[4]; state_ = kTLSHeader; body_offset_ = 5; } else { frame_len_ = (data_[0] << 8) + data_[1]; state_ = kSSLHeader; if (*data_ & 0x40) { // header with padding body_offset_ = 3; } else { // without padding body_offset_ = 2; } } // Sanity check (too big frame, or too small) if (frame_len_ >= sizeof(data_)) { // Let OpenSSL handle it Finish(); return copied; } case kTLSHeader: case kSSLHeader: // >= 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] << 8) + body[1]; if (body + 4 + ciphers_size < data_ + offset_) { session_size = (body[2] << 8) + body[3]; session_id = body + 4 + ciphers_size; } } } else { // Whoa? How did we get here? abort(); } // Check if we overflowed (do not reply with any private data) if (session_id == NULL || session_size > 32 || session_id + session_size > 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 = NODE_PSYMBOL("onclienthello"); } if (sessionid_sym.IsEmpty()) { sessionid_sym = NODE_PSYMBOL("sessionId"); } state_ = kPaused; hello = Object::New(); hello->Set(sessionid_sym, Buffer::New(reinterpret_cast(session_id), session_size)->handle_); argv[0] = hello; MakeCallback(conn_->handle_, onclienthello_sym, 1, 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; Local e = Exception::Error(String::New(ssl_error_buf)); handle_->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) { // Forcibly clear OpenSSL's error stack on return. This stops stale errors // from popping up later in the lifecycle of the SSL connection 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(); } }; 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 { HandleScope scope; 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_->Set(String::New("error"), e); BIO_free(bio); } return rv; } return 0; } void Connection::ClearError() { #ifndef NDEBUG HandleScope scope; // We should clear the error in JS-land assert(handle_->Get(String::New("error"))->BooleanValue() == false); #endif // NDEBUG } void Connection::SetShutdownFlags() { HandleScope scope; int flags = SSL_get_shutdown(ssl_); if (flags & SSL_SENT_SHUTDOWN) { handle_->Set(String::New("sentShutdown"), True()); } if (flags & SSL_RECEIVED_SHUTDOWN) { handle_->Set(String::New("receivedShutdown"), True()); } } void Connection::Initialize(Handle target) { HandleScope scope; 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 { *data = reinterpret_cast(Buffer::Data(p->npnProtos_)); *len = Buffer::Length(p->npnProtos_); } return SSL_TLSEXT_ERR_OK; } int Connection::SelectNextProtoCallback_(SSL *s, unsigned char** out, unsigned char* outlen, const unsigned char* in, unsigned int inlen, void *arg) { Connection *p = static_cast SSL_get_app_data(s); // Release old protocol handler if present if (!p->selectedNPNProto_.IsEmpty()) { p->selectedNPNProto_.Dispose(); } if (p->npnProtos_.IsEmpty()) { // We should at least select one protocol // If server is using NPN *out = reinterpret_cast(const_cast("http/1.1")); *outlen = 8; // set status unsupported p->selectedNPNProto_ = Persistent::New(False()); return SSL_TLSEXT_ERR_OK; } const unsigned char* npnProtos = reinterpret_cast(Buffer::Data(p->npnProtos_)); int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtos, Buffer::Length(p->npnProtos_)); switch (status) { case OPENSSL_NPN_UNSUPPORTED: p->selectedNPNProto_ = Persistent::New(Null()); break; case OPENSSL_NPN_NEGOTIATED: p->selectedNPNProto_ = Persistent::New(String::New( reinterpret_cast(*out), *outlen )); break; case OPENSSL_NPN_NO_OVERLAP: p->selectedNPNProto_ = Persistent::New(False()); break; default: break; } return SSL_TLSEXT_ERR_OK; } #endif #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) { HandleScope scope; Connection *p = static_cast SSL_get_app_data(s); const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); if (servername) { if (!p->servername_.IsEmpty()) { p->servername_.Dispose(); } p->servername_ = Persistent::New(String::New(servername)); // Call the SNI callback and use its return value as context if (!p->sniObject_.IsEmpty()) { if (!p->sniContext_.IsEmpty()) { p->sniContext_.Dispose(); } // Get callback init args Local argv[1] = {*p->servername_}; // Call it Local ret = Local::New(MakeCallback(p->sniObject_, "onselect", ARRAY_SIZE(argv), argv)); // If ret is SecureContext if (secure_context_constructor->HasInstance(ret)) { p->sniContext_ = Persistent::New(ret); SecureContext *sc = ObjectWrap::Unwrap( Local::Cast(ret)); SSL_set_SSL_CTX(s, sc->ctx_); } else { return SSL_TLSEXT_ERR_NOACK; } } } return SSL_TLSEXT_ERR_OK; } #endif Handle Connection::New(const Arguments& args) { HandleScope scope; Connection *p = new Connection(); p->Wrap(args.Holder()); if (args.Length() < 1 || !args[0]->IsObject()) { return ThrowException(Exception::Error(String::New( "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(BIO_s_mem()); p->bio_write_ = BIO_new(BIO_s_mem()); 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_); } return args.This(); } 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 <= 0.9.7e. SSL* ssl = const_cast(ssl_); if (where & SSL_CB_HANDSHAKE_START) { HandleScope scope; Connection* c = static_cast(SSL_get_app_data(ssl)); if (onhandshakestart_sym.IsEmpty()) { onhandshakestart_sym = NODE_PSYMBOL("onhandshakestart"); } MakeCallback(c->handle_, onhandshakestart_sym, 0, NULL); } if (where & SSL_CB_HANDSHAKE_DONE) { HandleScope scope; Connection* c = static_cast(SSL_get_app_data(ssl)); if (onhandshakedone_sym.IsEmpty()) { onhandshakedone_sym = NODE_PSYMBOL("onhandshakedone"); } MakeCallback(c->handle_, onhandshakedone_sym, 0, NULL); } } Handle Connection::EncIn(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 3) { return ThrowException(Exception::TypeError( String::New("Takes 3 parameters"))); } if (!Buffer::HasInstance(args[0])) { return ThrowException(Exception::TypeError( String::New("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(); if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("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(); } return scope.Close(Integer::New(bytes_written)); } Handle Connection::ClearOut(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 3) { return ThrowException(Exception::TypeError( String::New("Takes 3 parameters"))); } if (!Buffer::HasInstance(args[0])) { return ThrowException(Exception::TypeError( String::New("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(); if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("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); } else { rv = SSL_connect(ss->ssl_); ss->HandleSSLError("SSL_connect:ClearOut", rv, kZeroIsAnError); } if (rv < 0) return scope.Close(Integer::New(rv)); } int bytes_read = SSL_read(ss->ssl_, buffer_data + off, len); ss->HandleSSLError("SSL_read:ClearOut", bytes_read, kZeroIsNotAnError); ss->SetShutdownFlags(); return scope.Close(Integer::New(bytes_read)); } Handle Connection::ClearPending(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); int bytes_pending = BIO_pending(ss->bio_read_); return scope.Close(Integer::New(bytes_pending)); } Handle Connection::EncPending(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); int bytes_pending = BIO_pending(ss->bio_write_); return scope.Close(Integer::New(bytes_pending)); } Handle Connection::EncOut(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 3) { return ThrowException(Exception::TypeError( String::New("Takes 3 parameters"))); } if (!Buffer::HasInstance(args[0])) { return ThrowException(Exception::TypeError( String::New("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(); if (off >= buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("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(); return scope.Close(Integer::New(bytes_read)); } Handle Connection::ClearIn(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 3) { return ThrowException(Exception::TypeError( String::New("Takes 3 parameters"))); } if (!Buffer::HasInstance(args[0])) { return ThrowException(Exception::TypeError( String::New("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(); if (off > buffer_length) { return ThrowException(Exception::Error( String::New("Offset is out of bounds"))); } size_t len = args[2]->Int32Value(); if (off + len > buffer_length) { return ThrowException(Exception::Error( String::New("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); } else { rv = SSL_connect(ss->ssl_); ss->HandleSSLError("SSL_connect:ClearIn", rv, kZeroIsAnError); } if (rv < 0) return scope.Close(Integer::New(rv)); } int bytes_written = SSL_write(ss->ssl_, buffer_data + off, len); ss->HandleSSLError("SSL_write:ClearIn", bytes_written, kZeroIsAnError); ss->SetShutdownFlags(); return scope.Close(Integer::New(bytes_written)); } Handle Connection::GetPeerCertificate(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL) return Undefined(); 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(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), 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); } return scope.Close(info); } Handle Connection::GetSession(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL) return Undefined(); SSL_SESSION* sess = SSL_get_session(ss->ssl_); if (!sess) return Undefined(); int slen = i2d_SSL_SESSION(sess, NULL); assert(slen > 0); if (slen > 0) { unsigned char* sbuf = new unsigned char[slen]; unsigned char* p = sbuf; i2d_SSL_SESSION(sess, &p); Local s = Encode(sbuf, slen, BINARY); delete[] sbuf; return scope.Close(s); } return Null(); } Handle Connection::SetSession(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 1 || (!args[0]->IsString() && !Buffer::HasInstance(args[0]))) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } ASSERT_IS_BUFFER(args[0]); ssize_t slen = Buffer::Length(args[0]); if (slen < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } char* sbuf = new char[slen]; ssize_t wlen = DecodeWrite(sbuf, slen, args[0], BINARY); assert(wlen == slen); const unsigned char* p = reinterpret_cast(sbuf); SSL_SESSION* sess = d2i_SSL_SESSION(NULL, &p, wlen); delete [] sbuf; if (!sess) return Undefined(); int r = SSL_set_session(ss->ssl_, sess); SSL_SESSION_free(sess); if (!r) { Local eStr = String::New("SSL_set_session error"); return ThrowException(Exception::Error(eStr)); } return True(); } Handle Connection::LoadSession(const Arguments& args) { HandleScope scope; 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(); return True(); } Handle Connection::IsSessionReused(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL || SSL_session_reused(ss->ssl_) == false) { return False(); } return True(); } Handle Connection::Start(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (!SSL_is_init_finished(ss->ssl_)) { int rv; if (ss->is_server_) { rv = SSL_accept(ss->ssl_); ss->HandleSSLError("SSL_accept:Start", rv, kZeroIsAnError); } else { rv = SSL_connect(ss->ssl_); ss->HandleSSLError("SSL_connect:Start", rv, kZeroIsAnError); } return scope.Close(Integer::New(rv)); } return scope.Close(Integer::New(0)); } Handle Connection::Shutdown(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL) return False(); int rv = SSL_shutdown(ss->ssl_); ss->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError); ss->SetShutdownFlags(); return scope.Close(Integer::New(rv)); } Handle Connection::ReceivedShutdown(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL) return False(); int r = SSL_get_shutdown(ss->ssl_); if (r & SSL_RECEIVED_SHUTDOWN) return True(); return False(); } Handle Connection::IsInitFinished(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL || SSL_is_init_finished(ss->ssl_) == false) { return False(); } return True(); } Handle Connection::VerifyError(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ == NULL) return Null(); // 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 scope.Close(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: return Null(); 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; } return scope.Close(Exception::Error(s)); } Handle Connection::GetCurrentCipher(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); OPENSSL_CONST SSL_CIPHER *c; if ( ss->ssl_ == NULL ) return Undefined(); c = SSL_get_current_cipher(ss->ssl_); if ( c == NULL ) return Undefined(); 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)); return scope.Close(info); } Handle Connection::Close(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->ssl_ != NULL) { SSL_free(ss->ssl_); ss->ssl_ = NULL; } return True(); } #ifdef OPENSSL_NPN_NEGOTIATED Handle Connection::GetNegotiatedProto(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->is_server_) { const unsigned char* npn_proto; unsigned int npn_proto_len; SSL_get0_next_proto_negotiated(ss->ssl_, &npn_proto, &npn_proto_len); if (!npn_proto) { return False(); } return String::New((const char*) npn_proto, npn_proto_len); } else { return ss->selectedNPNProto_; } } Handle Connection::SetNPNProtocols(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 1 || !Buffer::HasInstance(args[0])) { return ThrowException(Exception::Error(String::New( "Must give a Buffer as first argument"))); } // Release old handle if (!ss->npnProtos_.IsEmpty()) { ss->npnProtos_.Dispose(); } ss->npnProtos_ = Persistent::New(args[0]->ToObject()); return True(); }; #endif #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB Handle Connection::GetServername(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (ss->is_server_ && !ss->servername_.IsEmpty()) { return ss->servername_; } else { return False(); } } Handle Connection::SetSNICallback(const Arguments& args) { HandleScope scope; Connection *ss = Connection::Unwrap(args); if (args.Length() < 1 || !args[0]->IsFunction()) { return ThrowException(Exception::Error(String::New( "Must give a Function as first argument"))); } // Release old handle if (!ss->sniObject_.IsEmpty()) { ss->sniObject_.Dispose(); } ss->sniObject_ = Persistent::New(Object::New()); ss->sniObject_->Set(String::New("onselect"), args[0]); return True(); } #endif class Cipher : public ObjectWrap { public: static void Initialize (v8::Handle target) { HandleScope scope; Local t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); NODE_SET_PROTOTYPE_METHOD(t, "init", CipherInit); NODE_SET_PROTOTYPE_METHOD(t, "initiv", CipherInitIv); NODE_SET_PROTOTYPE_METHOD(t, "update", CipherUpdate); NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding); NODE_SET_PROTOTYPE_METHOD(t, "final", CipherFinal); target->Set(String::NewSymbol("Cipher"), t->GetFunction()); } bool CipherInit(char* cipherType, char* key_buf, int key_buf_len) { cipher = EVP_get_cipherbyname(cipherType); if(!cipher) { fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType); return false; } unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH]; int key_len = EVP_BytesToKey(cipher, EVP_md5(), NULL, (unsigned char*) key_buf, key_buf_len, 1, key, iv); EVP_CIPHER_CTX_init(&ctx); EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true); if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } EVP_CipherInit_ex(&ctx, NULL, NULL, (unsigned char*)key, (unsigned char*)iv, true); initialised_ = true; return true; } bool CipherInitIv(char* cipherType, char* key, int key_len, char* iv, int iv_len) { cipher = EVP_get_cipherbyname(cipherType); if(!cipher) { fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType); return false; } /* 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)) { fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len); return false; } EVP_CIPHER_CTX_init(&ctx); EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, true); if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } EVP_CipherInit_ex(&ctx, NULL, NULL, (unsigned char*)key, (unsigned char*)iv, true); initialised_ = true; return true; } int CipherUpdate(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, (unsigned char*)data, len); } int SetAutoPadding(bool auto_padding) { if (!initialised_) return 0; return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0); } int CipherFinal(unsigned char** out, int *out_len) { if (!initialised_) return 0; *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)]; int r = EVP_CipherFinal_ex(&ctx,*out, out_len); EVP_CIPHER_CTX_cleanup(&ctx); initialised_ = false; return r; } protected: static Handle New(const Arguments& args) { HandleScope scope; Cipher *cipher = new Cipher(); cipher->Wrap(args.This()); return args.This(); } static Handle CipherInit(const Arguments& args) { HandleScope scope; Cipher *cipher = ObjectWrap::Unwrap(args.This()); if (args.Length() <= 1 || !args[0]->IsString() || !(args[1]->IsString() || Buffer::HasInstance(args[1]))) { return ThrowException(Exception::Error(String::New( "Must give cipher-type, key"))); } ASSERT_IS_BUFFER(args[1]); ssize_t key_buf_len = Buffer::Length(args[1]); if (key_buf_len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } char* key_buf = new char[key_buf_len]; ssize_t key_written = DecodeWrite(key_buf, key_buf_len, args[1], BINARY); assert(key_written == key_buf_len); String::Utf8Value cipherType(args[0]); bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len); delete [] key_buf; if (!r) return ThrowCryptoError(ERR_get_error()); return args.This(); } static Handle CipherInitIv(const Arguments& args) { Cipher *cipher = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (args.Length() <= 2 || !args[0]->IsString() || !(args[1]->IsString() || Buffer::HasInstance(args[1])) || !(args[2]->IsString() || Buffer::HasInstance(args[2]))) { return ThrowException(Exception::Error(String::New( "Must give cipher-type, key, and iv as argument"))); } ASSERT_IS_BUFFER(args[1]); ssize_t key_len = Buffer::Length(args[1]); if (key_len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } ASSERT_IS_BUFFER(args[2]); ssize_t iv_len = Buffer::Length(args[2]); if (iv_len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } char* key_buf = new char[key_len]; ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY); assert(key_written == key_len); char* iv_buf = new char[iv_len]; ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY); assert(iv_written == iv_len); String::Utf8Value cipherType(args[0]); bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); delete [] key_buf; delete [] iv_buf; if (!r) return ThrowCryptoError(ERR_get_error()); return args.This(); } static Handle CipherUpdate(const Arguments& args) { Cipher *cipher = ObjectWrap::Unwrap(args.This()); HandleScope scope; ASSERT_IS_BUFFER(args[0]); unsigned char* out=0; int out_len=0, r; char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); r = cipher->CipherUpdate(buffer_data, buffer_length, &out, &out_len); if (r == 0) { delete [] out; return ThrowCryptoTypeError(ERR_get_error()); } Local outString; outString = Encode(out, out_len, BUFFER); if (out) delete [] out; return scope.Close(outString); } static Handle SetAutoPadding(const Arguments& args) { HandleScope scope; Cipher *cipher = ObjectWrap::Unwrap(args.This()); cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue()); return Undefined(); } static Handle CipherFinal(const Arguments& args) { Cipher *cipher = ObjectWrap::Unwrap(args.This()); HandleScope scope; unsigned char* out_value = NULL; int out_len = -1; Local outString ; int r = cipher->CipherFinal(&out_value, &out_len); if (out_len <= 0 || r == 0) { delete[] out_value; out_value = NULL; if (r == 0) return ThrowCryptoTypeError(ERR_get_error()); } outString = Encode(out_value, out_len, BUFFER); delete [] out_value; return scope.Close(outString); } Cipher () : ObjectWrap () { initialised_ = false; } ~Cipher () { if (initialised_) { EVP_CIPHER_CTX_cleanup(&ctx); } } private: EVP_CIPHER_CTX ctx; /* coverity[member_decl] */ const EVP_CIPHER *cipher; /* coverity[member_decl] */ bool initialised_; }; class Decipher : public ObjectWrap { public: static void Initialize (v8::Handle target) { HandleScope scope; Local t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); NODE_SET_PROTOTYPE_METHOD(t, "init", DecipherInit); NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv); NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate); NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal); NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal); // remove someday NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding); target->Set(String::NewSymbol("Decipher"), t->GetFunction()); } bool DecipherInit(char* cipherType, char* key_buf, int key_buf_len) { cipher_ = EVP_get_cipherbyname(cipherType); if(!cipher_) { fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType); return false; } unsigned char key[EVP_MAX_KEY_LENGTH],iv[EVP_MAX_IV_LENGTH]; int key_len = EVP_BytesToKey(cipher_, EVP_md5(), NULL, (unsigned char*)(key_buf), key_buf_len, 1, key, iv); EVP_CIPHER_CTX_init(&ctx); EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false); if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } EVP_CipherInit_ex(&ctx, NULL, NULL, (unsigned char*)key, (unsigned char*)iv, false); initialised_ = true; return true; } bool DecipherInitIv(char* cipherType, char* key, int key_len, char* iv, int iv_len) { cipher_ = EVP_get_cipherbyname(cipherType); if(!cipher_) { fprintf(stderr, "node-crypto : Unknown cipher %s\n", cipherType); return false; } /* 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)) { fprintf(stderr, "node-crypto : Invalid IV length %d\n", iv_len); return false; } EVP_CIPHER_CTX_init(&ctx); EVP_CipherInit_ex(&ctx, cipher_, NULL, NULL, NULL, false); if (!EVP_CIPHER_CTX_set_key_length(&ctx, key_len)) { fprintf(stderr, "node-crypto : Invalid key length %d\n", key_len); EVP_CIPHER_CTX_cleanup(&ctx); return false; } EVP_CipherInit_ex(&ctx, NULL, NULL, (unsigned char*)key, (unsigned char*)iv, false); initialised_ = true; return true; } int DecipherUpdate(char* data, int len, unsigned char** out, int* out_len) { if (!initialised_) { *out_len = 0; *out = NULL; return 0; } *out_len=len+EVP_CIPHER_CTX_block_size(&ctx); *out= new unsigned char[*out_len]; return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len); } int SetAutoPadding(bool auto_padding) { if (!initialised_) return 0; return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0); } // coverity[alloc_arg] int DecipherFinal(unsigned char** out, int *out_len) { int r; if (!initialised_) { *out_len = 0; *out = NULL; return 0; } *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)]; r = EVP_CipherFinal_ex(&ctx,*out,out_len); EVP_CIPHER_CTX_cleanup(&ctx); initialised_ = false; return r; } protected: static Handle New (const Arguments& args) { HandleScope scope; Decipher *cipher = new Decipher(); cipher->Wrap(args.This()); return args.This(); } static Handle DecipherInit(const Arguments& args) { Decipher *cipher = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (args.Length() <= 1 || !args[0]->IsString() || !(args[1]->IsString() || Buffer::HasInstance(args[1]))) { return ThrowException(Exception::Error(String::New( "Must give cipher-type, key as argument"))); } ASSERT_IS_BUFFER(args[1]); ssize_t key_len = Buffer::Length(args[1]); if (key_len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } char* key_buf = new char[key_len]; ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY); assert(key_written == key_len); String::Utf8Value cipherType(args[0]); bool r = cipher->DecipherInit(*cipherType, key_buf,key_len); delete [] key_buf; if (!r) { return ThrowException(Exception::Error(String::New("DecipherInit error"))); } return args.This(); } static Handle DecipherInitIv(const Arguments& args) { Decipher *cipher = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (args.Length() <= 2 || !args[0]->IsString() || !(args[1]->IsString() || Buffer::HasInstance(args[1])) || !(args[2]->IsString() || Buffer::HasInstance(args[2]))) { return ThrowException(Exception::Error(String::New( "Must give cipher-type, key, and iv as argument"))); } ASSERT_IS_BUFFER(args[1]); ssize_t key_len = Buffer::Length(args[1]); if (key_len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } ASSERT_IS_BUFFER(args[2]); ssize_t iv_len = Buffer::Length(args[2]); if (iv_len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } char* key_buf = new char[key_len]; ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY); assert(key_written == key_len); char* iv_buf = new char[iv_len]; ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY); assert(iv_written == iv_len); String::Utf8Value cipherType(args[0]); bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); delete [] key_buf; delete [] iv_buf; if (!r) { return ThrowException(Exception::Error(String::New("DecipherInitIv error"))); } return args.This(); } static Handle DecipherUpdate(const Arguments& args) { HandleScope scope; Decipher *cipher = ObjectWrap::Unwrap(args.This()); ASSERT_IS_BUFFER(args[0]); ssize_t len; char* buf; // if alloc_buf then buf must be deleted later bool alloc_buf = false; char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); buf = buffer_data; len = buffer_length; unsigned char* out=0; int out_len=0; int r = cipher->DecipherUpdate(buf, len, &out, &out_len); if (!r) { delete [] out; return ThrowCryptoTypeError(ERR_get_error()); } Local outString; outString = Encode(out, out_len, BUFFER); if (out) delete [] out; if (alloc_buf) delete [] buf; return scope.Close(outString); } static Handle SetAutoPadding(const Arguments& args) { HandleScope scope; Decipher *cipher = ObjectWrap::Unwrap(args.This()); cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue()); return Undefined(); } static Handle DecipherFinal(const Arguments& args) { HandleScope scope; Decipher *cipher = ObjectWrap::Unwrap(args.This()); unsigned char* out_value = NULL; int out_len = -1; Local outString; int r = cipher->DecipherFinal(&out_value, &out_len); if (out_len <= 0 || r == 0) { delete [] out_value; // allocated even if out_len == 0 out_value = NULL; if (r == 0) return ThrowCryptoTypeError(ERR_get_error()); } outString = Encode(out_value, out_len, BUFFER); delete [] out_value; return scope.Close(outString); } Decipher () : ObjectWrap () { initialised_ = false; } ~Decipher () { if (initialised_) { EVP_CIPHER_CTX_cleanup(&ctx); } } private: EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher_; bool initialised_; }; class Hmac : public ObjectWrap { public: static void Initialize (v8::Handle target) { HandleScope scope; Local t = FunctionTemplate::New(New); t->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()); } bool HmacInit(char* hashType, char* key, int key_len) { md = EVP_get_digestbyname(hashType); if(!md) { fprintf(stderr, "node-crypto : Unknown message digest %s\n", hashType); return false; } HMAC_CTX_init(&ctx); if (key_len == 0) { HMAC_Init(&ctx, "", 0, md); } else { HMAC_Init(&ctx, key, key_len, md); } initialised_ = true; return true; } int HmacUpdate(char* data, int len) { if (!initialised_) return 0; HMAC_Update(&ctx, (unsigned char*)data, len); return 1; } int HmacDigest(unsigned char** md_value, unsigned int *md_len) { if (!initialised_) return 0; *md_value = new unsigned char[EVP_MAX_MD_SIZE]; HMAC_Final(&ctx, *md_value, md_len); HMAC_CTX_cleanup(&ctx); initialised_ = false; return 1; } protected: static Handle New (const Arguments& args) { HandleScope scope; Hmac *hmac = new Hmac(); hmac->Wrap(args.This()); return args.This(); } static Handle HmacInit(const Arguments& args) { Hmac *hmac = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (args.Length() == 0 || !args[0]->IsString()) { return ThrowException(Exception::Error(String::New( "Must give hashtype string as argument"))); } ASSERT_IS_BUFFER(args[1]); ssize_t len = Buffer::Length(args[1]); if (len < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } String::Utf8Value hashType(args[0]); bool r; if( Buffer::HasInstance(args[1])) { char* buffer_data = Buffer::Data(args[1]); size_t buffer_length = Buffer::Length(args[1]); r = hmac->HmacInit(*hashType, buffer_data, buffer_length); } else { char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[1], BINARY); assert(written == len); r = hmac->HmacInit(*hashType, buf, len); delete [] buf; } if (!r) { return ThrowException(Exception::Error(String::New("hmac error"))); } return args.This(); } static Handle HmacUpdate(const Arguments& args) { Hmac *hmac = ObjectWrap::Unwrap(args.This()); HandleScope scope; ASSERT_IS_BUFFER(args[0]); int r; char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); r = hmac->HmacUpdate(buffer_data, buffer_length); if (!r) { Local exception = Exception::TypeError(String::New("HmacUpdate fail")); return ThrowException(exception); } return args.This(); } static Handle HmacDigest(const Arguments& args) { Hmac *hmac = ObjectWrap::Unwrap(args.This()); HandleScope scope; unsigned char* md_value = NULL; unsigned int md_len = 0; Local outString; int r = hmac->HmacDigest(&md_value, &md_len); if (r == 0) { md_value = NULL; md_len = 0; } outString = Encode(md_value, md_len, BUFFER); delete [] md_value; return scope.Close(outString); } Hmac () : ObjectWrap () { initialised_ = false; } ~Hmac () { if (initialised_) { HMAC_CTX_cleanup(&ctx); } } private: HMAC_CTX ctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class Hash : public ObjectWrap { public: static void Initialize (v8::Handle target) { HandleScope scope; 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()); } bool HashInit (const char* hashType) { md = EVP_get_digestbyname(hashType); if(!md) return false; EVP_MD_CTX_init(&mdctx); EVP_DigestInit_ex(&mdctx, md, NULL); initialised_ = true; return true; } int HashUpdate(char* data, int len) { if (!initialised_) return 0; EVP_DigestUpdate(&mdctx, data, len); return 1; } protected: static Handle New (const Arguments& args) { HandleScope scope; if (args.Length() == 0 || !args[0]->IsString()) { return ThrowException(Exception::Error(String::New( "Must give hashtype string as argument"))); } String::Utf8Value hashType(args[0]); Hash *hash = new Hash(); if (!hash->HashInit(*hashType)) { delete hash; return ThrowException(Exception::Error(String::New( "Digest method not supported"))); } hash->Wrap(args.This()); return args.This(); } static Handle HashUpdate(const Arguments& args) { HandleScope scope; Hash *hash = ObjectWrap::Unwrap(args.This()); ASSERT_IS_BUFFER(args[0]); int r; char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); r = hash->HashUpdate(buffer_data, buffer_length); if (!r) { Local exception = Exception::TypeError(String::New("HashUpdate fail")); return ThrowException(exception); } return args.This(); } static Handle HashDigest(const Arguments& args) { HandleScope scope; Hash *hash = ObjectWrap::Unwrap(args.This()); if (!hash->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } 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 outString; outString = Encode(md_value, md_len, BUFFER); return scope.Close(outString); } Hash () : ObjectWrap () { initialised_ = false; } ~Hash () { if (initialised_) { EVP_MD_CTX_cleanup(&mdctx); } } private: EVP_MD_CTX mdctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class Sign : public ObjectWrap { public: static void Initialize (v8::Handle target) { HandleScope scope; 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()); } bool SignInit (const char* signType) { md = EVP_get_digestbyname(signType); if(!md) { printf("Unknown message digest %s\n", signType); return false; } EVP_MD_CTX_init(&mdctx); EVP_SignInit_ex(&mdctx, md, NULL); initialised_ = true; return true; } int SignUpdate(char* data, int len) { if (!initialised_) return 0; EVP_SignUpdate(&mdctx, data, len); return 1; } int SignFinal(unsigned char** md_value, unsigned int *md_len, char* key_pem, int key_pemLen) { if (!initialised_) return 0; BIO *bp = NULL; EVP_PKEY* pkey; bp = BIO_new(BIO_s_mem()); if(!BIO_write(bp, key_pem, key_pemLen)) return 0; 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(bp); return 1; } protected: static Handle New (const Arguments& args) { HandleScope scope; Sign *sign = new Sign(); sign->Wrap(args.This()); return args.This(); } static Handle SignInit(const Arguments& args) { HandleScope scope; Sign *sign = ObjectWrap::Unwrap(args.This()); if (args.Length() == 0 || !args[0]->IsString()) { return ThrowException(Exception::Error(String::New( "Must give signtype string as argument"))); } String::Utf8Value signType(args[0]); bool r = sign->SignInit(*signType); if (!r) { return ThrowException(Exception::Error(String::New("SignInit error"))); } return args.This(); } static Handle SignUpdate(const Arguments& args) { Sign *sign = ObjectWrap::Unwrap(args.This()); HandleScope scope; ASSERT_IS_BUFFER(args[0]); int r; char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); r = sign->SignUpdate(buffer_data, buffer_length); if (!r) { Local exception = Exception::TypeError(String::New("SignUpdate fail")); return ThrowException(exception); } return args.This(); } static Handle SignFinal(const Arguments& args) { Sign *sign = ObjectWrap::Unwrap(args.This()); HandleScope scope; unsigned char* md_value; unsigned int md_len; Local outString; md_len = 8192; // Maximum key size is 8192 bits md_value = new unsigned char[md_len]; ASSERT_IS_BUFFER(args[0]); ssize_t len = Buffer::Length(args[0]); char* buf = new char[len]; ssize_t written = DecodeWrite(buf, len, args[0], BUFFER); assert(written == len); int r = sign->SignFinal(&md_value, &md_len, buf, len); if (r == 0) { md_value = NULL; md_len = r; } delete [] buf; outString = Encode(md_value, md_len, BUFFER); delete [] md_value; return scope.Close(outString); } Sign () : ObjectWrap () { initialised_ = false; } ~Sign () { if (initialised_) { EVP_MD_CTX_cleanup(&mdctx); } } private: EVP_MD_CTX mdctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class Verify : public ObjectWrap { public: static void Initialize (v8::Handle target) { HandleScope scope; 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()); } bool VerifyInit (const char* verifyType) { md = EVP_get_digestbyname(verifyType); if(!md) { fprintf(stderr, "node-crypto : Unknown message digest %s\n", verifyType); return false; } EVP_MD_CTX_init(&mdctx); EVP_VerifyInit_ex(&mdctx, md, NULL); initialised_ = true; return true; } int VerifyUpdate(char* data, int len) { if (!initialised_) return 0; EVP_VerifyUpdate(&mdctx, data, len); return 1; } int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) { if (!initialised_) return 0; EVP_PKEY* pkey = NULL; BIO *bp = NULL; X509 *x509 = NULL; int r = 0; bp = BIO_new(BIO_s_mem()); if (bp == NULL) { ERR_print_errors_fp(stderr); return 0; } if(!BIO_write(bp, key_pem, key_pemLen)) { ERR_print_errors_fp(stderr); return 0; } // 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) { ERR_print_errors_fp(stderr); return 0; } } 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) { ERR_print_errors_fp(stderr); return 0; } } else { // X.509 fallback x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL); if (x509 == NULL) { ERR_print_errors_fp(stderr); return 0; } pkey = X509_get_pubkey(x509); if (pkey == NULL) { ERR_print_errors_fp(stderr); return 0; } } r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey); if(pkey != NULL) EVP_PKEY_free (pkey); if (x509 != NULL) X509_free(x509); if (bp != NULL) BIO_free(bp); EVP_MD_CTX_cleanup(&mdctx); initialised_ = false; return r; } protected: static Handle New (const Arguments& args) { HandleScope scope; Verify *verify = new Verify(); verify->Wrap(args.This()); return args.This(); } static Handle VerifyInit(const Arguments& args) { Verify *verify = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (args.Length() == 0 || !args[0]->IsString()) { return ThrowException(Exception::Error(String::New( "Must give verifytype string as argument"))); } String::Utf8Value verifyType(args[0]); bool r = verify->VerifyInit(*verifyType); if (!r) { return ThrowException(Exception::Error(String::New("VerifyInit error"))); } return args.This(); } static Handle VerifyUpdate(const Arguments& args) { HandleScope scope; Verify *verify = ObjectWrap::Unwrap(args.This()); ASSERT_IS_BUFFER(args[0]); int r; char* buffer_data = Buffer::Data(args[0]); size_t buffer_length = Buffer::Length(args[0]); r = verify->VerifyUpdate(buffer_data, buffer_length); if (!r) { Local exception = Exception::TypeError(String::New("VerifyUpdate fail")); return ThrowException(exception); } return args.This(); } static Handle VerifyFinal(const Arguments& args) { HandleScope scope; Verify *verify = ObjectWrap::Unwrap(args.This()); ASSERT_IS_BUFFER(args[0]); ssize_t klen = Buffer::Length(args[0]); if (klen < 0) { Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } char* kbuf = new char[klen]; ssize_t kwritten = DecodeWrite(kbuf, klen, args[0], BINARY); assert(kwritten == klen); ASSERT_IS_BUFFER(args[1]); ssize_t hlen = Buffer::Length(args[1]); if (hlen < 0) { delete [] kbuf; Local exception = Exception::TypeError(String::New("Bad argument")); return ThrowException(exception); } unsigned char* hbuf = new unsigned char[hlen]; ssize_t hwritten = DecodeWrite((char*)hbuf, hlen, args[1], BINARY); assert(hwritten == hlen); int r=-1; r = verify->VerifyFinal(kbuf, klen, hbuf, hlen); delete [] kbuf; delete [] hbuf; return Boolean::New(r && r != -1); } Verify () : ObjectWrap () { initialised_ = false; } ~Verify () { if (initialised_) { EVP_MD_CTX_cleanup(&mdctx); } } private: EVP_MD_CTX mdctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class DiffieHellman : public ObjectWrap { public: static void Initialize(v8::Handle target) { HandleScope scope; 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 Init(int primeLength) { dh = DH_new(); DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0); bool result = VerifyContext(); if (!result) return false; initialised_ = true; return true; } bool Init(unsigned char* p, int p_len) { dh = DH_new(); dh->p = BN_bin2bn(p, p_len, 0); dh->g = BN_new(); if (!BN_set_word(dh->g, 2)) return false; bool result = VerifyContext(); if (!result) return false; initialised_ = true; return true; } bool 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; } protected: static Handle DiffieHellmanGroup(const Arguments& args) { HandleScope scope; DiffieHellman* diffieHellman = new DiffieHellman(); if (args.Length() != 1 || !args[0]->IsString()) { return ThrowException(Exception::Error( String::New("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 ThrowException(Exception::Error( String::New("Unknown group"))); } diffieHellman->Wrap(args.This()); return args.This(); } static Handle New(const Arguments& args) { HandleScope scope; 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 ThrowException(Exception::Error( String::New("Initialization failed"))); } diffieHellman->Wrap(args.This()); return args.This(); } static Handle GenerateKeys(const Arguments& args) { DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (!diffieHellman->initialised_) { return ThrowException(Exception::Error( String::New("Not initialized"))); } if (!DH_generate_key(diffieHellman->dh)) { return ThrowException(Exception::Error( String::New("Key generation failed"))); } Local outString; int dataSize = BN_num_bytes(diffieHellman->dh->pub_key); char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->pub_key, reinterpret_cast(data)); outString = Encode(data, dataSize, BUFFER); delete[] data; return scope.Close(outString); } static Handle GetPrime(const Arguments& args) { DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (!diffieHellman->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } int dataSize = BN_num_bytes(diffieHellman->dh->p); char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->p, reinterpret_cast(data)); Local outString; outString = Encode(data, dataSize, BUFFER); delete[] data; return scope.Close(outString); } static Handle GetGenerator(const Arguments& args) { DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (!diffieHellman->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } int dataSize = BN_num_bytes(diffieHellman->dh->g); char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->g, reinterpret_cast(data)); Local outString; outString = Encode(data, dataSize, BUFFER); delete[] data; return scope.Close(outString); } static Handle GetPublicKey(const Arguments& args) { DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (!diffieHellman->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } if (diffieHellman->dh->pub_key == NULL) { return ThrowException(Exception::Error( String::New("No public key - did you forget to generate one?"))); } int dataSize = BN_num_bytes(diffieHellman->dh->pub_key); char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->pub_key, reinterpret_cast(data)); Local outString; outString = Encode(data, dataSize, BUFFER); delete[] data; return scope.Close(outString); } static Handle GetPrivateKey(const Arguments& args) { DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); HandleScope scope; if (!diffieHellman->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } if (diffieHellman->dh->priv_key == NULL) { return ThrowException(Exception::Error( String::New("No private key - did you forget to generate one?"))); } int dataSize = BN_num_bytes(diffieHellman->dh->priv_key); char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->priv_key, reinterpret_cast(data)); Local outString; outString = Encode(data, dataSize, BUFFER); delete[] data; return scope.Close(outString); } static Handle ComputeSecret(const Arguments& args) { HandleScope scope; DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); if (!diffieHellman->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } BIGNUM* key = 0; if (args.Length() == 0) { return ThrowException(Exception::Error( String::New("First argument must be other party's public key"))); } else { 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 ThrowException(Exception::Error(String::New("Invalid key"))); } else if (checkResult) { if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) { return ThrowException(Exception::Error( String::New("Supplied key is too small"))); } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) { return ThrowException(Exception::Error( String::New("Supplied key is too large"))); } else { return ThrowException(Exception::Error(String::New("Invalid key"))); } } else { return ThrowException(Exception::Error(String::New("Invalid key"))); } } 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); memset(data + size, 0, dataSize - size); } Local outString; outString = Encode(data, dataSize, BUFFER); delete[] data; return scope.Close(outString); } static Handle SetPublicKey(const Arguments& args) { HandleScope scope; DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); if (!diffieHellman->initialised_) { return ThrowException(Exception::Error(String::New("Not initialized"))); } if (args.Length() == 0) { return ThrowException(Exception::Error( String::New("First argument must be public key"))); } else { ASSERT_IS_BUFFER(args[0]); diffieHellman->dh->pub_key = BN_bin2bn( reinterpret_cast(Buffer::Data(args[0])), Buffer::Length(args[0]), 0); } return args.This(); } static Handle SetPrivateKey(const Arguments& args) { HandleScope scope; DiffieHellman* diffieHellman = ObjectWrap::Unwrap(args.This()); if (!diffieHellman->initialised_) { return ThrowException(Exception::Error( String::New("Not initialized"))); } if (args.Length() == 0) { return ThrowException(Exception::Error( String::New("First argument must be private key"))); } else { ASSERT_IS_BUFFER(args[0]); diffieHellman->dh->priv_key = BN_bin2bn( reinterpret_cast(Buffer::Data(args[0])), Buffer::Length(args[0]), 0); } return args.This(); } DiffieHellman() : ObjectWrap() { initialised_ = false; dh = NULL; } ~DiffieHellman() { if (dh != NULL) { DH_free(dh); } } private: bool VerifyContext() { int codes; if (!DH_check(dh, &codes)) return false; if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false; if (codes & DH_CHECK_P_NOT_PRIME) return false; if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false; if (codes & DH_NOT_SUITABLE_GENERATOR) return false; return true; } bool initialised_; DH* dh; }; 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] = Local::New(Undefined()); 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] = Local::New(Undefined()); } 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; Local argv[2]; Persistent obj = req->obj; EIO_PBKDF2After(req, argv); MakeCallback(obj, "ondone", ARRAY_SIZE(argv), argv); obj.Dispose(); } Handle PBKDF2(const Arguments& args) { HandleScope scope; 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()) { req->obj = Persistent::New(Object::New()); req->obj->Set(String::New("ondone"), args[4]); uv_queue_work(uv_default_loop(), &req->work_req, EIO_PBKDF2, EIO_PBKDF2After); return Undefined(); } else { Local argv[2]; EIO_PBKDF2(req); EIO_PBKDF2After(req, argv); if (argv[0]->IsObject()) return ThrowException(argv[0]); return scope.Close(argv[1]); } err: delete[] salt; delete[] pass; return ThrowException(Exception::TypeError(String::New(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() { if (obj_.IsEmpty()) return; obj_.Dispose(); obj_.Clear(); } void RandomBytesFree(char* data, void* hint) { delete[] data; } 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] = Local::New(Null()); } else { // avoids the malloc + memcpy Buffer* buffer = Buffer::New(req->data_, req->size_, RandomBytesFree, NULL); argv[0] = Local::New(Null()); argv[1] = Local::New(buffer->handle_); } } void RandomBytesAfter(uv_work_t* work_req, int status) { assert(status == 0); RandomBytesRequest* req = container_of(work_req, RandomBytesRequest, work_req_); HandleScope scope; Local argv[2]; RandomBytesCheck(req, argv); MakeCallback(req->obj_, "ondone", ARRAY_SIZE(argv), argv); delete req; } template Handle RandomBytes(const Arguments& args) { HandleScope scope; // maybe allow a buffer to write to? cuts down on object creation // when generating random data in a loop if (!args[0]->IsUint32()) { Local s = String::New("Argument #1 must be number > 0"); return ThrowException(Exception::TypeError(s)); } const size_t size = args[0]->Uint32Value(); RandomBytesRequest* req = new RandomBytesRequest(); req->error_ = 0; req->data_ = new char[size]; req->size_ = size; if (args[1]->IsFunction()) { req->obj_ = Persistent::New(Object::New()); req->obj_->Set(String::New("ondone"), args[1]); uv_queue_work(uv_default_loop(), &req->work_req_, RandomBytesWork, RandomBytesAfter); return req->obj_; } else { Local argv[2]; RandomBytesWork(&req->work_req_); RandomBytesCheck(req, argv); delete req; if (!argv[0]->IsNull()) return ThrowException(argv[0]); else return argv[1]; } } Handle GetCiphers(const Arguments& args) { HandleScope scope; 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); return scope.Close(arr); } static void add_hash_to_array(const EVP_MD* md, const char* from, const char* to, void* arg) { Local& arr = *static_cast*>(arg); arr->Set(arr->Length(), String::New(from)); } Handle GetHashes(const Arguments& args) { HandleScope scope; Local arr = Array::New(); EVP_MD_do_all_sorted(add_hash_to_array, &arr); return scope.Close(arr); } void InitCrypto(Handle target) { HandleScope scope; 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); Cipher::Initialize(target); Decipher::Initialize(target); DiffieHellman::Initialize(target); Hmac::Initialize(target); Hash::Initialize(target); Sign::Initialize(target); Verify::Initialize(target); NODE_SET_METHOD(target, "PBKDF2", PBKDF2); NODE_SET_METHOD(target, "randomBytes", RandomBytes); NODE_SET_METHOD(target, "pseudoRandomBytes", RandomBytes); NODE_SET_METHOD(target, "getCiphers", GetCiphers); NODE_SET_METHOD(target, "getHashes", GetHashes); subject_symbol = NODE_PSYMBOL("subject"); issuer_symbol = NODE_PSYMBOL("issuer"); valid_from_symbol = NODE_PSYMBOL("valid_from"); valid_to_symbol = NODE_PSYMBOL("valid_to"); subjectaltname_symbol = NODE_PSYMBOL("subjectaltname"); modulus_symbol = NODE_PSYMBOL("modulus"); exponent_symbol = NODE_PSYMBOL("exponent"); fingerprint_symbol = NODE_PSYMBOL("fingerprint"); name_symbol = NODE_PSYMBOL("name"); version_symbol = NODE_PSYMBOL("version"); ext_key_usage_symbol = NODE_PSYMBOL("ext_key_usage"); } } // namespace crypto } // namespace node NODE_MODULE(node_crypto, node::crypto::InitCrypto)