diff --git a/src/h2load.cc b/src/h2load.cc index 281b5410..e0f75596 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -1027,12 +1027,9 @@ int Client::connection_made() { if (next_proto) { auto proto = StringRef{next_proto, next_proto_len}; if (config.is_quic()) { - if (util::streq(StringRef{&NGTCP2_ALPN_H3[1]}, proto)) { - auto s = std::make_unique(this); - if (s->init_conn() == -1) { - return -1; - } - session = std::move(s); + assert(session); + if (!util::streq(StringRef{&NGTCP2_ALPN_H3[1]}, proto)) { + return -1; } } else if (util::check_h2_is_selected(proto)) { session = std::make_unique(this); @@ -1043,6 +1040,9 @@ int Client::connection_made() { // Just assign next_proto to selected_proto anyway to show the // negotiation result. selected_proto = proto.str(); + } else if (config.is_quic()) { + std::cerr << "QUIC requires ALPN negotiation" << std::endl; + return -1; } else { std::cout << "No protocol negotiated. Fallback behaviour may be activated" << std::endl; @@ -1778,79 +1778,6 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out, } // namespace #endif // !OPENSSL_NO_NEXTPROTONEG -namespace { -int quic_transport_params_add_cb(SSL *ssl, unsigned int ext_type, - unsigned int content, - const unsigned char **out, size_t *outlen, - X509 *x, size_t chainidx, int *al, - void *add_arg) { - auto c = static_cast(SSL_get_app_data(ssl)); - auto conn = c->quic.conn; - - ngtcp2_transport_params params; - - ngtcp2_conn_get_local_transport_params(conn, ¶ms); - - constexpr size_t bufsize = 128; - auto buf = std::make_unique(bufsize); - - auto nwrite = ngtcp2_encode_transport_params( - buf.get(), bufsize, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ¶ms); - if (nwrite < 0) { - std::cerr << "ngtcp2_encode_transport_params: " << ngtcp2_strerror(nwrite) - << std::endl; - *al = SSL_AD_INTERNAL_ERROR; - return -1; - } - - *out = buf.release(); - *outlen = static_cast(nwrite); - - return 1; -} -} // namespace - -namespace { -void quic_transport_params_free_cb(SSL *ssl, unsigned int ext_type, - unsigned int context, - const unsigned char *out, void *add_arg) { - delete[] const_cast(out); -} -} // namespace - -namespace { -int quic_transport_params_parse_cb(SSL *ssl, unsigned int ext_type, - unsigned int context, - const unsigned char *in, size_t inlen, - X509 *x, size_t chainidx, int *al, - void *parse_arg) { - auto c = static_cast(SSL_get_app_data(ssl)); - auto conn = c->quic.conn; - - int rv; - ngtcp2_transport_params params; - - rv = ngtcp2_decode_transport_params( - ¶ms, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, in, inlen); - if (rv != 0) { - std::cerr << "ngtcp2_decode_transport_params: " << ngtcp2_strerror(rv) - << std::endl; - *al = SSL_AD_ILLEGAL_PARAMETER; - return -1; - } - - rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); - if (rv != 0) { - std::cerr << "ngtcp2_conn_set_remote_transport_params: " - << ngtcp2_strerror(rv) << std::endl; - *al = SSL_AD_ILLEGAL_PARAMETER; - return -1; - } - - return 1; -} -} // namespace - namespace { constexpr char UNIX_PATH_PREFIX[] = "unix:"; } // namespace @@ -2250,8 +2177,6 @@ Options: } } // namespace -extern ngtcp2_crypto_ctx in_crypto_ctx; - int main(int argc, char **argv) { tls::libssl_init(); @@ -2753,20 +2678,6 @@ int main(int argc, char **argv) { if (config.is_quic()) { SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); - - SSL_CTX_clear_options(ssl_ctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT); - SSL_CTX_set_mode(ssl_ctx, SSL_MODE_QUIC_HACK); - - if (SSL_CTX_add_custom_ext( - ssl_ctx, NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS, - SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, - quic_transport_params_add_cb, quic_transport_params_free_cb, - nullptr, quic_transport_params_parse_cb, nullptr) != 1) { - std::cerr << "SSL_CTX_add_custom_ext(NGTCP2_TLSEXT_QUIC_TRANSPORT_" - "PARAMETERS) failed: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - exit(EXIT_FAILURE); - } } else if (nghttp2::tls::ssl_ctx_set_proto_versions( ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { @@ -2909,8 +2820,6 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } - ngtcp2_crypto_ctx_initial(&in_crypto_ctx); - resolve_host(); std::cout << "starting benchmark..." << std::endl; diff --git a/src/h2load.h b/src/h2load.h index 6d746b35..c65f3f0f 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -328,11 +328,6 @@ struct Client { ev_timer pkt_timer; ngtcp2_conn *conn; quic::Error last_error; - ngtcp2_crypto_level tx_crypto_level; - ngtcp2_crypto_level rx_crypto_level; - std::vector server_handshake; - size_t server_handshake_nread; - ngtcp2_crypto_ctx crypto_ctx; // Client never send CRYPTO in Short packet. std::array crypto; size_t max_pktlen; @@ -461,45 +456,21 @@ struct Client { void quic_close_connection(); int quic_setup_initial_crypto(); - int quic_client_initial(); int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level, const uint8_t *data, size_t datalen); int quic_handshake_completed(); - int quic_in_encrypt(uint8_t *dest, const uint8_t *plaintext, - size_t plaintextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, const uint8_t *ad, - size_t adlen); - int quic_in_decrypt(uint8_t *dest, const uint8_t *ciphertext, - size_t ciphertextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, const uint8_t *ad, - size_t adlen); - int quic_encrypt(uint8_t *dest, const uint8_t *plaintext, size_t plaintextlen, - const uint8_t *key, const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen); - int quic_decrypt(uint8_t *dest, const uint8_t *ciphertext, - size_t ciphertextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, const uint8_t *ad, - size_t adlen); - int quic_in_hp_mask(uint8_t *dest, const uint8_t *key, const uint8_t *sample); - int quic_hp_mask(uint8_t *dest, const uint8_t *key, const uint8_t *sample); int quic_recv_stream_data(int64_t stream_id, int fin, const uint8_t *data, size_t datalen); int quic_stream_close(int64_t stream_id, uint64_t app_error_code); int quic_stream_reset(int64_t stream_id, uint64_t app_error_code); int quic_extend_max_local_streams(); - int quic_tls_handshake(bool initial = false); - int quic_read_tls(); - - int quic_on_key(int name, const uint8_t *secret, size_t secretlen); + int quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen); void quic_set_tls_alert(uint8_t alert); - size_t quic_read_server_handshake(uint8_t *buf, size_t buflen); - int quic_write_server_handshake(ngtcp2_crypto_level crypto_level, - const uint8_t *data, size_t datalen); - void quic_write_client_handshake(const uint8_t *data, size_t datalen); - void quic_write_client_handshake(Crypto &crypto, const uint8_t *data, - size_t datalen); + void quic_write_client_handshake(ngtcp2_crypto_level level, + const uint8_t *data, size_t datalen); int quic_pkt_timeout(); void quic_restart_pkt_timer(); }; diff --git a/src/h2load_quic.cc b/src/h2load_quic.cc index 87c519b3..a010a682 100644 --- a/src/h2load_quic.cc +++ b/src/h2load_quic.cc @@ -27,7 +27,6 @@ #include #include -#include #include "h2load_http3_session.h" @@ -37,13 +36,11 @@ namespace { auto randgen = util::make_mt19937(); } // namespace -ngtcp2_crypto_ctx in_crypto_ctx; - namespace { int client_initial(ngtcp2_conn *conn, void *user_data) { auto c = static_cast(user_data); - if (c->quic_client_initial() != 0) { + if (c->quic_recv_crypto_data(NGTCP2_CRYPTO_LEVEL_INITIAL, nullptr, 0) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -51,13 +48,6 @@ int client_initial(ngtcp2_conn *conn, void *user_data) { } } // namespace -int Client::quic_client_initial() { - if (quic_tls_handshake(true) != 0) { - return -1; - } - return 0; -} - namespace { int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset, const uint8_t *data, size_t datalen, @@ -74,20 +64,8 @@ int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, int Client::quic_recv_crypto_data(ngtcp2_crypto_level crypto_level, const uint8_t *data, size_t datalen) { - if (quic_write_server_handshake(crypto_level, data, datalen) != 0) { - return -1; - } - - if (!ngtcp2_conn_get_handshake_completed(quic.conn)) { - if (quic_tls_handshake() != 0) { - return -1; - } - return 0; - } - - // SSL_do_handshake() might not consume all data (e.g., - // NewSessionTicket). - return quic_read_tls(); + return ngtcp2_crypto_read_write_crypto_data(quic.conn, ssl, crypto_level, + data, datalen); } namespace { @@ -102,12 +80,7 @@ int handshake_completed(ngtcp2_conn *conn, void *user_data) { } } // namespace -int Client::quic_handshake_completed() { - quic.tx_crypto_level = NGTCP2_CRYPTO_LEVEL_APP; - - // TODO Create Http3Session here. - return connection_made(); -} +int Client::quic_handshake_completed() { return connection_made(); } namespace { int recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, @@ -124,136 +97,6 @@ int recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, } } // namespace -namespace { -int in_encrypt(ngtcp2_conn *conn, uint8_t *dest, const uint8_t *plaintext, - size_t plaintextlen, const uint8_t *key, const uint8_t *nonce, - size_t noncelen, const uint8_t *ad, size_t adlen, - void *user_data) { - auto c = static_cast(user_data); - - if (c->quic_in_encrypt(dest, plaintext, plaintextlen, key, nonce, noncelen, - ad, adlen) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} -} // namespace - -int Client::quic_in_encrypt(uint8_t *dest, const uint8_t *plaintext, - size_t plaintextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { - return ngtcp2_crypto_encrypt(dest, &in_crypto_ctx.aead, plaintext, - plaintextlen, key, nonce, noncelen, ad, adlen); -} - -namespace { -int in_decrypt(ngtcp2_conn *conn, uint8_t *dest, const uint8_t *ciphertext, - size_t ciphertextlen, const uint8_t *key, const uint8_t *nonce, - size_t noncelen, const uint8_t *ad, size_t adlen, - void *user_data) { - auto c = static_cast(user_data); - - if (c->quic_in_decrypt(dest, ciphertext, ciphertextlen, key, nonce, noncelen, - ad, adlen) != 0) { - return NGTCP2_ERR_TLS_DECRYPT; - } - - return 0; -} -} // namespace - -int Client::quic_in_decrypt(uint8_t *dest, const uint8_t *ciphertext, - size_t ciphertextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { - return ngtcp2_crypto_decrypt(dest, &in_crypto_ctx.aead, ciphertext, - ciphertextlen, key, nonce, noncelen, ad, adlen); -} - -namespace { -int encrypt(ngtcp2_conn *conn, uint8_t *dest, const uint8_t *plaintext, - size_t plaintextlen, const uint8_t *key, const uint8_t *nonce, - size_t noncelen, const uint8_t *ad, size_t adlen, void *user_data) { - auto c = static_cast(user_data); - - if (c->quic_encrypt(dest, plaintext, plaintextlen, key, nonce, noncelen, ad, - adlen) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} -} // namespace - -int Client::quic_encrypt(uint8_t *dest, const uint8_t *plaintext, - size_t plaintextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { - return ngtcp2_crypto_encrypt(dest, &quic.crypto_ctx.aead, plaintext, - plaintextlen, key, nonce, noncelen, ad, adlen); -} - -namespace { -int decrypt(ngtcp2_conn *conn, uint8_t *dest, const uint8_t *ciphertext, - size_t ciphertextlen, const uint8_t *key, const uint8_t *nonce, - size_t noncelen, const uint8_t *ad, size_t adlen, void *user_data) { - auto c = static_cast(user_data); - - if (c->quic_decrypt(dest, ciphertext, ciphertextlen, key, nonce, noncelen, ad, - adlen) != 0) { - return NGTCP2_ERR_TLS_DECRYPT; - } - - return 0; -} -} // namespace - -int Client::quic_decrypt(uint8_t *dest, const uint8_t *ciphertext, - size_t ciphertextlen, const uint8_t *key, - const uint8_t *nonce, size_t noncelen, - const uint8_t *ad, size_t adlen) { - return ngtcp2_crypto_decrypt(dest, &quic.crypto_ctx.aead, ciphertext, - ciphertextlen, key, nonce, noncelen, ad, adlen); -} - -namespace { -int in_hp_mask(ngtcp2_conn *conn, uint8_t *dest, const uint8_t *key, - const uint8_t *sample, void *user_data) { - auto c = static_cast(user_data); - - if (c->quic_in_hp_mask(dest, key, sample) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} -} // namespace - -int Client::quic_in_hp_mask(uint8_t *dest, const uint8_t *key, - const uint8_t *sample) { - return ngtcp2_crypto_hp_mask(dest, &in_crypto_ctx.hp, key, sample); -} - -namespace { -int hp_mask(ngtcp2_conn *conn, uint8_t *dest, const uint8_t *key, - const uint8_t *sample, void *user_data) { - auto c = static_cast(user_data); - - if (c->quic_hp_mask(dest, key, sample) != 0) { - return NGTCP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} -} // namespace - -int Client::quic_hp_mask(uint8_t *dest, const uint8_t *key, - const uint8_t *sample) { - return ngtcp2_crypto_hp_mask(dest, &quic.crypto_ctx.hp, key, sample); -} - namespace { int recv_stream_data(ngtcp2_conn *conn, int64_t stream_id, int fin, uint64_t offset, const uint8_t *data, size_t datalen, @@ -393,119 +236,67 @@ ngtcp2_tstamp timestamp(struct ev_loop *loop) { } // namespace namespace { -int bio_write(BIO *b, const char *buf, int len) { - assert(0); - return -1; -} -} // namespace - -namespace { -int bio_read(BIO *b, char *buf, int len) { - BIO_clear_retry_flags(b); - - auto c = static_cast(BIO_get_data(b)); - - len = c->quic_read_server_handshake(reinterpret_cast(buf), len); - if (len == 0) { - BIO_set_retry_read(b); - return -1; - } - - return len; -} -} // namespace - -namespace { -int bio_puts(BIO *b, const char *str) { return bio_write(b, str, strlen(str)); } -} // namespace - -namespace { -int bio_gets(BIO *b, char *buf, int len) { return -1; } -} // namespace - -namespace { -long bio_ctrl(BIO *b, int cmd, long num, void *ptr) { - switch (cmd) { - case BIO_CTRL_FLUSH: - return 1; - } - - return 0; -} -} // namespace - -namespace { -int bio_create(BIO *b) { - BIO_set_init(b, 1); - return 1; -} -} // namespace - -namespace { -int bio_destroy(BIO *b) { - if (b == nullptr) { - return 0; - } - - return 1; -} -} // namespace - -namespace { -BIO_METHOD *create_bio_method() { - static auto meth = BIO_meth_new(BIO_TYPE_FD, "bio"); - BIO_meth_set_write(meth, bio_write); - BIO_meth_set_read(meth, bio_read); - BIO_meth_set_puts(meth, bio_puts); - BIO_meth_set_gets(meth, bio_gets); - BIO_meth_set_ctrl(meth, bio_ctrl); - BIO_meth_set_create(meth, bio_create); - BIO_meth_set_destroy(meth, bio_destroy); - return meth; -} -} // namespace - -namespace { -int key_cb(SSL *ssl, int name, const unsigned char *secret, size_t secretlen, - void *arg) { - auto c = static_cast(arg); - - if (c->quic_on_key(name, secret, secretlen) != 0) { - return 0; - } - - return 1; -} -} // namespace - -namespace { -void msg_cb(int write_p, int version, int content_type, const void *buf, - size_t len, SSL *ssl, void *arg) { - if (!write_p) { - return; - } - - auto c = static_cast(arg); - auto msg = reinterpret_cast(buf); - - switch (content_type) { - case SSL3_RT_HANDSHAKE: - break; - case SSL3_RT_ALERT: - assert(len == 2); - if (msg[0] != 2 /* FATAL */) { - return; - } - c->quic_set_tls_alert(msg[1]); - return; +ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) { + switch (ossl_level) { + case ssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case ssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case ssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case ssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APP; default: - return; + assert(0); + } +} +} // namespace + +namespace { +int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *rx_secret, const uint8_t *tx_secret, + size_t secret_len) { + auto c = static_cast(SSL_get_app_data(ssl)); + + if (c->quic_on_key(from_ossl_level(ossl_level), rx_secret, tx_secret, + secret_len) != 0) { + return 0; } - c->quic_write_client_handshake(reinterpret_cast(buf), len); + return 1; } } // namespace +namespace { +int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t *data, size_t len) { + auto c = static_cast(SSL_get_app_data(ssl)); + c->quic_write_client_handshake(from_ossl_level(ossl_level), data, len); + return 1; +} +} // namespace + +namespace { +int flush_flight(SSL *ssl) { return 1; } +} // namespace + +namespace { +int send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) { + auto c = static_cast(SSL_get_app_data(ssl)); + c->quic_set_tls_alert(alert); + return 1; +} +} // namespace + +namespace { +auto quic_method = SSL_QUIC_METHOD{ + set_encryption_secrets, + add_handshake_data, + flush_flight, + send_alert, +}; +} // namespace + int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen, const sockaddr *remote_addr, socklen_t remote_addrlen) { int rv; @@ -513,15 +304,9 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen, if (!ssl) { ssl = SSL_new(worker->ssl_ctx); - auto bio = BIO_new(create_bio_method()); - BIO_set_data(bio, this); - - SSL_set_bio(ssl, bio, bio); SSL_set_app_data(ssl, this); SSL_set_connect_state(ssl); - SSL_set_msg_callback(ssl, msg_cb); - SSL_set_msg_callback_arg(ssl, this); - SSL_set_key_callback(ssl, key_cb, this); + SSL_set_quic_method(ssl, &quic_method); } switch (remote_addr->sa_family) { @@ -541,12 +326,9 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen, h2load::recv_crypto_data, h2load::handshake_completed, nullptr, // recv_version_negotiation - h2load::in_encrypt, - h2load::in_decrypt, - h2load::encrypt, - h2load::decrypt, - h2load::in_hp_mask, - h2load::hp_mask, + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, h2load::recv_stream_data, nullptr, // acked_crypto_offset nullptr, // acked_stream_data_offset @@ -600,6 +382,25 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen, return -1; } + ngtcp2_transport_params params; + ngtcp2_conn_get_local_transport_params(quic.conn, ¶ms); + + std::array buf; + + auto nwrite = ngtcp2_encode_transport_params( + buf.data(), buf.size(), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, + ¶ms); + if (nwrite < 0) { + std::cerr << "ngtcp2_encode_transport_params: " << ngtcp2_strerror(nwrite) + << std::endl; + return -1; + } + + if (SSL_set_quic_transport_params(ssl, buf.data(), nwrite) != 1) { + std::cerr << "SSL_set_quic_transport_params failed" << std::endl; + return -1; + } + rv = quic_setup_initial_crypto(); if (rv != 0) { ngtcp2_conn_del(quic.conn); @@ -648,216 +449,55 @@ void Client::quic_close_connection() { } int Client::quic_setup_initial_crypto() { - std::array tx_secret, rx_secret; auto dcid = ngtcp2_conn_get_dcid(quic.conn); - if (ngtcp2_crypto_derive_initial_secrets(rx_secret.data(), tx_secret.data(), - nullptr, dcid, - NGTCP2_CRYPTO_SIDE_CLIENT) != 0) { - std::cerr << "ngtcp2_crypto_derive_initial_secrets() failed" << std::endl; + + if (ngtcp2_crypto_derive_and_install_initial_key( + quic.conn, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, dcid, NGTCP2_CRYPTO_SIDE_CLIENT) != 0) { + std::cerr << "ngtcp2_crypto_derive_and_install_initial_key() failed" + << std::endl; return -1; } - auto aead = &in_crypto_ctx.aead; - auto md = &in_crypto_ctx.md; - - std::array key, iv, hp; - auto keylen = ngtcp2_crypto_aead_keylen(aead); - auto ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - auto hplen = keylen; - - if (ngtcp2_crypto_derive_packet_protection_key(key.data(), iv.data(), aead, - md, tx_secret.data(), - tx_secret.size()) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_header_protection_key( - hp.data(), aead, md, tx_secret.data(), tx_secret.size()) != 0) { - return -1; - } - - ngtcp2_conn_install_initial_tx_keys(quic.conn, key.data(), keylen, iv.data(), - ivlen, hp.data(), hplen); - - if (ngtcp2_crypto_derive_packet_protection_key(key.data(), iv.data(), aead, - md, rx_secret.data(), - rx_secret.size()) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_header_protection_key( - hp.data(), aead, md, rx_secret.data(), rx_secret.size()) != 0) { - return -1; - } - - ngtcp2_conn_install_initial_rx_keys(quic.conn, key.data(), keylen, iv.data(), - ivlen, hp.data(), hplen); - - return 0; -} - -int Client::quic_on_key(int name, const uint8_t *secret, size_t secretlen) { - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - break; - default: - return 0; - } - - if (quic.crypto_ctx.aead.native_handle == nullptr) { - ngtcp2_crypto_ctx_tls(&quic.crypto_ctx, ssl); - ngtcp2_conn_set_aead_overhead( - quic.conn, ngtcp2_crypto_aead_taglen(&quic.crypto_ctx.aead)); - } - - auto aead = &quic.crypto_ctx.aead; - auto md = &quic.crypto_ctx.md; - - std::array key, iv, hp; - auto keylen = ngtcp2_crypto_aead_keylen(aead); - auto ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - auto hplen = keylen; - - if (ngtcp2_crypto_derive_packet_protection_key(key.data(), iv.data(), aead, - md, secret, secretlen) != 0) { - return -1; - } - - if (ngtcp2_crypto_derive_header_protection_key(hp.data(), aead, md, secret, - secretlen) != 0) { - return -1; - } - - switch (name) { - case SSL_KEY_CLIENT_EARLY_TRAFFIC: - ngtcp2_conn_install_early_keys(quic.conn, key.data(), keylen, iv.data(), - ivlen, hp.data(), hplen); - break; - case SSL_KEY_CLIENT_HANDSHAKE_TRAFFIC: - ngtcp2_conn_install_handshake_tx_keys(quic.conn, key.data(), keylen, - iv.data(), ivlen, hp.data(), hplen); - quic.tx_crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - break; - case SSL_KEY_CLIENT_APPLICATION_TRAFFIC: - ngtcp2_conn_install_tx_keys(quic.conn, key.data(), keylen, iv.data(), ivlen, - hp.data(), hplen); - break; - case SSL_KEY_SERVER_HANDSHAKE_TRAFFIC: - ngtcp2_conn_install_handshake_rx_keys(quic.conn, key.data(), keylen, - iv.data(), ivlen, hp.data(), hplen); - quic.rx_crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; - break; - case SSL_KEY_SERVER_APPLICATION_TRAFFIC: - ngtcp2_conn_install_rx_keys(quic.conn, key.data(), keylen, iv.data(), ivlen, - hp.data(), hplen); - quic.rx_crypto_level = NGTCP2_CRYPTO_LEVEL_APP; - break; - } - return 0; } -int Client::quic_tls_handshake(bool initial) { - ERR_clear_error(); +int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen) { + if (ngtcp2_crypto_derive_and_install_key( + quic.conn, ssl, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + level, rx_secret, tx_secret, secretlen, + NGTCP2_CRYPTO_SIDE_CLIENT) != 0) { + std::cerr << "ngtcp2_crypto_derive_and_install_key() failed" << std::endl; + return -1; + } - int rv; - - rv = SSL_do_handshake(ssl); - if (rv <= 0) { - auto err = SSL_get_error(ssl, rv); - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return 0; - case SSL_ERROR_SSL: - std::cerr << "TLS handshake error: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return -1; - default: - std::cerr << "TLS handshake error: " << err << std::endl; + if (level == NGTCP2_CRYPTO_LEVEL_APP) { + auto s = std::make_unique(this); + if (s->init_conn() == -1) { return -1; } - } - - ngtcp2_conn_handshake_completed(quic.conn); - - if (quic_read_tls() != 0) { - return -1; + session = std::move(s); } return 0; } -int Client::quic_read_tls() { - ERR_clear_error(); - - std::array buf; - size_t nread; - - for (;;) { - auto rv = SSL_read_ex(ssl, buf.data(), buf.size(), &nread); - if (rv == 1) { - continue; - } - auto err = SSL_get_error(ssl, 0); - switch (err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return 0; - case SSL_ERROR_SSL: - case SSL_ERROR_ZERO_RETURN: - std::cerr << "TLS read error: " - << ERR_error_string(ERR_get_error(), nullptr) << std::endl; - return NGTCP2_ERR_CRYPTO; - default: - std::cerr << "TLS read error: " << err << std::endl; - return NGTCP2_ERR_CRYPTO; - } - } -} - void Client::quic_set_tls_alert(uint8_t alert) { quic.last_error = quic::err_transport_tls(alert); } -size_t Client::quic_read_server_handshake(uint8_t *buf, size_t buflen) { - auto n = std::min(buflen, - quic.server_handshake.size() - quic.server_handshake_nread); - std::copy_n(std::begin(quic.server_handshake) + quic.server_handshake_nread, - n, buf); - quic.server_handshake_nread += n; - return n; -} - -int Client::quic_write_server_handshake(ngtcp2_crypto_level crypto_level, - const uint8_t *data, size_t datalen) { - if (quic.rx_crypto_level != crypto_level) { - std::cerr << "Got crypto level " - << ", want " << quic.rx_crypto_level << std::endl; - return -1; - } - std::copy_n(data, datalen, std::back_inserter(quic.server_handshake)); - return 0; -} - -void Client::quic_write_client_handshake(const uint8_t *data, size_t datalen) { - assert(quic.tx_crypto_level < 2); - quic_write_client_handshake(quic.crypto[quic.tx_crypto_level], data, datalen); -} - -void Client::quic_write_client_handshake(Crypto &crypto, const uint8_t *data, - size_t datalen) { +void Client::quic_write_client_handshake(ngtcp2_crypto_level level, + const uint8_t *data, size_t datalen) { + assert(level < 2); + auto &crypto = quic.crypto[level]; assert(crypto.data.size() >= crypto.datalen + datalen); auto p = std::begin(crypto.data) + crypto.datalen; std::copy_n(data, datalen, p); crypto.datalen += datalen; - ngtcp2_conn_submit_crypto_data(quic.conn, quic.tx_crypto_level, p, datalen); + ngtcp2_conn_submit_crypto_data(quic.conn, level, p, datalen); } void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {