Compile with the latest ngtcp2 and ngtcp2_crypto_openssl

This commit is contained in:
Tatsuhiro Tsujikawa 2019-08-29 17:15:59 +09:00
parent e45b10ca20
commit 1a63c02c0e
3 changed files with 116 additions and 596 deletions

View File

@ -1027,12 +1027,9 @@ int Client::connection_made() {
if (next_proto) { if (next_proto) {
auto proto = StringRef{next_proto, next_proto_len}; auto proto = StringRef{next_proto, next_proto_len};
if (config.is_quic()) { if (config.is_quic()) {
if (util::streq(StringRef{&NGTCP2_ALPN_H3[1]}, proto)) { assert(session);
auto s = std::make_unique<Http3Session>(this); if (!util::streq(StringRef{&NGTCP2_ALPN_H3[1]}, proto)) {
if (s->init_conn() == -1) { return -1;
return -1;
}
session = std::move(s);
} }
} else if (util::check_h2_is_selected(proto)) { } else if (util::check_h2_is_selected(proto)) {
session = std::make_unique<Http2Session>(this); session = std::make_unique<Http2Session>(this);
@ -1043,6 +1040,9 @@ int Client::connection_made() {
// Just assign next_proto to selected_proto anyway to show the // Just assign next_proto to selected_proto anyway to show the
// negotiation result. // negotiation result.
selected_proto = proto.str(); selected_proto = proto.str();
} else if (config.is_quic()) {
std::cerr << "QUIC requires ALPN negotiation" << std::endl;
return -1;
} else { } else {
std::cout << "No protocol negotiated. Fallback behaviour may be activated" std::cout << "No protocol negotiated. Fallback behaviour may be activated"
<< std::endl; << std::endl;
@ -1778,79 +1778,6 @@ int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
} // namespace } // namespace
#endif // !OPENSSL_NO_NEXTPROTONEG #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<Client *>(SSL_get_app_data(ssl));
auto conn = c->quic.conn;
ngtcp2_transport_params params;
ngtcp2_conn_get_local_transport_params(conn, &params);
constexpr size_t bufsize = 128;
auto buf = std::make_unique<uint8_t[]>(bufsize);
auto nwrite = ngtcp2_encode_transport_params(
buf.get(), bufsize, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, &params);
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<size_t>(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<unsigned char *>(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<Client *>(SSL_get_app_data(ssl));
auto conn = c->quic.conn;
int rv;
ngtcp2_transport_params params;
rv = ngtcp2_decode_transport_params(
&params, 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, &params);
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 { namespace {
constexpr char UNIX_PATH_PREFIX[] = "unix:"; constexpr char UNIX_PATH_PREFIX[] = "unix:";
} // namespace } // namespace
@ -2250,8 +2177,6 @@ Options:
} }
} // namespace } // namespace
extern ngtcp2_crypto_ctx in_crypto_ctx;
int main(int argc, char **argv) { int main(int argc, char **argv) {
tls::libssl_init(); tls::libssl_init();
@ -2753,20 +2678,6 @@ int main(int argc, char **argv) {
if (config.is_quic()) { if (config.is_quic()) {
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_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( } else if (nghttp2::tls::ssl_ctx_set_proto_versions(
ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION, ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) { nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
@ -2909,8 +2820,6 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ngtcp2_crypto_ctx_initial(&in_crypto_ctx);
resolve_host(); resolve_host();
std::cout << "starting benchmark..." << std::endl; std::cout << "starting benchmark..." << std::endl;

View File

@ -328,11 +328,6 @@ struct Client {
ev_timer pkt_timer; ev_timer pkt_timer;
ngtcp2_conn *conn; ngtcp2_conn *conn;
quic::Error last_error; quic::Error last_error;
ngtcp2_crypto_level tx_crypto_level;
ngtcp2_crypto_level rx_crypto_level;
std::vector<uint8_t> server_handshake;
size_t server_handshake_nread;
ngtcp2_crypto_ctx crypto_ctx;
// Client never send CRYPTO in Short packet. // Client never send CRYPTO in Short packet.
std::array<Crypto, 2> crypto; std::array<Crypto, 2> crypto;
size_t max_pktlen; size_t max_pktlen;
@ -461,45 +456,21 @@ struct Client {
void quic_close_connection(); void quic_close_connection();
int quic_setup_initial_crypto(); int quic_setup_initial_crypto();
int quic_client_initial();
int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level, int quic_recv_crypto_data(ngtcp2_crypto_level crypto_level,
const uint8_t *data, size_t datalen); const uint8_t *data, size_t datalen);
int quic_handshake_completed(); 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, int quic_recv_stream_data(int64_t stream_id, int fin, const uint8_t *data,
size_t datalen); size_t datalen);
int quic_stream_close(int64_t stream_id, uint64_t app_error_code); 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_stream_reset(int64_t stream_id, uint64_t app_error_code);
int quic_extend_max_local_streams(); int quic_extend_max_local_streams();
int quic_tls_handshake(bool initial = false); int quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
int quic_read_tls(); const uint8_t *tx_secret, size_t secretlen);
int quic_on_key(int name, const uint8_t *secret, size_t secretlen);
void quic_set_tls_alert(uint8_t alert); void quic_set_tls_alert(uint8_t alert);
size_t quic_read_server_handshake(uint8_t *buf, size_t buflen); void quic_write_client_handshake(ngtcp2_crypto_level level,
int quic_write_server_handshake(ngtcp2_crypto_level crypto_level, const uint8_t *data, size_t datalen);
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);
int quic_pkt_timeout(); int quic_pkt_timeout();
void quic_restart_pkt_timer(); void quic_restart_pkt_timer();
}; };

View File

@ -27,7 +27,6 @@
#include <iostream> #include <iostream>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/bio.h>
#include "h2load_http3_session.h" #include "h2load_http3_session.h"
@ -37,13 +36,11 @@ namespace {
auto randgen = util::make_mt19937(); auto randgen = util::make_mt19937();
} // namespace } // namespace
ngtcp2_crypto_ctx in_crypto_ctx;
namespace { namespace {
int client_initial(ngtcp2_conn *conn, void *user_data) { int client_initial(ngtcp2_conn *conn, void *user_data) {
auto c = static_cast<Client *>(user_data); auto c = static_cast<Client *>(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; return NGTCP2_ERR_CALLBACK_FAILURE;
} }
@ -51,13 +48,6 @@ int client_initial(ngtcp2_conn *conn, void *user_data) {
} }
} // namespace } // namespace
int Client::quic_client_initial() {
if (quic_tls_handshake(true) != 0) {
return -1;
}
return 0;
}
namespace { namespace {
int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
uint64_t offset, const uint8_t *data, size_t datalen, 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, int Client::quic_recv_crypto_data(ngtcp2_crypto_level crypto_level,
const uint8_t *data, size_t datalen) { const uint8_t *data, size_t datalen) {
if (quic_write_server_handshake(crypto_level, data, datalen) != 0) { return ngtcp2_crypto_read_write_crypto_data(quic.conn, ssl, crypto_level,
return -1; data, datalen);
}
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();
} }
namespace { namespace {
@ -102,12 +80,7 @@ int handshake_completed(ngtcp2_conn *conn, void *user_data) {
} }
} // namespace } // namespace
int Client::quic_handshake_completed() { int Client::quic_handshake_completed() { return connection_made(); }
quic.tx_crypto_level = NGTCP2_CRYPTO_LEVEL_APP;
// TODO Create Http3Session here.
return connection_made();
}
namespace { namespace {
int recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, 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
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<Client *>(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<Client *>(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<Client *>(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<Client *>(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<Client *>(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<Client *>(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 { namespace {
int recv_stream_data(ngtcp2_conn *conn, int64_t stream_id, int fin, int recv_stream_data(ngtcp2_conn *conn, int64_t stream_id, int fin,
uint64_t offset, const uint8_t *data, size_t datalen, uint64_t offset, const uint8_t *data, size_t datalen,
@ -393,119 +236,67 @@ ngtcp2_tstamp timestamp(struct ev_loop *loop) {
} // namespace } // namespace
namespace { namespace {
int bio_write(BIO *b, const char *buf, int len) { ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) {
assert(0); switch (ossl_level) {
return -1; case ssl_encryption_initial:
} return NGTCP2_CRYPTO_LEVEL_INITIAL;
} // namespace case ssl_encryption_early_data:
return NGTCP2_CRYPTO_LEVEL_EARLY;
namespace { case ssl_encryption_handshake:
int bio_read(BIO *b, char *buf, int len) { return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
BIO_clear_retry_flags(b); case ssl_encryption_application:
return NGTCP2_CRYPTO_LEVEL_APP;
auto c = static_cast<Client *>(BIO_get_data(b));
len = c->quic_read_server_handshake(reinterpret_cast<uint8_t *>(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<Client *>(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<Client *>(arg);
auto msg = reinterpret_cast<const uint8_t *>(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;
default: 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<Client *>(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<const uint8_t *>(buf), len); return 1;
} }
} // namespace } // namespace
namespace {
int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
const uint8_t *data, size_t len) {
auto c = static_cast<Client *>(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<Client *>(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, int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
const sockaddr *remote_addr, socklen_t remote_addrlen) { const sockaddr *remote_addr, socklen_t remote_addrlen) {
int rv; int rv;
@ -513,15 +304,9 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
if (!ssl) { if (!ssl) {
ssl = SSL_new(worker->ssl_ctx); 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_app_data(ssl, this);
SSL_set_connect_state(ssl); SSL_set_connect_state(ssl);
SSL_set_msg_callback(ssl, msg_cb); SSL_set_quic_method(ssl, &quic_method);
SSL_set_msg_callback_arg(ssl, this);
SSL_set_key_callback(ssl, key_cb, this);
} }
switch (remote_addr->sa_family) { 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::recv_crypto_data,
h2load::handshake_completed, h2load::handshake_completed,
nullptr, // recv_version_negotiation nullptr, // recv_version_negotiation
h2load::in_encrypt, ngtcp2_crypto_encrypt_cb,
h2load::in_decrypt, ngtcp2_crypto_decrypt_cb,
h2load::encrypt, ngtcp2_crypto_hp_mask_cb,
h2load::decrypt,
h2load::in_hp_mask,
h2load::hp_mask,
h2load::recv_stream_data, h2load::recv_stream_data,
nullptr, // acked_crypto_offset nullptr, // acked_crypto_offset
nullptr, // acked_stream_data_offset nullptr, // acked_stream_data_offset
@ -600,6 +382,25 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
return -1; return -1;
} }
ngtcp2_transport_params params;
ngtcp2_conn_get_local_transport_params(quic.conn, &params);
std::array<uint8_t, 64> buf;
auto nwrite = ngtcp2_encode_transport_params(
buf.data(), buf.size(), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
&params);
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(); rv = quic_setup_initial_crypto();
if (rv != 0) { if (rv != 0) {
ngtcp2_conn_del(quic.conn); ngtcp2_conn_del(quic.conn);
@ -648,216 +449,55 @@ void Client::quic_close_connection() {
} }
int Client::quic_setup_initial_crypto() { int Client::quic_setup_initial_crypto() {
std::array<uint8_t, 32> tx_secret, rx_secret;
auto dcid = ngtcp2_conn_get_dcid(quic.conn); auto dcid = ngtcp2_conn_get_dcid(quic.conn);
if (ngtcp2_crypto_derive_initial_secrets(rx_secret.data(), tx_secret.data(),
nullptr, dcid, if (ngtcp2_crypto_derive_and_install_initial_key(
NGTCP2_CRYPTO_SIDE_CLIENT) != 0) { quic.conn, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
std::cerr << "ngtcp2_crypto_derive_initial_secrets() failed" << std::endl; nullptr, nullptr, nullptr, dcid, NGTCP2_CRYPTO_SIDE_CLIENT) != 0) {
std::cerr << "ngtcp2_crypto_derive_and_install_initial_key() failed"
<< std::endl;
return -1; return -1;
} }
auto aead = &in_crypto_ctx.aead;
auto md = &in_crypto_ctx.md;
std::array<uint8_t, 16> 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<uint8_t, 64> 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; return 0;
} }
int Client::quic_tls_handshake(bool initial) { int Client::quic_on_key(ngtcp2_crypto_level level, const uint8_t *rx_secret,
ERR_clear_error(); 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; if (level == NGTCP2_CRYPTO_LEVEL_APP) {
auto s = std::make_unique<Http3Session>(this);
rv = SSL_do_handshake(ssl); if (s->init_conn() == -1) {
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;
return -1; return -1;
} }
} session = std::move(s);
ngtcp2_conn_handshake_completed(quic.conn);
if (quic_read_tls() != 0) {
return -1;
} }
return 0; return 0;
} }
int Client::quic_read_tls() {
ERR_clear_error();
std::array<uint8_t, 4096> 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) { void Client::quic_set_tls_alert(uint8_t alert) {
quic.last_error = quic::err_transport_tls(alert); quic.last_error = quic::err_transport_tls(alert);
} }
size_t Client::quic_read_server_handshake(uint8_t *buf, size_t buflen) { void Client::quic_write_client_handshake(ngtcp2_crypto_level level,
auto n = std::min(buflen, const uint8_t *data, size_t datalen) {
quic.server_handshake.size() - quic.server_handshake_nread); assert(level < 2);
std::copy_n(std::begin(quic.server_handshake) + quic.server_handshake_nread, auto &crypto = quic.crypto[level];
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) {
assert(crypto.data.size() >= crypto.datalen + datalen); assert(crypto.data.size() >= crypto.datalen + datalen);
auto p = std::begin(crypto.data) + crypto.datalen; auto p = std::begin(crypto.data) + crypto.datalen;
std::copy_n(data, datalen, p); std::copy_n(data, datalen, p);
crypto.datalen += datalen; 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) { void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {