}
-size_t ClientHelloParser::Write(const uint8_t* data, size_t len) {
- HandleScope scope;
+void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) {
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
+ HandleScope handle_scope(args.GetIsolate());
- // Just accumulate data, everything will be pushed to BIO later
- if (state_ == kPaused) return 0;
+ SecureContext* wrap = Unwrap<SecureContext>(args.This());
- // Copy incoming data to the internal buffer
- // (which has a size of the biggest possible TLS frame)
- size_t available = sizeof(data_) - offset_;
- size_t copied = len < available ? len : available;
- memcpy(data_ + offset_, data, copied);
- offset_ += copied;
+ Local<Object> buff = Buffer::New(wrap->env(), 48);
+ if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_,
+ Buffer::Data(buff),
+ Buffer::Length(buff)) != 1) {
+ return ThrowError("Failed to fetch tls ticket keys");
+ }
- // Vars for parsing hello
- bool is_clienthello = false;
- uint8_t session_size = -1;
- uint8_t* session_id = NULL;
- Local<Object> hello;
- Handle<Value> argv[1];
+ args.GetReturnValue().Set(buff);
+#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
+}
- switch (state_) {
- case kWaiting:
- // >= 5 bytes for header parsing
- if (offset_ < 5) break;
- if (data_[0] == kChangeCipherSpec || data_[0] == kAlert ||
- data_[0] == kHandshake || data_[0] == kApplicationData) {
- frame_len_ = (data_[3] << 8) + data_[4];
- state_ = kTLSHeader;
- body_offset_ = 5;
- } else {
- frame_len_ = (data_[0] << 8) + data_[1];
- state_ = kSSLHeader;
- if (*data_ & 0x40) {
- // header with padding
- body_offset_ = 3;
- } else {
- // without padding
- body_offset_ = 2;
- }
- }
+void SecureContext::SetTicketKeys(const FunctionCallbackInfo<Value>& args) {
+#if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys)
+ HandleScope scope(node_isolate);
- // Sanity check (too big frame, or too small)
- if (frame_len_ >= sizeof(data_)) {
- // Let OpenSSL handle it
- Finish();
- return copied;
- }
- case kTLSHeader:
- case kSSLHeader:
- // >= 5 + frame size bytes for frame parsing
- if (offset_ < body_offset_ + frame_len_) break;
-
- // Skip unsupported frames and gather some data from frame
-
- // TODO: Check protocol version
- if (data_[body_offset_] == kClientHello) {
- is_clienthello = true;
- uint8_t* body;
- size_t session_offset;
-
- if (state_ == kTLSHeader) {
- // Skip frame header, hello header, protocol version and random data
- session_offset = body_offset_ + 4 + 2 + 32;
-
- if (session_offset + 1 < offset_) {
- body = data_ + session_offset;
- session_size = *body;
- session_id = body + 1;
- }
- } else if (state_ == kSSLHeader) {
- // Skip header, version
- session_offset = body_offset_ + 3;
+ if (args.Length() < 1 ||
+ !Buffer::HasInstance(args[0]) ||
+ Buffer::Length(args[0]) != 48) {
+ return ThrowTypeError("Bad argument");
+ }
- if (session_offset + 4 < offset_) {
- body = data_ + session_offset;
+ SecureContext* wrap = Unwrap<SecureContext>(args.This());
- int ciphers_size = (body[0] << 8) + body[1];
+ if (SSL_CTX_set_tlsext_ticket_keys(wrap->ctx_,
+ Buffer::Data(args[0]),
+ Buffer::Length(args[0])) != 1) {
+ return ThrowError("Failed to fetch tls ticket keys");
+ }
- if (body + 4 + ciphers_size < data_ + offset_) {
- session_size = (body[2] << 8) + body[3];
- session_id = body + 4 + ciphers_size;
- }
- }
- } else {
- // Whoa? How did we get here?
- abort();
- }
+ args.GetReturnValue().Set(true);
+#endif // !def(OPENSSL_NO_TLSEXT) && def(SSL_CTX_get_tlsext_ticket_keys)
+}
- // Check if we overflowed (do not reply with any private data)
- if (session_id == NULL ||
- session_size > 32 ||
- session_id + session_size > data_ + offset_) {
- Finish();
- return copied;
- }
- // TODO: Parse other things?
- }
+template <class Base>
+void SSLWrap<Base>::AddMethods(Handle<FunctionTemplate> t) {
+ HandleScope scope(node_isolate);
- // Not client hello - let OpenSSL handle it
- if (!is_clienthello) {
- Finish();
- return copied;
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "getPeerCertificate", GetPeerCertificate);
+ NODE_SET_PROTOTYPE_METHOD(t, "getSession", GetSession);
+ NODE_SET_PROTOTYPE_METHOD(t, "setSession", SetSession);
+ NODE_SET_PROTOTYPE_METHOD(t, "loadSession", LoadSession);
+ NODE_SET_PROTOTYPE_METHOD(t, "isSessionReused", IsSessionReused);
+ NODE_SET_PROTOTYPE_METHOD(t, "isInitFinished", IsInitFinished);
+ NODE_SET_PROTOTYPE_METHOD(t, "verifyError", VerifyError);
+ NODE_SET_PROTOTYPE_METHOD(t, "getCurrentCipher", GetCurrentCipher);
- NODE_SET_PROTOTYPE_METHOD(t, "receivedShutdown", ReceivedShutdown);
+ NODE_SET_PROTOTYPE_METHOD(t, "endParser", EndParser);
+ NODE_SET_PROTOTYPE_METHOD(t, "renegotiate", Renegotiate);
++ NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Shutdown);
- // Parse frame, call javascript handler and
- // move parser into the paused state
- if (onclienthello_sym.IsEmpty()) {
- onclienthello_sym = NODE_PSYMBOL("onclienthello");
- }
- if (sessionid_sym.IsEmpty()) {
- sessionid_sym = NODE_PSYMBOL("sessionId");
- }
+#ifdef OPENSSL_NPN_NEGOTIATED
+ NODE_SET_PROTOTYPE_METHOD(t, "getNegotiatedProtocol", GetNegotiatedProto);
+ NODE_SET_PROTOTYPE_METHOD(t, "setNPNProtocols", SetNPNProtocols);
+#endif // OPENSSL_NPN_NEGOTIATED
+}
- state_ = kPaused;
- hello = Object::New();
- hello->Set(sessionid_sym,
- Buffer::New(reinterpret_cast<char*>(session_id),
- session_size)->handle_);
- argv[0] = hello;
- MakeCallback(conn_->handle_, onclienthello_sym, 1, argv);
- break;
- case kEnded:
- default:
- break;
+template <class Base>
+void SSLWrap<Base>::InitNPN(SecureContext* sc, Base* base) {
+ if (base->is_server()) {
+#ifdef OPENSSL_NPN_NEGOTIATED
+ // Server should advertise NPN protocols
+ SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
+ AdvertiseNextProtoCallback,
+ base);
+#endif // OPENSSL_NPN_NEGOTIATED
+ } else {
+#ifdef OPENSSL_NPN_NEGOTIATED
+ // Client should select protocol from list of advertised
+ // If server supports NPN
+ SSL_CTX_set_next_proto_select_cb(sc->ctx_, SelectNextProtoCallback, base);
+#endif // OPENSSL_NPN_NEGOTIATED
}
-
- return copied;
}
}
-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")));
- }
-
- char* buffer_data = Buffer::Data(args[0]);
- size_t buffer_length = Buffer::Length(args[0]);
-
- size_t off = args[1]->Int32Value();
- size_t len = args[2]->Int32Value();
- if (off + len > buffer_length) {
- return ThrowException(Exception::Error(
- String::New("off + len > buffer.length")));
- }
-
- int bytes_written;
- char* data = buffer_data + off;
-
- if (ss->is_server_ && !ss->hello_parser_.ended()) {
- bytes_written = ss->hello_parser_.Write(reinterpret_cast<uint8_t*>(data),
- len);
- } else {
- bytes_written = BIO_write(ss->bio_read_, data, len);
- ss->HandleBIOError(ss->bio_read_, "BIO_write", bytes_written);
- ss->SetShutdownFlags();
- }
-
- return scope.Close(Integer::New(bytes_written));
+template <class Base>
- void SSLWrap<Base>::ReceivedShutdown(const FunctionCallbackInfo<Value>& args) {
- HandleScope scope(node_isolate);
- Base* w = Unwrap<Base>(args.This());
- bool yes = SSL_get_shutdown(w->ssl_) == SSL_RECEIVED_SHUTDOWN;
- args.GetReturnValue().Set(yes);
- }
-
-
- template <class Base>
+void SSLWrap<Base>::EndParser(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ Base* w = Unwrap<Base>(args.This());
+ w->hello_parser_.End();
}
}
-Handle<Value> Connection::ClearPending(const Arguments& args) {
- HandleScope scope;
+template <class Base>
++void SSLWrap<Base>::Shutdown(const FunctionCallbackInfo<Value>& args) {
++ HandleScope scope(node_isolate);
+
- Connection *ss = Connection::Unwrap(args);
++ Base* w = Unwrap<Base>(args.This());
+
- int bytes_pending = BIO_pending(ss->bio_read_);
- return scope.Close(Integer::New(bytes_pending));
++ int rv = SSL_shutdown(w->ssl_);
++ args.GetReturnValue().Set(rv);
+ }
+
+
-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));
++template <class Base>
+void SSLWrap<Base>::IsInitFinished(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ Base* w = Unwrap<Base>(args.This());
+ bool yes = SSL_is_init_finished(w->ssl_);
+ args.GetReturnValue().Set(yes);
}
}
-Handle<Value> Connection::GetCurrentCipher(const Arguments& args) {
- HandleScope scope;
+void Connection::SetShutdownFlags() {
+ HandleScope scope(node_isolate);
- Connection *ss = Connection::Unwrap(args);
+ int flags = SSL_get_shutdown(ssl_);
- OPENSSL_CONST SSL_CIPHER *c;
+ if (flags & SSL_SENT_SHUTDOWN) {
+ Local<String> sent_shutdown_key =
+ FIXED_ONE_BYTE_STRING(node_isolate, "sentShutdown");
+ object()->Set(sent_shutdown_key, True(node_isolate));
+ }
- 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);
+ if (flags & SSL_RECEIVED_SHUTDOWN) {
+ Local<String> received_shutdown_key =
+ FIXED_ONE_BYTE_STRING(node_isolate, "receivedShutdown");
+ object()->Set(received_shutdown_key, True(node_isolate));
+ }
}
-Handle<Value> Connection::Close(const Arguments& args) {
- HandleScope scope;
- Connection *ss = Connection::Unwrap(args);
+void Connection::Initialize(Environment* env, Handle<Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(Connection::New);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
+ t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Connection"));
- if (ss->ssl_ != NULL) {
- SSL_free(ss->ssl_);
- ss->ssl_ = NULL;
- }
- return True();
-}
+ 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, "start", Connection::Start);
- NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Connection::Shutdown);
+ NODE_SET_PROTOTYPE_METHOD(t, "close", Connection::Close);
+ SSLWrap<Connection>::AddMethods(t);
-void Connection::InitNPN(SecureContext* sc, bool is_server) {
#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);
- }
+ NODE_SET_PROTOTYPE_METHOD(t,
+ "getNegotiatedProtocol",
+ Connection::GetNegotiatedProto);
+ NODE_SET_PROTOTYPE_METHOD(t,
+ "setNPNProtocols",
+ Connection::SetNPNProtocols);
#endif
-}
-
-#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;
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+ NODE_SET_PROTOTYPE_METHOD(t, "getServername", Connection::GetServername);
+ NODE_SET_PROTOTYPE_METHOD(t, "setSNICallback", Connection::SetSNICallback);
+#endif
- SSL_get0_next_proto_negotiated(ss->ssl_, &npn_proto, &npn_proto_len);
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Connection"),
+ t->GetFunction());
+}
- if (!npn_proto) {
- return False();
- }
- return scope.Close(String::New(reinterpret_cast<const char*>(npn_proto),
- npn_proto_len));
- } else {
- return ss->selectedNPNProto_;
- }
+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;
}
-Handle<Value> Connection::SetNPNProtocols(const Arguments& args) {
- HandleScope scope;
- Connection *ss = Connection::Unwrap(args);
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) {
+ HandleScope scope(node_isolate);
- if (args.Length() < 1 || !Buffer::HasInstance(args[0])) {
- return ThrowException(Exception::Error(String::New(
- "Must give a Buffer as first argument")));
- }
+ Connection* conn = static_cast<Connection*>(SSL_get_app_data(s));
+ Environment* env = conn->env();
- // Release old handle
- if (!ss->npnProtos_.IsEmpty()) {
- ss->npnProtos_.Dispose();
- }
- ss->npnProtos_ = Persistent<Object>::New(args[0]->ToObject());
+ const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
- return True();
-};
-#endif
+ if (servername) {
+ conn->servername_.Reset(node_isolate,
+ OneByteString(node_isolate, servername));
-#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
-Handle<Value> Connection::GetServername(const Arguments& args) {
- HandleScope scope;
+ // Call the SNI callback and use its return value as context
+ if (!conn->sniObject_.IsEmpty()) {
+ conn->sniContext_.Dispose();
- Connection *ss = Connection::Unwrap(args);
+ Local<Value> arg = PersistentToLocal(node_isolate, conn->servername_);
+ Local<Value> ret = conn->MakeCallback(env->onselect_string(), 1, &arg);
- if (ss->is_server_ && !ss->servername_.IsEmpty()) {
- return ss->servername_;
- } else {
- return False();
+ // If ret is SecureContext
+ Local<FunctionTemplate> secure_context_constructor_template =
+ env->secure_context_constructor_template();
+ if (secure_context_constructor_template->HasInstance(ret)) {
+ conn->sniContext_.Reset(node_isolate, ret);
+ SecureContext* sc = Unwrap<SecureContext>(ret.As<Object>());
+ InitNPN(sc, conn);
+ SSL_set_SSL_CTX(s, sc->ctx_);
+ } else {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+ }
}
-}
-Handle<Value> Connection::SetSNICallback(const Arguments& args) {
- HandleScope scope;
-
- Connection *ss = Connection::Unwrap(args);
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
- if (args.Length() < 1 || !args[0]->IsFunction()) {
- return ThrowException(Exception::Error(String::New(
- "Must give a Function as first argument")));
- }
+void Connection::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- // Release old handle
- if (!ss->sniObject_.IsEmpty()) {
- ss->sniObject_.Dispose();
+ if (args.Length() < 1 || !args[0]->IsObject()) {
+ return ThrowError("First argument must be a crypto module Credentials");
}
- ss->sniObject_ = Persistent<Object>::New(Object::New());
- ss->sniObject_->Set(String::New("onselect"), args[0]);
- return True();
-}
-#endif
+ SecureContext* sc = Unwrap<SecureContext>(args[0]->ToObject());
+ Environment* env = sc->env();
+ bool is_server = args[1]->BooleanValue();
-class Cipher : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
+ SSLWrap<Connection>::Kind kind =
+ is_server ? SSLWrap<Connection>::kServer : SSLWrap<Connection>::kClient;
+ Connection* conn = new Connection(env, args.This(), sc, kind);
+ conn->ssl_ = SSL_new(sc->ctx_);
+ conn->bio_read_ = NodeBIO::New();
+ conn->bio_write_ = NodeBIO::New();
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ SSL_set_app_data(conn->ssl_, conn);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+ if (is_server)
+ SSL_set_info_callback(conn->ssl_, SSLInfoCallback);
- 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);
+ InitNPN(sc, conn);
- target->Set(String::NewSymbol("Cipher"), t->GetFunction());
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+ if (is_server) {
+ SSL_CTX_set_tlsext_servername_callback(sc->ctx_, SelectSNIContextCallback_);
+ } else if (args[2]->IsString()) {
+ const String::Utf8Value servername(args[2]);
+ SSL_set_tlsext_host_name(conn->ssl_, *servername);
}
+#endif
+ SSL_set_bio(conn->ssl_, conn->bio_read_, conn->bio_write_);
- 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;
- }
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ long mode = SSL_get_mode(conn->ssl_);
+ SSL_set_mode(conn->ssl_, mode | SSL_MODE_RELEASE_BUFFERS);
+#endif
- 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;
+ 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;
}
- EVP_CipherInit_ex(&ctx, NULL, NULL,
- (unsigned char*)key,
- (unsigned char*)iv, true);
- initialised_ = true;
- return true;
+ } else {
+ // Note request_cert and reject_unauthorized are ignored for clients.
+ verify_mode = SSL_VERIFY_NONE;
}
- int CipherUpdate(char* data, int len, unsigned char** out, int* out_len) {
- if (!initialised_) return 0;
- *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
- *out = new unsigned char[*out_len];
- return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
- }
- int SetAutoPadding(bool auto_padding) {
- if (!initialised_) return 0;
- return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
- }
+ // Always allow a connection. We'll reject in javascript.
+ SSL_set_verify(conn->ssl_, verify_mode, VerifyCallback);
- 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;
+ if (is_server) {
+ SSL_set_accept_state(conn->ssl_);
+ } else {
+ SSL_set_connect_state(conn->ssl_);
}
+}
- protected:
+void Connection::SSLInfoCallback(const SSL *ssl_, int where, int ret) {
+ if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE)))
+ return;
- static Handle<Value> New(const Arguments& args) {
- HandleScope scope;
+ // 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_);
+ Connection* conn = static_cast<Connection*>(SSL_get_app_data(ssl));
+ Environment* env = conn->env();
+ HandleScope handle_scope(env->isolate());
+ Context::Scope context_scope(env->context());
- Cipher *cipher = new Cipher();
- cipher->Wrap(args.This());
- return args.This();
+ if (where & SSL_CB_HANDSHAKE_START) {
+ conn->MakeCallback(env->onhandshakestart_string(), 0, NULL);
}
- static Handle<Value> CipherInit(const Arguments& args) {
- HandleScope scope;
-
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
-
- if (args.Length() <= 1
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key")));
- }
-
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_buf_len = Buffer::Length(args[1]);
-
- if (key_buf_len < 0) {
- Local<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]);
+ if (where & SSL_CB_HANDSHAKE_DONE) {
+ conn->MakeCallback(env->onhandshakedone_string(), 0, NULL);
+ }
+}
- bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len);
- delete [] key_buf;
+void Connection::EncIn(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!r) return ThrowCryptoError(ERR_get_error());
+ Connection* conn = Unwrap<Connection>(args.This());
- return args.This();
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
}
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
+ }
- static Handle<Value> CipherInitIv(const Arguments& args) {
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- HandleScope scope;
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
+ }
+ int bytes_written;
+ char* data = buffer_data + off;
- if (args.Length() <= 2
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
- || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key, and iv as argument")));
+ if (conn->is_server() && !conn->hello_parser_.IsEnded()) {
+ // Just accumulate data, everything will be pushed to BIO later
+ if (conn->hello_parser_.IsPaused()) {
+ bytes_written = 0;
+ } else {
+ // Copy incoming data to the internal buffer
+ // (which has a size of the biggest possible TLS frame)
+ size_t available = sizeof(conn->hello_data_) - conn->hello_offset_;
+ size_t copied = len < available ? len : available;
+ memcpy(conn->hello_data_ + conn->hello_offset_, data, copied);
+ conn->hello_offset_ += copied;
+
+ conn->hello_parser_.Parse(conn->hello_data_, conn->hello_offset_);
+ bytes_written = copied;
}
+ } else {
+ bytes_written = BIO_write(conn->bio_read_, data, len);
+ conn->HandleBIOError(conn->bio_read_, "BIO_write", bytes_written);
+ conn->SetShutdownFlags();
+ }
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_len = Buffer::Length(args[1]);
+ args.GetReturnValue().Set(bytes_written);
+}
- if (key_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
- ASSERT_IS_BUFFER(args[2]);
- ssize_t iv_len = Buffer::Length(args[2]);
+void Connection::ClearOut(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (iv_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ Connection* conn = Unwrap<Connection>(args.This());
- char* key_buf = new char[key_len];
- ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
- assert(key_written == key_len);
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
+ }
- char* iv_buf = new char[iv_len];
- ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
- assert(iv_written == iv_len);
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
+ }
- String::Utf8Value cipherType(args[0]);
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
+ }
- delete [] key_buf;
- delete [] iv_buf;
+ if (!SSL_is_init_finished(conn->ssl_)) {
+ int rv;
- if (!r) return ThrowCryptoError(ERR_get_error());
+ if (conn->is_server()) {
+ rv = SSL_accept(conn->ssl_);
+ conn->HandleSSLError("SSL_accept:ClearOut",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ } else {
+ rv = SSL_connect(conn->ssl_);
+ conn->HandleSSLError("SSL_connect:ClearOut",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ }
- return args.This();
+ if (rv < 0) {
+ return args.GetReturnValue().Set(rv);
+ }
}
- static Handle<Value> CipherUpdate(const Arguments& args) {
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+ int bytes_read = SSL_read(conn->ssl_, buffer_data + off, len);
+ conn->HandleSSLError("SSL_read:ClearOut",
+ bytes_read,
+ kZeroIsNotAnError,
+ kSyscallError);
+ conn->SetShutdownFlags();
- HandleScope scope;
+ args.GetReturnValue().Set(bytes_read);
+}
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
- // Only copy the data if we have to, because it's a string
- unsigned char* out = 0;
- int out_len = 0, r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = cipher->CipherUpdate(buf, written, &out, &out_len);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = cipher->CipherUpdate(buf, buflen, &out, &out_len);
- }
+void Connection::ClearPending(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ Connection* conn = Unwrap<Connection>(args.This());
+ int bytes_pending = BIO_pending(conn->bio_read_);
+ args.GetReturnValue().Set(bytes_pending);
+}
- if (r == 0) {
- delete[] out;
- return ThrowCryptoTypeError(ERR_get_error());
- }
- Local<Value> outString;
- outString = Encode(out, out_len, BUFFER);
+void Connection::EncPending(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ Connection* conn = Unwrap<Connection>(args.This());
+ int bytes_pending = BIO_pending(conn->bio_write_);
+ args.GetReturnValue().Set(bytes_pending);
+}
+
- if (out) delete[] out;
+void Connection::EncOut(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- return scope.Close(outString);
+ Connection* conn = Unwrap<Connection>(args.This());
+
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
}
- static Handle<Value> SetAutoPadding(const Arguments& args) {
- HandleScope scope;
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
+ }
- cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
- return Undefined();
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
}
- static Handle<Value> CipherFinal(const Arguments& args) {
- Cipher *cipher = ObjectWrap::Unwrap<Cipher>(args.This());
+ int bytes_read = BIO_read(conn->bio_write_, buffer_data + off, len);
- HandleScope scope;
+ conn->HandleBIOError(conn->bio_write_, "BIO_read:EncOut", bytes_read);
+ conn->SetShutdownFlags();
- unsigned char* out_value = NULL;
- int out_len = -1;
- Local<Value> outString ;
+ args.GetReturnValue().Set(bytes_read);
+}
- int r = cipher->CipherFinal(&out_value, &out_len);
- if (out_len <= 0 || r == 0) {
- delete[] out_value;
- out_value = NULL;
- if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
- }
+void Connection::ClearIn(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- outString = Encode(out_value, out_len, BUFFER);
+ Connection* conn = Unwrap<Connection>(args.This());
- delete [] out_value;
- return scope.Close(outString);
+ if (args.Length() < 3) {
+ return ThrowTypeError("Takes 3 parameters");
}
- Cipher () : ObjectWrap ()
- {
- initialised_ = false;
+ if (!Buffer::HasInstance(args[0])) {
+ return ThrowTypeError("Second argument should be a buffer");
}
- ~Cipher () {
- if (initialised_) {
- EVP_CIPHER_CTX_cleanup(&ctx);
- }
+ char* buffer_data = Buffer::Data(args[0]);
+ size_t buffer_length = Buffer::Length(args[0]);
+
+ size_t off = args[1]->Int32Value();
+ size_t len = args[2]->Int32Value();
+ if (off + len > buffer_length) {
+ return ThrowError("off + len > buffer.length");
}
- private:
+ if (!SSL_is_init_finished(conn->ssl_)) {
+ int rv;
+ if (conn->is_server()) {
+ rv = SSL_accept(conn->ssl_);
+ conn->HandleSSLError("SSL_accept:ClearIn",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ } else {
+ rv = SSL_connect(conn->ssl_);
+ conn->HandleSSLError("SSL_connect:ClearIn",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ }
- EVP_CIPHER_CTX ctx; /* coverity[member_decl] */
- const EVP_CIPHER *cipher; /* coverity[member_decl] */
- bool initialised_;
-};
+ if (rv < 0) {
+ return args.GetReturnValue().Set(rv);
+ }
+ }
+ int bytes_written = SSL_write(conn->ssl_, buffer_data + off, len);
+ conn->HandleSSLError("SSL_write:ClearIn",
+ bytes_written,
+ len == 0 ? kZeroIsNotAnError : kZeroIsAnError,
+ kSyscallError);
+ conn->SetShutdownFlags();
-class Decipher : public ObjectWrap {
- public:
- static void
- Initialize (v8::Handle<v8::Object> target)
- {
- HandleScope scope;
+ args.GetReturnValue().Set(bytes_written);
+}
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+void Connection::Start(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- NODE_SET_PROTOTYPE_METHOD(t, "init", DecipherInit);
- NODE_SET_PROTOTYPE_METHOD(t, "initiv", DecipherInitIv);
- NODE_SET_PROTOTYPE_METHOD(t, "update", DecipherUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "final", DecipherFinal);
- NODE_SET_PROTOTYPE_METHOD(t, "finaltol", DecipherFinal); // remove someday
- NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
+ Connection* conn = Unwrap<Connection>(args.This());
- target->Set(String::NewSymbol("Decipher"), t->GetFunction());
+ int rv = 0;
+ if (!SSL_is_init_finished(conn->ssl_)) {
+ if (conn->is_server()) {
+ rv = SSL_accept(conn->ssl_);
+ conn->HandleSSLError("SSL_accept:Start",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ } else {
+ rv = SSL_connect(conn->ssl_);
+ conn->HandleSSLError("SSL_connect:Start",
+ rv,
+ kZeroIsAnError,
+ kSyscallError);
+ }
}
+ args.GetReturnValue().Set(rv);
+}
- 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;
- }
- void Connection::Shutdown(const FunctionCallbackInfo<Value>& args) {
- HandleScope scope(node_isolate);
-
- Connection* conn = Unwrap<Connection>(args.This());
- 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);
--
- if (conn->ssl_ == NULL) {
- return args.GetReturnValue().Set(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 rv = SSL_shutdown(conn->ssl_);
- conn->HandleSSLError("SSL_shutdown", rv, kZeroIsNotAnError, kIgnoreSyscall);
- conn->SetShutdownFlags();
- args.GetReturnValue().Set(rv);
- }
--
- 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;
- }
+void Connection::Close(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- *out_len = len+EVP_CIPHER_CTX_block_size(&ctx);
- *out = new unsigned char[*out_len];
+ Connection* conn = Unwrap<Connection>(args.This());
- return EVP_CipherUpdate(&ctx, *out, out_len, (unsigned char*)data, len);
+ if (conn->ssl_ != NULL) {
+ SSL_free(conn->ssl_);
+ conn->ssl_ = NULL;
}
+}
- int SetAutoPadding(bool auto_padding) {
- if (!initialised_) return 0;
- return EVP_CIPHER_CTX_set_padding(&ctx, auto_padding ? 1 : 0);
- }
- // coverity[alloc_arg]
- int DecipherFinal(unsigned char** out, int *out_len) {
- int r;
+#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
+void Connection::GetServername(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!initialised_) {
- *out_len = 0;
- *out = NULL;
- return 0;
- }
+ Connection* conn = Unwrap<Connection>(args.This());
- *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx)];
- r = EVP_CipherFinal_ex(&ctx,*out,out_len);
- EVP_CIPHER_CTX_cleanup(&ctx);
- initialised_ = false;
- return r;
+ if (conn->is_server() && !conn->servername_.IsEmpty()) {
+ args.GetReturnValue().Set(conn->servername_);
+ } else {
+ args.GetReturnValue().Set(false);
}
+}
- protected:
+void Connection::SetSNICallback(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+ Connection* conn = Unwrap<Connection>(args.This());
- Decipher *cipher = new Decipher();
- cipher->Wrap(args.This());
- return args.This();
+ if (args.Length() < 1 || !args[0]->IsFunction()) {
+ return ThrowError("Must give a Function as first argument");
}
- static Handle<Value> DecipherInit(const Arguments& args) {
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
+ Local<Object> obj = Object::New();
+ obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "onselect"), args[0]);
+ conn->sniObject_.Reset(node_isolate, obj);
+}
+#endif
- HandleScope scope;
- if (args.Length() <= 1
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key as argument")));
- }
+void CipherBase::Initialize(Environment* env, Handle<Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_len = Buffer::Length(args[1]);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (key_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", Init);
+ NODE_SET_PROTOTYPE_METHOD(t, "initiv", InitIv);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", Update);
+ NODE_SET_PROTOTYPE_METHOD(t, "final", Final);
+ NODE_SET_PROTOTYPE_METHOD(t, "setAutoPadding", SetAutoPadding);
+ NODE_SET_PROTOTYPE_METHOD(t, "getAuthTag", GetAuthTag);
+ NODE_SET_PROTOTYPE_METHOD(t, "setAuthTag", SetAuthTag);
- char* key_buf = new char[key_len];
- ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY);
- assert(key_written == key_len);
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "CipherBase"),
+ t->GetFunction());
+}
- String::Utf8Value cipherType(args[0]);
- bool r = cipher->DecipherInit(*cipherType, key_buf,key_len);
+void CipherBase::New(const FunctionCallbackInfo<Value>& args) {
+ assert(args.IsConstructCall() == true);
+ HandleScope handle_scope(args.GetIsolate());
+ CipherKind kind = args[0]->IsTrue() ? kCipher : kDecipher;
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ new CipherBase(env, args.This(), kind);
+}
- delete [] key_buf;
- if (!r) {
- return ThrowException(Exception::Error(String::New("DecipherInit error")));
- }
+void CipherBase::Init(const char* cipher_type,
+ const char* key_buf,
+ int key_buf_len) {
+ HandleScope scope(node_isolate);
- return args.This();
+ assert(cipher_ == NULL);
+ cipher_ = EVP_get_cipherbyname(cipher_type);
+ if (cipher_ == NULL) {
+ return ThrowError("Unknown cipher");
}
- static Handle<Value> DecipherInitIv(const Arguments& args) {
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
-
- HandleScope scope;
-
- if (args.Length() <= 2
- || !args[0]->IsString()
- || !(args[1]->IsString() || Buffer::HasInstance(args[1]))
- || !(args[2]->IsString() || Buffer::HasInstance(args[2])))
- {
- return ThrowException(Exception::Error(String::New(
- "Must give cipher-type, key, and iv as argument")));
- }
+ unsigned char key[EVP_MAX_KEY_LENGTH];
+ unsigned char iv[EVP_MAX_IV_LENGTH];
- ASSERT_IS_BUFFER(args[1]);
- ssize_t key_len = Buffer::Length(args[1]);
+ int key_len = EVP_BytesToKey(cipher_,
+ EVP_md5(),
+ NULL,
+ reinterpret_cast<const unsigned char*>(key_buf),
+ key_buf_len,
+ 1,
+ key,
+ iv);
- if (key_len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ EVP_CIPHER_CTX_init(&ctx_);
+ EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
+ if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ return ThrowError("Invalid key length");
+ }
- ASSERT_IS_BUFFER(args[2]);
- ssize_t iv_len = Buffer::Length(args[2]);
+ EVP_CipherInit_ex(&ctx_,
+ NULL,
+ NULL,
+ reinterpret_cast<unsigned char*>(key),
+ reinterpret_cast<unsigned char*>(iv),
+ kind_ == kCipher);
+ initialised_ = true;
+}
- 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);
+void CipherBase::Init(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- char* iv_buf = new char[iv_len];
- ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY);
- assert(iv_written == iv_len);
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
- String::Utf8Value cipherType(args[0]);
+ if (args.Length() < 2 ||
+ !(args[0]->IsString() && Buffer::HasInstance(args[1]))) {
+ return ThrowError("Must give cipher-type, key");
+ }
- bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len);
+ const String::Utf8Value cipher_type(args[0]);
+ const char* key_buf = Buffer::Data(args[1]);
+ ssize_t key_buf_len = Buffer::Length(args[1]);
+ cipher->Init(*cipher_type, key_buf, key_buf_len);
+}
- delete [] key_buf;
- delete [] iv_buf;
- if (!r) {
- return ThrowException(Exception::Error(String::New("DecipherInitIv error")));
- }
+void CipherBase::InitIv(const char* cipher_type,
+ const char* key,
+ int key_len,
+ const char* iv,
+ int iv_len) {
+ HandleScope scope(node_isolate);
- return args.This();
+ cipher_ = EVP_get_cipherbyname(cipher_type);
+ if (cipher_ == NULL) {
+ return ThrowError("Unknown cipher");
}
- static Handle<Value> DecipherUpdate(const Arguments& args) {
- HandleScope scope;
-
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
-
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
-
- // Only copy the data if we have to, because it's a string
- unsigned char* out = 0;
- int out_len = 0, r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = cipher->DecipherUpdate(buf, written, &out, &out_len);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = cipher->DecipherUpdate(buf, buflen, &out, &out_len);
- }
+ /* OpenSSL versions up to 0.9.8l failed to return the correct
+ iv_length (0) for ECB ciphers */
+ if (EVP_CIPHER_iv_length(cipher_) != iv_len &&
+ !(EVP_CIPHER_mode(cipher_) == EVP_CIPH_ECB_MODE && iv_len == 0)) {
+ return ThrowError("Invalid IV length");
+ }
+ EVP_CIPHER_CTX_init(&ctx_);
+ EVP_CipherInit_ex(&ctx_, cipher_, NULL, NULL, NULL, kind_ == kCipher);
+ if (!EVP_CIPHER_CTX_set_key_length(&ctx_, key_len)) {
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ return ThrowError("Invalid key length");
+ }
+
+ EVP_CipherInit_ex(&ctx_,
+ NULL,
+ NULL,
+ reinterpret_cast<const unsigned char*>(key),
+ reinterpret_cast<const unsigned char*>(iv),
+ kind_ == kCipher);
+ initialised_ = true;
+}
- if (r == 0) {
- delete[] out;
- return ThrowCryptoTypeError(ERR_get_error());
- }
- Local<Value> outString;
- outString = Encode(out, out_len, BUFFER);
+void CipherBase::InitIv(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (out) delete [] out;
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
- return scope.Close(outString);
+ if (args.Length() < 3 || !args[0]->IsString()) {
+ return ThrowError("Must give cipher-type, key, and iv as argument");
}
- static Handle<Value> SetAutoPadding(const Arguments& args) {
- HandleScope scope;
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
+ ASSERT_IS_BUFFER(args[1]);
+ ASSERT_IS_BUFFER(args[2]);
+
+ const String::Utf8Value cipher_type(args[0]);
+ ssize_t key_len = Buffer::Length(args[1]);
+ const char* key_buf = Buffer::Data(args[1]);
+ ssize_t iv_len = Buffer::Length(args[2]);
+ const char* iv_buf = Buffer::Data(args[2]);
+ cipher->InitIv(*cipher_type, key_buf, key_len, iv_buf, iv_len);
+}
- cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
- return Undefined();
- }
+bool CipherBase::IsAuthenticatedMode() const {
+ // check if this cipher operates in an AEAD mode that we support.
+ if (!cipher_)
+ return false;
+ int mode = EVP_CIPHER_mode(cipher_);
+ return mode == EVP_CIPH_GCM_MODE;
+}
- static Handle<Value> DecipherFinal(const Arguments& args) {
- HandleScope scope;
- Decipher *cipher = ObjectWrap::Unwrap<Decipher>(args.This());
+bool CipherBase::GetAuthTag(char** out, unsigned int* out_len) const {
+ // only callable after Final and if encrypting.
+ if (initialised_ || kind_ != kCipher || !auth_tag_)
+ return false;
+ *out_len = auth_tag_len_;
+ *out = new char[auth_tag_len_];
+ memcpy(*out, auth_tag_, auth_tag_len_);
+ return true;
+}
- unsigned char* out_value = NULL;
- int out_len = -1;
- Local<Value> outString;
- int r = cipher->DecipherFinal(&out_value, &out_len);
+void CipherBase::GetAuthTag(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ HandleScope handle_scope(args.GetIsolate());
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
- if (out_len <= 0 || r == 0) {
- delete [] out_value; // allocated even if out_len == 0
- out_value = NULL;
- if (r == 0) return ThrowCryptoTypeError(ERR_get_error());
- }
+ char* out = NULL;
+ unsigned int out_len = 0;
- outString = Encode(out_value, out_len, BUFFER);
- delete [] out_value;
- return scope.Close(outString);
+ if (cipher->GetAuthTag(&out, &out_len)) {
+ Local<Object> buf = Buffer::Use(env, out, out_len);
+ args.GetReturnValue().Set(buf);
+ } else {
+ ThrowError("Attempting to get auth tag in unsupported state");
}
+}
- Decipher () : ObjectWrap () {
- initialised_ = false;
- }
- ~Decipher () {
- if (initialised_) {
- EVP_CIPHER_CTX_cleanup(&ctx);
- }
- }
+bool CipherBase::SetAuthTag(const char* data, unsigned int len) {
+ if (!initialised_ || !IsAuthenticatedMode() || kind_ != kDecipher)
+ return false;
+ delete[] auth_tag_;
+ auth_tag_len_ = len;
+ auth_tag_ = new char[len];
+ memcpy(auth_tag_, data, len);
+ return true;
+}
- private:
- EVP_CIPHER_CTX ctx;
- const EVP_CIPHER *cipher_;
- bool initialised_;
-};
+void CipherBase::SetAuthTag(const FunctionCallbackInfo<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Local<Object> buf = args[0].As<Object>();
+ if (!buf->IsObject() || !Buffer::HasInstance(buf))
+ return ThrowTypeError("Argument must be a Buffer");
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
+ if (!cipher->SetAuthTag(Buffer::Data(buf), Buffer::Length(buf)))
+ ThrowError("Attempting to set auth tag in unsupported state");
+}
-class Hmac : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+bool CipherBase::Update(const char* data,
+ int len,
+ unsigned char** out,
+ int* out_len) {
+ if (!initialised_)
+ return 0;
- t->InstanceTemplate()->SetInternalFieldCount(1);
+ // on first update:
+ if (kind_ == kDecipher && IsAuthenticatedMode() && auth_tag_ != NULL) {
+ EVP_CIPHER_CTX_ctrl(&ctx_,
+ EVP_CTRL_GCM_SET_TAG,
+ auth_tag_len_,
+ reinterpret_cast<unsigned char*>(auth_tag_));
+ delete[] auth_tag_;
+ auth_tag_ = NULL;
+ }
+
+ *out_len = len + EVP_CIPHER_CTX_block_size(&ctx_);
+ *out = new unsigned char[*out_len];
+ return EVP_CipherUpdate(&ctx_,
+ *out,
+ out_len,
+ reinterpret_cast<const unsigned char*>(data),
+ len);
+}
- NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
- NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
- target->Set(String::NewSymbol("Hmac"), t->GetFunction());
- }
+void CipherBase::Update(const FunctionCallbackInfo<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
- bool HmacInit(char* hashType, char* key, int key_len) {
- md = EVP_get_digestbyname(hashType);
- if(!md) {
- fprintf(stderr, "node-crypto : Unknown message digest %s\n", hashType);
- return false;
- }
- HMAC_CTX_init(&ctx);
- if (key_len == 0) {
- HMAC_Init(&ctx, "", 0, md);
- } else {
- HMAC_Init(&ctx, key, key_len, md);
- }
- initialised_ = true;
- return true;
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- int HmacUpdate(char* data, int len) {
- if (!initialised_) return 0;
- HMAC_Update(&ctx, (unsigned char*)data, len);
- return 1;
- }
+ unsigned char* out = NULL;
+ bool r;
+ int out_len = 0;
- 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;
+ // Only copy the data if we have to, because it's a string
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = cipher->Update(buf, written, &out, &out_len);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = cipher->Update(buf, buflen, &out, &out_len);
}
+ if (!r) {
+ delete[] out;
+ return ThrowCryptoTypeError(ERR_get_error());
+ }
- protected:
+ Local<Object> buf = Buffer::New(env, reinterpret_cast<char*>(out), out_len);
+ if (out)
+ delete[] out;
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+ args.GetReturnValue().Set(buf);
+}
- 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());
+bool CipherBase::SetAutoPadding(bool auto_padding) {
+ if (!initialised_)
+ return false;
+ return EVP_CIPHER_CTX_set_padding(&ctx_, auto_padding);
+}
- HandleScope scope;
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give hashtype string as argument")));
- }
+void CipherBase::SetAutoPadding(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
+ cipher->SetAutoPadding(args.Length() < 1 || args[0]->BooleanValue());
+}
- ASSERT_IS_BUFFER(args[1]);
- ssize_t len = Buffer::Length(args[1]);
- if (len < 0) {
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
+bool CipherBase::Final(unsigned char** out, int *out_len) {
+ if (!initialised_)
+ return false;
+
+ *out = new unsigned char[EVP_CIPHER_CTX_block_size(&ctx_)];
+ bool r = EVP_CipherFinal_ex(&ctx_, *out, out_len);
+
+ if (r && kind_ == kCipher) {
+ delete[] auth_tag_;
+ auth_tag_ = NULL;
+ if (IsAuthenticatedMode()) {
+ auth_tag_len_ = EVP_GCM_TLS_TAG_LEN; // use default tag length
+ auth_tag_ = new char[auth_tag_len_];
+ memset(auth_tag_, 0, auth_tag_len_);
+ EVP_CIPHER_CTX_ctrl(&ctx_,
+ EVP_CTRL_GCM_GET_TAG,
+ auth_tag_len_,
+ reinterpret_cast<unsigned char*>(auth_tag_));
}
+ }
- String::Utf8Value hashType(args[0]);
+ EVP_CIPHER_CTX_cleanup(&ctx_);
+ initialised_ = false;
- bool r;
+ return r;
+}
- if( Buffer::HasInstance(args[1])) {
- char* buffer_data = Buffer::Data(args[1]);
- size_t buffer_length = Buffer::Length(args[1]);
- r = hmac->HmacInit(*hashType, buffer_data, buffer_length);
- } else {
- char* buf = new char[len];
- ssize_t written = DecodeWrite(buf, len, args[1], BINARY);
- assert(written == len);
+void CipherBase::Final(const FunctionCallbackInfo<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
- r = hmac->HmacInit(*hashType, buf, len);
+ CipherBase* cipher = Unwrap<CipherBase>(args.This());
- delete [] buf;
- }
+ unsigned char* out_value = NULL;
+ int out_len = -1;
+ Local<Value> outString;
- if (!r) {
- return ThrowException(Exception::Error(String::New("hmac error")));
- }
+ bool r = cipher->Final(&out_value, &out_len);
- return args.This();
+ if (out_len <= 0 || !r) {
+ delete[] out_value;
+ out_value = NULL;
+ out_len = 0;
+ if (!r)
+ return ThrowCryptoTypeError(ERR_get_error());
}
- static Handle<Value> HmacUpdate(const Arguments& args) {
- Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
+ args.GetReturnValue().Set(
+ Buffer::New(env, reinterpret_cast<char*>(out_value), out_len));
+}
- HandleScope scope;
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+void Hmac::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = hmac->HmacUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = hmac->HmacUpdate(buf, buflen);
- }
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("HmacUpdate fail"));
- return ThrowException(exception);
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", HmacInit);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", HmacUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "digest", HmacDigest);
- return args.This();
- }
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Hmac"), t->GetFunction());
+}
- static Handle<Value> HmacDigest(const Arguments& args) {
- Hmac *hmac = ObjectWrap::Unwrap<Hmac>(args.This());
- HandleScope scope;
+void Hmac::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ new Hmac(env, args.This());
+}
- enum encoding encoding = BUFFER;
- if (args.Length() >= 1) {
- encoding = ParseEncoding(args[0]->ToString(), BUFFER);
- }
- unsigned char* md_value = NULL;
- unsigned int md_len = 0;
- Local<Value> outString;
+void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
+ HandleScope scope(node_isolate);
- int r = hmac->HmacDigest(&md_value, &md_len);
- if (r == 0) {
- md_value = NULL;
- md_len = 0;
- }
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(hash_type);
+ if (md_ == NULL) {
+ return ThrowError("Unknown message digest");
+ }
+ HMAC_CTX_init(&ctx_);
+ if (key_len == 0) {
+ HMAC_Init(&ctx_, "", 0, md_);
+ } else {
+ HMAC_Init(&ctx_, key, key_len, md_);
+ }
+ initialised_ = true;
+}
- outString = StringBytes::Encode(
- reinterpret_cast<const char*>(md_value), md_len, encoding);
- delete[] md_value;
- return scope.Close(outString);
- }
+void Hmac::HmacInit(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Hmac () : ObjectWrap () {
- initialised_ = false;
- }
+ Hmac* hmac = Unwrap<Hmac>(args.This());
- ~Hmac () {
- if (initialised_) {
- HMAC_CTX_cleanup(&ctx);
- }
+ if (args.Length() < 2 || !args[0]->IsString()) {
+ return ThrowError("Must give hashtype string, key as arguments");
}
- private:
+ ASSERT_IS_BUFFER(args[1]);
- HMAC_CTX ctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
-};
+ const String::Utf8Value hash_type(args[0]);
+ const char* buffer_data = Buffer::Data(args[1]);
+ size_t buffer_length = Buffer::Length(args[1]);
+ hmac->HmacInit(*hash_type, buffer_data, buffer_length);
+}
-class Hash : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
+bool Hmac::HmacUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ HMAC_Update(&ctx_, reinterpret_cast<const unsigned char*>(data), len);
+ return true;
+}
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
+ Hmac* hmac = Unwrap<Hmac>(args.This());
- target->Set(String::NewSymbol("Hash"), t->GetFunction());
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- 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;
+ // Only copy the data if we have to, because it's a string
+ bool r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = hmac->HmacUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = hmac->HmacUpdate(buf, buflen);
}
- int HashUpdate(char* data, int len) {
- if (!initialised_) return 0;
- EVP_DigestUpdate(&mdctx, data, len);
- return 1;
+ if (!r) {
+ return ThrowTypeError("HmacUpdate fail");
}
+}
- protected:
-
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+bool Hmac::HmacDigest(unsigned char** md_value, unsigned int* md_len) {
+ if (!initialised_)
+ return false;
+ *md_value = new unsigned char[EVP_MAX_MD_SIZE];
+ HMAC_Final(&ctx_, *md_value, md_len);
+ HMAC_CTX_cleanup(&ctx_);
+ initialised_ = false;
+ return true;
+}
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give hashtype string as argument")));
- }
- String::Utf8Value hashType(args[0]);
+void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Hash *hash = new Hash();
- if (!hash->HashInit(*hashType)) {
- delete hash;
- return ThrowException(Exception::Error(String::New(
- "Digest method not supported")));
- }
+ Hmac* hmac = Unwrap<Hmac>(args.This());
- hash->Wrap(args.This());
- return args.This();
+ enum encoding encoding = BUFFER;
+ if (args.Length() >= 1) {
+ encoding = ParseEncoding(args[0]->ToString(), BUFFER);
}
- static Handle<Value> HashUpdate(const Arguments& args) {
- HandleScope scope;
-
- Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
-
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ unsigned char* md_value = NULL;
+ unsigned int md_len = 0;
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = hash->HashUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = hash->HashUpdate(buf, buflen);
- }
+ bool r = hmac->HmacDigest(&md_value, &md_len);
+ if (!r) {
+ md_value = NULL;
+ md_len = 0;
+ }
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("HashUpdate fail"));
- return ThrowException(exception);
- }
+ Local<Value> rc = StringBytes::Encode(
+ reinterpret_cast<const char*>(md_value), md_len, encoding);
+ delete[] md_value;
+ args.GetReturnValue().Set(rc);
+}
- return args.This();
- }
- static Handle<Value> HashDigest(const Arguments& args) {
- HandleScope scope;
+void Hash::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- Hash *hash = ObjectWrap::Unwrap<Hash>(args.This());
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (!hash->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "update", HashUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "digest", HashDigest);
- enum encoding encoding = BUFFER;
- if (args.Length() >= 1) {
- encoding = ParseEncoding(args[0]->ToString(), BUFFER);
- }
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Hash"), t->GetFunction());
+}
- 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;
+void Hash::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- return scope.Close(StringBytes::Encode(
- reinterpret_cast<const char*>(md_value), md_len, encoding));
+ if (args.Length() == 0 || !args[0]->IsString()) {
+ return ThrowError("Must give hashtype string as argument");
}
- Hash () : ObjectWrap () {
- initialised_ = false;
- }
+ const String::Utf8Value hash_type(args[0]);
- ~Hash () {
- if (initialised_) {
- EVP_MD_CTX_cleanup(&mdctx);
- }
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ Hash* hash = new Hash(env, args.This());
+ if (!hash->HashInit(*hash_type)) {
+ return ThrowError("Digest method not supported");
}
+}
- private:
- EVP_MD_CTX mdctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
-};
+bool Hash::HashInit(const char* hash_type) {
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(hash_type);
+ if (md_ == NULL)
+ return false;
+ EVP_MD_CTX_init(&mdctx_);
+ EVP_DigestInit_ex(&mdctx_, md_, NULL);
+ initialised_ = true;
+ return true;
+}
-class Sign : public ObjectWrap {
- public:
- static void
- Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+bool Hash::HashUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ EVP_DigestUpdate(&mdctx_, data, len);
+ return true;
+}
- 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);
+void Hash::HashUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- target->Set(String::NewSymbol("Sign"), t->GetFunction());
- }
+ Hash* hash = Unwrap<Hash>(args.This());
- 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;
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ // Only copy the data if we have to, because it's a string
+ bool r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = hash->HashUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = hash->HashUpdate(buf, buflen);
}
- int SignUpdate(char* data, int len) {
- if (!initialised_) return 0;
- EVP_SignUpdate(&mdctx, data, len);
- return 1;
+ if (!r) {
+ return ThrowTypeError("HashUpdate fail");
}
+}
- 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;
+void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- pkey = PEM_read_bio_PrivateKey( bp, NULL, NULL, NULL );
- if (pkey == NULL) return 0;
+ Hash* hash = Unwrap<Hash>(args.This());
- EVP_SignFinal(&mdctx, *md_value, md_len, pkey);
- EVP_MD_CTX_cleanup(&mdctx);
- initialised_ = false;
- EVP_PKEY_free(pkey);
- BIO_free(bp);
- return 1;
+ if (!hash->initialised_) {
+ return ThrowError("Not initialized");
}
+ enum encoding encoding = BUFFER;
+ if (args.Length() >= 1) {
+ encoding = ParseEncoding(args[0]->ToString(), BUFFER);
+ }
- protected:
+ unsigned char md_value[EVP_MAX_MD_SIZE];
+ unsigned int md_len;
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+ EVP_DigestFinal_ex(&hash->mdctx_, md_value, &md_len);
+ EVP_MD_CTX_cleanup(&hash->mdctx_);
+ hash->initialised_ = false;
- Sign *sign = new Sign();
- sign->Wrap(args.This());
+ Local<Value> rc = StringBytes::Encode(
+ reinterpret_cast<const char*>(md_value), md_len, encoding);
+ args.GetReturnValue().Set(rc);
+}
- return args.This();
- }
- static Handle<Value> SignInit(const Arguments& args) {
- HandleScope scope;
+void Sign::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give signtype string as argument")));
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", SignInit);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", SignUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "sign", SignFinal);
- String::Utf8Value signType(args[0]);
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Sign"), t->GetFunction());
+}
- bool r = sign->SignInit(*signType);
- if (!r) {
- return ThrowException(Exception::Error(String::New("SignInit error")));
- }
+void Sign::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ new Sign(env, args.This());
+}
- return args.This();
- }
- static Handle<Value> SignUpdate(const Arguments& args) {
- Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
+void Sign::SignInit(const char* sign_type) {
+ HandleScope scope(node_isolate);
- HandleScope scope;
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(sign_type);
+ if (!md_) {
+ return ThrowError("Uknown message digest");
+ }
+ EVP_MD_CTX_init(&mdctx_);
+ EVP_SignInit_ex(&mdctx_, md_, NULL);
+ initialised_ = true;
+}
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = sign->SignUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = sign->SignUpdate(buf, buflen);
- }
+void Sign::SignInit(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("SignUpdate fail"));
- return ThrowException(exception);
- }
+ Sign* sign = Unwrap<Sign>(args.This());
- return args.This();
+ if (args.Length() == 0 || !args[0]->IsString()) {
+ return ThrowError("Must give signtype string as argument");
}
- static Handle<Value> SignFinal(const Arguments& args) {
- Sign *sign = ObjectWrap::Unwrap<Sign>(args.This());
+ const String::Utf8Value sign_type(args[0]);
+ sign->SignInit(*sign_type);
+}
- HandleScope scope;
- unsigned char* md_value;
- unsigned int md_len;
- Local<Value> outString;
+bool Sign::SignUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ EVP_SignUpdate(&mdctx_, data, len);
+ return true;
+}
- ASSERT_IS_BUFFER(args[0]);
- ssize_t len = Buffer::Length(args[0]);
- enum encoding encoding = BUFFER;
- if (args.Length() >= 2) {
- encoding = ParseEncoding(args[1]->ToString(), BUFFER);
- }
+void Sign::SignUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- char* buf = new char[len];
- ssize_t written = DecodeWrite(buf, len, args[0], BUFFER);
- assert(written == len);
+ Sign* sign = Unwrap<Sign>(args.This());
- md_len = 8192; // Maximum key size is 8192 bits
- md_value = new unsigned char[md_len];
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- int r = sign->SignFinal(&md_value, &md_len, buf, len);
- if (r == 0) {
- md_value = NULL;
- md_len = r;
- }
+ // Only copy the data if we have to, because it's a string
+ int r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = sign->SignUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = sign->SignUpdate(buf, buflen);
+ }
- delete [] buf;
+ if (!r) {
+ return ThrowTypeError("SignUpdate fail");
+ }
+}
- outString = StringBytes::Encode(
- reinterpret_cast<const char*>(md_value), md_len, encoding);
- delete [] md_value;
- return scope.Close(outString);
+bool Sign::SignFinal(const char* key_pem,
+ int key_pem_len,
+ const char* passphrase,
+ unsigned char** sig,
+ unsigned int *sig_len) {
+ if (!initialised_) {
+ ThrowError("Sign not initalised");
+ return false;
}
- Sign () : ObjectWrap () {
- initialised_ = false;
- }
+ BIO* bp = NULL;
+ EVP_PKEY* pkey = NULL;
+ bool fatal = true;
- ~Sign () {
- if (initialised_) {
- EVP_MD_CTX_cleanup(&mdctx);
- }
- }
+ bp = BIO_new(BIO_s_mem());
+ if (bp == NULL)
+ goto exit;
- private:
+ if (!BIO_write(bp, key_pem, key_pem_len))
+ goto exit;
- EVP_MD_CTX mdctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
-};
+ pkey = PEM_read_bio_PrivateKey(bp,
+ NULL,
+ CryptoPemCallback,
+ const_cast<char*>(passphrase));
+ if (pkey == NULL)
+ goto exit;
-class Verify : public ObjectWrap {
- public:
- static void Initialize (v8::Handle<v8::Object> target) {
- HandleScope scope;
+ if (EVP_SignFinal(&mdctx_, *sig, sig_len, pkey))
+ fatal = false;
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ initialised_ = false;
- t->InstanceTemplate()->SetInternalFieldCount(1);
+ exit:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (bp != NULL)
+ BIO_free_all(bp);
- NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
- NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
- NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
+ EVP_MD_CTX_cleanup(&mdctx_);
- target->Set(String::NewSymbol("Verify"), t->GetFunction());
+ if (fatal) {
+ unsigned long err = ERR_get_error();
+ if (err) {
+ ThrowCryptoError(err);
+ } else {
+ ThrowError("PEM_read_bio_PrivateKey");
+ }
+ return false;
}
+ return true;
+}
- 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;
+
+void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
+
+ Sign* sign = Unwrap<Sign>(args.This());
+
+ unsigned char* md_value;
+ unsigned int md_len;
+
+ unsigned int len = args.Length();
+ enum encoding encoding = BUFFER;
+ if (len >= 2 && args[1]->IsString()) {
+ encoding = ParseEncoding(args[1]->ToString(), BUFFER);
}
+ String::Utf8Value passphrase(args[2]);
+
+ ASSERT_IS_BUFFER(args[0]);
+ size_t buf_len = Buffer::Length(args[0]);
+ char* buf = Buffer::Data(args[0]);
+
+ md_len = 8192; // Maximum key size is 8192 bits
+ md_value = new unsigned char[md_len];
- int VerifyUpdate(char* data, int len) {
- if (!initialised_) return 0;
- EVP_VerifyUpdate(&mdctx, data, len);
- return 1;
+ bool r = sign->SignFinal(buf,
+ buf_len,
+ len >= 3 && !args[2]->IsNull() ? *passphrase : NULL,
+ &md_value,
+ &md_len);
+ if (!r) {
+ delete[] md_value;
+ md_value = NULL;
+ md_len = 0;
}
+ Local<Value> rc = StringBytes::Encode(
+ reinterpret_cast<const char*>(md_value), md_len, encoding);
+ delete[] md_value;
+ args.GetReturnValue().Set(rc);
+}
- int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) {
- if (!initialised_) return 0;
- ClearErrorOnReturn clear_error_on_return;
- (void) &clear_error_on_return; // Silence compiler warning.
+void Verify::Initialize(Environment* env, v8::Handle<v8::Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- EVP_PKEY* pkey = NULL;
- BIO *bp = NULL;
- X509 *x509 = NULL;
- int r = 0;
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- 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;
- }
+ NODE_SET_PROTOTYPE_METHOD(t, "init", VerifyInit);
+ NODE_SET_PROTOTYPE_METHOD(t, "update", VerifyUpdate);
+ NODE_SET_PROTOTYPE_METHOD(t, "verify", VerifyFinal);
- // 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;
- }
+ target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Verify"), t->GetFunction());
+}
- pkey = X509_get_pubkey(x509);
- if (pkey == NULL) {
- ERR_print_errors_fp(stderr);
- return 0;
- }
- }
- r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey);
+void Verify::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ new Verify(env, args.This());
+}
+
- 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;
+void Verify::VerifyInit(const char* verify_type) {
+ HandleScope scope(node_isolate);
- return r;
+ assert(md_ == NULL);
+ md_ = EVP_get_digestbyname(verify_type);
+ if (md_ == NULL) {
+ return ThrowError("Unknown message digest");
}
+ EVP_MD_CTX_init(&mdctx_);
+ EVP_VerifyInit_ex(&mdctx_, md_, NULL);
+ initialised_ = true;
+}
- protected:
- static Handle<Value> New (const Arguments& args) {
- HandleScope scope;
+void Verify::VerifyInit(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Verify *verify = new Verify();
- verify->Wrap(args.This());
+ Verify* verify = Unwrap<Verify>(args.This());
- return args.This();
+ if (args.Length() == 0 || !args[0]->IsString()) {
+ return ThrowError("Must give verifytype string as argument");
}
+ const String::Utf8Value verify_type(args[0]);
+ verify->VerifyInit(*verify_type);
+}
- static Handle<Value> VerifyInit(const Arguments& args) {
- Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
- HandleScope scope;
+bool Verify::VerifyUpdate(const char* data, int len) {
+ if (!initialised_)
+ return false;
+ EVP_VerifyUpdate(&mdctx_, data, len);
+ return true;
+}
- if (args.Length() == 0 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(String::New(
- "Must give verifytype string as argument")));
- }
- String::Utf8Value verifyType(args[0]);
+void Verify::VerifyUpdate(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- bool r = verify->VerifyInit(*verifyType);
+ Verify* verify = Unwrap<Verify>(args.This());
- if (!r) {
- return ThrowException(Exception::Error(String::New("VerifyInit error")));
- }
+ ASSERT_IS_STRING_OR_BUFFER(args[0]);
- return args.This();
+ // Only copy the data if we have to, because it's a string
+ bool r;
+ if (args[0]->IsString()) {
+ Local<String> string = args[0].As<String>();
+ enum encoding encoding = ParseEncoding(args[1], BINARY);
+ if (!StringBytes::IsValidString(string, encoding))
+ return ThrowTypeError("Bad input string");
+ size_t buflen = StringBytes::StorageSize(string, encoding);
+ char* buf = new char[buflen];
+ size_t written = StringBytes::Write(buf, buflen, string, encoding);
+ r = verify->VerifyUpdate(buf, written);
+ delete[] buf;
+ } else {
+ char* buf = Buffer::Data(args[0]);
+ size_t buflen = Buffer::Length(args[0]);
+ r = verify->VerifyUpdate(buf, buflen);
}
+ if (!r) {
+ return ThrowTypeError("VerifyUpdate fail");
+ }
+}
- static Handle<Value> VerifyUpdate(const Arguments& args) {
- HandleScope scope;
- Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
+bool Verify::VerifyFinal(const char* key_pem,
+ int key_pem_len,
+ const char* sig,
+ int siglen) {
+ HandleScope scope(node_isolate);
- ASSERT_IS_STRING_OR_BUFFER(args[0]);
+ if (!initialised_) {
+ ThrowError("Verify not initalised");
+ return false;
+ }
- // Only copy the data if we have to, because it's a string
- int r;
- if (args[0]->IsString()) {
- Local<String> string = args[0].As<String>();
- enum encoding encoding = ParseEncoding(args[1], BINARY);
- if (!StringBytes::IsValidString(string, encoding))
- return ThrowTypeError("Bad input string");
- size_t buflen = StringBytes::StorageSize(string, encoding);
- char* buf = new char[buflen];
- size_t written = StringBytes::Write(buf, buflen, string, encoding);
- r = verify->VerifyUpdate(buf, written);
- delete[] buf;
- } else {
- char* buf = Buffer::Data(args[0]);
- size_t buflen = Buffer::Length(args[0]);
- r = verify->VerifyUpdate(buf, buflen);
- }
+ ClearErrorOnReturn clear_error_on_return;
+ (void) &clear_error_on_return; // Silence compiler warning.
- if (!r) {
- Local<Value> exception = Exception::TypeError(String::New("VerifyUpdate fail"));
- return ThrowException(exception);
+ EVP_PKEY* pkey = NULL;
+ BIO* bp = NULL;
+ X509* x509 = NULL;
+ bool fatal = true;
+ int r = 0;
+
+ bp = BIO_new(BIO_s_mem());
+ if (bp == NULL)
+ goto exit;
+
+ if (!BIO_write(bp, key_pem, key_pem_len))
+ goto exit;
+
+ // Check if this is a PKCS#8 or RSA public key before trying as X.509.
+ // Split this out into a separate function once we have more than one
+ // consumer of public keys.
+ if (strncmp(key_pem, PUBLIC_KEY_PFX, PUBLIC_KEY_PFX_LEN) == 0) {
+ pkey = PEM_read_bio_PUBKEY(bp, NULL, CryptoPemCallback, NULL);
+ if (pkey == NULL)
+ goto exit;
+ } else if (strncmp(key_pem, PUBRSA_KEY_PFX, PUBRSA_KEY_PFX_LEN) == 0) {
+ RSA* rsa = PEM_read_bio_RSAPublicKey(bp, NULL, CryptoPemCallback, NULL);
+ if (rsa) {
+ pkey = EVP_PKEY_new();
+ if (pkey)
+ EVP_PKEY_set1_RSA(pkey, rsa);
+ RSA_free(rsa);
}
+ if (pkey == NULL)
+ goto exit;
+ } else {
+ // X.509 fallback
+ x509 = PEM_read_bio_X509(bp, NULL, CryptoPemCallback, NULL);
+ if (x509 == NULL)
+ goto exit;
- return args.This();
+ pkey = X509_get_pubkey(x509);
+ if (pkey == NULL)
+ goto exit;
}
+ fatal = false;
+ r = EVP_VerifyFinal(&mdctx_,
+ reinterpret_cast<const unsigned char*>(sig),
+ siglen,
+ pkey);
+
+ exit:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+ if (bp != NULL)
+ BIO_free_all(bp);
+ if (x509 != NULL)
+ X509_free(x509);
- static Handle<Value> VerifyFinal(const Arguments& args) {
- HandleScope scope;
+ EVP_MD_CTX_cleanup(&mdctx_);
+ initialised_ = false;
- Verify *verify = ObjectWrap::Unwrap<Verify>(args.This());
+ if (fatal) {
+ unsigned long err = ERR_get_error();
+ ThrowCryptoError(err);
+ return false;
+ }
- ASSERT_IS_BUFFER(args[0]);
- ssize_t klen = Buffer::Length(args[0]);
+ return r == 1;
+}
- 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);
+void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- ASSERT_IS_STRING_OR_BUFFER(args[1]);
+ Verify* verify = Unwrap<Verify>(args.This());
- // BINARY works for both buffers and binary strings.
- enum encoding encoding = BINARY;
- if (args.Length() >= 3) {
- encoding = ParseEncoding(args[2]->ToString(), BINARY);
- }
+ ASSERT_IS_BUFFER(args[0]);
+ char* kbuf = Buffer::Data(args[0]);
+ ssize_t klen = Buffer::Length(args[0]);
- ssize_t hlen = StringBytes::Size(args[1], encoding);
+ ASSERT_IS_STRING_OR_BUFFER(args[1]);
+ // BINARY works for both buffers and binary strings.
+ enum encoding encoding = BINARY;
+ if (args.Length() >= 3) {
+ encoding = ParseEncoding(args[2]->ToString(), BINARY);
+ }
- if (hlen < 0) {
- delete[] kbuf;
- Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
- return ThrowException(exception);
- }
+ ssize_t hlen = StringBytes::Size(args[1], encoding);
- unsigned char* hbuf = new unsigned char[hlen];
- ssize_t hwritten = StringBytes::Write(
- reinterpret_cast<char*>(hbuf), hlen, args[1], encoding);
+ // only copy if we need to, because it's a string.
+ char* hbuf;
+ if (args[1]->IsString()) {
+ hbuf = new char[hlen];
+ ssize_t hwritten = StringBytes::Write(hbuf, hlen, args[1], encoding);
assert(hwritten == hlen);
+ } else {
+ hbuf = Buffer::Data(args[1]);
+ }
- int r;
- r = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
-
- delete[] kbuf;
+ bool rc = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
+ if (args[1]->IsString()) {
delete[] hbuf;
-
- return Boolean::New(r && r != -1);
}
+ args.GetReturnValue().Set(rc);
+}
- Verify () : ObjectWrap () {
- initialised_ = false;
- }
- ~Verify () {
- if (initialised_) {
- EVP_MD_CTX_cleanup(&mdctx);
- }
- }
+void DiffieHellman::Initialize(Environment* env, Handle<Object> target) {
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
- private:
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- EVP_MD_CTX mdctx; /* coverity[member_decl] */
- const EVP_MD *md; /* coverity[member_decl] */
- bool initialised_;
+ 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(FIXED_ONE_BYTE_STRING(node_isolate, "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(FIXED_ONE_BYTE_STRING(node_isolate, "DiffieHellmanGroup"),
+ t2->GetFunction());
+}
-};
-class DiffieHellman : public ObjectWrap {
- public:
- static void Initialize(v8::Handle<v8::Object> target) {
- HandleScope scope;
+bool DiffieHellman::Init(int primeLength) {
+ dh = DH_new();
+ DH_generate_parameters_ex(dh, primeLength, DH_GENERATOR_2, 0);
+ bool result = VerifyContext();
+ if (!result)
+ return false;
+ initialised_ = true;
+ return true;
+}
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->InstanceTemplate()->SetInternalFieldCount(1);
+bool DiffieHellman::Init(const char* p, int p_len) {
+ dh = DH_new();
+ dh->p = BN_bin2bn(reinterpret_cast<const unsigned char*>(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;
+}
- NODE_SET_PROTOTYPE_METHOD(t, "generateKeys", GenerateKeys);
- NODE_SET_PROTOTYPE_METHOD(t, "computeSecret", ComputeSecret);
- NODE_SET_PROTOTYPE_METHOD(t, "getPrime", GetPrime);
- NODE_SET_PROTOTYPE_METHOD(t, "getGenerator", GetGenerator);
- NODE_SET_PROTOTYPE_METHOD(t, "getPublicKey", GetPublicKey);
- NODE_SET_PROTOTYPE_METHOD(t, "getPrivateKey", GetPrivateKey);
- NODE_SET_PROTOTYPE_METHOD(t, "setPublicKey", SetPublicKey);
- NODE_SET_PROTOTYPE_METHOD(t, "setPrivateKey", SetPrivateKey);
- target->Set(String::NewSymbol("DiffieHellman"), t->GetFunction());
+bool DiffieHellman::Init(const char* p, int p_len, const char* g, int g_len) {
+ dh = DH_new();
+ dh->p = BN_bin2bn(reinterpret_cast<const unsigned char*>(p), p_len, 0);
+ dh->g = BN_bin2bn(reinterpret_cast<const unsigned char*>(g), g_len, 0);
+ initialised_ = true;
+ return true;
+}
- 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);
+void DiffieHellman::DiffieHellmanGroup(
+ const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- target->Set(String::NewSymbol("DiffieHellmanGroup"), t2->GetFunction());
- }
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ DiffieHellman* diffieHellman = new DiffieHellman(env, args.This());
- 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;
+ if (args.Length() != 1 || !args[0]->IsString()) {
+ return ThrowError("No group name given");
}
- 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;
- }
+ const String::Utf8Value group_name(args[0]);
+ for (unsigned int i = 0; i < ARRAY_SIZE(modp_groups); ++i) {
+ const modp_group* it = modp_groups + i;
- 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;
+ if (strcasecmp(*group_name, it->name) != 0)
+ continue;
+
+ diffieHellman->Init(it->prime,
+ it->prime_size,
+ it->gen,
+ it->gen_size);
+ return;
}
- protected:
- static Handle<Value> DiffieHellmanGroup(const Arguments& args) {
- HandleScope scope;
+ ThrowError("Unknown group");
+}
+
+
+void DiffieHellman::New(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- DiffieHellman* diffieHellman = new DiffieHellman();
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ DiffieHellman* diffieHellman =
+ new DiffieHellman(env, args.This());
+ bool initialized = false;
- if (args.Length() != 1 || !args[0]->IsString()) {
- return ThrowException(Exception::Error(
- String::New("No group name given")));
+ if (args.Length() > 0) {
+ if (args[0]->IsInt32()) {
+ initialized = diffieHellman->Init(args[0]->Int32Value());
+ } else {
+ initialized = diffieHellman->Init(Buffer::Data(args[0]),
+ Buffer::Length(args[0]));
}
+ }
- String::Utf8Value group_name(args[0]);
+ if (!initialized) {
+ return ThrowError("Initialization failed");
+ }
+}
- modp_group* it = modp_groups;
- while(it->name != NULL) {
- if (!strcasecmp(*group_name, it->name))
- break;
- it++;
- }
+void DiffieHellman::GenerateKeys(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- 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* diffieHellman = Unwrap<DiffieHellman>(args.This());
- diffieHellman->Wrap(args.This());
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- return args.This();
+ if (!DH_generate_key(diffieHellman->dh)) {
+ return ThrowError("Key generation failed");
}
- static Handle<Value> New(const Arguments& args) {
- HandleScope scope;
+ 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));
- DiffieHellman* diffieHellman = new DiffieHellman();
- bool initialized = false;
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- if (args.Length() > 0) {
- if (args[0]->IsInt32()) {
- initialized = diffieHellman->Init(args[0]->Int32Value());
- } else {
- initialized = diffieHellman->Init(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]));
- }
- }
- if (!initialized) {
- return ThrowException(Exception::Error(
- String::New("Initialization failed")));
- }
+void DiffieHellman::GetPrime(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- diffieHellman->Wrap(args.This());
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- return args.This();
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
}
- static Handle<Value> GenerateKeys(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ int dataSize = BN_num_bytes(diffieHellman->dh->p);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
- HandleScope scope;
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- 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")));
- }
+void DiffieHellman::GetGenerator(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- Local<Value> outString;
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- 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 (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- outString = Encode(data, dataSize, BUFFER);
- delete[] data;
+ int dataSize = BN_num_bytes(diffieHellman->dh->g);
+ char* data = new char[dataSize];
+ BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
- return scope.Close(outString);
- }
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- static Handle<Value> GetPrime(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
- HandleScope scope;
+void DiffieHellman::GetPublicKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- int dataSize = BN_num_bytes(diffieHellman->dh->p);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->p, reinterpret_cast<unsigned char*>(data));
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- Local<Value> outString;
+ if (diffieHellman->dh->pub_key == NULL) {
+ return ThrowError("No public key - did you forget to generate one?");
+ }
- outString = Encode(data, dataSize, BUFFER);
+ 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));
- delete[] data;
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- return scope.Close(outString);
- }
- static Handle<Value> GetGenerator(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+void DiffieHellman::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- HandleScope scope;
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- int dataSize = BN_num_bytes(diffieHellman->dh->g);
- char* data = new char[dataSize];
- BN_bn2bin(diffieHellman->dh->g, reinterpret_cast<unsigned char*>(data));
+ if (diffieHellman->dh->priv_key == NULL) {
+ return ThrowError("No private key - did you forget to generate one?");
+ }
- Local<Value> outString;
+ 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));
- outString = Encode(data, dataSize, BUFFER);
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- delete[] data;
- return scope.Close(outString);
- }
+void DiffieHellman::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- static Handle<Value> GetPublicKey(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- HandleScope scope;
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ ClearErrorOnReturn clear_error_on_return;
+ (void) &clear_error_on_return; // Silence compiler warning.
+ BIGNUM* key = NULL;
- if (diffieHellman->dh->pub_key == NULL) {
- return ThrowException(Exception::Error(
- String::New("No public key - did you forget to generate one?")));
- }
+ if (args.Length() == 0) {
+ return ThrowError("First argument must be other party's public key");
+ } else {
+ ASSERT_IS_BUFFER(args[0]);
+ key = BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
+ Buffer::Length(args[0]),
+ 0);
+ }
- 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));
+ int dataSize = DH_size(diffieHellman->dh);
+ char* data = new char[dataSize];
- Local<Value> outString;
+ int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
+ key,
+ diffieHellman->dh);
- outString = Encode(data, dataSize, BUFFER);
+ if (size == -1) {
+ int checkResult;
+ int checked;
+ checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
+ BN_free(key);
delete[] data;
- return scope.Close(outString);
+ if (!checked) {
+ return ThrowError("Invalid key");
+ } else if (checkResult) {
+ if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
+ return ThrowError("Supplied key is too small");
+ } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
+ return ThrowError("Supplied key is too large");
+ } else {
+ return ThrowError("Invalid key");
+ }
+ } else {
+ return ThrowError("Invalid key");
+ }
}
- static Handle<Value> GetPrivateKey(const Arguments& args) {
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ BN_free(key);
+ assert(size >= 0);
- HandleScope scope;
-
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ // DH_size returns number of bytes in a prime number
+ // DH_compute_key returns number of bytes in a remainder of exponent, which
+ // may have less bytes than a prime number. Therefore add 0-padding to the
+ // allocated buffer.
+ if (size != dataSize) {
+ assert(dataSize > size);
+ memmove(data + dataSize - size, data, size);
+ memset(data, 0, dataSize - size);
+ }
- if (diffieHellman->dh->priv_key == NULL) {
- return ThrowException(Exception::Error(
- String::New("No private key - did you forget to generate one?")));
- }
+ args.GetReturnValue().Set(Encode(data, dataSize, BUFFER));
+ delete[] data;
+}
- 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;
+void DiffieHellman::SetPublicKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- outString = Encode(data, dataSize, BUFFER);
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- delete[] data;
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- return scope.Close(outString);
+ if (args.Length() == 0) {
+ return ThrowError("First argument must be public key");
+ } else {
+ ASSERT_IS_BUFFER(args[0]);
+ diffieHellman->dh->pub_key = BN_bin2bn(
+ reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
+ Buffer::Length(args[0]), 0);
}
+}
- static Handle<Value> ComputeSecret(const Arguments& args) {
- HandleScope scope;
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+void DiffieHellman::SetPrivateKey(const FunctionCallbackInfo<Value>& args) {
+ HandleScope scope(node_isolate);
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ DiffieHellman* diffieHellman = Unwrap<DiffieHellman>(args.This());
- ClearErrorOnReturn clear_error_on_return;
- (void) &clear_error_on_return; // Silence compiler warning.
- BIGNUM* key = 0;
+ if (!diffieHellman->initialised_) {
+ return ThrowError("Not initialized");
+ }
- if (args.Length() == 0) {
- return ThrowException(Exception::Error(
- String::New("First argument must be other party's public key")));
- } else {
- ASSERT_IS_BUFFER(args[0]);
- key = BN_bin2bn(
+ if (args.Length() == 0) {
+ return ThrowError("First argument must be private key");
+ } else {
+ ASSERT_IS_BUFFER(args[0]);
+ diffieHellman->dh->priv_key = BN_bin2bn(
reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]), 0);
- }
-
- int dataSize = DH_size(diffieHellman->dh);
- char* data = new char[dataSize];
-
- int size = DH_compute_key(reinterpret_cast<unsigned char*>(data),
- key, diffieHellman->dh);
-
- if (size == -1) {
- int checkResult;
- int checked;
-
- checked = DH_check_pub_key(diffieHellman->dh, key, &checkResult);
- BN_free(key);
- delete[] data;
-
- if (!checked) {
- return ThrowException(Exception::Error(String::New("Invalid key")));
- } else if (checkResult) {
- if (checkResult & DH_CHECK_PUBKEY_TOO_SMALL) {
- return ThrowException(Exception::Error(
- String::New("Supplied key is too small")));
- } else if (checkResult & DH_CHECK_PUBKEY_TOO_LARGE) {
- return ThrowException(Exception::Error(
- String::New("Supplied key is too large")));
- } else {
- return ThrowException(Exception::Error(String::New("Invalid key")));
- }
- } else {
- return ThrowException(Exception::Error(String::New("Invalid key")));
- }
- }
+ Buffer::Length(args[0]),
+ 0);
+ }
+}
- BN_free(key);
- assert(size >= 0);
-
- // DH_size returns number of bytes in a prime number
- // DH_compute_key returns number of bytes in a remainder of exponent, which
- // may have less bytes than a prime number. Therefore add 0-padding to the
- // allocated buffer.
- if (size != dataSize) {
- assert(dataSize > size);
- memmove(data + dataSize - size, data, size);
- memset(data, 0, dataSize - size);
- }
- Local<Value> outString;
+bool DiffieHellman::VerifyContext() {
+ int codes;
+ if (!DH_check(dh, &codes))
+ return false;
+ if (codes & DH_CHECK_P_NOT_SAFE_PRIME)
+ return false;
+ if (codes & DH_CHECK_P_NOT_PRIME)
+ return false;
+ if (codes & DH_UNABLE_TO_CHECK_GENERATOR)
+ return false;
+ if (codes & DH_NOT_SUITABLE_GENERATOR)
+ return false;
+ return true;
+}
- outString = Encode(data, dataSize, BUFFER);
- delete[] data;
- return scope.Close(outString);
+class PBKDF2Request : public AsyncWrap {
+ public:
+ PBKDF2Request(Environment* env,
+ Local<Object> object,
+ ssize_t passlen,
+ char* pass,
+ ssize_t saltlen,
+ char* salt,
+ ssize_t iter,
+ ssize_t keylen)
+ : AsyncWrap(env, object),
+ error_(0),
+ passlen_(passlen),
+ pass_(pass),
+ saltlen_(saltlen),
+ salt_(salt),
+ keylen_(keylen),
+ key_(static_cast<char*>(malloc(keylen))),
+ iter_(iter) {
+ if (key() == NULL)
+ FatalError("node::PBKDF2Request()", "Out of Memory");
}
- static Handle<Value> SetPublicKey(const Arguments& args) {
- HandleScope scope;
-
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ ~PBKDF2Request() {
+ persistent().Dispose();
+ }
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(String::New("Not initialized")));
- }
+ uv_work_t* work_req() {
+ return &work_req_;
+ }
- if (args.Length() == 0) {
- return ThrowException(Exception::Error(
- String::New("First argument must be public key")));
- } else {
- ASSERT_IS_BUFFER(args[0]);
- diffieHellman->dh->pub_key =
- BN_bin2bn(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]), 0);
- }
+ inline ssize_t passlen() const {
+ return passlen_;
+ }
- return args.This();
+ inline char* pass() const {
+ return pass_;
}
- static Handle<Value> SetPrivateKey(const Arguments& args) {
- HandleScope scope;
+ inline ssize_t saltlen() const {
+ return saltlen_;
+ }
- DiffieHellman* diffieHellman =
- ObjectWrap::Unwrap<DiffieHellman>(args.This());
+ inline char* salt() const {
+ return salt_;
+ }
- if (!diffieHellman->initialised_) {
- return ThrowException(Exception::Error(
- String::New("Not initialized")));
- }
+ inline ssize_t keylen() const {
+ return keylen_;
+ }
- if (args.Length() == 0) {
- return ThrowException(Exception::Error(
- String::New("First argument must be private key")));
- } else {
- ASSERT_IS_BUFFER(args[0]);
- diffieHellman->dh->priv_key =
- BN_bin2bn(
- reinterpret_cast<unsigned char*>(Buffer::Data(args[0])),
- Buffer::Length(args[0]), 0);
- }
+ inline char* key() const {
+ return key_;
+ }
- return args.This();
+ inline ssize_t iter() const {
+ return iter_;
}
- DiffieHellman() : ObjectWrap() {
- initialised_ = false;
- dh = NULL;
+ inline void release() {
+ free(pass_);
+ passlen_ = 0;
+ free(salt_);
+ saltlen_ = 0;
+ free(key_);
+ keylen_ = 0;
}
- ~DiffieHellman() {
- if (dh != NULL) {
- DH_free(dh);
- }
+ inline int error() const {
+ return error_;
}
- private:
- bool VerifyContext() {
- int codes;
- if (!DH_check(dh, &codes)) return false;
- if (codes & DH_CHECK_P_NOT_SAFE_PRIME) return false;
- if (codes & DH_CHECK_P_NOT_PRIME) return false;
- if (codes & DH_UNABLE_TO_CHECK_GENERATOR) return false;
- if (codes & DH_NOT_SUITABLE_GENERATOR) return false;
- return true;
- }
-
- bool initialised_;
- DH* dh;
-};
+ inline void set_error(int err) {
+ error_ = err;
+ }
+ // TODO(trevnorris): Make private and make work with CONTAINER_OF macro.
+ uv_work_t work_req_;
-struct pbkdf2_req {
- uv_work_t work_req;
- int err;
- char* pass;
- size_t passlen;
- char* salt;
- size_t saltlen;
- size_t iter;
- char* key;
- size_t keylen;
- Persistent<Object> obj;
+ private:
+ int error_;
+ ssize_t passlen_;
+ char* pass_;
+ ssize_t saltlen_;
+ char* salt_;
+ ssize_t keylen_;
+ char* key_;
+ ssize_t iter_;
};