Compile with the latest ngtcp2 and ngtcp2_crypto_openssl
This commit is contained in:
parent
e45b10ca20
commit
1a63c02c0e
103
src/h2load.cc
103
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<Http3Session>(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<Http2Session>(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<Client *>(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<uint8_t[]>(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<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(
|
||||
¶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;
|
||||
|
|
37
src/h2load.h
37
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<uint8_t> server_handshake;
|
||||
size_t server_handshake_nread;
|
||||
ngtcp2_crypto_ctx crypto_ctx;
|
||||
// Client never send CRYPTO in Short packet.
|
||||
std::array<Crypto, 2> 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();
|
||||
};
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/bio.h>
|
||||
|
||||
#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<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;
|
||||
}
|
||||
|
||||
|
@ -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<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 {
|
||||
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<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;
|
||||
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<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 {
|
||||
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,
|
||||
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<uint8_t, 64> 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<uint8_t, 32> 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<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;
|
||||
}
|
||||
|
||||
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<Http3Session>(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<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) {
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue