nghttpx: Send NEW_TOKEN and very token from client
This commit is contained in:
parent
7a5082e8c4
commit
a0066a1ccf
|
@ -401,6 +401,41 @@ int stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
int handshake_completed(ngtcp2_conn *conn, void *user_data) {
|
||||||
|
auto upstream = static_cast<Http3Upstream *>(user_data);
|
||||||
|
|
||||||
|
if (upstream->handshake_completed() != 0) {
|
||||||
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int Http3Upstream::handshake_completed() {
|
||||||
|
std::array<uint8_t, SHRPX_QUIC_MAX_TOKENLEN> token;
|
||||||
|
size_t tokenlen = token.size();
|
||||||
|
|
||||||
|
auto path = ngtcp2_conn_get_path(conn_);
|
||||||
|
auto worker = handler_->get_worker();
|
||||||
|
auto &quic_secret = worker->get_quic_secret();
|
||||||
|
auto &secret = quic_secret->token_secret;
|
||||||
|
|
||||||
|
if (generate_token(token.data(), tokenlen, path->remote.addr,
|
||||||
|
path->remote.addrlen, secret.data()) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rv = ngtcp2_conn_submit_new_token(conn_, token.data(), tokenlen);
|
||||||
|
if (rv != 0) {
|
||||||
|
LOG(ERROR) << "ngtcp2_conn_submit_new_token: " << ngtcp2_strerror(rv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
|
int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
|
||||||
const Address &local_addr,
|
const Address &local_addr,
|
||||||
const ngtcp2_pkt_hd &initial_hd,
|
const ngtcp2_pkt_hd &initial_hd,
|
||||||
|
@ -414,7 +449,7 @@ int Http3Upstream::init(const UpstreamAddr *faddr, const Address &remote_addr,
|
||||||
nullptr, // client_initial
|
nullptr, // client_initial
|
||||||
ngtcp2_crypto_recv_client_initial_cb,
|
ngtcp2_crypto_recv_client_initial_cb,
|
||||||
ngtcp2_crypto_recv_crypto_data_cb,
|
ngtcp2_crypto_recv_crypto_data_cb,
|
||||||
nullptr, // handshake_completed
|
shrpx::handshake_completed,
|
||||||
nullptr, // recv_version_negotiation
|
nullptr, // recv_version_negotiation
|
||||||
ngtcp2_crypto_encrypt_cb,
|
ngtcp2_crypto_encrypt_cb,
|
||||||
ngtcp2_crypto_decrypt_cb,
|
ngtcp2_crypto_decrypt_cb,
|
||||||
|
|
|
@ -143,6 +143,7 @@ public:
|
||||||
int http_send_stop_sending(int64_t stream_id, uint64_t app_error_code);
|
int http_send_stop_sending(int64_t stream_id, uint64_t app_error_code);
|
||||||
int http_recv_data(Downstream *downstream, const uint8_t *data,
|
int http_recv_data(Downstream *downstream, const uint8_t *data,
|
||||||
size_t datalen);
|
size_t datalen);
|
||||||
|
int handshake_completed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClientHandler *handler_;
|
ClientHandler *handler_;
|
||||||
|
|
|
@ -403,4 +403,182 @@ int verify_retry_token(ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
size_t generate_token_aad(uint8_t *dest, size_t destlen, const sockaddr *sa,
|
||||||
|
size_t salen) {
|
||||||
|
const uint8_t *addr;
|
||||||
|
size_t addrlen;
|
||||||
|
|
||||||
|
switch (sa->sa_family) {
|
||||||
|
case AF_INET:
|
||||||
|
addr = reinterpret_cast<const uint8_t *>(
|
||||||
|
&reinterpret_cast<const sockaddr_in *>(sa)->sin_addr);
|
||||||
|
addrlen = sizeof(reinterpret_cast<const sockaddr_in *>(sa)->sin_addr);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
addr = reinterpret_cast<const uint8_t *>(
|
||||||
|
&reinterpret_cast<const sockaddr_in6 *>(sa)->sin6_addr);
|
||||||
|
addrlen = sizeof(reinterpret_cast<const sockaddr_in6 *>(sa)->sin6_addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(destlen >= addrlen);
|
||||||
|
|
||||||
|
return std::copy_n(addr, addrlen, dest) - dest;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int generate_token(uint8_t *token, size_t &tokenlen, const sockaddr *sa,
|
||||||
|
size_t salen, const uint8_t *token_secret) {
|
||||||
|
std::array<uint8_t, 8> plaintext;
|
||||||
|
|
||||||
|
uint64_t t = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
std::array<uint8_t, 256> aad;
|
||||||
|
auto aadlen = generate_token_aad(aad.data(), aad.size(), sa, salen);
|
||||||
|
if (aadlen == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto p = std::begin(plaintext);
|
||||||
|
// Host byte order
|
||||||
|
p = std::copy_n(reinterpret_cast<uint8_t *>(&t), sizeof(t), p);
|
||||||
|
|
||||||
|
std::array<uint8_t, SHRPX_QUIC_TOKEN_RAND_DATALEN> rand_data;
|
||||||
|
std::array<uint8_t, 32> key, iv;
|
||||||
|
auto keylen = key.size();
|
||||||
|
auto ivlen = iv.size();
|
||||||
|
|
||||||
|
if (RAND_bytes(rand_data.data(), rand_data.size()) != 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngtcp2_crypto_aead aead;
|
||||||
|
ngtcp2_crypto_aead_init(&aead, const_cast<EVP_CIPHER *>(EVP_aes_128_gcm()));
|
||||||
|
|
||||||
|
ngtcp2_crypto_md md;
|
||||||
|
ngtcp2_crypto_md_init(&md, const_cast<EVP_MD *>(EVP_sha256()));
|
||||||
|
|
||||||
|
if (derive_token_key(key.data(), keylen, iv.data(), ivlen, token_secret,
|
||||||
|
rand_data.data(), rand_data.size(), &aead, &md) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto plaintextlen = std::distance(std::begin(plaintext), p);
|
||||||
|
|
||||||
|
ngtcp2_crypto_aead_ctx aead_ctx;
|
||||||
|
if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key.data(),
|
||||||
|
ivlen) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
token[0] = SHRPX_QUIC_TOKEN_MAGIC;
|
||||||
|
auto rv =
|
||||||
|
ngtcp2_crypto_encrypt(token + 1, &aead, &aead_ctx, plaintext.data(),
|
||||||
|
plaintextlen, iv.data(), ivlen, aad.data(), aadlen);
|
||||||
|
|
||||||
|
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1 for magic byte */
|
||||||
|
tokenlen = 1 + plaintextlen + aead.max_overhead;
|
||||||
|
memcpy(token + tokenlen, rand_data.data(), rand_data.size());
|
||||||
|
tokenlen += rand_data.size();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int verify_token(const uint8_t *token, size_t tokenlen, const sockaddr *sa,
|
||||||
|
socklen_t salen, const uint8_t *token_secret) {
|
||||||
|
std::array<char, NI_MAXHOST> host;
|
||||||
|
std::array<char, NI_MAXSERV> port;
|
||||||
|
|
||||||
|
if (getnameinfo(sa, salen, host.data(), host.size(), port.data(), port.size(),
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1 for TOKEN_MAGIC */
|
||||||
|
if (tokenlen < SHRPX_QUIC_TOKEN_RAND_DATALEN + 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (tokenlen > SHRPX_QUIC_MAX_TOKENLEN) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(token[0] == SHRPX_QUIC_TOKEN_MAGIC);
|
||||||
|
|
||||||
|
std::array<uint8_t, 256> aad;
|
||||||
|
auto aadlen = generate_token_aad(aad.data(), aad.size(), sa, salen);
|
||||||
|
if (aadlen == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rand_data = token + tokenlen - SHRPX_QUIC_TOKEN_RAND_DATALEN;
|
||||||
|
auto ciphertext = token + 1;
|
||||||
|
auto ciphertextlen = tokenlen - SHRPX_QUIC_TOKEN_RAND_DATALEN - 1;
|
||||||
|
|
||||||
|
std::array<uint8_t, 32> key, iv;
|
||||||
|
auto keylen = key.size();
|
||||||
|
auto ivlen = iv.size();
|
||||||
|
|
||||||
|
ngtcp2_crypto_aead aead;
|
||||||
|
ngtcp2_crypto_aead_init(&aead, const_cast<EVP_CIPHER *>(EVP_aes_128_gcm()));
|
||||||
|
|
||||||
|
ngtcp2_crypto_md md;
|
||||||
|
ngtcp2_crypto_md_init(&md, const_cast<EVP_MD *>(EVP_sha256()));
|
||||||
|
|
||||||
|
if (derive_token_key(key.data(), keylen, iv.data(), ivlen, token_secret,
|
||||||
|
rand_data, SHRPX_QUIC_TOKEN_RAND_DATALEN, &aead,
|
||||||
|
&md) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngtcp2_crypto_aead_ctx aead_ctx;
|
||||||
|
if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key.data(),
|
||||||
|
ivlen) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint8_t, SHRPX_QUIC_MAX_TOKENLEN> plaintext;
|
||||||
|
|
||||||
|
auto rv = ngtcp2_crypto_decrypt(plaintext.data(), &aead, &aead_ctx,
|
||||||
|
ciphertext, ciphertextlen, iv.data(), ivlen,
|
||||||
|
aad.data(), aadlen);
|
||||||
|
|
||||||
|
ngtcp2_crypto_aead_ctx_free(&aead_ctx);
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(ciphertextlen >= aead.max_overhead);
|
||||||
|
|
||||||
|
auto plaintextlen = ciphertextlen - aead.max_overhead;
|
||||||
|
if (plaintextlen != sizeof(uint64_t)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t t;
|
||||||
|
memcpy(&t, plaintext.data(), sizeof(uint64_t));
|
||||||
|
|
||||||
|
uint64_t now = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
// Allow 1 hour window
|
||||||
|
if (t + 3600ULL * NGTCP2_SECONDS < now) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -84,6 +84,12 @@ int verify_retry_token(ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
|
||||||
const ngtcp2_cid *dcid, const sockaddr *sa,
|
const ngtcp2_cid *dcid, const sockaddr *sa,
|
||||||
socklen_t salen, const uint8_t *token_secret);
|
socklen_t salen, const uint8_t *token_secret);
|
||||||
|
|
||||||
|
int generate_token(uint8_t *token, size_t &tokenlen, const sockaddr *sa,
|
||||||
|
size_t salen, const uint8_t *token_secret);
|
||||||
|
|
||||||
|
int verify_token(const uint8_t *token, size_t tokenlen, const sockaddr *sa,
|
||||||
|
socklen_t salen, const uint8_t *token_secret);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
#endif // SHRPX_QUIC_H
|
#endif // SHRPX_QUIC_H
|
||||||
|
|
|
@ -85,6 +85,8 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
|
||||||
|
|
||||||
ngtcp2_pkt_hd hd;
|
ngtcp2_pkt_hd hd;
|
||||||
ngtcp2_cid odcid, *podcid = nullptr;
|
ngtcp2_cid odcid, *podcid = nullptr;
|
||||||
|
const uint8_t *token = nullptr;
|
||||||
|
size_t tokenlen = 0;
|
||||||
|
|
||||||
switch (ngtcp2_accept(&hd, data, datalen)) {
|
switch (ngtcp2_accept(&hd, data, datalen)) {
|
||||||
case 0: {
|
case 0: {
|
||||||
|
@ -95,12 +97,31 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
|
||||||
auto &quic_secret = worker_->get_quic_secret();
|
auto &quic_secret = worker_->get_quic_secret();
|
||||||
auto &secret = quic_secret->token_secret;
|
auto &secret = quic_secret->token_secret;
|
||||||
|
|
||||||
if (hd.token.base[0] == SHRPX_QUIC_RETRY_TOKEN_MAGIC) {
|
switch (hd.token.base[0]) {
|
||||||
|
case SHRPX_QUIC_RETRY_TOKEN_MAGIC:
|
||||||
if (verify_retry_token(&odcid, hd.token.base, hd.token.len, &hd.dcid,
|
if (verify_retry_token(&odcid, hd.token.base, hd.token.len, &hd.dcid,
|
||||||
&remote_addr.su.sa, remote_addr.len,
|
&remote_addr.su.sa, remote_addr.len,
|
||||||
secret.data()) == 0) {
|
secret.data()) != 0) {
|
||||||
podcid = &odcid;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
podcid = &odcid;
|
||||||
|
token = hd.token.base;
|
||||||
|
tokenlen = hd.token.len;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SHRPX_QUIC_TOKEN_MAGIC:
|
||||||
|
if (verify_token(hd.token.base, hd.token.len, &remote_addr.su.sa,
|
||||||
|
remote_addr.len, secret.data()) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = hd.token.base;
|
||||||
|
tokenlen = hd.token.len;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -139,7 +160,8 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
handler = handle_new_connection(faddr, remote_addr, local_addr, hd, podcid);
|
handler = handle_new_connection(faddr, remote_addr, local_addr, hd, podcid,
|
||||||
|
token, tokenlen);
|
||||||
if (handler == nullptr) {
|
if (handler == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -159,8 +181,8 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
|
||||||
|
|
||||||
ClientHandler *QUICConnectionHandler::handle_new_connection(
|
ClientHandler *QUICConnectionHandler::handle_new_connection(
|
||||||
const UpstreamAddr *faddr, const Address &remote_addr,
|
const UpstreamAddr *faddr, const Address &remote_addr,
|
||||||
const Address &local_addr, const ngtcp2_pkt_hd &hd,
|
const Address &local_addr, const ngtcp2_pkt_hd &hd, const ngtcp2_cid *odcid,
|
||||||
const ngtcp2_cid *odcid) {
|
const uint8_t *token, size_t tokenlen) {
|
||||||
std::array<char, NI_MAXHOST> host;
|
std::array<char, NI_MAXHOST> host;
|
||||||
std::array<char, NI_MAXSERV> service;
|
std::array<char, NI_MAXSERV> service;
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -198,14 +220,6 @@ ClientHandler *QUICConnectionHandler::handle_new_connection(
|
||||||
worker_, faddr->fd, ssl, StringRef{host.data()},
|
worker_, faddr->fd, ssl, StringRef{host.data()},
|
||||||
StringRef{service.data()}, remote_addr.su.sa.sa_family, faddr);
|
StringRef{service.data()}, remote_addr.su.sa.sa_family, faddr);
|
||||||
|
|
||||||
const uint8_t *token = nullptr;
|
|
||||||
size_t tokenlen = 0;
|
|
||||||
|
|
||||||
if (odcid) {
|
|
||||||
token = hd.token.base;
|
|
||||||
tokenlen = hd.token.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto upstream = std::make_unique<Http3Upstream>(handler.get());
|
auto upstream = std::make_unique<Http3Upstream>(handler.get());
|
||||||
if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token,
|
if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token,
|
||||||
tokenlen) != 0) {
|
tokenlen) != 0) {
|
||||||
|
|
|
@ -75,7 +75,8 @@ public:
|
||||||
const Address &remote_addr,
|
const Address &remote_addr,
|
||||||
const Address &local_addr,
|
const Address &local_addr,
|
||||||
const ngtcp2_pkt_hd &hd,
|
const ngtcp2_pkt_hd &hd,
|
||||||
const ngtcp2_cid *odcid);
|
const ngtcp2_cid *odcid,
|
||||||
|
const uint8_t *token, size_t tokenlen);
|
||||||
void add_connection_id(const ngtcp2_cid *cid, ClientHandler *handler);
|
void add_connection_id(const ngtcp2_cid *cid, ClientHandler *handler);
|
||||||
void remove_connection_id(const ngtcp2_cid *cid);
|
void remove_connection_id(const ngtcp2_cid *cid);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue