Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

4434 lines (3467 sloc) 124.835 kb
// 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 <string.h>
#ifdef _MSC_VER
#define snprintf _snprintf
#define strcasecmp _stricmp
#endif
#include <stdlib.h>
#include <errno.h>
/* Sigh. */
#ifdef _WIN32
# include <windows.h>
#else
# include <pthread.h>
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
# define OPENSSL_CONST const
#else
# define OPENSSL_CONST
#endif
#define ASSERT_IS_STRING_OR_BUFFER(val) \
if (!val->IsString() && !Buffer::HasInstance(val)) { \
return ThrowException(Exception::TypeError(String::New("Not a string or 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<String> errno_symbol;
static Persistent<String> syscall_symbol;
static Persistent<String> subject_symbol;
static Persistent<String> subjectaltname_symbol;
static Persistent<String> modulus_symbol;
static Persistent<String> exponent_symbol;
static Persistent<String> issuer_symbol;
static Persistent<String> valid_from_symbol;
static Persistent<String> valid_to_symbol;
static Persistent<String> fingerprint_symbol;
static Persistent<String> name_symbol;
static Persistent<String> version_symbol;
static Persistent<String> ext_key_usage_symbol;
static Persistent<String> onhandshakestart_sym;
static Persistent<String> onhandshakedone_sym;
static Persistent<FunctionTemplate> secure_context_constructor;
static uv_rwlock_t* locks;
static unsigned long crypto_id_cb(void) {
#ifdef _WIN32
return (unsigned long) GetCurrentThreadId();
#else /* !_WIN32 */
return (unsigned long) pthread_self();
#endif /* !_WIN32 */
}
static void crypto_lock_init(void) {
int i, n;
n = CRYPTO_num_locks();
locks = new uv_rwlock_t[n];
for (i = 0; i < n; i++)
if (uv_rwlock_init(locks + i))
abort();
}
static void crypto_lock_cb(int mode, int n, const char* file, int line) {
assert((mode & CRYPTO_LOCK) || (mode & CRYPTO_UNLOCK));
assert((mode & CRYPTO_READ) || (mode & CRYPTO_WRITE));
if (mode & CRYPTO_LOCK) {
if (mode & CRYPTO_READ)
uv_rwlock_rdlock(locks + n);
else
uv_rwlock_wrlock(locks + n);
} else {
if (mode & CRYPTO_READ)
uv_rwlock_rdunlock(locks + n);
else
uv_rwlock_wrunlock(locks + n);
}
}
void SecureContext::Initialize(Handle<Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(SecureContext::New);
secure_context_constructor = Persistent<FunctionTemplate>::New(t);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewSymbol("SecureContext"));
NODE_SET_PROTOTYPE_METHOD(t, "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);
target->Set(String::NewSymbol("SecureContext"), t->GetFunction());
}
Handle<Value> SecureContext::New(const Arguments& args) {
HandleScope scope;
SecureContext *p = new SecureContext();
p->Wrap(args.Holder());
return args.This();
}
Handle<Value> SecureContext::Init(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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);
// Enable session caching?
SSL_CTX_set_session_cache_mode(sc->ctx_, SSL_SESS_CACHE_SERVER);
// SSL_CTX_set_session_cache_mode(sc->ctx_,SSL_SESS_CACHE_OFF);
sc->ca_store_ = NULL;
return True();
}
// Takes a string or buffer and loads it into a BIO.
// Caller responsible for BIO_free-ing the returned object.
static BIO* LoadBIO (Handle<Value> 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)) {
Local<Object> buffer_obj = v->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
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<Value> 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<Value> SecureContext::SetKey(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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);
return False();
}
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<Value> SecureContext::SetCert(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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")));
}
char string[120];
ERR_error_string_n(err, string, sizeof string);
return ThrowException(Exception::Error(String::New(string)));
}
return True();
}
Handle<Value> SecureContext::AddCACert(const Arguments& args) {
bool newCAStore = false;
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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<Value> SecureContext::AddCRL(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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<Value> SecureContext::AddRootCerts(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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<Value> SecureContext::SetCiphers(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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<Value> SecureContext::SetOptions(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
if (args.Length() != 1 || !args[0]->IsUint32()) {
return ThrowException(Exception::TypeError(String::New("Bad parameter")));
}
unsigned int opts = args[0]->Uint32Value();
SSL_CTX_set_options(sc->ctx_, opts);
return True();
}
Handle<Value> SecureContext::SetSessionIdContext(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(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<String> 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<Value> SecureContext::Close(const Arguments& args) {
HandleScope scope;
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(args.Holder());
sc->FreeCTXMem();
return False();
}
#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<Value> 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) {
if (rv >= 0) 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<Value> 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<Object> target) {
HandleScope scope;
Local<FunctionTemplate> 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, "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<Connection*>(SSL_get_app_data(s));
if (p->npnProtos_.IsEmpty()) {
// No initialization - no NPN protocols
*data = reinterpret_cast<const unsigned char*>("");
*len = 0;
} else {
*data = reinterpret_cast<const unsigned char*>(Buffer::Data(p->npnProtos_));
*len = Buffer::Length(p->npnProtos_);
}
return SSL_TLSEXT_ERR_OK;
}
int Connection::SelectNextProtoCallback_(SSL *s,
unsigned char **out, unsigned char *outlen,
const unsigned char* in,
unsigned int inlen, void *arg) {
Connection *p = static_cast<Connection*> SSL_get_app_data(s);
// Release old protocol handler if present
if (!p->selectedNPNProto_.IsEmpty()) {
p->selectedNPNProto_.Dispose();
}
if (p->npnProtos_.IsEmpty()) {
// We should at least select one protocol
// If server is using NPN
*out = reinterpret_cast<unsigned char*>(const_cast<char*>("http/1.1"));
*outlen = 8;
// set status unsupported
p->selectedNPNProto_ = Persistent<Value>::New(False());
return SSL_TLSEXT_ERR_OK;
}
const unsigned char* npnProtos =
reinterpret_cast<const unsigned char*>(Buffer::Data(p->npnProtos_));
int status = SSL_select_next_proto(out, outlen, in, inlen, npnProtos,
Buffer::Length(p->npnProtos_));
switch (status) {
case OPENSSL_NPN_UNSUPPORTED:
p->selectedNPNProto_ = Persistent<Value>::New(Null());
break;
case OPENSSL_NPN_NEGOTIATED:
p->selectedNPNProto_ = Persistent<Value>::New(String::New(
reinterpret_cast<const char*>(*out), *outlen
));
break;
case OPENSSL_NPN_NO_OVERLAP:
p->selectedNPNProto_ = Persistent<Value>::New(False());
break;
default:
break;
}
return SSL_TLSEXT_ERR_OK;
}
#endif
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
HandleScope scope;
Connection *p = static_cast<Connection*> SSL_get_app_data(s);
const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
if (servername) {
if (!p->servername_.IsEmpty()) {
p->servername_.Dispose();
}
p->servername_ = Persistent<String>::New(String::New(servername));
// Call sniCallback_ and use it's return value as context
if (!p->sniCallback_.IsEmpty()) {
if (!p->sniContext_.IsEmpty()) {
p->sniContext_.Dispose();
}
// Get callback init args
Local<Value> argv[1] = {*p->servername_};
Local<Function> callback = *p->sniCallback_;
// Call it
//
// XXX There should be an object connected to this that
// we can attach a domain onto.
Local<Value> ret;
ret = Local<Value>::New(MakeCallback(Context::GetCurrent()->Global(),
callback, ARRAY_SIZE(argv), argv));
// If ret is SecureContext
if (secure_context_constructor->HasInstance(ret)) {
p->sniContext_ = Persistent<Value>::New(ret);
SecureContext *sc = ObjectWrap::Unwrap<SecureContext>(
Local<Object>::Cast(ret));
SSL_set_SSL_CTX(s, sc->ctx_);
} else {
return SSL_TLSEXT_ERR_NOACK;
}
}
}
return SSL_TLSEXT_ERR_OK;
}
#endif
Handle<Value> Connection::New(const Arguments& args) {
HandleScope scope;
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<SecureContext>(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*>(ssl_);
if (where & SSL_CB_HANDSHAKE_START) {
HandleScope scope;
Connection* c = static_cast<Connection*>(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<Connection*>(SSL_get_app_data(ssl));
if (onhandshakedone_sym.IsEmpty()) {
onhandshakedone_sym = NODE_PSYMBOL("onhandshakedone");
}
MakeCallback(c->handle_, onhandshakedone_sym, 0, NULL);
}
}
Handle<Value> 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")));
}
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
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 = BIO_write(ss->bio_read_, buffer_data + off, len);
ss->HandleBIOError(ss->bio_read_, "BIO_write", bytes_written);
ss->SetShutdownFlags();
return scope.Close(Integer::New(bytes_written));
}
Handle<Value> 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")));
}
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
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);
} else {
rv = SSL_connect(ss->ssl_);
ss->HandleSSLError("SSL_connect:ClearOut", rv);
}
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);
ss->SetShutdownFlags();
return scope.Close(Integer::New(bytes_read));
}
Handle<Value> 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<Value> 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<Value> 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")));
}
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
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<Value> 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")));
}
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
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);
} else {
rv = SSL_connect(ss->ssl_);
ss->HandleSSLError("SSL_connect:ClearIn", rv);
}
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);
ss->SetShutdownFlags();
return scope.Close(Integer::New(bytes_written));
}
Handle<Value> Connection::GetPeerCertificate(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (ss->ssl_ == NULL) return Undefined();
Local<Object> 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);
}
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<md_size; i++) {
fingerprint[3*i] = hex[(md[i] & 0xf0) >> 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<Array> ext_key_usage = Array::New();
char buf[256];
for (int i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
memset(buf, 0, sizeof(buf));
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<Value> Connection::GetSession(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (ss->ssl_ == NULL) return Undefined();
SSL_SESSION* sess = SSL_get_session(ss->ssl_);
if (!sess) return Undefined();
int slen = i2d_SSL_SESSION(sess, NULL);
assert(slen > 0);
if (slen > 0) {
unsigned char* sbuf = new unsigned char[slen];
unsigned char* p = sbuf;
i2d_SSL_SESSION(sess, &p);
Local<Value> s = Encode(sbuf, slen, BINARY);
delete[] sbuf;
return scope.Close(s);
}
return Null();
}
Handle<Value> Connection::SetSession(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (args.Length() < 1 || !args[0]->IsString()) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
ASSERT_IS_STRING_OR_BUFFER(args[0]);
ssize_t slen = DecodeBytes(args[0], BINARY);
if (slen < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
char* sbuf = new char[slen];
ssize_t wlen = DecodeWrite(sbuf, slen, args[0], BINARY);
assert(wlen == slen);
const unsigned char* p = (unsigned char*) sbuf;
SSL_SESSION* sess = d2i_SSL_SESSION(NULL, &p, wlen);
delete [] sbuf;
if (!sess)
return Undefined();
int r = SSL_set_session(ss->ssl_, sess);
SSL_SESSION_free(sess);
if (!r) {
Local<String> eStr = String::New("SSL_set_session error");
return ThrowException(Exception::Error(eStr));
}
return True();
}
Handle<Value> Connection::IsSessionReused(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (ss->ssl_ == NULL) return False();
return SSL_session_reused(ss->ssl_) ? True() : False();
}
Handle<Value> Connection::Start(const Arguments& args) {
HandleScope scope;
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);
} else {
rv = SSL_connect(ss->ssl_);
ss->HandleSSLError("SSL_connect:Start", rv);
}
return scope.Close(Integer::New(rv));
}
return scope.Close(Integer::New(0));
}
Handle<Value> 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);
ss->SetShutdownFlags();
return scope.Close(Integer::New(rv));
}
Handle<Value> 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<Value> Connection::IsInitFinished(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (ss->ssl_ == NULL) return False();
return SSL_is_init_finished(ss->ssl_) ? True() : False();
}
Handle<Value> 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(String::New("UNABLE_TO_GET_ISSUER_CERT"));
}
X509_free(peer_cert);
long x509_verify_error = SSL_get_verify_result(ss->ssl_);
Local<String> 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(s);
}
Handle<Value> 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<Object> 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<Value> 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<Value> Connection::GetNegotiatedProto(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (ss->is_server_) {
const unsigned char *npn_proto;
unsigned int npn_proto_len;
SSL_get0_next_proto_negotiated(ss->ssl_, &npn_proto, &npn_proto_len);
if (!npn_proto) {
return False();
}
return String::New((const char*) npn_proto, npn_proto_len);
} else {
return ss->selectedNPNProto_;
}
}
Handle<Value> Connection::SetNPNProtocols(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (args.Length() < 1 || !Buffer::HasInstance(args[0])) {
return ThrowException(Exception::Error(String::New(
"Must give a Buffer as first argument")));
}
// Release old handle
if (!ss->npnProtos_.IsEmpty()) {
ss->npnProtos_.Dispose();
}
ss->npnProtos_ = Persistent<Object>::New(args[0]->ToObject());
return True();
};
#endif
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
Handle<Value> Connection::GetServername(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (ss->is_server_ && !ss->servername_.IsEmpty()) {
return ss->servername_;
} else {
return False();
}
}
Handle<Value> Connection::SetSNICallback(const Arguments& args) {
HandleScope scope;
Connection *ss = Connection::Unwrap(args);
if (args.Length() < 1 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New(
"Must give a Function as first argument")));
}
// Release old handle
if (!ss->sniCallback_.IsEmpty()) {
ss->sniCallback_.Dispose();
}
ss->sniCallback_ = Persistent<Function>::New(
Local<Function>::Cast(args[0]));
return True();
}
#endif
static void HexEncode(unsigned char *md_value,
int md_len,
char** md_hexdigest,
int* md_hex_len) {
*md_hex_len = (2*(md_len));
*md_hexdigest = new char[*md_hex_len + 1];
char* buff = *md_hexdigest;
const int len = *md_hex_len;
for (int i = 0; i < len; i += 2) {
// nibble nibble
const int index = i / 2;
const char msb = (md_value[index] >> 4) & 0x0f;
const char lsb = md_value[index] & 0x0f;
buff[i] = (msb < 10) ? msb + '0' : (msb - 10) + 'a';
buff[i + 1] = (lsb < 10) ? lsb + '0' : (lsb - 10) + 'a';
}
// null terminator
buff[*md_hex_len] = '\0';
}
#define hex2i(c) ((c) <= '9' ? ((c) - '0') : (c) <= 'Z' ? ((c) - 'A' + 10) \
: ((c) - 'a' + 10))
static void HexDecode(unsigned char *input,
int length,
char** buf64,
int* buf64_len) {
*buf64_len = (length/2);
*buf64 = new char[length/2 + 1];
char *b = *buf64;
for(int i = 0; i < length-1; i+=2) {
b[i/2] = (hex2i(input[i])<<4) | (hex2i(input[i+1]));
}
}
void base64(unsigned char *input, int length, char** buf64, int* buf64_len) {
BIO *b64 = BIO_new(BIO_f_base64());
BIO *bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
int len = BIO_write(b64, input, length);
assert(len == length);
int r = BIO_flush(b64);
assert(r == 1);
BUF_MEM *bptr;
BIO_get_mem_ptr(b64, &bptr);
*buf64_len = bptr->length;
*buf64 = new char[*buf64_len+1];
memcpy(*buf64, bptr->data, *buf64_len);
char* b = *buf64;
b[*buf64_len] = 0;
BIO_free_all(b64);
}
void unbase64(unsigned char *input,
int length,
char** buffer,
int* buffer_len) {
BIO *b64, *bmem;
*buffer = new char[length];
memset(*buffer, 0, length);
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(input, length);
bmem = BIO_push(b64, bmem);
*buffer_len = BIO_read(bmem, *buffer, length);
BIO_free_all(bmem);
}
// LengthWithoutIncompleteUtf8 from V8 d8-posix.cc
// see http://v8.googlecode.com/svn/trunk/src/d8-posix.cc
static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
int answer = len;
// 1-byte encoding.
static const int kUtf8SingleByteMask = 0x80;
static const int kUtf8SingleByteValue = 0x00;
// 2-byte encoding.
static const int kUtf8TwoByteMask = 0xe0;
static const int kUtf8TwoByteValue = 0xc0;
// 3-byte encoding.
static const int kUtf8ThreeByteMask = 0xf0;
static const int kUtf8ThreeByteValue = 0xe0;
// 4-byte encoding.
static const int kUtf8FourByteMask = 0xf8;
static const int kUtf8FourByteValue = 0xf0;
// Subsequent bytes of a multi-byte encoding.
static const int kMultiByteMask = 0xc0;
static const int kMultiByteValue = 0x80;
int multi_byte_bytes_seen = 0;
while (answer > 0) {
int c = buffer[answer - 1];
// Ends in valid single-byte sequence?
if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
// Ends in one or more subsequent bytes of a multi-byte value?
if ((c & kMultiByteMask) == kMultiByteValue) {
multi_byte_bytes_seen++;
answer--;
} else {
if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
if (multi_byte_bytes_seen >= 1) {
return answer + 2;
}
return answer - 1;
} else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
if (multi_byte_bytes_seen >= 2) {
return answer + 3;
}
return answer - 1;
} else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
if (multi_byte_bytes_seen >= 3) {
return answer + 4;
}
return answer - 1;
} else {
return answer; // Malformed UTF-8.
}
}
}
return 0;
}
// local decrypt final without strict padding check
// to work with php mcrypt
// see http://www.mail-archive.com/openssl-dev@openssl.org/msg19927.html
int local_EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx,
unsigned char *out,
int *outl) {
int i,b;
int n;
*outl=0;
b=ctx->cipher->block_size;
if (ctx->flags & EVP_CIPH_NO_PADDING) {
if(ctx->buf_len) {
EVPerr(EVP_F_EVP_DECRYPTFINAL,EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);
return 0;
}
*outl = 0;
return 1;
}
if (b > 1) {
if (ctx->buf_len || !ctx->final_used) {
EVPerr(EVP_F_EVP_DECRYPTFINAL,EVP_R_WRONG_FINAL_BLOCK_LENGTH);
return(0);
}
if (b > (int)(sizeof(ctx->final) / sizeof(ctx->final[0]))) {
EVPerr(EVP_F_EVP_DECRYPTFINAL,EVP_R_BAD_DECRYPT);
return(0);
}
n=ctx->final[b-1];
if (n > b) {
EVPerr(EVP_F_EVP_DECRYPTFINAL,EVP_R_BAD_DECRYPT);
return(0);
}
for (i=0; i<n; i++) {
if (ctx->final[--b] != n) {
EVPerr(EVP_F_EVP_DECRYPTFINAL,EVP_R_BAD_DECRYPT);
return(0);
}
}
n=ctx->cipher->block_size-n;
for (i=0; i<n; i++) {
out[i]=ctx->final[i];
}
*outl=n;
} else {
*outl=0;
}
return(1);
}
class Cipher : public ObjectWrap {
public:
static void Initialize (v8::Handle<v8::Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "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];
EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
return 1;
}
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<Value> New(const Arguments& args) {
HandleScope scope;
Cipher *cipher = new Cipher();
cipher->Wrap(args.This());
return args.This();
}
static Handle<Value> CipherInit(const Arguments& args) {
HandleScope scope;
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
cipher->incomplete_base64=NULL;
if (args.Length() <= 1 || !args[0]->IsString() || !args[1]->IsString()) {
return ThrowException(Exception::Error(String::New(
"Must give cipher-type, key")));
}
ASSERT_IS_STRING_OR_BUFFER(args[1]);
ssize_t key_buf_len = DecodeBytes(args[1], BINARY);
if (key_buf_len < 0) {
Local<Value> 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 ThrowException(Exception::Error(String::New("CipherInit error")));
}
return args.This();
}
static Handle<Value> CipherInitIv(const Arguments& args) {
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
HandleScope scope;
cipher->incomplete_base64=NULL;
if (args.Length() <= 2 || !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsString()) {
return ThrowException(Exception::Error(String::New(
"Must give cipher-type, key, and iv as argument")));
}
ASSERT_IS_STRING_OR_BUFFER(args[1]);
ssize_t key_len = DecodeBytes(args[1], BINARY);
if (key_len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
ASSERT_IS_STRING_OR_BUFFER(args[2]);
ssize_t iv_len = DecodeBytes(args[2], BINARY);
if (iv_len < 0) {
Local<Value> 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 ThrowException(Exception::Error(String::New("CipherInitIv error")));
}
return args.This();
}
static Handle<Value> CipherUpdate(const Arguments& args) {
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
HandleScope scope;
ASSERT_IS_STRING_OR_BUFFER(args[0]);
enum encoding enc = ParseEncoding(args[1]);
ssize_t len = DecodeBytes(args[0], enc);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
unsigned char *out=0;
int out_len=0, r;
if (Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
r = cipher->CipherUpdate(buffer_data, buffer_length, &out, &out_len);
} else {
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc);
assert(written == len);
r = cipher->CipherUpdate(buf, len,&out,&out_len);
delete [] buf;
}
if (!r) {
delete [] out;
Local<Value> exception = Exception::TypeError(String::New("DecipherUpdate fail"));
return ThrowException(exception);
}
Local<Value> outString;
if (out_len==0) {
outString=String::New("");
} else {
char* out_hexdigest;
int out_hex_len;
enum encoding enc = ParseEncoding(args[2], BINARY);
if (enc == HEX) {
// Hex encoding
HexEncode(out, out_len, &out_hexdigest, &out_hex_len);
outString = Encode(out_hexdigest, out_hex_len, BINARY);
delete [] out_hexdigest;
} else if (enc == BASE64) {
// Base64 encoding
// Check to see if we need to add in previous base64 overhang
if (cipher->incomplete_base64!=NULL){
unsigned char* complete_base64 = new unsigned char[out_len+cipher->incomplete_base64_len+1];
memcpy(complete_base64, cipher->incomplete_base64, cipher->incomplete_base64_len);
memcpy(&complete_base64[cipher->incomplete_base64_len], out, out_len);
delete [] out;
delete [] cipher->incomplete_base64;
cipher->incomplete_base64=NULL;
out=complete_base64;
out_len += cipher->incomplete_base64_len;
}
// Check to see if we need to trim base64 stream
if (out_len%3!=0){
cipher->incomplete_base64_len = out_len%3;
cipher->incomplete_base64 = new char[cipher->incomplete_base64_len+1];
memcpy(cipher->incomplete_base64,
&out[out_len-cipher->incomplete_base64_len],
cipher->incomplete_base64_len);
out_len -= cipher->incomplete_base64_len;
out[out_len]=0;
}
base64(out, out_len, &out_hexdigest, &out_hex_len);
outString = Encode(out_hexdigest, out_hex_len, BINARY);
delete [] out_hexdigest;
} else if (enc == BINARY) {
outString = Encode(out, out_len, BINARY);
} else {
fprintf(stderr, "node-crypto : Cipher .update encoding "
"can be binary, hex or base64\n");
}
}
if (out) delete [] out;
return scope.Close(outString);
}
static Handle<Value> SetAutoPadding(const Arguments& args) {
HandleScope scope;
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
return Undefined();
}
static Handle<Value> CipherFinal(const Arguments& args) {
Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
HandleScope scope;
unsigned char* out_value = NULL;
int out_len = -1;
char* out_hexdigest;
int out_hex_len;
Local<Value> outString ;
int r = cipher->CipherFinal(&out_value, &out_len);
assert(out_value != NULL);
assert(out_len != -1 || r == 0);
if (out_len == 0 || r == 0) {
// out_value always get allocated.
delete[] out_value;
if (r == 0) {
Local<Value> exception = Exception::TypeError(
String::New("CipherFinal fail"));
return ThrowException(exception);
} else {
return scope.Close(String::New(""));
}
}
enum encoding enc = ParseEncoding(args[0], BINARY);
if (enc == HEX) {
// Hex encoding
HexEncode(out_value, out_len, &out_hexdigest, &out_hex_len);
outString = Encode(out_hexdigest, out_hex_len, BINARY);
delete [] out_hexdigest;
} else if (enc == BASE64) {
// Check to see if we need to add in previous base64 overhang
if (cipher->incomplete_base64!=NULL){
unsigned char* complete_base64 = new unsigned char[out_len+cipher->incomplete_base64_len+1];
memcpy(complete_base64, cipher->incomplete_base64, cipher->incomplete_base64_len);
memcpy(&complete_base64[cipher->incomplete_base64_len], out_value, out_len);
delete [] out_value;
delete [] cipher->incomplete_base64;
cipher->incomplete_base64=NULL;
out_value=complete_base64;
out_len += cipher->incomplete_base64_len;
}
base64(out_value, out_len, &out_hexdigest, &out_hex_len);
outString = Encode(out_hexdigest, out_hex_len, BINARY);
delete [] out_hexdigest;
} else if (enc == BINARY) {
outString = Encode(out_value, out_len, BINARY);
} else {
fprintf(stderr, "node-crypto : Cipher .final encoding "
"can be binary, hex or base64\n");
}
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_;
char* incomplete_base64; /* coverity[member_decl] */
int incomplete_base64_len; /* coverity[member_decl] */
};
class Decipher : public ObjectWrap {
public:
static void
Initialize (v8::Handle<v8::Object> target)
{
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "init", DecipherInit);
NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv);
NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate);
NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal<false>);
// This is completely undocumented:
NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal<true>);
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];
EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
return 1;
}
int SetAutoPadding(bool auto_padding) {
if (!initialised_) return 0;
return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
}
// coverity[alloc_arg]
template <bool TOLERATE_PADDING>
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)];
if (TOLERATE_PADDING) {
r = local_EVP_DecryptFinal_ex(&ctx,*out,out_len);
} else {
r = EVP_CipherFinal_ex(&ctx,*out,out_len);
}
EVP_CIPHER_CTX_cleanup(&ctx);
initialised_ = false;
return r;
}
protected:
static Handle<Value> New (const Arguments& args) {
HandleScope scope;
Decipher *cipher = new Decipher();
cipher->Wrap(args.This());
return args.This();
}
static Handle<Value> DecipherInit(const Arguments& args) {
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
HandleScope scope;
cipher->incomplete_utf8=NULL;
cipher->incomplete_hex_flag=false;
if (args.Length() <= 1 || !args[0]->IsString() || !args[1]->IsString()) {
return ThrowException(Exception::Error(String::New(
"Must give cipher-type, key as argument")));
}
ASSERT_IS_STRING_OR_BUFFER(args[1]);
ssize_t key_len = DecodeBytes(args[1], BINARY);
if (key_len < 0) {
Local<Value> 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<Value> DecipherInitIv(const Arguments& args) {
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
HandleScope scope;
cipher->incomplete_utf8=NULL;
cipher->incomplete_hex_flag=false;
if (args.Length() <= 2 || !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsString()) {
return ThrowException(Exception::Error(String::New(
"Must give cipher-type, key, and iv as argument")));
}
ASSERT_IS_STRING_OR_BUFFER(args[1]);
ssize_t key_len = DecodeBytes(args[1], BINARY);
if (key_len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
ASSERT_IS_STRING_OR_BUFFER(args[2]);
ssize_t iv_len = DecodeBytes(args[2], BINARY);
if (iv_len < 0) {
Local<Value> 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<Value> DecipherUpdate(const Arguments& args) {
HandleScope scope;
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
ASSERT_IS_STRING_OR_BUFFER(args[0]);
ssize_t len = DecodeBytes(args[0], BINARY);
if (len < 0) {
return ThrowException(Exception::Error(String::New(
"node`DecodeBytes() failed")));
}
char* buf;
// if alloc_buf then buf must be deleted later
bool alloc_buf = false;
if (Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
buf = buffer_data;
len = buffer_length;
} else {
alloc_buf = true;
buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], BINARY);
assert(written == len);
}
char* ciphertext;
int ciphertext_len;
enum encoding enc = ParseEncoding(args[1], BINARY);
if (enc == HEX) {
// Hex encoding
// Do we have a previous hex carry over?
if (cipher->incomplete_hex_flag) {
char* complete_hex = new char[len+2];
memcpy(complete_hex, &cipher->incomplete_hex, 1);
memcpy(complete_hex+1, buf, len);
if (alloc_buf) {
delete [] buf;
alloc_buf = false;
}
buf = complete_hex;
len += 1;
}
// Do we have an incomplete hex stream?
if ((len>0) && (len % 2 !=0)) {
len--;
cipher->incomplete_hex=buf[len];
cipher->incomplete_hex_flag=true;
buf[len]=0;
}
HexDecode((unsigned char*)buf, len, (char **)&ciphertext, &ciphertext_len);
if (alloc_buf) {
delete [] buf;
}
buf = ciphertext;
len = ciphertext_len;
alloc_buf = true;
} else if (enc == BASE64) {
unbase64((unsigned char*)buf, len, (char **)&ciphertext, &ciphertext_len);
if (alloc_buf) {
delete [] buf;
}
buf = ciphertext;
len = ciphertext_len;
alloc_buf = true;
} else if (enc == BINARY) {
// Binary - do nothing
} else {
fprintf(stderr, "node-crypto : Decipher .update encoding "
"can be binary, hex or base64\n");
}
unsigned char *out=0;
int out_len=0;
int r = cipher->DecipherUpdate(buf, len, &out, &out_len);
if (!r) {
delete [] out;
Local<Value> exception = Exception::TypeError(String::New("DecipherUpdate fail"));
return ThrowException(exception);
}
Local<Value> outString;
if (out_len==0) {
outString=String::New("");
} else {
enum encoding enc = ParseEncoding(args[2], BINARY);
if (enc == UTF8) {
// See if we have any overhang from last utf8 partial ending
if (cipher->incomplete_utf8!=NULL) {
char* complete_out = new char[cipher->incomplete_utf8_len + out_len];
memcpy(complete_out, cipher->incomplete_utf8, cipher->incomplete_utf8_len);
memcpy((char *)complete_out+cipher->incomplete_utf8_len, out, out_len);
delete [] out;
delete [] cipher->incomplete_utf8;
cipher->incomplete_utf8 = NULL;
out = (unsigned char*)complete_out;
out_len += cipher->incomplete_utf8_len;
}
// Check to see if we have a complete utf8 stream
int utf8_len = LengthWithoutIncompleteUtf8((char *)out, out_len);
if (utf8_len<out_len) { // We have an incomplete ut8 ending
cipher->incomplete_utf8_len = out_len-utf8_len;
cipher->incomplete_utf8 = new unsigned char[cipher->incomplete_utf8_len+1];
memcpy(cipher->incomplete_utf8, &out[utf8_len], cipher->incomplete_utf8_len);
}
outString = Encode(out, utf8_len, enc);
} else {
outString = Encode(out, out_len, enc);
}
}
if (out) delete [] out;
if (alloc_buf) delete [] buf;
return scope.Close(outString);
}
static Handle<Value> SetAutoPadding(const Arguments& args) {
HandleScope scope;
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
return Undefined();
}
template <bool TOLERATE_PADDING>
static Handle<Value> DecipherFinal(const Arguments& args) {
HandleScope scope;
Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
unsigned char* out_value = NULL;
int out_len = -1;
Local<Value> outString;
int r = cipher->DecipherFinal<TOLERATE_PADDING>(&out_value, &out_len);
assert(out_value != NULL);
assert(out_len != -1);
if (out_len == 0 || r == 0) {
delete [] out_value; // allocated even if out_len == 0
if (r == 0) {
Local<Value> exception = Exception::TypeError(
String::New("DecipherFinal fail"));
return ThrowException(exception);
} else {
return scope.Close(String::New(""));
}
}
if (args.Length() == 0 || !args[0]->IsString()) {
outString = Encode(out_value, out_len, BINARY);
} else {
enum encoding enc = ParseEncoding(args[0]);
if (enc == UTF8) {
// See if we have any overhang from last utf8 partial ending
if (cipher->incomplete_utf8!=NULL) {
char* complete_out = new char[cipher->incomplete_utf8_len + out_len];
memcpy(complete_out, cipher->incomplete_utf8, cipher->incomplete_utf8_len);
memcpy((char *)complete_out+cipher->incomplete_utf8_len, out_value, out_len);
delete [] cipher->incomplete_utf8;
cipher->incomplete_utf8=NULL;
outString = Encode(complete_out, cipher->incomplete_utf8_len+out_len, enc);
delete [] complete_out;
} else {
outString = Encode(out_value, out_len, enc);
}
} else {
outString = Encode(out_value, out_len, enc);
}
}
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_;
unsigned char* incomplete_utf8;
int incomplete_utf8_len;
char incomplete_hex;
bool incomplete_hex_flag;
};
class Hmac : public ObjectWrap {
public:
static void Initialize (v8::Handle<v8::Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "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);
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<Value> New (const Arguments& args) {
HandleScope scope;
Hmac *hmac = new Hmac();
hmac->Wrap(args.This());
return args.This();
}
static Handle<Value> HmacInit(const Arguments& args) {
Hmac *hmac = ObjectWrap::Unwrap<Hmac>(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_STRING_OR_BUFFER(args[1]);
ssize_t len = DecodeBytes(args[1], BINARY);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
String::Utf8Value hashType(args[0]);
bool r;
if( Buffer::HasInstance(args[1])) {
Local<Object> buffer_obj = args[1]->ToObject();
char* buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
r = hmac->HmacInit(*hashType, buffer_data, buffer_length);
} else {
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
assert(written == len);
r = hmac->HmacInit(*hashType, buf, len);
delete [] buf;
}
if (!r) {
return ThrowException(Exception::Error(String::New("hmac error")));
}
return args.This();
}
static Handle<Value> HmacUpdate(const Arguments& args) {
Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
HandleScope scope;
ASSERT_IS_STRING_OR_BUFFER(args[0]);
enum encoding enc = ParseEncoding(args[1]);
ssize_t len = DecodeBytes(args[0], enc);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
int r;
if( Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
r = hmac->HmacUpdate(buffer_data, buffer_length);
} else {
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc);
assert(written == len);
r = hmac->HmacUpdate(buf, len);
delete [] buf;
}
if (!r) {
Local<Value> exception = Exception::TypeError(String::New("HmacUpdate fail"));
return ThrowException(exception);
}
return args.This();
}
static Handle<Value> HmacDigest(const Arguments& args) {
Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
HandleScope scope;
unsigned char* md_value = NULL;
unsigned int md_len = 0;
char* md_hexdigest;
int md_hex_len;
Local<Value> outString;
int r = hmac->HmacDigest(&md_value, &md_len);
if (md_len == 0 || r == 0) {
return scope.Close(String::New(""));
}
enum encoding enc = ParseEncoding(args[0], BINARY);
if (enc == HEX) {
// Hex encoding
HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (enc == BASE64) {
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (enc == BINARY) {
outString = Encode(md_value, md_len, BINARY);
} else {
fprintf(stderr, "node-crypto : Hmac .digest encoding "
"can be binary, hex or base64\n");
}
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<v8::Object> target) {
HandleScope scope;
Local<FunctionTemplate> 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<Value> 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<Value> HashUpdate(const Arguments& args) {
HandleScope scope;
Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
ASSERT_IS_STRING_OR_BUFFER(args[0]);
enum encoding enc = ParseEncoding(args[1]);
ssize_t len = DecodeBytes(args[0], enc);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
int r;
if (Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
r = hash->HashUpdate(buffer_data, buffer_length);
} else {
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc);
assert(written == len);
r = hash->HashUpdate(buf, len);
delete[] buf;
}
if (!r) {
Local<Value> exception = Exception::TypeError(String::New("HashUpdate fail"));
return ThrowException(exception);
}
return args.This();
}
static Handle<Value> HashDigest(const Arguments& args) {
HandleScope scope;
Hash *hash = ObjectWrap::Unwrap<Hash>(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;
if (md_len == 0) {
return scope.Close(String::New(""));
}
Local<Value> outString;
enum encoding enc = ParseEncoding(args[0], BINARY);
if (enc == HEX) {
// Hex encoding
char* md_hexdigest;
int md_hex_len;
HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (enc == BASE64) {
char* md_hexdigest;
int md_hex_len;
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (enc == BINARY) {
outString = Encode(md_value, md_len, BINARY);
} else {
fprintf(stderr, "node-crypto : Hash .digest encoding "
"can be binary, hex or base64\n");
}
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<v8::Object> target) {
HandleScope scope;
Local<FunctionTemplate> 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<Value> New (const Arguments& args) {
HandleScope scope;
Sign *sign = new Sign();
sign->Wrap(args.This());
return args.This();
}
static Handle<Value> SignInit(const Arguments& args) {
HandleScope scope;
Sign *sign = ObjectWrap::Unwrap<Sign>(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<Value> SignUpdate(const Arguments& args) {
Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
HandleScope scope;
ASSERT_IS_STRING_OR_BUFFER(args[0]);
enum encoding enc = ParseEncoding(args[1]);
ssize_t len = DecodeBytes(args[0], enc);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
int r;
if (Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
r = sign->SignUpdate(buffer_data, buffer_length);
} else {
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc);
assert(written == len);
r = sign->SignUpdate(buf, len);
delete [] buf;
}
if (!r) {
Local<Value> exception = Exception::TypeError(String::New("SignUpdate fail"));
return ThrowException(exception);
}
return args.This();
}
static Handle<Value> SignFinal(const Arguments& args) {
Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
HandleScope scope;
unsigned char* md_value;
unsigned int md_len;
char* md_hexdigest;
int md_hex_len;
Local<Value> outString;
md_len = 8192; // Maximum key size is 8192 bits
md_value = new unsigned char[md_len];
ASSERT_IS_STRING_OR_BUFFER(args[0]);
ssize_t len = DecodeBytes(args[0], BINARY);
if (len < 0) {
delete [] md_value;
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], BINARY);
assert(written == len);
int r = sign->SignFinal(&md_value, &md_len, buf, len);
delete [] buf;
if (md_len == 0 || r == 0) {
delete [] md_value;
return scope.Close(String::New(""));
}
enum encoding enc = ParseEncoding(args[1], BINARY);
if (enc == HEX) {
// Hex encoding
HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (enc == BASE64) {
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
} else if (enc == BINARY) {
outString = Encode(md_value, md_len, BINARY);
} else {
outString = String::New("");
fprintf(stderr, "node-crypto : Sign .sign encoding "
"can be binary, hex or base64\n");
}
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<v8::Object> target) {
HandleScope scope;
Local<FunctionTemplate> 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<Value> New (const Arguments& args) {
HandleScope scope;
Verify *verify = new Verify();
verify->Wrap(args.This());
return args.This();
}
static Handle<Value> VerifyInit(const Arguments& args) {
Verify *verify = ObjectWrap::Unwrap<Verify>(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<Value> VerifyUpdate(const Arguments& args) {
HandleScope scope;
Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
ASSERT_IS_STRING_OR_BUFFER(args[0]);
enum encoding enc = ParseEncoding(args[1]);
ssize_t len = DecodeBytes(args[0], enc);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
int r;
if(Buffer::HasInstance(args[0])) {
Local<Object> buffer_obj = args[0]->ToObject();
char *buffer_data = Buffer::Data(buffer_obj);
size_t buffer_length = Buffer::Length(buffer_obj);
r = verify->VerifyUpdate(buffer_data, buffer_length);
} else {
char* buf = new char[len];
ssize_t written = DecodeWrite(buf, len, args[0], enc);
assert(written == len);
r = verify->VerifyUpdate(buf, len);
delete [] buf;
}
if (!r) {
Local<Value> exception = Exception::TypeError(String::New("VerifyUpdate fail"));
return ThrowException(exception);
}
return args.This();
}
static Handle<Value> VerifyFinal(const Arguments& args) {
HandleScope scope;
Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
ASSERT_IS_STRING_OR_BUFFER(args[0]);
ssize_t klen = DecodeBytes(args[0], BINARY);
if (klen < 0) {
Local<Value> 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_STRING_OR_BUFFER(args[1]);
ssize_t hlen = DecodeBytes(args[1], BINARY);
if (hlen < 0) {
delete [] kbuf;
Local<Value> 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);
unsigned char* dbuf;
int dlen;
int r=-1;
enum encoding enc = ParseEncoding(args[2], BINARY);
if (enc == HEX) {
// Hex encoding
HexDecode(hbuf, hlen, (char **)&dbuf, &dlen);
r = verify->VerifyFinal(kbuf, klen, dbuf, dlen);
delete [] dbuf;
} else if (enc == BASE64) {
// Base64 encoding
unbase64(hbuf, hlen, (char **)&dbuf, &dlen);
r = verify->VerifyFinal(kbuf, klen, dbuf, dlen);
delete [] dbuf;
} else if (enc == BINARY) {
r = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
} else {
fprintf(stderr, "node-crypto : Verify .verify encoding "
"can be binary, hex or base64\n");
}
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<v8::Object> target) {
HandleScope scope;
Local<FunctionTemplate> t = FunctionTemplate::New(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
Local<FunctionTemplate> 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<Value> 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<Value> 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 {
if (args[0]->IsString()) {
char* buf;
int len;
if (args.Length() > 1 && args[1]->IsString()) {
len = DecodeWithEncoding(args[0], args[1], &buf);
} else {
len = DecodeBinary(args[0], &buf);
}
if (len == -1) {
delete[] buf;
return ThrowException(Exception::Error(
String::New("Invalid argument")));
} else {
initialized = diffieHellman->Init(
reinterpret_cast<unsigned char*>(buf), len);
delete[] buf;
}
} else if (Buffer::HasInstance(args[0])) {
Local<Object> buffer = args[0]->ToObject();
initialized = diffieHellman->Init(
reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
Buffer::Length(buffer));
}
}
}
if (!initialized) {
return ThrowException(Exception::Error(
String::New("Initialization failed")));
}
diffieHellman->Wrap(args.This());
return args.This();
}
static Handle<Value> GenerateKeys(const Arguments& args) {
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
HandleScope scope;
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(
String::New("Not initialized")));
}
if (!DH_generate_key(diffieHellman->dh)) {
return ThrowException(Exception::Error(
String::New("Key generation failed")));
}
Local<Value> outString;
int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
char* data = new char[dataSize];
BN_bn2bin(diffieHellman->dh->pub_key,
reinterpret_cast<unsigned char*>(data));
if (args.Length() > 0 && args[0]->IsString()) {
outString = EncodeWithEncoding(args[0], data, dataSize);
} else {
outString = Encode(data, dataSize, BINARY);
}
delete[] data;
return scope.Close(outString);
}
static Handle<Value> GetPrime(const Arguments& args) {
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
HandleScope scope;
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
int dataSize = BN_num_bytes(diffieHellman->dh->p);
char* data = new char[dataSize];
BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
Local<Value> outString;
if (args.Length() > 0 && args[0]->IsString()) {
outString = EncodeWithEncoding(args[0], data, dataSize);
} else {
outString = Encode(data, dataSize, BINARY);
}
delete[] data;
return scope.Close(outString);
}
static Handle<Value> GetGenerator(const Arguments& args) {
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
HandleScope scope;
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
int dataSize = BN_num_bytes(diffieHellman->dh->g);
char* data = new char[dataSize];
BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
Local<Value> outString;
if (args.Length() > 0 && args[0]->IsString()) {
outString = EncodeWithEncoding(args[0], data, dataSize);
} else {
outString = Encode(data, dataSize, BINARY);
}
delete[] data;
return scope.Close(outString);
}
static Handle<Value> GetPublicKey(const Arguments& args) {
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
HandleScope scope;
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
if (diffieHellman->dh->pub_key == NULL) {
return ThrowException(Exception::Error(
String::New("No public key - did you forget to generate one?")));
}
int dataSize = BN_num_bytes(diffieHellman->dh->pub_key);
char* data = new char[dataSize];
BN_bn2bin(diffieHellman->dh->pub_key,
reinterpret_cast<unsigned char*>(data));
Local<Value> outString;
if (args.Length() > 0 && args[0]->IsString()) {
outString = EncodeWithEncoding(args[0], data, dataSize);
} else {
outString = Encode(data, dataSize, BINARY);
}
delete[] data;
return scope.Close(outString);
}
static Handle<Value> GetPrivateKey(const Arguments& args) {
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
HandleScope scope;
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
if (diffieHellman->dh->priv_key == NULL) {
return ThrowException(Exception::Error(
String::New("No private key - did you forget to generate one?")));
}
int dataSize = BN_num_bytes(diffieHellman->dh->priv_key);
char* data = new char[dataSize];
BN_bn2bin(diffieHellman->dh->priv_key,
reinterpret_cast<unsigned char*>(data));
Local<Value> outString;
if (args.Length() > 0 && args[0]->IsString()) {
outString = EncodeWithEncoding(args[0], data, dataSize);
} else {
outString = Encode(data, dataSize, BINARY);
}
delete[] data;
return scope.Close(outString);
}
static Handle<Value> ComputeSecret(const Arguments& args) {
HandleScope scope;
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
BIGNUM* key = 0;
if (args.Length() == 0) {
return ThrowException(Exception::Error(
String::New("First argument must be other party's public key")));
} else {
if (args[0]->IsString()) {
char* buf;
int len;
if (args.Length() > 1) {
len = DecodeWithEncoding(args[0], args[1], &buf);
} else {
len = DecodeBinary(args[0], &buf);
}
if (len == -1) {
delete[] buf;
return ThrowException(Exception::Error(
String::New("Invalid argument")));
}
key = BN_bin2bn(reinterpret_cast<unsigned char*>(buf), len, 0);
delete[] buf;
} else if (Buffer::HasInstance(args[0])) {
Local<Object> buffer = args[0]->ToObject();
key = BN_bin2bn(
reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
Buffer::Length(buffer), 0);
} else {
return ThrowException(Exception::Error(
String::New("First argument must be other party's public key")));
}
}
int dataSize = DH_size(diffieHellman->dh);
char* data = new char[dataSize];
int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
key, diffieHellman->dh);
BN_free(key);
Local<Value> outString;
if (size == -1) {
int checkResult;
if (!DH_check_pub_key(diffieHellman->dh, key, &checkResult)) {
return ThrowException(Exception::Error(String::New("Invalid key")));
} else if (checkResult) {
if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
return ThrowException(Exception::Error(
String::New("Supplied key is too small")));
} else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
return ThrowException(Exception::Error(
String::New("Supplied key is too large")));
} else {
return ThrowException(Exception::Error(String::New("Invalid key")));
}
} else {
return ThrowException(Exception::Error(String::New("Invalid key")));
}
} else {
if (args.Length() > 2 && args[2]->IsString()) {
outString = EncodeWithEncoding(args[2], data, dataSize);
} else if (args.Length() > 1 && args[1]->IsString()) {
outString = EncodeWithEncoding(args[1], data, dataSize);
} else {
outString = Encode(data, dataSize, BINARY);
}
}
delete[] data;
return scope.Close(outString);
}
static Handle<Value> SetPublicKey(const Arguments& args) {
HandleScope scope;
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(String::New("Not initialized")));
}
if (args.Length() == 0) {
return ThrowException(Exception::Error(
String::New("First argument must be public key")));
} else {
if (args[0]->IsString()) {
char* buf;
int len;
if (args.Length() > 1) {
len = DecodeWithEncoding(args[0], args[1], &buf);
} else {
len = DecodeBinary(args[0], &buf);
}
if (len == -1) {
delete[] buf;
return ThrowException(Exception::Error(
String::New("Invalid argument")));
}
diffieHellman->dh->pub_key =
BN_bin2bn(reinterpret_cast<unsigned char*>(buf), len, 0);
delete[] buf;
} else if (Buffer::HasInstance(args[0])) {
Local<Object> buffer = args[0]->ToObject();
diffieHellman->dh->pub_key =
BN_bin2bn(
reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
Buffer::Length(buffer), 0);
} else {
return ThrowException(Exception::Error(
String::New("First argument must be public key")));
}
}
return args.This();
}
static Handle<Value> SetPrivateKey(const Arguments& args) {
HandleScope scope;
DiffieHellman* diffieHellman =
ObjectWrap::Unwrap<DiffieHellman>(args.This());
if (!diffieHellman->initialised_) {
return ThrowException(Exception::Error(
String::New("Not initialized")));
}
if (args.Length() == 0) {
return ThrowException(Exception::Error(
String::New("First argument must be private key")));
} else {
if (args[0]->IsString()) {
char* buf;
int len;
if (args.Length() > 1) {
len = DecodeWithEncoding(args[0], args[1], &buf);
} else {
len = DecodeBinary(args[0], &buf);
}
if (len == -1) {
delete[] buf;
return ThrowException(Exception::Error(
String::New("Invalid argument")));
}
diffieHellman->dh->priv_key =
BN_bin2bn(reinterpret_cast<unsigned char*>(buf), len, 0);
delete[] buf;
} else if (Buffer::HasInstance(args[0])) {
Local<Object> buffer = args[0]->ToObject();
diffieHellman->dh->priv_key =
BN_bin2bn(
reinterpret_cast<unsigned char*>(Buffer::Data(buffer)),
Buffer::Length(buffer), 0);
} else {
return ThrowException(Exception::Error(
String::New("First argument must be private key")));
}
}
return args.This();
}
DiffieHellman() : ObjectWrap() {
initialised_ = false;
dh = NULL;
}
~DiffieHellman() {
if (dh != NULL) {
DH_free(dh);
}
}
private:
bool VerifyContext() {
int codes;
if (!DH_check(dh, &codes)) return false;
if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
if (codes & DH_CHECK_P_NOT_PRIME) return false;
if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
return true;
}
static int DecodeBinary(Handle<Value> str, char** buf) {
int len = DecodeBytes(str);
*buf = new char[len];
int written = DecodeWrite(*buf, len, str, BINARY);
if (written != len) {
return -1;
}
return len;
}
static int DecodeWithEncoding(Handle<Value> str, Handle<Value> encoding_v,
char** buf) {
int len = DecodeBinary(str, buf);
if (len == -1) {
return len;
}
enum encoding enc = ParseEncoding(encoding_v, (enum encoding) -1);
char* retbuf = 0;
int retlen;
if (enc == HEX) {
HexDecode((unsigned char*)*buf, len, &retbuf, &retlen);
} else if (enc == BASE64) {
unbase64((unsigned char*)*buf, len, &retbuf, &retlen);
} else if (enc == BINARY) {
// Binary - do nothing
} else {
fprintf(stderr, "node-crypto : Diffie-Hellman parameter encoding "
"can be binary, hex or base64\n");
}
if (retbuf != 0) {
delete [] *buf;
*buf = retbuf;
len = retlen;
}
return len;
}
static Local<Value> EncodeWithEncoding(Handle<Value> encoding_v, char* buf,
int len) {
HandleScope scope;
Local<Value> outString;
enum encoding enc = ParseEncoding(encoding_v, (enum encoding) -1);
char* retbuf;
int retlen;
if (enc == HEX) {
// Hex encoding
HexEncode(reinterpret_cast<unsigned char*>(buf), len, &retbuf, &retlen);
outString = Encode(retbuf, retlen, BINARY);
delete [] retbuf;
} else if (enc == BASE64) {
base64(reinterpret_cast<unsigned char*>(buf), len, &retbuf, &retlen);
outString = Encode(retbuf, retlen, BINARY);
delete [] retbuf;
} else if (enc == BINARY) {
outString = Encode(buf, len, BINARY);
} else {
fprintf(stderr, "node-crypto : Diffie-Hellman parameter encoding "
"can be binary, hex or base64\n");
}
return scope.Close(outString);
}
bool initialised_;
DH* dh;