diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc index 6a2312821f3..799b9946eea 100644 --- a/iocore/net/QUICNetVConnection.cc +++ b/iocore/net/QUICNetVConnection.cc @@ -2236,8 +2236,10 @@ QUICNetVConnection::_setup_handshake_protocol(SSL_CTX *ctx) { // Initialize handshake protocol specific stuff // For QUICv1 TLS is the only option - QUICTLS *tls = new QUICTLS(this->_pp_key_info, ctx, this->direction(), this->options, this->_quic_config->session_file()); + QUICTLS *tls = new QUICTLS(this->_pp_key_info, ctx, this->direction(), this->options, this->_quic_config->client_session_file(), + this->_quic_config->client_keylog_file()); SSL_set_ex_data(tls->ssl_handle(), QUIC::ssl_quic_qc_index, static_cast(this)); + return tls; } diff --git a/iocore/net/quic/QUICConfig.cc b/iocore/net/quic/QUICConfig.cc index 0213cee9b1e..86e97070366 100644 --- a/iocore/net/quic/QUICConfig.cc +++ b/iocore/net/quic/QUICConfig.cc @@ -82,11 +82,17 @@ quic_init_client_ssl_ctx(const QUICConfigParams *params) } } - if (params->session_file() != nullptr) { + if (params->client_session_file() != nullptr) { SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE); SSL_CTX_sess_set_new_cb(ssl_ctx, QUIC::ssl_client_new_session); } +#ifdef SSL_MODE_QUIC_HACK + if (params->client_keylog_file() != nullptr) { + SSL_CTX_set_keylog_callback(ssl_ctx, QUIC::ssl_client_keylog_cb); + } +#endif + return ssl_ctx; } @@ -113,7 +119,8 @@ QUICConfigParams::initialize() REC_ReadConfigStringAlloc(this->_server_supported_groups, "proxy.config.quic.server.supported_groups"); REC_ReadConfigStringAlloc(this->_client_supported_groups, "proxy.config.quic.client.supported_groups"); - REC_ReadConfigStringAlloc(this->_session_file, "proxy.config.quic.client.session_file"); + REC_ReadConfigStringAlloc(this->_client_session_file, "proxy.config.quic.client.session_file"); + REC_ReadConfigStringAlloc(this->_client_keylog_file, "proxy.config.quic.client.keylog_file"); // Transport Parameters REC_EstablishStaticConfigInt32U(this->_no_activity_timeout_in, "proxy.config.quic.no_activity_timeout_in"); @@ -417,9 +424,15 @@ QUICConfigParams::scid_len() } const char * -QUICConfigParams::session_file() const +QUICConfigParams::client_session_file() const +{ + return this->_client_session_file; +} + +const char * +QUICConfigParams::client_keylog_file() const { - return _session_file; + return this->_client_keylog_file; } // diff --git a/iocore/net/quic/QUICConfig.h b/iocore/net/quic/QUICConfig.h index 4ab237a9b1b..c56d74f76bf 100644 --- a/iocore/net/quic/QUICConfig.h +++ b/iocore/net/quic/QUICConfig.h @@ -45,7 +45,8 @@ class QUICConfigParams : public ConfigInfo const char *server_supported_groups() const; const char *client_supported_groups() const; - const char *session_file() const; + const char *client_session_file() const; + const char *client_keylog_file() const; SSL_CTX *client_ssl_ctx() const; @@ -100,7 +101,8 @@ class QUICConfigParams : public ConfigInfo char *_server_supported_groups = nullptr; char *_client_supported_groups = nullptr; - char *_session_file = nullptr; + char *_client_session_file = nullptr; + char *_client_keylog_file = nullptr; SSL_CTX *_client_ssl_ctx = nullptr; diff --git a/iocore/net/quic/QUICGlobals.cc b/iocore/net/quic/QUICGlobals.cc index 0798020d165..661f816a57e 100644 --- a/iocore/net/quic/QUICGlobals.cc +++ b/iocore/net/quic/QUICGlobals.cc @@ -24,6 +24,7 @@ #include "QUICGlobals.h" #include +#include #include "P_SSLNextProtocolSet.h" @@ -85,6 +86,23 @@ QUIC::ssl_client_new_session(SSL *ssl, SSL_SESSION *session) return 0; } +void +QUIC::ssl_client_keylog_cb(const SSL *ssl, const char *line) +{ + QUICTLS *qtls = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_tls_index)); + const char *keylog_file = qtls->keylog_file(); + std::ofstream file(keylog_file, std::ios_base::app); + + if (!file.is_open()) { + QUICGlobalDebug("could not open keylog file: %s", keylog_file); + return; + } + + file.write(line, strlen(line)); + file.put('\n'); + file.flush(); +} + int QUIC::ssl_cert_cb(SSL *ssl, void * /*arg*/) { diff --git a/iocore/net/quic/QUICGlobals.h b/iocore/net/quic/QUICGlobals.h index cd4ac546f2a..0bbde28373d 100644 --- a/iocore/net/quic/QUICGlobals.h +++ b/iocore/net/quic/QUICGlobals.h @@ -34,6 +34,7 @@ class QUIC static int ssl_select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned inlen, void *); static int ssl_client_new_session(SSL *ssl, SSL_SESSION *session); + static void ssl_client_keylog_cb(const SSL *ssl, const char *line); static int ssl_cert_cb(SSL *ssl, void *arg); static int ssl_sni_cb(SSL *ssl, int *ad, void *arg); diff --git a/iocore/net/quic/QUICTLS.cc b/iocore/net/quic/QUICTLS.cc index de11e02ebd7..f03a5a7bac3 100644 --- a/iocore/net/quic/QUICTLS.cc +++ b/iocore/net/quic/QUICTLS.cc @@ -68,6 +68,12 @@ QUICTLS::session_file() const return this->_session_file; } +const char * +QUICTLS::keylog_file() const +{ + return this->_keylog_file; +} + QUICTLS::~QUICTLS() { SSL_free(this->_ssl); diff --git a/iocore/net/quic/QUICTLS.h b/iocore/net/quic/QUICTLS.h index 5a2ac563ba0..7bba45aff38 100644 --- a/iocore/net/quic/QUICTLS.h +++ b/iocore/net/quic/QUICTLS.h @@ -40,7 +40,7 @@ class QUICTLS : public QUICHandshakeProtocol { public: QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, - const NetVCOptions &netvc_options, const char *session_file = nullptr); + const NetVCOptions &netvc_options, const char *session_file = nullptr, const char *keylog_file = nullptr); ~QUICTLS(); // TODO: integrate with _early_data_processed @@ -58,6 +58,7 @@ class QUICTLS : public QUICHandshakeProtocol void set_remote_transport_parameters(std::shared_ptr tp) override; const char *session_file() const; + const char *keylog_file() const; // FIXME Should not exist SSL *ssl_handle(); @@ -79,8 +80,6 @@ class QUICTLS : public QUICHandshakeProtocol QUICKeyGenerator _keygen_for_server = QUICKeyGenerator(QUICKeyGenerator::Context::SERVER); const EVP_MD *_get_handshake_digest() const; - const char *_session_file; - int _read_early_data(); int _write_early_data(); int _handshake(QUICHandshakeMsgs *out, const QUICHandshakeMsgs *in); @@ -94,6 +93,8 @@ class QUICTLS : public QUICHandshakeProtocol void _print_km(const char *header, const uint8_t *key_for_hp, size_t key_for_hp_len, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *secret = nullptr, size_t secret_len = 0); + const char *_session_file = nullptr; + const char *_keylog_file = nullptr; SSL *_ssl = nullptr; NetVConnectionContext_t _netvc_context = NET_VCONNECTION_UNSET; bool _early_data_processed = false; diff --git a/iocore/net/quic/QUICTLS_openssl.cc b/iocore/net/quic/QUICTLS_openssl.cc index 94e1867307f..7f7c9613459 100644 --- a/iocore/net/quic/QUICTLS_openssl.cc +++ b/iocore/net/quic/QUICTLS_openssl.cc @@ -20,8 +20,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "QUICGlobals.h" -#include "QUICPacketProtectionKeyInfo.h" #include "QUICTLS.h" #include @@ -31,11 +29,21 @@ #include #include "QUICConfig.h" - +#include "QUICGlobals.h" #include "QUICDebugNames.h" +#include "QUICPacketProtectionKeyInfo.h" static constexpr char tag[] = "quic_tls"; +using namespace std::literals; + +static constexpr std::string_view QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_EARLY_TRAFFIC_SECRET"sv); +static constexpr std::string_view QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"sv); +static constexpr std::string_view QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL("QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"sv); +// TODO: support key update +static constexpr std::string_view QUIC_CLIENT_TRAFFIC_SECRET_LABEL("QUIC_CLIENT_TRAFFIC_SECRET_0"sv); +static constexpr std::string_view QUIC_SERVER_TRAFFIC_SECRET_LABEL("QUIC_SERVER_TRAFFIC_SECRET_0"sv); + static const char * content_type_str(int type) { @@ -139,6 +147,58 @@ msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, return; } +/** + This is very inspired from writting keylog format of ngtcp2's examples + https://github.com/ngtcp2/ngtcp2/blob/894ed23c970d61eede74f69d9178090af63fdf70/examples/keylog.cc + */ +static void +log_secret(SSL *ssl, int 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, sizeof(crandom)) != sizeof(crandom)) { + return; + } + uint8_t line[256] = {0}; + size_t len = 0; + switch (name) { + case SSL_KEY_CLIENT_EARLY_TRAFFIC: + memcpy(line, QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: + memcpy(line, QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: + memcpy(line, QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: + memcpy(line, QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data(), QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_CLIENT_TRAFFIC_SECRET_LABEL.size(); + break; + case SSL_KEY_SERVER_APPLICATION_TRAFFIC: + memcpy(line, QUIC_SERVER_TRAFFIC_SECRET_LABEL.data(), QUIC_SERVER_TRAFFIC_SECRET_LABEL.size()); + len += QUIC_SERVER_TRAFFIC_SECRET_LABEL.size(); + break; + + default: + return; + } + + line[len] = ' '; + ++len; + QUICDebug::to_hex(line + len, crandom, sizeof(crandom)); + len += sizeof(crandom) * 2; + line[len] = ' '; + ++len; + QUICDebug::to_hex(line + len, secret, secretlen); + + keylog_cb(ssl, reinterpret_cast(line)); + } +} + static int key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secret_len, void *arg) { @@ -149,6 +209,8 @@ key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secret_len, void QUICTLS *qtls = reinterpret_cast(arg); qtls->update_key_materials_on_key_cb(name, secret, secret_len); + log_secret(ssl, name, secret, secret_len); + return 1; } @@ -158,19 +220,19 @@ QUICTLS::update_key_materials_on_key_cb(int name, const uint8_t *secret, size_t if (is_debug_tag_set("vv_quic_crypto")) { switch (name) { case SSL_KEY_CLIENT_EARLY_TRAFFIC: - Debug("vv_quic_crypto", "client_early_traffic"); + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_EARLY_TRAFFIC_SECRET_LABEL.data()); break; case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - Debug("vv_quic_crypto", "client_handshake_traffic"); - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - Debug("vv_quic_crypto", "client_application_traffic"); + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); break; case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - Debug("vv_quic_crypto", "server_handshake_traffic"); + Debug("vv_quic_crypto", "%s", QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET_LABEL.data()); + break; + case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: + Debug("vv_quic_crypto", "%s", QUIC_CLIENT_TRAFFIC_SECRET_LABEL.data()); break; case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - Debug("vv_quic_crypto", "server_application_traffic"); + Debug("vv_quic_crypto", "%s", QUIC_SERVER_TRAFFIC_SECRET_LABEL.data()); break; default: break; @@ -329,8 +391,12 @@ QUICTLS::update_key_materials_on_key_cb(int name, const uint8_t *secret, size_t } QUICTLS::QUICTLS(QUICPacketProtectionKeyInfo &pp_key_info, SSL_CTX *ssl_ctx, NetVConnectionContext_t nvc_ctx, - const NetVCOptions &netvc_options, const char *session_file) - : QUICHandshakeProtocol(pp_key_info), _session_file(session_file), _ssl(SSL_new(ssl_ctx)), _netvc_context(nvc_ctx) + const NetVCOptions &netvc_options, const char *session_file, const char *keylog_file) + : QUICHandshakeProtocol(pp_key_info), + _session_file(session_file), + _keylog_file(keylog_file), + _ssl(SSL_new(ssl_ctx)), + _netvc_context(nvc_ctx) { ink_assert(this->_netvc_context != NET_VCONNECTION_UNSET); diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc index 982bb8a7388..5094613aa3c 100644 --- a/mgmt/RecordsConfig.cc +++ b/mgmt/RecordsConfig.cc @@ -1372,6 +1372,8 @@ static const RecordElement RecordsConfig[] = , {RECT_CONFIG, "proxy.config.quic.client.session_file", RECD_STRING, nullptr , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} , + {RECT_CONFIG, "proxy.config.quic.client.keylog_file", RECD_STRING, nullptr , RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL} + , // Transport Parameters {RECT_CONFIG, "proxy.config.quic.no_activity_timeout_in", RECD_INT, "30", RECU_DYNAMIC, RR_NULL, RECC_STR, "^-?[0-9]+$", RECA_NULL} ,