From d0df3f3c673a3e620cd01b674b36cd082f461b1b Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 27 Nov 2019 10:27:16 -0800 Subject: [PATCH 1/6] quic: miscelaneous http3 notes --- src/node_quic_http3_application.cc | 68 ++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/src/node_quic_http3_application.cc b/src/node_quic_http3_application.cc index 7167ad29ee..ee80487edb 100644 --- a/src/node_quic_http3_application.cc +++ b/src/node_quic_http3_application.cc @@ -26,6 +26,11 @@ bool IsZeroLengthHeader(nghttp3_rcbuf* name, nghttp3_rcbuf* value) { Http3RcBufferPointer::IsZeroLength(value); } +// nghttp3 uses a numeric identifier for a large number +// of known HTTP header names. These allow us to use +// static strings for those rather than allocating new +// strings all of the time. The list of strings supported +// is included in node_http_common.h const char* to_http_header_name(int32_t token) { switch (token) { default: @@ -72,6 +77,7 @@ MaybeLocal Http3Header::GetName(QuicApplication* app) const { return eternal.Get(env->isolate()); } + // This is exceedingly unlikely but we need to be prepared just in case. if (UNLIKELY(!name_)) return String::Empty(env->isolate()); @@ -104,7 +110,7 @@ Http3Application::Http3Application( QuicSession* session) : QuicApplication(session), alloc_info_(MakeAllocator()) { - // Collect Configuration Details + // Collect Configuration Details. An aliased buffer is used here. Environment* env = session->env(); SetConfig(env, IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, &qpack_max_table_capacity_); @@ -115,11 +121,18 @@ Http3Application::Http3Application( env->quic_state()->http3config_buffer[IDX_HTTP3_CONFIG_COUNT] = 0; // Reset } +// Submit informational headers (response headers that use a 1xx +// status code). bool Http3Application::SubmitInformation( int64_t stream_id, v8::Local headers) { + // If the QuicSession is not a server session, return false + // immediately. Informational headers cannot be sent by an + // HTTP/3 client + if (!Session()->IsServer()) + return false; + Http3Headers nva(Session()->env(), headers); - // TODO(@jasnell): Do we need more granularity on error conditions? Debug( Session(), "Submitting %d informational headers for stream %" PRId64, @@ -146,7 +159,6 @@ bool Http3Application::SubmitHeaders( if (!(flags & QUICSTREAM_HEADER_FLAGS_TERMINAL)) reader_ptr = &reader; - // TODO(@jasnell): Do we need more granularity on error conditions? switch (Session()->CryptoContext()->Side()) { case NGTCP2_CRYPTO_SIDE_CLIENT: Debug( @@ -183,7 +195,6 @@ bool Http3Application::SubmitTrailers( int64_t stream_id, v8::Local headers) { Http3Headers nva(Session()->env(), headers); - // TODO(@jasnell): Do we need more granularity on error conditions? Debug( Session(), "Submitting %d trailing headers for stream %" PRId64, @@ -235,6 +246,9 @@ nghttp3_conn* Http3Application::CreateConnection( return conn; } +// The HTTP/3 QUIC binding uses a single unidirectional control +// stream in each direction to exchange frames impacting the entire +// connection. bool Http3Application::CreateAndBindControlStream() { if (!Session()->OpenUnidirectionalStream(&control_stream_id_)) return false; @@ -247,6 +261,8 @@ bool Http3Application::CreateAndBindControlStream() { control_stream_id_) == 0; } +// The HTTP/3 QUIC binding creates two unidirectional streams in +// each direction to exchange header compression details. bool Http3Application::CreateAndBindQPackStreams() { if (!Session()->OpenUnidirectionalStream(&qpack_enc_stream_id_) || !Session()->OpenUnidirectionalStream(&qpack_dec_stream_id_)) { @@ -268,7 +284,8 @@ bool Http3Application::Initialize() { return false; // The QuicSession must allow for at least three local unidirectional streams. - // This number is fixed by the http3 specification. + // This number is fixed by the http3 specification and represent the + // control stream and two qpack management streams. if (Session()->GetMaxLocalStreamsUni() < 3) return false; @@ -308,6 +325,9 @@ bool Http3Application::Initialize() { return true; } +// All HTTP/3 control, header, and stream data arrives as QUIC stream data. +// Here we pass the received data off to nghttp3 for processing. This will +// trigger the invocation of the various nghttp3 callbacks. bool Http3Application::ReceiveStreamData( int64_t stream_id, int fin, @@ -334,7 +354,7 @@ void Http3Application::AcknowledgeStreamData( } void Http3Application::StreamOpen(int64_t stream_id) { - // FindOrCreateStream(stream_id); + Debug(Session(), "HTTP/3 Stream %" PRId64 " is open."); } void Http3Application::StreamClose( @@ -395,6 +415,7 @@ bool Http3Application::SendPendingData() { int fin = 0; ssize_t sveccnt = 0; + // First, grab the outgoing data from nghttp3 if (Connection() && Session()->GetMaxDataLeft()) { sveccnt = nghttp3_conn_writev_stream( @@ -414,6 +435,10 @@ bool Http3Application::SendPendingData() { nghttp3_vec* v = vec.data(); size_t vcnt = static_cast(sveccnt); + // Second, serialize as much of the outgoing data as possible to a + // QUIC packet for transmission. We'll keep iterating until there + // is no more data to transmit. + // TODO(@jasnell): Support the use of the NGTCP2_WRITE_STREAM_FLAG_MORE // flag to allow more efficient coallescing of packets. MallocedBuffer dest(Session()->GetMaxPacketLength()); @@ -480,6 +505,8 @@ bool Http3Application::SendStreamData(QuicStream* stream) { return SendPendingData(); } +// This is where nghttp3 pulls the data from the outgoing +// buffer to prepare it to be sent on the QUIC stream. ssize_t Http3Application::H3ReadData( int64_t stream_id, nghttp3_vec* vec, @@ -505,9 +532,14 @@ ssize_t Http3Application::H3ReadData( return count; } + // If count is zero here, it means that there is no data currently + // available to send but there might be later, so return WOULDBLOCK + // to tell nghttp3 to hold off attempting to serialize any more + // data for this stream until it is resumed. return count == 0 ? NGHTTP3_ERR_WOULDBLOCK : count; } +// Outgoing data is retained in memory until it is acknowledged. void Http3Application::H3AckedStreamData( int64_t stream_id, size_t datalen) { @@ -574,13 +606,18 @@ void Http3Application::H3BeginHeaders( stream->BeginHeaders(kind); } +// As each header name+value pair is received, it is stored internally +// by the QuicStream until stream->EndHeaders() is called, during which +// the collected headers are converted to an array and passed off to +// the javascript side. bool Http3Application::H3ReceiveHeader( int64_t stream_id, int32_t token, nghttp3_rcbuf* name, nghttp3_rcbuf* value, uint8_t flags) { - // Protect against zero-length headers + // Protect against zero-length headers (zero-length if either the + // name or value are zero-length). Such headers are simply ignored. if (!IsZeroLengthHeader(name, value)) { Debug(Session(), "Receiving header for stream %" PRId64, stream_id); QuicStream* stream = Session()->FindStream(stream_id); @@ -606,12 +643,14 @@ void Http3Application::H3EndHeaders(int64_t stream_id) { stream->EndHeaders(); } +// TODO(@jasnell): Implement Push Promise Support int Http3Application::H3BeginPushPromise( int64_t stream_id, int64_t push_id) { return 0; } +// TODO(@jasnell): Implement Push Promise Support bool Http3Application::H3ReceivePushPromise( int64_t stream_id, int64_t push_id, @@ -622,29 +661,32 @@ bool Http3Application::H3ReceivePushPromise( return true; } +// TODO(@jasnell): Implement Push Promise Support int Http3Application::H3EndPushPromise( int64_t stream_id, int64_t push_id) { return 0; } +// TODO(@jasnell): Implement Push Promise Support void Http3Application::H3CancelPush( int64_t push_id, int64_t stream_id) { } -void Http3Application::H3SendStopSending( - int64_t stream_id, - uint64_t app_error_code) { - Session()->ResetStream(stream_id, app_error_code); -} - +// TODO(@jasnell): Implement Push Promise Support int Http3Application::H3PushStream( int64_t push_id, int64_t stream_id) { return 0; } +void Http3Application::H3SendStopSending( + int64_t stream_id, + uint64_t app_error_code) { + Session()->ResetStream(stream_id, app_error_code); +} + int Http3Application::H3EndStream( int64_t stream_id) { QuicStream* stream = FindOrCreateStream(stream_id); From a48ac838d050f73e89e64dcee9fbc90508e5e832 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 27 Nov 2019 11:09:47 -0800 Subject: [PATCH 2/6] quic: use const refs for QuicCID passing --- src/node_quic_session.cc | 3 +-- src/node_quic_session.h | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/node_quic_session.cc b/src/node_quic_session.cc index c98e611335..018622b808 100644 --- a/src/node_quic_session.cc +++ b/src/node_quic_session.cc @@ -963,8 +963,7 @@ void QuicSession::AddToSocket(QuicSocket* socket) { switch (crypto_context_->Side()) { case NGTCP2_CRYPTO_SIDE_SERVER: { - QuicCID rcid(rcid_); - socket->AssociateCID(rcid, scid); + socket->AssociateCID(QuicCID(rcid_), scid); if (pscid_.datalen) socket->AssociateCID(QuicCID(pscid_), scid); diff --git a/src/node_quic_session.h b/src/node_quic_session.h index 76587f3fa3..ea23918331 100644 --- a/src/node_quic_session.h +++ b/src/node_quic_session.h @@ -164,16 +164,23 @@ enum QuicSessionState : int { IDX_QUIC_SESSION_STATE_COUNT }; +// The QuicCryptoContext class encapsulates all of the crypto/TLS +// handshake details on behalf of a QuicSession. class QuicCryptoContext : public MemoryRetainer { public: SSL* operator*() { return ssl_.get(); } uint64_t Cancel(); + // Outgoing crypto data must be retained in memory until it is + // explicitly acknowledged. void AcknowledgeCryptoData(ngtcp2_crypto_level level, size_t datalen); + // Enables openssl's TLS tracing mechanism void EnableTrace(); + // Returns the server's prepared OCSP response for transmission. This + // is not used by client QuicSession instances. std::string GetOCSPResponse(); ngtcp2_crypto_level GetReadCryptoLevel(); @@ -184,6 +191,7 @@ class QuicCryptoContext : public MemoryRetainer { return options_ & option; } + // Emits a single keylog line to the JavaScript layer void Keylog(const char* line); int OnClientHello(); @@ -204,12 +212,15 @@ class QuicCryptoContext : public MemoryRetainer { int OnTLSStatus(); + // Receives and processes TLS handshake details int Receive( ngtcp2_crypto_level crypto_level, uint64_t offset, const uint8_t* data, size_t datalen); + // Resumes the TLS handshake following a client hello or + // OCSP callback void ResumeHandshake(); void SetOption(uint32_t option, bool on = true) { @@ -235,6 +246,7 @@ class QuicCryptoContext : public MemoryRetainer { size_t datalen); bool InitiateKeyUpdate(); + bool KeyUpdate( uint8_t* rx_key, uint8_t* rx_iv, From 94b380d2205e3a890349f0cdab48a79aea1123a4 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 27 Nov 2019 12:56:29 -0800 Subject: [PATCH 3/6] quic: minor additional improvements to QuicCID --- src/node_quic_util.h | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/node_quic_util.h b/src/node_quic_util.h index 58b9610601..2b61bd5091 100644 --- a/src/node_quic_util.h +++ b/src/node_quic_util.h @@ -326,29 +326,40 @@ struct QuicPathStorage { std::array remote_addrbuf; }; +// Simple wrapper for ngtcp2_cid that handles hex encoding +// and conversion to std::string automatically class QuicCID { public: - explicit QuicCID(ngtcp2_cid* cid) : cid_(*cid) {} - explicit QuicCID(const ngtcp2_cid* cid) : cid_(*cid) {} - explicit QuicCID(const ngtcp2_cid& cid) : cid_(cid) {} + explicit QuicCID(ngtcp2_cid* cid) : + cid_(*cid), + str_(cid->data, cid->data + cid->datalen) {} + explicit QuicCID(const ngtcp2_cid* cid) : + cid_(*cid), + str_(cid->data, cid->data + cid->datalen) {} + explicit QuicCID(const ngtcp2_cid& cid) : + cid_(cid), + str_(cid.data, cid.data + cid.datalen) {} QuicCID(const uint8_t* cid, size_t len) { ngtcp2_cid_init(&cid_, cid, len); + str_ = std::string(cid_.data, cid_.data + cid_.datalen); } - std::string ToStr() const { - return std::string(cid_.data, cid_.data + cid_.datalen); - } + std::string ToStr() const { return str_; } std::string ToHex() const { - MaybeStackBuffer dest; - dest.AllocateSufficientStorage(cid_.datalen * 2); - dest.SetLengthAndZeroTerminate(cid_.datalen * 2); - size_t written = StringBytes::hex_encode( - reinterpret_cast(cid_.data), - cid_.datalen, - *dest, - dest.length()); - return std::string(*dest, written); + if (hex_.empty() && cid_.datalen > 0) { + size_t len = cid_.datalen * 2; + MaybeStackBuffer dest; + dest.AllocateSufficientStorage(len); + dest.SetLengthAndZeroTerminate(len); + size_t written = StringBytes::hex_encode( + reinterpret_cast(cid_.data), + cid_.datalen, + *dest, + dest.length()); + hex_ = std::string(*dest, written); + } + return hex_; } const ngtcp2_cid* operator*() const { return &cid_; } @@ -358,6 +369,8 @@ class QuicCID { private: ngtcp2_cid cid_; + std::string str_; + mutable std::string hex_; }; // https://stackoverflow.com/questions/33701430/template-function-to-access-struct-members From af430843f2c3eb294c853f0a4e524bf4755b68dc Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 27 Nov 2019 14:26:56 -0800 Subject: [PATCH 4/6] quic: move common crypto functions to node_crypto_common.h First step to reconciling shared code in node_crypto.cc --- src/node_crypto_common-inl.h | 422 ++++++++++++++++++++++++++++++++ src/node_crypto_common.h | 67 +++++ src/node_quic.cc | 3 +- src/node_quic_crypto.cc | 460 ++--------------------------------- src/node_quic_crypto.h | 50 ---- src/node_quic_session.cc | 58 +++-- 6 files changed, 549 insertions(+), 511 deletions(-) create mode 100644 src/node_crypto_common-inl.h create mode 100644 src/node_crypto_common.h diff --git a/src/node_crypto_common-inl.h b/src/node_crypto_common-inl.h new file mode 100644 index 0000000000..3fcd7fb0fc --- /dev/null +++ b/src/node_crypto_common-inl.h @@ -0,0 +1,422 @@ +#ifndef SRC_NODE_CRYPTO_COMMON_INL_H_ +#define SRC_NODE_CRYPTO_COMMON_INL_H_ + +#include "env-inl.h" +#include "node_crypto.h" +#include "node_crypto_common.h" +#include "node.h" +#include "node_url.h" +#include "string_bytes.h" +#include "v8.h" + +#include +#include + +#include +#include + +namespace node { + +using v8::Array; +using v8::Context; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::Value; + +namespace crypto { + +inline void LogSecret( + SSL* ssl, + const char* name, + const unsigned char* secret, + size_t secretlen) { + if (auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl))) { + unsigned char crandom[32]; + if (SSL_get_client_random(ssl, crandom, 32) != 32) + return; + std::string line = name; + line += " " + StringBytes::hex_encode( + reinterpret_cast(crandom), 32); + line += " " + StringBytes::hex_encode( + reinterpret_cast(secret), secretlen); + keylog_cb(ssl, line.c_str()); + } +} + +inline void SetALPN(SSL* ssl, const std::string& alpn) { + SSL_set_alpn_protos( + ssl, + reinterpret_cast(alpn.c_str()), + alpn.length()); +} + +inline std::string GetSSLOCSPResponse(SSL* ssl) { + const unsigned char* resp; + int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); + if (len < 0) len = 0; + return std::string(reinterpret_cast(resp), len); +} + +inline bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length) { + crypto::SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); + return s != nullptr && SSL_set_session(ssl, s.get()) == 1; +} + +inline std::unordered_multimap GetCertificateAltNames( + X509* cert) { + std::unordered_multimap map; + crypto::BIOPointer bio(BIO_new(BIO_s_mem())); + BUF_MEM* mem; + int idx = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); + if (idx < 0) // There is no subject alt name + return map; + + X509_EXTENSION* ext = X509_get_ext(cert, idx); + CHECK_NOT_NULL(ext); + const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); + CHECK_EQ(method, X509V3_EXT_get_nid(NID_subject_alt_name)); + + GENERAL_NAMES* names = static_cast(X509V3_EXT_d2i(ext)); + if (names == nullptr) // There are no names + return map; + + for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { + USE(BIO_reset(bio.get())); + GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); + if (gen->type == GEN_DNS) { + ASN1_IA5STRING* name = gen->d.dNSName; + BIO_write(bio.get(), name->data, name->length); + BIO_get_mem_ptr(bio.get(), &mem); + map.emplace("dns", std::string(mem->data, mem->length)); + } else { + STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( + const_cast(method), gen, nullptr); + if (nval == nullptr) + continue; + X509V3_EXT_val_prn(bio.get(), nval, 0, 0); + sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); + BIO_get_mem_ptr(bio.get(), &mem); + std::string value(mem->data, mem->length); + if (value.compare(0, 11, "IP Address:") == 0) { + map.emplace("ip", value.substr(11)); + } else if (value.compare(0, 4, "URI:") == 0) { + url::URL url(value.substr(4)); + if (url.flags() & url::URL_FLAGS_CANNOT_BE_BASE || + url.flags() & url::URL_FLAGS_FAILED) { + continue; // Skip this one + } + map.emplace("uri", url.host()); + } + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + bio.reset(); + return map; +} + +inline std::string GetCertificateCN(X509* cert) { + X509_NAME* subject = X509_get_subject_name(cert); + if (subject != nullptr) { + int nid = OBJ_txt2nid("CN"); + int idx = X509_NAME_get_index_by_NID(subject, nid, -1); + if (idx != -1) { + X509_NAME_ENTRY* cn = X509_NAME_get_entry(subject, idx); + if (cn != nullptr) { + ASN1_STRING* cn_str = X509_NAME_ENTRY_get_data(cn); + if (cn_str != nullptr) { + return std::string(reinterpret_cast( + ASN1_STRING_get0_data(cn_str))); + } + } + } + } + return std::string(); +} + +inline int VerifyPeerCertificate(SSL* ssl) { + int err = X509_V_ERR_UNSPECIFIED; + if (X509* peer_cert = SSL_get_peer_certificate(ssl)) { + X509_free(peer_cert); + err = SSL_get_verify_result(ssl); + } + return err; +} + +inline int UseSNIContext(SSL* ssl, SecureContext* context) { + SSL_CTX* ctx = context->ctx_.get(); + X509* x509 = SSL_CTX_get0_certificate(ctx); + EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); + STACK_OF(X509)* chain; + + int err = SSL_CTX_get0_chain_certs(ctx, &chain); + if (err) + err = SSL_use_certificate(ssl, x509); + if (err) + err = SSL_use_PrivateKey(ssl, pkey); + if (err && chain != nullptr) + err = SSL_set1_chain(ssl, chain); + return err; +} + + +inline const char* GetClientHelloALPN(SSL* ssl) { + const unsigned char* buf; + size_t len; + size_t rem; + + if (!SSL_client_hello_get0_ext( + ssl, + TLSEXT_TYPE_application_layer_protocol_negotiation, + &buf, &rem) || rem < 2) { + return nullptr; + } + + len = (buf[0] << 8) | buf[1]; + if (len + 2 != rem) + return nullptr; + buf += 3; + return reinterpret_cast(buf); +} + +inline const char* GetClientHelloServerName(SSL* ssl) { + const unsigned char* buf; + size_t len; + size_t rem; + + if (!SSL_client_hello_get0_ext( + ssl, + TLSEXT_TYPE_server_name, + &buf, + &rem) || rem <= 2) { + return nullptr; + } + + len = *(buf++) << 8; + len += *(buf++); + if (len + 2 != rem) + return nullptr; + rem = len; + + if (rem == 0 || *buf++ != TLSEXT_NAMETYPE_host_name) + return nullptr; + rem--; + if (rem <= 2) + return nullptr; + len = *(buf++) << 8; + len += *(buf++); + if (len + 2 > rem) + return nullptr; + rem = len; + return reinterpret_cast(buf); +} + +inline const char* GetServerName(SSL* ssl) { + return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); +} + +inline Local GetClientHelloCiphers(Environment* env, SSL* ssl) { + const unsigned char* buf; + size_t len = SSL_client_hello_get0_ciphers(ssl, &buf); + std::vector> ciphers_array; + for (size_t n = 0; n < len; n += 2) { + const SSL_CIPHER* cipher = SSL_CIPHER_find(ssl, buf); + buf += 2; + const char* cipher_name = SSL_CIPHER_get_name(cipher); + const char* cipher_version = SSL_CIPHER_get_version(cipher); + Local obj = Object::New(env->isolate()); + obj->Set( + env->context(), + env->name_string(), + OneByteString(env->isolate(), cipher_name)).FromJust(); + obj->Set( + env->context(), + env->version_string(), + OneByteString(env->isolate(), cipher_version)).FromJust(); + ciphers_array.push_back(obj); + } + return Array::New(env->isolate(), ciphers_array.data(), ciphers_array.size()); +} + +inline bool SetGroups(SecureContext* sc, const char* groups) { + return SSL_CTX_set1_groups_list(**sc, groups) == 1; +} + +inline const char* X509ErrorCode(int err) { + const char* code = "UNSPECIFIED"; +#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; + switch (err) { + CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) + CASE_X509_ERR(UNABLE_TO_GET_CRL) + CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) + CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) + CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) + CASE_X509_ERR(CERT_SIGNATURE_FAILURE) + CASE_X509_ERR(CRL_SIGNATURE_FAILURE) + CASE_X509_ERR(CERT_NOT_YET_VALID) + CASE_X509_ERR(CERT_HAS_EXPIRED) + CASE_X509_ERR(CRL_NOT_YET_VALID) + CASE_X509_ERR(CRL_HAS_EXPIRED) + CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) + CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) + CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) + CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) + CASE_X509_ERR(OUT_OF_MEM) + CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) + CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) + CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) + CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) + CASE_X509_ERR(CERT_CHAIN_TOO_LONG) + CASE_X509_ERR(CERT_REVOKED) + CASE_X509_ERR(INVALID_CA) + CASE_X509_ERR(PATH_LENGTH_EXCEEDED) + CASE_X509_ERR(INVALID_PURPOSE) + CASE_X509_ERR(CERT_UNTRUSTED) + CASE_X509_ERR(CERT_REJECTED) + CASE_X509_ERR(HOSTNAME_MISMATCH) + } +#undef CASE_X509_ERR + return code; +} + +inline Local GetValidationErrorReason(Environment* env, int err) { + const char* reason = X509_verify_cert_error_string(err); + return OneByteString(env->isolate(), reason); +} + +inline Local GetValidationErrorCode(Environment* env, int err) { + return OneByteString(env->isolate(), X509ErrorCode(err)); +} + +inline Local GetCertificate(Environment* env, SSL* ssl) { + ClearErrorOnReturn clear_error_on_return; + Local value = v8::Undefined(env->isolate()); + X509* cert = SSL_get_certificate(ssl); + if (cert != nullptr) + value = X509ToObject(env, cert); + return value; +} + +inline Local GetCipherName(Environment* env, SSL* ssl) { + Local cipher; + const SSL_CIPHER* c = SSL_get_current_cipher(ssl); + if (c != nullptr) { + const char* cipher_name = SSL_CIPHER_get_name(c); + cipher = OneByteString(env->isolate(), cipher_name); + } + return cipher; +} + +inline Local GetCipherVersion(Environment* env, SSL* ssl) { + Local version; + const SSL_CIPHER* c = SSL_get_current_cipher(ssl); + if (c != nullptr) { + const char* cipher_version = SSL_CIPHER_get_version(c); + version = OneByteString(env->isolate(), cipher_version); + } + return version; +} + +inline Local GetEphemeralKey(Environment* env, SSL* ssl) { + Local context = env->context(); + + Local info = Object::New(env->isolate()); + + EVP_PKEY* raw_key; + if (SSL_get_server_tmp_key(ssl, &raw_key)) { + crypto::EVPKeyPointer key(raw_key); + int kid = EVP_PKEY_id(key.get()); + switch (kid) { + case EVP_PKEY_DH: + info->Set(context, env->type_string(), + FIXED_ONE_BYTE_STRING(env->isolate(), "DH")).FromJust(); + info->Set(context, env->size_string(), + Integer::New(env->isolate(), EVP_PKEY_bits(key.get()))) + .FromJust(); + break; + case EVP_PKEY_EC: + case EVP_PKEY_X25519: + case EVP_PKEY_X448: + { + const char* curve_name; + if (kid == EVP_PKEY_EC) { + EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key.get()); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + curve_name = OBJ_nid2sn(nid); + EC_KEY_free(ec); + } else { + curve_name = OBJ_nid2sn(kid); + } + info->Set(context, env->type_string(), + FIXED_ONE_BYTE_STRING( + env->isolate(), + "ECDH")).FromJust(); + info->Set(context, env->name_string(), + OneByteString( + env->isolate(), + curve_name)).FromJust(); + info->Set(context, env->size_string(), + Integer::New( + env->isolate(), + EVP_PKEY_bits(key.get()))).FromJust(); + } + break; + default: + break; + } + } + return info; +} + +inline Local GetPeerCertificate( + Environment* env, + SSL* ssl, + bool abbreviated, + bool is_server) { + ClearErrorOnReturn clear_error_on_return; + + Local result = v8::Undefined(env->isolate()); + Local issuer_chain; + + // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` + // contains the `peer_certificate`, but on server it doesn't. + X509Pointer cert(is_server ? SSL_get_peer_certificate(ssl) : nullptr); + STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(ssl); + if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) + return result; + + // Short result requested. + if (abbreviated) + return X509ToObject(env, cert ? cert.get() : sk_X509_value(ssl_certs, 0)); + + if (auto peer_certs = CloneSSLCerts(std::move(cert), ssl_certs)) { + // First and main certificate. + X509Pointer cert(sk_X509_value(peer_certs.get(), 0)); + CHECK(cert); + result = X509ToObject(env, cert.release()); + + Local issuer_chain = + GetLastIssuedCert( + &cert, + ssl, + AddIssuerChainToObject( + &cert, + result.As(), + std::move(peer_certs), + env), + env); + // Last certificate should be self-signed. + if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK) + USE(issuer_chain->Set( + env->context(), + env->issuercert_string(), + issuer_chain)); + } + return result; +} + +} // namespace crypto +} // namespace node + +#endif // SRC_NODE_CRYPTO_COMMON_INL_H_ diff --git a/src/node_crypto_common.h b/src/node_crypto_common.h new file mode 100644 index 0000000000..ac8e1b5d20 --- /dev/null +++ b/src/node_crypto_common.h @@ -0,0 +1,67 @@ +#ifndef SRC_NODE_CRYPTO_COMMON_H_ +#define SRC_NODE_CRYPTO_COMMON_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "env.h" +#include "node_crypto.h" +#include "v8.h" +#include +#include +#include +#include + +namespace node { +namespace crypto { + +void LogSecret( + SSL* ssl, + const char* name, + const unsigned char* secret, + size_t secretlen); + +void SetALPN(SSL* ssl, const std::string& alpn); + +std::string GetSSLOCSPResponse(SSL* ssl); + +bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length); + +std::unordered_multimap GetCertificateAltNames( + X509* cert); + +std::string GetCertificateCN(X509* cert); + +int VerifyPeerCertificate(SSL* ssl); + +int UseSNIContext(SSL* ssl, SecureContext* context); + +const char* GetClientHelloServerName(SSL* ssl); + +bool SetGroups(SecureContext* sc, const char* groups); + +const char* X509ErrorCode(int err); + +v8::Local GetCertificate(Environment* env, SSL* ssl); + +v8::Local GetCipherName( + Environment* env, + SSL* ssl); + +v8::Local GetCipherVersion( + Environment* env, + SSL* ssl); + +v8::Local GetEphemeralKey(Environment* env, SSL* ssl); + +v8::Local GetPeerCertificate( + Environment* env, + SSL* ssl, + bool abbreviated = false, + bool is_server = false); + +} // namespace crypto +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_CRYPTO_COMMON_H_ diff --git a/src/node_quic.cc b/src/node_quic.cc index 20b36639fe..91b2c14bb8 100755 --- a/src/node_quic.cc +++ b/src/node_quic.cc @@ -3,6 +3,7 @@ #include "env-inl.h" #include "histogram-inl.h" #include "node_crypto.h" // SecureContext +#include "node_crypto_common-inl.h" #include "node_process.h" #include "node_quic_crypto.h" #include "node_quic_session-inl.h" @@ -90,7 +91,7 @@ void QuicInitSecureContext(const FunctionCallbackInfo& args) { InitializeSecureContext(sc, side); // TODO(@jasnell): Throw a proper node.js error with code - if (!SetGroups(sc, *groups)) + if (!crypto::SetGroups(sc, *groups)) return env->ThrowError("Failed to set groups"); } } // namespace diff --git a/src/node_quic_crypto.cc b/src/node_quic_crypto.cc index 35c4208d77..6880fbdf48 100644 --- a/src/node_quic_crypto.cc +++ b/src/node_quic_crypto.cc @@ -1,6 +1,7 @@ #include "node_quic_crypto.h" #include "env-inl.h" #include "node_crypto.h" +#include "node_crypto_common-inl.h" #include "node_quic_session-inl.h" #include "node_quic_util.h" #include "node_url.h" @@ -123,102 +124,6 @@ bool GenerateRandData(uint8_t* buf, size_t len) { return true; } -Local GetClientHelloCiphers(QuicSession* session) { - const unsigned char* buf; - Environment* env = session->env(); - QuicCryptoContext* ctx = session->CryptoContext(); - size_t len = SSL_client_hello_get0_ciphers(**ctx, &buf); - std::vector> ciphers_array; - for (size_t n = 0; n < len; n += 2) { - const SSL_CIPHER* cipher = SSL_CIPHER_find(**ctx, buf); - buf += 2; - const char* cipher_name = SSL_CIPHER_get_name(cipher); - const char* cipher_version = SSL_CIPHER_get_version(cipher); - Local obj = Object::New(env->isolate()); - obj->Set( - env->context(), - env->name_string(), - OneByteString(env->isolate(), cipher_name)).FromJust(); - obj->Set( - env->context(), - env->version_string(), - OneByteString(env->isolate(), cipher_version)).FromJust(); - ciphers_array.push_back(obj); - } - return Array::New(env->isolate(), ciphers_array.data(), ciphers_array.size()); -} - -const char* GetClientHelloServerName(QuicSession* session) { - const unsigned char* buf; - size_t len; - size_t rem; - - QuicCryptoContext* ctx = session->CryptoContext(); - - if (!SSL_client_hello_get0_ext( - **ctx, - TLSEXT_TYPE_server_name, - &buf, - &rem) || rem <= 2) { - return nullptr; - } - - len = *(buf++) << 8; - len += *(buf++); - if (len + 2 != rem) - return nullptr; - rem = len; - - if (rem == 0 || *buf++ != TLSEXT_NAMETYPE_host_name) - return nullptr; - rem--; - if (rem <= 2) - return nullptr; - len = *(buf++) << 8; - len += *(buf++); - if (len + 2 > rem) - return nullptr; - rem = len; - return reinterpret_cast(buf); -} - -const char* GetClientHelloALPN(QuicSession* session) { - const unsigned char* buf; - size_t len; - size_t rem; - - QuicCryptoContext* ctx = session->CryptoContext(); - - if (!SSL_client_hello_get0_ext( - **ctx, - TLSEXT_TYPE_application_layer_protocol_negotiation, - &buf, &rem) || rem < 2) { - return nullptr; - } - - len = (buf[0] << 8) | buf[1]; - if (len + 2 != rem) - return nullptr; - buf += 3; - return reinterpret_cast(buf); -} - -int UseSNIContext(SSL* ssl, crypto::SecureContext* context) { - SSL_CTX* ctx = context->ctx_.get(); - X509* x509 = SSL_CTX_get0_certificate(ctx); - EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); - STACK_OF(X509)* chain; - - int err = SSL_CTX_get0_chain_certs(ctx, &chain); - if (err) - err = SSL_use_certificate(ssl, x509); - if (err) - err = SSL_use_PrivateKey(ssl, pkey); - if (err && chain != nullptr) - err = SSL_set1_chain(ssl, chain); - return err; -} - // The Retry Token is an encrypted token that is sent to the client // by the server as part of the path validation flow. The plaintext // format within the token is opaque and only meaningful the server. @@ -360,86 +265,7 @@ bool InvalidRetryToken( return false; } -int VerifyPeerCertificate(SSL* ssl) { - int err = X509_V_ERR_UNSPECIFIED; - if (X509* peer_cert = SSL_get_peer_certificate(ssl)) { - X509_free(peer_cert); - err = SSL_get_verify_result(ssl); - } - return err; -} - namespace { -std::string GetCertificateCN(X509* cert) { - X509_NAME* subject = X509_get_subject_name(cert); - if (subject != nullptr) { - int nid = OBJ_txt2nid("CN"); - int idx = X509_NAME_get_index_by_NID(subject, nid, -1); - if (idx != -1) { - X509_NAME_ENTRY* cn = X509_NAME_get_entry(subject, idx); - if (cn != nullptr) { - ASN1_STRING* cn_str = X509_NAME_ENTRY_get_data(cn); - if (cn_str != nullptr) { - return std::string(reinterpret_cast( - ASN1_STRING_get0_data(cn_str))); - } - } - } - } - return std::string(); -} - -std::unordered_multimap GetCertificateAltNames( - X509* cert) { - std::unordered_multimap map; - crypto::BIOPointer bio(BIO_new(BIO_s_mem())); - BUF_MEM* mem; - int idx = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); - if (idx < 0) // There is no subject alt name - return map; - - X509_EXTENSION* ext = X509_get_ext(cert, idx); - CHECK_NOT_NULL(ext); - const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext); - CHECK_EQ(method, X509V3_EXT_get_nid(NID_subject_alt_name)); - - GENERAL_NAMES* names = static_cast(X509V3_EXT_d2i(ext)); - if (names == nullptr) // There are no names - return map; - - for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { - USE(BIO_reset(bio.get())); - GENERAL_NAME* gen = sk_GENERAL_NAME_value(names, i); - if (gen->type == GEN_DNS) { - ASN1_IA5STRING* name = gen->d.dNSName; - BIO_write(bio.get(), name->data, name->length); - BIO_get_mem_ptr(bio.get(), &mem); - map.emplace("dns", std::string(mem->data, mem->length)); - } else { - STACK_OF(CONF_VALUE)* nval = i2v_GENERAL_NAME( - const_cast(method), gen, nullptr); - if (nval == nullptr) - continue; - X509V3_EXT_val_prn(bio.get(), nval, 0, 0); - sk_CONF_VALUE_pop_free(nval, X509V3_conf_free); - BIO_get_mem_ptr(bio.get(), &mem); - std::string value(mem->data, mem->length); - if (value.compare(0, 11, "IP Address:") == 0) { - map.emplace("ip", value.substr(11)); - } else if (value.compare(0, 4, "URI:") == 0) { - url::URL url(value.substr(4)); - if (url.flags() & url::URL_FLAGS_CANNOT_BE_BASE || - url.flags() & url::URL_FLAGS_FAILED) { - continue; // Skip this one - } - map.emplace("uri", url.host()); - } - } - } - sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); - bio.reset(); - return map; -} bool SplitHostname( const char* hostname, @@ -533,55 +359,8 @@ bool CheckCertNames( return true; } - -const char* X509ErrorCode(int err) { - const char* code = "UNSPECIFIED"; -#define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; - switch (err) { - CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT) - CASE_X509_ERR(UNABLE_TO_GET_CRL) - CASE_X509_ERR(UNABLE_TO_DECRYPT_CERT_SIGNATURE) - CASE_X509_ERR(UNABLE_TO_DECRYPT_CRL_SIGNATURE) - CASE_X509_ERR(UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY) - CASE_X509_ERR(CERT_SIGNATURE_FAILURE) - CASE_X509_ERR(CRL_SIGNATURE_FAILURE) - CASE_X509_ERR(CERT_NOT_YET_VALID) - CASE_X509_ERR(CERT_HAS_EXPIRED) - CASE_X509_ERR(CRL_NOT_YET_VALID) - CASE_X509_ERR(CRL_HAS_EXPIRED) - CASE_X509_ERR(ERROR_IN_CERT_NOT_BEFORE_FIELD) - CASE_X509_ERR(ERROR_IN_CERT_NOT_AFTER_FIELD) - CASE_X509_ERR(ERROR_IN_CRL_LAST_UPDATE_FIELD) - CASE_X509_ERR(ERROR_IN_CRL_NEXT_UPDATE_FIELD) - CASE_X509_ERR(OUT_OF_MEM) - CASE_X509_ERR(DEPTH_ZERO_SELF_SIGNED_CERT) - CASE_X509_ERR(SELF_SIGNED_CERT_IN_CHAIN) - CASE_X509_ERR(UNABLE_TO_GET_ISSUER_CERT_LOCALLY) - CASE_X509_ERR(UNABLE_TO_VERIFY_LEAF_SIGNATURE) - CASE_X509_ERR(CERT_CHAIN_TOO_LONG) - CASE_X509_ERR(CERT_REVOKED) - CASE_X509_ERR(INVALID_CA) - CASE_X509_ERR(PATH_LENGTH_EXCEEDED) - CASE_X509_ERR(INVALID_PURPOSE) - CASE_X509_ERR(CERT_UNTRUSTED) - CASE_X509_ERR(CERT_REJECTED) - CASE_X509_ERR(HOSTNAME_MISMATCH) - } -#undef CASE_X509_ERR - return code; -} - } // namespace -Local GetValidationErrorReason(Environment* env, int err) { - const char* reason = X509_verify_cert_error_string(err); - return OneByteString(env->isolate(), reason); -} - -Local GetValidationErrorCode(Environment* env, int err) { - return OneByteString(env->isolate(), X509ErrorCode(err)); -} - int VerifyHostnameIdentity(SSL* ssl, const char* hostname) { int err = X509_V_ERR_HOSTNAME_MISMATCH; crypto::X509Pointer cert(SSL_get_peer_certificate(ssl)); @@ -624,8 +403,8 @@ int VerifyHostnameIdentity(SSL* ssl, const char* hostname) { // If we've made it this far, then we have to perform a more check return VerifyHostnameIdentity( hostname, - GetCertificateCN(cert.get()), - GetCertificateAltNames(cert.get())); + crypto::GetCertificateCN(cert.get()), + crypto::GetCertificateAltNames(cert.get())); } int VerifyHostnameIdentity( @@ -707,26 +486,6 @@ int VerifyHostnameIdentity( return err; } -const char* GetServerName(QuicSession* session) { - QuicCryptoContext* ctx = session->CryptoContext(); - return SSL_get_servername(**ctx, TLSEXT_NAMETYPE_host_name); -} - -// Get the SNI hostname requested by the client for the session -Local GetServerName( - Environment* env, - SSL* ssl, - const char* host_name) { - Local servername; - if (host_name != nullptr) { - servername = String::NewFromUtf8( - env->isolate(), - host_name, - v8::NewStringType::kNormal).ToLocalChecked(); - } - return servername; -} - // Get the ALPN protocol identifier that was negotiated for the session Local GetALPNProtocol(QuicSession* session) { Local alpn; @@ -744,155 +503,6 @@ Local GetALPNProtocol(QuicSession* session) { return alpn; } -Local GetCertificate(QuicSession* session) { - crypto::ClearErrorOnReturn clear_error_on_return; - QuicCryptoContext* ctx = session->CryptoContext(); - Local value = v8::Undefined(session->env()->isolate()); - X509* cert = SSL_get_certificate(**ctx); - if (cert != nullptr) - value = crypto::X509ToObject(session->env(), cert); - return value; -} - -Local GetEphemeralKey(QuicSession* session) { - Environment* env = session->env(); - Local context = env->context(); - - Local info = Object::New(env->isolate()); - QuicCryptoContext* ctx = session->CryptoContext(); - - EVP_PKEY* raw_key; - if (SSL_get_server_tmp_key(**ctx, &raw_key)) { - crypto::EVPKeyPointer key(raw_key); - int kid = EVP_PKEY_id(key.get()); - switch (kid) { - case EVP_PKEY_DH: - info->Set(context, env->type_string(), - FIXED_ONE_BYTE_STRING(env->isolate(), "DH")).FromJust(); - info->Set(context, env->size_string(), - Integer::New(env->isolate(), EVP_PKEY_bits(key.get()))) - .FromJust(); - break; - case EVP_PKEY_EC: - case EVP_PKEY_X25519: - case EVP_PKEY_X448: - { - const char* curve_name; - if (kid == EVP_PKEY_EC) { - EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key.get()); - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - curve_name = OBJ_nid2sn(nid); - EC_KEY_free(ec); - } else { - curve_name = OBJ_nid2sn(kid); - } - info->Set(context, env->type_string(), - FIXED_ONE_BYTE_STRING( - env->isolate(), - "ECDH")).FromJust(); - info->Set(context, env->name_string(), - OneByteString( - env->isolate(), - curve_name)).FromJust(); - info->Set(context, env->size_string(), - Integer::New( - env->isolate(), - EVP_PKEY_bits(key.get()))).FromJust(); - } - break; - default: - break; - } - } - return info; -} - -Local GetCipherName(QuicSession* session) { - Local cipher; - QuicCryptoContext* ctx = session->CryptoContext(); - const SSL_CIPHER* c = SSL_get_current_cipher(**ctx); - if (c != nullptr) { - const char* cipher_name = SSL_CIPHER_get_name(c); - cipher = OneByteString(session->env()->isolate(), cipher_name); - } - return cipher; -} - -Local GetCipherVersion(QuicSession* session) { - Local version; - QuicCryptoContext* ctx = session->CryptoContext(); - const SSL_CIPHER* c = SSL_get_current_cipher(**ctx); - if (c != nullptr) { - const char* cipher_version = SSL_CIPHER_get_version(c); - version = OneByteString(session->env()->isolate(), cipher_version); - } - return version; -} - -bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length) { - crypto::SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); - return s != nullptr && SSL_set_session(ssl, s.get()) == 1; -} - -std::string GetSSLOCSPResponse(SSL* ssl) { - const unsigned char* resp; - int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); - if (len < 0) len = 0; - return std::string(reinterpret_cast(resp), len); -} - -Local GetPeerCertificate( - QuicSession* session, - bool abbreviated) { - crypto::ClearErrorOnReturn clear_error_on_return; - - QuicCryptoContext* ctx = session->CryptoContext(); - - Local result = v8::Undefined(session->env()->isolate()); - Local issuer_chain; - - // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` - // contains the `peer_certificate`, but on server it doesn't. - crypto::X509Pointer cert( - session->IsServer() ? SSL_get_peer_certificate(**ctx) : nullptr); - STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(**ctx); - if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) - return result; - - // Short result requested. - if (abbreviated) { - return - crypto::X509ToObject( - session->env(), - cert ? cert.get() : sk_X509_value(ssl_certs, 0)); - } - - if (auto peer_certs = crypto::CloneSSLCerts(std::move(cert), ssl_certs)) { - // First and main certificate. - crypto::X509Pointer cert(sk_X509_value(peer_certs.get(), 0)); - CHECK(cert); - result = crypto::X509ToObject(session->env(), cert.release()); - - Local issuer_chain = - crypto::GetLastIssuedCert( - &cert, - **ctx, - crypto::AddIssuerChainToObject( - &cert, - result.As(), - std::move(peer_certs), - session->env()), - session->env()); - // Last certificate should be self-signed. - if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK) - USE(issuer_chain->Set( - session->env()->context(), - session->env()->issuercert_string(), - issuer_chain)); - } - return result; -} - namespace { int CertCB(SSL* ssl, void* arg) { QuicSession* session = static_cast(arg); @@ -1004,24 +614,6 @@ int SendAlert( return 1; } -void SetALPN(SSL* ssl, const std::string& alpn) { - SSL_set_alpn_protos( - ssl, - reinterpret_cast(alpn.c_str()), - alpn.length()); -} - -void SetHostname(SSL* ssl, const std::string& hostname) { - // TODO(@jasnell): Need to determine if setting localhost - // here is the right thing to do. - if (hostname.length() == 0 || - SocketAddress::numeric_host(hostname.c_str())) { - SSL_set_tlsext_host_name(ssl, "localhost"); - } else { - SSL_set_tlsext_host_name(ssl, hostname.c_str()); - } -} - bool SetTransportParams(ngtcp2_conn* connection, SSL* ssl) { ngtcp2_transport_params params; ngtcp2_conn_get_local_transport_params(connection, ¶ms); @@ -1035,30 +627,24 @@ bool SetTransportParams(ngtcp2_conn* connection, SSL* ssl) { SSL_set_quic_transport_params(ssl, buf.data(), nwrite) == 1; } -void LogSecret( - SSL* ssl, - const char* name, - const unsigned char* secret, - size_t secretlen) { - if (auto keylog_cb = SSL_CTX_get_keylog_callback(SSL_get_SSL_CTX(ssl))) { - unsigned char crandom[32]; - if (SSL_get_client_random(ssl, crandom, 32) != 32) - return; - std::string line = name; - line += " " + StringBytes::hex_encode( - reinterpret_cast(crandom), 32); - line += " " + StringBytes::hex_encode( - reinterpret_cast(secret), secretlen); - keylog_cb(ssl, line.c_str()); - } -} - SSL_QUIC_METHOD quic_method = SSL_QUIC_METHOD{ SetEncryptionSecrets, AddHandshakeData, FlushFlight, SendAlert }; + +void SetHostname(SSL* ssl, const std::string& hostname) { + // TODO(@jasnell): Need to determine if setting localhost + // here is the right thing to do. + if (hostname.length() == 0 || + SocketAddress::numeric_host(hostname.c_str())) { + SSL_set_tlsext_host_name(ssl, "localhost"); + } else { + SSL_set_tlsext_host_name(ssl, hostname.c_str()); + } +} + } // namespace void InitializeTLS(QuicSession* session) { @@ -1077,7 +663,7 @@ void InitializeTLS(QuicSession* session) { switch (ctx->Side()) { case NGTCP2_CRYPTO_SIDE_CLIENT: { SSL_set_connect_state(**ctx); - SetALPN(**ctx, session->GetALPN()); + crypto::SetALPN(**ctx, session->GetALPN()); SetHostname(**ctx, session->GetHostname()); if (ctx->IsOptionSet(QUICCLIENTSESSION_OPTION_REQUEST_OCSP)) SSL_set_tlsext_status_type(**ctx, TLSEXT_STATUSTYPE_ocsp); @@ -1100,10 +686,6 @@ void InitializeTLS(QuicSession* session) { SetTransportParams(session->Connection(), **ctx); } -bool SetGroups(crypto::SecureContext* sc, const char* groups) { - return SSL_CTX_set1_groups_list(**sc, groups) == 1; -} - void InitializeSecureContext( crypto::SecureContext* sc, ngtcp2_crypto_side side) { @@ -1173,31 +755,31 @@ bool SetCryptoSecrets( switch (level) { case NGTCP2_CRYPTO_LEVEL_EARLY: - LogSecret( + crypto::LogSecret( **ctx, QUIC_CLIENT_EARLY_TRAFFIC_SECRET, rx_secret, secretlen); break; case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: - LogSecret( + crypto::LogSecret( **ctx, QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET, rx_secret, secretlen); - LogSecret( + crypto::LogSecret( **ctx, QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET, tx_secret, secretlen); break; case NGTCP2_CRYPTO_LEVEL_APP: - LogSecret( + crypto::LogSecret( **ctx, QUIC_CLIENT_TRAFFIC_SECRET_0, rx_secret, secretlen); - LogSecret( + crypto::LogSecret( **ctx, QUIC_SERVER_TRAFFIC_SECRET_0, tx_secret, diff --git a/src/node_quic_crypto.h b/src/node_quic_crypto.h index a158b81653..c5424cbc81 100644 --- a/src/node_quic_crypto.h +++ b/src/node_quic_crypto.h @@ -33,10 +33,6 @@ bool SetCryptoSecrets( const uint8_t* tx_secret, size_t secretlen); -// Called by QuicInitSecureContext in node_quic.cc -// to set the TLS groups for the context. -bool SetGroups(crypto::SecureContext* sc, const char* groups); - // Called by QuicInitSecureContext to initialize the // given SecureContext with the defaults for the given // QUIC side (client or server). @@ -67,17 +63,6 @@ bool UpdateKey( std::vector* current_rx_secret, std::vector* current_tx_secret); -// Get the server name identified in the client hello -const char* GetClientHelloServerName(QuicSession* session); - -// Get the alpn protocol identified in the client hello -const char* GetClientHelloALPN(QuicSession* session); - -const char* GetServerName(QuicSession* session); - -// Replaces the SecureContext to be used in the handshake. -int UseSNIContext(SSL* ssl, crypto::SecureContext* context); - bool GenerateRetryToken( uint8_t* token, size_t* tokenlen, @@ -93,50 +78,15 @@ bool InvalidRetryToken( std::array* token_secret, uint64_t verification_expiration); -// Called by QuicSession::VerifyPeerIdentity to perform basic -// validation checks against the peer provided certificate. -int VerifyPeerCertificate(SSL* ssl); - int VerifyHostnameIdentity(SSL* ssl, const char* hostname); int VerifyHostnameIdentity( const char* hostname, const std::string& cert_cn, const std::unordered_multimap& altnames); -v8::Local GetValidationErrorReason(Environment* env, int err); -v8::Local GetValidationErrorCode(Environment* env, int err); - -v8::Local GetCertificate(QuicSession* session); -v8::Local GetEphemeralKey(QuicSession* session); - -bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length); - -std::string GetSSLOCSPResponse(SSL* ssl); - -// Get the SNI hostname requested by the client for the session -v8::Local GetServerName( - Environment* env, - SSL* ssl, - const char* host_name); - -// Get the list of cipher algorithms advertised in the client hello -v8::Local GetClientHelloCiphers(QuicSession* Session); - // Get the ALPN protocol identifier that was negotiated for the session v8::Local GetALPNProtocol(QuicSession* session); -// Get the negotiated cipher name for the TLS session -v8::Local GetCipherName(QuicSession* session); - -// Get the negotiated cipher version for the TLS session -v8::Local GetCipherVersion(QuicSession* session); - -// Get a JavaScript rendering of the X509 certificate provided by the peer -// TODO(@jasnell): This currently only works for the Client side -v8::Local GetPeerCertificate( - QuicSession* session, - bool abbreviated); - } // namespace quic } // namespace node diff --git a/src/node_quic_session.cc b/src/node_quic_session.cc index 018622b808..00a891b112 100644 --- a/src/node_quic_session.cc +++ b/src/node_quic_session.cc @@ -1,6 +1,7 @@ #include "aliased_buffer.h" #include "debug_utils.h" #include "env-inl.h" +#include "node_crypto_common-inl.h" #include "ngtcp2/ngtcp2.h" #include "ngtcp2/ngtcp2_crypto.h" #include "ngtcp2/ngtcp2_crypto_openssl.h" @@ -279,7 +280,7 @@ void QuicCryptoContext::EnableTrace() { } std::string QuicCryptoContext::GetOCSPResponse() { - return GetSSLOCSPResponse(ssl()); + return crypto::GetSSLOCSPResponse(ssl()); } ngtcp2_crypto_level QuicCryptoContext::GetReadCryptoLevel() { @@ -352,13 +353,19 @@ int QuicCryptoContext::OnClientHello() { HandleScope scope(env->isolate()); Context::Scope context_scope(env->context()); - const char* alpn = GetClientHelloALPN(session_); - const char* server_name = GetClientHelloServerName(session_); + const char* alpn = + crypto::GetClientHelloALPN( + session_->CryptoContext()->ssl()); + const char* server_name = + crypto::GetClientHelloServerName( + session_->CryptoContext()->ssl()); Local argv[] = { Undefined(env->isolate()), Undefined(env->isolate()), - GetClientHelloCiphers(session_) + crypto::GetClientHelloCiphers( + session_->env(), + session_->CryptoContext()->ssl()) }; if (alpn != nullptr) { @@ -421,7 +428,9 @@ int QuicCryptoContext::OnOCSP() { Context::Scope context_scope(env->context()); Local servername_str; - const char* servername = GetServerName(session_); + const char* servername = + crypto::GetServerName( + session_->CryptoContext()->ssl()); Local argv[] = { servername == nullptr ? @@ -460,7 +469,7 @@ void QuicCryptoContext::OnOCSPDone( session_->state_[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 0; if (context != nullptr) { - int err = UseSNIContext(ssl(), context); + int err = crypto::UseSNIContext(ssl(), context); if (!err) { unsigned long err = ERR_get_error(); // NOLINT(runtime/int) if (!err) { @@ -598,7 +607,7 @@ void QuicCryptoContext::ResumeHandshake() { } bool QuicCryptoContext::SetSession(const unsigned char* data, size_t length) { - return SetTLSSession(ssl(), data, length); + return crypto::SetTLSSession(ssl(), data, length); } void QuicCryptoContext::SetTLSAlert(int err) { @@ -650,7 +659,7 @@ bool QuicCryptoContext::KeyUpdate( } int QuicCryptoContext::VerifyPeerIdentity(const char* hostname) { - int err = VerifyPeerCertificate(ssl()); + int err = crypto::VerifyPeerCertificate(ssl()); if (err) return err; @@ -1237,7 +1246,9 @@ void QuicSession::HandshakeCompleted() { Context::Scope context_scope(env()->context()); Local servername = Undefined(env()->isolate()); - const char* hostname = GetServerName(this); + const char* hostname = + crypto::GetServerName( + this->CryptoContext()->ssl()); if (hostname != nullptr) { servername = String::NewFromUtf8( @@ -1254,14 +1265,14 @@ void QuicSession::HandshakeCompleted() { Local argv[] = { servername, GetALPNProtocol(this), - GetCipherName(this), - GetCipherVersion(this), + crypto::GetCipherName(this->env(), this->CryptoContext()->ssl()), + crypto::GetCipherVersion(this->env(), this->CryptoContext()->ssl()), Integer::New(env()->isolate(), max_pktlen_), err != 0 ? - GetValidationErrorReason(env(), err) : + crypto::GetValidationErrorReason(env(), err) : v8::Undefined(env()->isolate()).As(), err != 0 ? - GetValidationErrorCode(env(), err) : + crypto::GetValidationErrorCode(env(), err) : v8::Undefined(env()->isolate()).As() }; @@ -3041,21 +3052,24 @@ void QuicSessionDestroy(const FunctionCallbackInfo& args) { session->Destroy(); } -// TODO(@jasnell): Consolidate shared code with node_crypto void QuicSessionGetEphemeralKeyInfo(const FunctionCallbackInfo& args) { QuicSession* session; ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - return args.GetReturnValue().Set(GetEphemeralKey(session)); + return args.GetReturnValue().Set( + crypto::GetEphemeralKey( + session->env(), + session->CryptoContext()->ssl())); } -// TODO(@jasnell): Consolidate with shared code in node_crypto void QuicSessionGetPeerCertificate(const FunctionCallbackInfo& args) { QuicSession* session; ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); args.GetReturnValue().Set( - GetPeerCertificate( - session, - !args[0]->IsTrue())); + crypto::GetPeerCertificate( + session->env(), + session->CryptoContext()->ssl(), + !args[0]->IsTrue(), + session->IsServer())); } void QuicSessionGetRemoteAddress( @@ -3068,12 +3082,14 @@ void QuicSessionGetRemoteAddress( AddressToJS(env, **session->GetRemoteAddress(), args[0].As())); } -// TODO(@jasnell): Reconcile with shared code in node_crypto void QuicSessionGetCertificate( const FunctionCallbackInfo& args) { QuicSession* session; ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); - args.GetReturnValue().Set(GetCertificate(session)); + args.GetReturnValue().Set( + crypto::GetCertificate( + session->env(), + session->CryptoContext()->ssl())); } void QuicSessionPing(const FunctionCallbackInfo& args) { From 41568200a8a1a8483c4154717fdc34501e3a2585 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 27 Nov 2019 14:56:03 -0800 Subject: [PATCH 5/6] src: use node_crypto_common for node_crypto --- src/node_crypto.cc | 128 ++++------------------------------- src/node_crypto_common-inl.h | 15 ++-- src/node_crypto_common.h | 6 +- src/node_quic_session.cc | 4 +- 4 files changed, 28 insertions(+), 125 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index bae09d65d6..c498d0af65 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -24,6 +24,7 @@ #include "node_crypto_bio.h" #include "node_crypto_clienthello-inl.h" #include "node_crypto_groups.h" +#include "node_crypto_common-inl.h" #include "node_errors.h" #include "node_mutex.h" #include "node_process.h" @@ -2270,46 +2271,12 @@ void SSLWrap::GetPeerCertificate( const FunctionCallbackInfo& args) { Base* w; ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->ssl_env(); - - ClearErrorOnReturn clear_error_on_return; - - Local result; - // Used to build the issuer certificate chain. - Local issuer_chain; - - // NOTE: This is because of the odd OpenSSL behavior. On client `cert_chain` - // contains the `peer_certificate`, but on server it doesn't. - X509Pointer cert( - w->is_server() ? SSL_get_peer_certificate(w->ssl_.get()) : nullptr); - STACK_OF(X509)* ssl_certs = SSL_get_peer_cert_chain(w->ssl_.get()); - if (!cert && (ssl_certs == nullptr || sk_X509_num(ssl_certs) == 0)) - goto done; - - // Short result requested. - if (args.Length() < 1 || !args[0]->IsTrue()) { - result = X509ToObject(env, cert ? cert.get() : sk_X509_value(ssl_certs, 0)); - goto done; - } - - if (auto peer_certs = CloneSSLCerts(std::move(cert), ssl_certs)) { - // First and main certificate. - X509Pointer cert(sk_X509_value(peer_certs.get(), 0)); - CHECK(cert); - result = X509ToObject(env, cert.release()); - - issuer_chain = - AddIssuerChainToObject(&cert, result, std::move(peer_certs), env); - issuer_chain = GetLastIssuedCert(&cert, w->ssl_.get(), issuer_chain, env); - // Last certificate should be self-signed. - if (X509_check_issued(cert.get(), cert.get()) == X509_V_OK) - issuer_chain->Set(env->context(), - env->issuercert_string(), - issuer_chain).Check(); - } - - done: - args.GetReturnValue().Set(result); + args.GetReturnValue().Set( + GetPeerCert( + w->ssl_env(), + w->ssl_.get(), + args.Length() < 1 || !args[0]->IsTrue(), + w->is_server())); } @@ -2318,18 +2285,7 @@ void SSLWrap::GetCertificate( const FunctionCallbackInfo& args) { Base* w; ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder()); - Environment* env = w->ssl_env(); - - ClearErrorOnReturn clear_error_on_return; - - Local result; - - X509* cert = SSL_get_certificate(w->ssl_.get()); - - if (cert != nullptr) - result = X509ToObject(env, cert); - - args.GetReturnValue().Set(result); + args.GetReturnValue().Set(GetCert(w->ssl_env(), w->ssl_.get())); } @@ -2416,14 +2372,8 @@ void SSLWrap::SetSession(const FunctionCallbackInfo& args) { ArrayBufferViewContents sbuf(args[0].As()); const unsigned char* p = sbuf.data(); - SSLSessionPointer sess(d2i_SSL_SESSION(nullptr, &p, sbuf.length())); - - if (sess == nullptr) - return; - - int r = SSL_set_session(w->ssl_.get(), sess.get()); - if (!r) + if (!SetTLSSession(w->ssl_.get(), p, sbuf.length())) return env->ThrowError("SSL_set_session error"); } @@ -2547,51 +2497,8 @@ void SSLWrap::GetEphemeralKeyInfo( if (w->is_server()) return args.GetReturnValue().SetNull(); - Local info = Object::New(env->isolate()); - - EVP_PKEY* raw_key; - if (SSL_get_server_tmp_key(w->ssl_.get(), &raw_key)) { - EVPKeyPointer key(raw_key); - int kid = EVP_PKEY_id(key.get()); - switch (kid) { - case EVP_PKEY_DH: - info->Set(context, env->type_string(), - FIXED_ONE_BYTE_STRING(env->isolate(), "DH")).Check(); - info->Set(context, env->size_string(), - Integer::New(env->isolate(), EVP_PKEY_bits(key.get()))) - .Check(); - break; - case EVP_PKEY_EC: - case EVP_PKEY_X25519: - case EVP_PKEY_X448: - { - const char* curve_name; - if (kid == EVP_PKEY_EC) { - EC_KEY* ec = EVP_PKEY_get1_EC_KEY(key.get()); - int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - curve_name = OBJ_nid2sn(nid); - EC_KEY_free(ec); - } else { - curve_name = OBJ_nid2sn(kid); - } - info->Set(context, env->type_string(), - FIXED_ONE_BYTE_STRING(env->isolate(), "ECDH")).Check(); - info->Set(context, env->name_string(), - OneByteString(args.GetIsolate(), - curve_name)).Check(); - info->Set(context, env->size_string(), - Integer::New(env->isolate(), - EVP_PKEY_bits(key.get()))).Check(); - } - break; - default: - break; - } - } - // TODO(@sam-github) semver-major: else return ThrowCryptoError(env, - // ERR_get_error()) - - return args.GetReturnValue().Set(info); + return args.GetReturnValue().Set( + crypto::GetEphemeralKey(env, w->ssl_.get())); } @@ -2621,11 +2528,8 @@ void SSLWrap::VerifyError(const FunctionCallbackInfo& args) { // peer certificate is questionable but it's compatible with what was // here before. long x509_verify_error = // NOLINT(runtime/int) - X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; - if (X509* peer_cert = SSL_get_peer_certificate(w->ssl_.get())) { - X509_free(peer_cert); - x509_verify_error = SSL_get_verify_result(w->ssl_.get()); - } + VerifyPeerCertificate(w->ssl_.get(), + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); if (x509_verify_error == X509_V_OK) return args.GetReturnValue().SetNull(); @@ -2687,12 +2591,10 @@ void SSLWrap::GetCipher(const FunctionCallbackInfo& args) { return; Local info = Object::New(env->isolate()); - const char* cipher_name = SSL_CIPHER_get_name(c); info->Set(context, env->name_string(), - OneByteString(args.GetIsolate(), cipher_name)).Check(); - const char* cipher_version = SSL_CIPHER_get_version(c); + GetCipherName(env, w->ssl_.get())).Check(); info->Set(context, env->version_string(), - OneByteString(args.GetIsolate(), cipher_version)).Check(); + GetCipherVersion(env, w->ssl_.get())).Check(); args.GetReturnValue().Set(info); } diff --git a/src/node_crypto_common-inl.h b/src/node_crypto_common-inl.h index 3fcd7fb0fc..516e6b888b 100644 --- a/src/node_crypto_common-inl.h +++ b/src/node_crypto_common-inl.h @@ -5,6 +5,7 @@ #include "node_crypto.h" #include "node_crypto_common.h" #include "node.h" +#include "node_internals.h" #include "node_url.h" #include "string_bytes.h" #include "v8.h" @@ -46,10 +47,10 @@ inline void LogSecret( } inline void SetALPN(SSL* ssl, const std::string& alpn) { - SSL_set_alpn_protos( + CHECK_EQ(SSL_set_alpn_protos( ssl, reinterpret_cast(alpn.c_str()), - alpn.length()); + alpn.length()), 0); } inline std::string GetSSLOCSPResponse(SSL* ssl) { @@ -60,7 +61,7 @@ inline std::string GetSSLOCSPResponse(SSL* ssl) { } inline bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length) { - crypto::SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); + SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); return s != nullptr && SSL_set_session(ssl, s.get()) == 1; } @@ -135,8 +136,8 @@ inline std::string GetCertificateCN(X509* cert) { return std::string(); } -inline int VerifyPeerCertificate(SSL* ssl) { - int err = X509_V_ERR_UNSPECIFIED; +inline int VerifyPeerCertificate(SSL* ssl, int def) { + int err = def; if (X509* peer_cert = SSL_get_peer_certificate(ssl)) { X509_free(peer_cert); err = SSL_get_verify_result(ssl); @@ -289,7 +290,7 @@ inline Local GetValidationErrorCode(Environment* env, int err) { return OneByteString(env->isolate(), X509ErrorCode(err)); } -inline Local GetCertificate(Environment* env, SSL* ssl) { +inline Local GetCert(Environment* env, SSL* ssl) { ClearErrorOnReturn clear_error_on_return; Local value = v8::Undefined(env->isolate()); X509* cert = SSL_get_certificate(ssl); @@ -369,7 +370,7 @@ inline Local GetEphemeralKey(Environment* env, SSL* ssl) { return info; } -inline Local GetPeerCertificate( +inline Local GetPeerCert( Environment* env, SSL* ssl, bool abbreviated, diff --git a/src/node_crypto_common.h b/src/node_crypto_common.h index ac8e1b5d20..f674507e4d 100644 --- a/src/node_crypto_common.h +++ b/src/node_crypto_common.h @@ -31,7 +31,7 @@ std::unordered_multimap GetCertificateAltNames( std::string GetCertificateCN(X509* cert); -int VerifyPeerCertificate(SSL* ssl); +int VerifyPeerCertificate(SSL* ssl, int def = X509_V_ERR_UNSPECIFIED); int UseSNIContext(SSL* ssl, SecureContext* context); @@ -41,7 +41,7 @@ bool SetGroups(SecureContext* sc, const char* groups); const char* X509ErrorCode(int err); -v8::Local GetCertificate(Environment* env, SSL* ssl); +v8::Local GetCert(Environment* env, SSL* ssl); v8::Local GetCipherName( Environment* env, @@ -53,7 +53,7 @@ v8::Local GetCipherVersion( v8::Local GetEphemeralKey(Environment* env, SSL* ssl); -v8::Local GetPeerCertificate( +v8::Local GetPeerCert( Environment* env, SSL* ssl, bool abbreviated = false, diff --git a/src/node_quic_session.cc b/src/node_quic_session.cc index 00a891b112..38ba0066eb 100644 --- a/src/node_quic_session.cc +++ b/src/node_quic_session.cc @@ -3065,7 +3065,7 @@ void QuicSessionGetPeerCertificate(const FunctionCallbackInfo& args) { QuicSession* session; ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); args.GetReturnValue().Set( - crypto::GetPeerCertificate( + crypto::GetPeerCert( session->env(), session->CryptoContext()->ssl(), !args[0]->IsTrue(), @@ -3087,7 +3087,7 @@ void QuicSessionGetCertificate( QuicSession* session; ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); args.GetReturnValue().Set( - crypto::GetCertificate( + crypto::GetCert( session->env(), session->CryptoContext()->ssl())); } From 2c16580910c842a160101e5acf36ae6b008d862c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 3 Dec 2019 17:43:37 -0800 Subject: [PATCH 6/6] src: move node_crypto_common-inl to cc --- node.gyp | 2 + src/node_crypto.cc | 2 +- ...pto_common-inl.h => node_crypto_common.cc} | 50 ++++++++----------- src/node_crypto_common.h | 22 +++++--- src/node_quic.cc | 2 +- src/node_quic_crypto.cc | 2 +- src/node_quic_session.cc | 2 +- 7 files changed, 42 insertions(+), 40 deletions(-) rename src/{node_crypto_common-inl.h => node_crypto_common.cc} (89%) diff --git a/node.gyp b/node.gyp index 23134d1056..62b393b7d2 100644 --- a/node.gyp +++ b/node.gyp @@ -801,12 +801,14 @@ [ 'node_use_openssl=="true"', { 'sources': [ 'src/node_crypto.cc', + 'src/node_crypto_common.cc', 'src/node_crypto_bio.cc', 'src/node_crypto_clienthello.cc', 'src/node_crypto.h', 'src/node_crypto_bio.h', 'src/node_crypto_clienthello.h', 'src/node_crypto_clienthello-inl.h', + 'src/node_crypto_common.h', 'src/node_crypto_groups.h', 'src/tls_wrap.cc', 'src/tls_wrap.h', diff --git a/src/node_crypto.cc b/src/node_crypto.cc index c498d0af65..6d91b57c2e 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -24,7 +24,7 @@ #include "node_crypto_bio.h" #include "node_crypto_clienthello-inl.h" #include "node_crypto_groups.h" -#include "node_crypto_common-inl.h" +#include "node_crypto_common.h" #include "node_errors.h" #include "node_mutex.h" #include "node_process.h" diff --git a/src/node_crypto_common-inl.h b/src/node_crypto_common.cc similarity index 89% rename from src/node_crypto_common-inl.h rename to src/node_crypto_common.cc index 516e6b888b..7ad1337ae8 100644 --- a/src/node_crypto_common-inl.h +++ b/src/node_crypto_common.cc @@ -1,6 +1,3 @@ -#ifndef SRC_NODE_CRYPTO_COMMON_INL_H_ -#define SRC_NODE_CRYPTO_COMMON_INL_H_ - #include "env-inl.h" #include "node_crypto.h" #include "node_crypto_common.h" @@ -28,7 +25,7 @@ using v8::Value; namespace crypto { -inline void LogSecret( +void LogSecret( SSL* ssl, const char* name, const unsigned char* secret, @@ -46,26 +43,27 @@ inline void LogSecret( } } -inline void SetALPN(SSL* ssl, const std::string& alpn) { +void SetALPN(SSL* ssl, const std::string& alpn) { CHECK_EQ(SSL_set_alpn_protos( ssl, reinterpret_cast(alpn.c_str()), alpn.length()), 0); } -inline std::string GetSSLOCSPResponse(SSL* ssl) { +std::string GetSSLOCSPResponse(SSL* ssl) { const unsigned char* resp; int len = SSL_get_tlsext_status_ocsp_resp(ssl, &resp); if (len < 0) len = 0; return std::string(reinterpret_cast(resp), len); } -inline bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length) { +bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length) { SSLSessionPointer s(d2i_SSL_SESSION(nullptr, &buf, length)); return s != nullptr && SSL_set_session(ssl, s.get()) == 1; } -inline std::unordered_multimap GetCertificateAltNames( +std::unordered_multimap +GetCertificateAltNames( X509* cert) { std::unordered_multimap map; crypto::BIOPointer bio(BIO_new(BIO_s_mem())); @@ -113,11 +111,10 @@ inline std::unordered_multimap GetCertificateAltNames( } } sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); - bio.reset(); return map; } -inline std::string GetCertificateCN(X509* cert) { +std::string GetCertificateCN(X509* cert) { X509_NAME* subject = X509_get_subject_name(cert); if (subject != nullptr) { int nid = OBJ_txt2nid("CN"); @@ -136,7 +133,7 @@ inline std::string GetCertificateCN(X509* cert) { return std::string(); } -inline int VerifyPeerCertificate(SSL* ssl, int def) { +int VerifyPeerCertificate(SSL* ssl, int def) { int err = def; if (X509* peer_cert = SSL_get_peer_certificate(ssl)) { X509_free(peer_cert); @@ -145,7 +142,7 @@ inline int VerifyPeerCertificate(SSL* ssl, int def) { return err; } -inline int UseSNIContext(SSL* ssl, SecureContext* context) { +int UseSNIContext(SSL* ssl, SecureContext* context) { SSL_CTX* ctx = context->ctx_.get(); X509* x509 = SSL_CTX_get0_certificate(ctx); EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); @@ -161,8 +158,7 @@ inline int UseSNIContext(SSL* ssl, SecureContext* context) { return err; } - -inline const char* GetClientHelloALPN(SSL* ssl) { +const char* GetClientHelloALPN(SSL* ssl) { const unsigned char* buf; size_t len; size_t rem; @@ -181,7 +177,7 @@ inline const char* GetClientHelloALPN(SSL* ssl) { return reinterpret_cast(buf); } -inline const char* GetClientHelloServerName(SSL* ssl) { +const char* GetClientHelloServerName(SSL* ssl) { const unsigned char* buf; size_t len; size_t rem; @@ -213,11 +209,11 @@ inline const char* GetClientHelloServerName(SSL* ssl) { return reinterpret_cast(buf); } -inline const char* GetServerName(SSL* ssl) { +const char* GetServerName(SSL* ssl) { return SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); } -inline Local GetClientHelloCiphers(Environment* env, SSL* ssl) { +Local GetClientHelloCiphers(Environment* env, SSL* ssl) { const unsigned char* buf; size_t len = SSL_client_hello_get0_ciphers(ssl, &buf); std::vector> ciphers_array; @@ -240,11 +236,11 @@ inline Local GetClientHelloCiphers(Environment* env, SSL* ssl) { return Array::New(env->isolate(), ciphers_array.data(), ciphers_array.size()); } -inline bool SetGroups(SecureContext* sc, const char* groups) { +bool SetGroups(SecureContext* sc, const char* groups) { return SSL_CTX_set1_groups_list(**sc, groups) == 1; } -inline const char* X509ErrorCode(int err) { +const char* X509ErrorCode(int err) { const char* code = "UNSPECIFIED"; #define CASE_X509_ERR(CODE) case X509_V_ERR_##CODE: code = #CODE; break; switch (err) { @@ -281,16 +277,16 @@ inline const char* X509ErrorCode(int err) { return code; } -inline Local GetValidationErrorReason(Environment* env, int err) { +Local GetValidationErrorReason(Environment* env, int err) { const char* reason = X509_verify_cert_error_string(err); return OneByteString(env->isolate(), reason); } -inline Local GetValidationErrorCode(Environment* env, int err) { +Local GetValidationErrorCode(Environment* env, int err) { return OneByteString(env->isolate(), X509ErrorCode(err)); } -inline Local GetCert(Environment* env, SSL* ssl) { +Local GetCert(Environment* env, SSL* ssl) { ClearErrorOnReturn clear_error_on_return; Local value = v8::Undefined(env->isolate()); X509* cert = SSL_get_certificate(ssl); @@ -299,7 +295,7 @@ inline Local GetCert(Environment* env, SSL* ssl) { return value; } -inline Local GetCipherName(Environment* env, SSL* ssl) { +Local GetCipherName(Environment* env, SSL* ssl) { Local cipher; const SSL_CIPHER* c = SSL_get_current_cipher(ssl); if (c != nullptr) { @@ -309,7 +305,7 @@ inline Local GetCipherName(Environment* env, SSL* ssl) { return cipher; } -inline Local GetCipherVersion(Environment* env, SSL* ssl) { +Local GetCipherVersion(Environment* env, SSL* ssl) { Local version; const SSL_CIPHER* c = SSL_get_current_cipher(ssl); if (c != nullptr) { @@ -319,7 +315,7 @@ inline Local GetCipherVersion(Environment* env, SSL* ssl) { return version; } -inline Local GetEphemeralKey(Environment* env, SSL* ssl) { +Local GetEphemeralKey(Environment* env, SSL* ssl) { Local context = env->context(); Local info = Object::New(env->isolate()); @@ -370,7 +366,7 @@ inline Local GetEphemeralKey(Environment* env, SSL* ssl) { return info; } -inline Local GetPeerCert( +Local GetPeerCert( Environment* env, SSL* ssl, bool abbreviated, @@ -419,5 +415,3 @@ inline Local GetPeerCert( } // namespace crypto } // namespace node - -#endif // SRC_NODE_CRYPTO_COMMON_INL_H_ diff --git a/src/node_crypto_common.h b/src/node_crypto_common.h index f674507e4d..cf77b1eeb3 100644 --- a/src/node_crypto_common.h +++ b/src/node_crypto_common.h @@ -26,8 +26,8 @@ std::string GetSSLOCSPResponse(SSL* ssl); bool SetTLSSession(SSL* ssl, const unsigned char* buf, size_t length); -std::unordered_multimap GetCertificateAltNames( - X509* cert); +std::unordered_multimap +GetCertificateAltNames(X509* cert); std::string GetCertificateCN(X509* cert); @@ -35,21 +35,27 @@ int VerifyPeerCertificate(SSL* ssl, int def = X509_V_ERR_UNSPECIFIED); int UseSNIContext(SSL* ssl, SecureContext* context); +const char* GetClientHelloALPN(SSL* ssl); + const char* GetClientHelloServerName(SSL* ssl); +const char* GetServerName(SSL* ssl); + +v8::Local GetClientHelloCiphers(Environment* env, SSL* ssl); + bool SetGroups(SecureContext* sc, const char* groups); const char* X509ErrorCode(int err); +v8::Local GetValidationErrorReason(Environment* env, int err); + +v8::Local GetValidationErrorCode(Environment* env, int err); + v8::Local GetCert(Environment* env, SSL* ssl); -v8::Local GetCipherName( - Environment* env, - SSL* ssl); +v8::Local GetCipherName(Environment* env, SSL* ssl); -v8::Local GetCipherVersion( - Environment* env, - SSL* ssl); +v8::Local GetCipherVersion(Environment* env, SSL* ssl); v8::Local GetEphemeralKey(Environment* env, SSL* ssl); diff --git a/src/node_quic.cc b/src/node_quic.cc index 91b2c14bb8..2237d7232b 100755 --- a/src/node_quic.cc +++ b/src/node_quic.cc @@ -3,7 +3,7 @@ #include "env-inl.h" #include "histogram-inl.h" #include "node_crypto.h" // SecureContext -#include "node_crypto_common-inl.h" +#include "node_crypto_common.h" #include "node_process.h" #include "node_quic_crypto.h" #include "node_quic_session-inl.h" diff --git a/src/node_quic_crypto.cc b/src/node_quic_crypto.cc index 6880fbdf48..c75cdc14fe 100644 --- a/src/node_quic_crypto.cc +++ b/src/node_quic_crypto.cc @@ -1,7 +1,7 @@ #include "node_quic_crypto.h" #include "env-inl.h" #include "node_crypto.h" -#include "node_crypto_common-inl.h" +#include "node_crypto_common.h" #include "node_quic_session-inl.h" #include "node_quic_util.h" #include "node_url.h" diff --git a/src/node_quic_session.cc b/src/node_quic_session.cc index 38ba0066eb..4f485f6ebf 100644 --- a/src/node_quic_session.cc +++ b/src/node_quic_session.cc @@ -1,7 +1,7 @@ #include "aliased_buffer.h" #include "debug_utils.h" #include "env-inl.h" -#include "node_crypto_common-inl.h" +#include "node_crypto_common.h" #include "ngtcp2/ngtcp2.h" #include "ngtcp2/ngtcp2_crypto.h" #include "ngtcp2/ngtcp2_crypto_openssl.h"