diff --git a/src/shrpx_http3_upstream.cc b/src/shrpx_http3_upstream.cc index 098bc1b2..184e3435 100644 --- a/src/shrpx_http3_upstream.cc +++ b/src/shrpx_http3_upstream.cc @@ -471,8 +471,8 @@ int handshake_completed(ngtcp2_conn *conn, void *user_data) { } // namespace int Http3Upstream::handshake_completed() { - std::array token; - size_t tokenlen = token.size(); + std::array token; + size_t tokenlen; auto path = ngtcp2_conn_get_path(conn_); auto worker = handler_->get_worker(); diff --git a/src/shrpx_quic.cc b/src/shrpx_quic.cc index 360b61c0..0cde3618 100644 --- a/src/shrpx_quic.cc +++ b/src/shrpx_quic.cc @@ -173,11 +173,8 @@ int generate_quic_connection_id(ngtcp2_cid *cid, size_t cidlen, int generate_quic_stateless_reset_token(uint8_t *token, const ngtcp2_cid *cid, const uint8_t *secret, size_t secretlen) { - ngtcp2_crypto_md md; - ngtcp2_crypto_md_init(&md, const_cast(EVP_sha256())); - - if (ngtcp2_crypto_generate_stateless_reset_token(token, &md, secret, - secretlen, cid) != 0) { + if (ngtcp2_crypto_generate_stateless_reset_token(token, secret, secretlen, + cid) != 0) { return -1; } @@ -200,117 +197,21 @@ int generate_quic_token_secret(uint8_t *secret) { return 0; } -namespace { -int derive_token_key(uint8_t *key, size_t &keylen, uint8_t *iv, size_t &ivlen, - const uint8_t *token_secret, const uint8_t *rand_data, - size_t rand_datalen, const ngtcp2_crypto_aead *aead, - const ngtcp2_crypto_md *md) { - std::array secret; - - if (ngtcp2_crypto_hkdf_extract(secret.data(), md, token_secret, - SHRPX_QUIC_TOKEN_SECRETLEN, rand_data, - rand_datalen) != 0) { - return -1; - } - - auto aead_keylen = ngtcp2_crypto_aead_keylen(aead); - if (keylen < aead_keylen) { - return -1; - } - - keylen = aead_keylen; - - auto aead_ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); - if (ivlen < aead_ivlen) { - return -1; - } - - ivlen = aead_ivlen; - - if (ngtcp2_crypto_derive_packet_protection_key( - key, iv, nullptr, aead, md, secret.data(), secret.size()) != 0) { - return -1; - } - - return 0; -} -} // namespace - -namespace { -size_t generate_retry_token_aad(uint8_t *dest, size_t destlen, - const sockaddr *sa, socklen_t salen, - const ngtcp2_cid *retry_scid) { - assert(destlen >= salen + retry_scid->datalen); - - auto p = std::copy_n(reinterpret_cast(sa), salen, dest); - p = std::copy_n(retry_scid->data, retry_scid->datalen, p); - - return p - dest; -} -} // namespace - int generate_retry_token(uint8_t *token, size_t &tokenlen, const sockaddr *sa, socklen_t salen, const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, const uint8_t *token_secret) { - std::array plaintext; + auto t = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); - uint64_t t = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - auto p = std::begin(plaintext); - // Host byte order - p = std::copy_n(reinterpret_cast(&t), sizeof(t), p); - p = std::copy_n(odcid->data, odcid->datalen, p); - - std::array rand_data; - std::array key, iv; - auto keylen = key.size(); - auto ivlen = iv.size(); - - if (RAND_bytes(rand_data.data(), rand_data.size()) != 1) { + auto stokenlen = ngtcp2_crypto_generate_retry_token( + token, token_secret, SHRPX_QUIC_TOKEN_SECRETLEN, sa, salen, retry_scid, + odcid, t); + if (stokenlen < 0) { return -1; } - ngtcp2_crypto_aead aead; - ngtcp2_crypto_aead_init(&aead, const_cast(EVP_aes_128_gcm())); - - ngtcp2_crypto_md md; - ngtcp2_crypto_md_init(&md, const_cast(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); - - std::array aad; - auto aadlen = - generate_retry_token_aad(aad.data(), aad.size(), sa, salen, retry_scid); - - token[0] = SHRPX_QUIC_RETRY_TOKEN_MAGIC; - - ngtcp2_crypto_aead_ctx aead_ctx; - if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key.data(), - ivlen) != 0) { - return -1; - } - - 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(); + tokenlen = stokenlen; return 0; } @@ -318,267 +219,46 @@ int generate_retry_token(uint8_t *token, size_t &tokenlen, const sockaddr *sa, int verify_retry_token(ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, const ngtcp2_cid *dcid, const sockaddr *sa, socklen_t salen, const uint8_t *token_secret) { - std::array host; - std::array port; - if (getnameinfo(sa, salen, host.data(), host.size(), port.data(), port.size(), - NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + auto t = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + if (ngtcp2_crypto_verify_retry_token(odcid, token, tokenlen, token_secret, + SHRPX_QUIC_TOKEN_SECRETLEN, sa, salen, + dcid, 10 * NGTCP2_SECONDS, t) != 0) { return -1; } - /* 1 for SHRPX_QUIC_RETRY_TOKEN_MAGIC */ - if (tokenlen < SHRPX_QUIC_TOKEN_RAND_DATALEN + 1) { - return -1; - } - if (tokenlen > SHRPX_QUIC_MAX_RETRY_TOKENLEN) { - return -1; - } - - assert(token[0] == SHRPX_QUIC_RETRY_TOKEN_MAGIC); - - 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 key, iv; - auto keylen = key.size(); - auto ivlen = iv.size(); - - ngtcp2_crypto_aead aead; - ngtcp2_crypto_aead_init(&aead, const_cast(EVP_aes_128_gcm())); - - ngtcp2_crypto_md md; - ngtcp2_crypto_md_init(&md, const_cast(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; - } - - std::array aad; - auto aadlen = - generate_retry_token_aad(aad.data(), aad.size(), sa, salen, dcid); - - ngtcp2_crypto_aead_ctx aead_ctx; - if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key.data(), - ivlen) != 0) { - return -1; - } - - std::array 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; - } - - auto cil = plaintextlen - sizeof(uint64_t); - if (cil != 0 && (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN)) { - return -1; - } - - uint64_t t; - memcpy(&t, plaintext.data(), sizeof(uint64_t)); - - uint64_t now = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - // Allow 10 seconds window - if (t + 10ULL * NGTCP2_SECONDS < now) { - return -1; - } - - ngtcp2_cid_init(odcid, plaintext.data() + sizeof(uint64_t), cil); - 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( - &reinterpret_cast(sa)->sin_addr); - addrlen = sizeof(reinterpret_cast(sa)->sin_addr); - break; - case AF_INET6: - addr = reinterpret_cast( - &reinterpret_cast(sa)->sin6_addr); - addrlen = sizeof(reinterpret_cast(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 plaintext; + auto t = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); - uint64_t t = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - - std::array aad; - auto aadlen = generate_token_aad(aad.data(), aad.size(), sa, salen); - if (aadlen == 0) { + auto stokenlen = ngtcp2_crypto_generate_regular_token( + token, token_secret, SHRPX_QUIC_TOKEN_SECRETLEN, sa, salen, t); + if (stokenlen < 0) { return -1; } - auto p = std::begin(plaintext); - // Host byte order - p = std::copy_n(reinterpret_cast(&t), sizeof(t), p); - - std::array rand_data; - std::array 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_aes_128_gcm())); - - ngtcp2_crypto_md md; - ngtcp2_crypto_md_init(&md, const_cast(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(); + tokenlen = stokenlen; 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 host; - std::array port; + auto t = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); - 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 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 key, iv; - auto keylen = key.size(); - auto ivlen = iv.size(); - - ngtcp2_crypto_aead aead; - ngtcp2_crypto_aead_init(&aead, const_cast(EVP_aes_128_gcm())); - - ngtcp2_crypto_md md; - ngtcp2_crypto_md_init(&md, const_cast(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 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::system_clock::now().time_since_epoch()) - .count(); - - // Allow 1 hour window - if (t + 3600ULL * NGTCP2_SECONDS < now) { + if (ngtcp2_crypto_verify_regular_token(token, tokenlen, token_secret, + SHRPX_QUIC_TOKEN_SECRETLEN, sa, salen, + 3600 * NGTCP2_SECONDS, t) != 0) { return -1; } diff --git a/src/shrpx_quic.h b/src/shrpx_quic.h index 39480446..2fa12e2d 100644 --- a/src/shrpx_quic.h +++ b/src/shrpx_quic.h @@ -62,24 +62,9 @@ constexpr size_t SHRPX_QUIC_CID_PREFIXLEN = 8; constexpr size_t SHRPX_QUIC_MAX_UDP_PAYLOAD_SIZE = 1472; constexpr size_t SHRPX_QUIC_STATELESS_RESET_SECRETLEN = 32; constexpr size_t SHRPX_QUIC_TOKEN_SECRETLEN = 32; -constexpr size_t SHRPX_QUIC_TOKEN_RAND_DATALEN = 16; constexpr size_t SHRPX_QUIC_CONN_CLOSE_PKTLEN = 256; constexpr size_t SHRPX_QUIC_STATELESS_RESET_BURST = 100; -// SHRPX_QUIC_RETRY_TOKEN_MAGIC is the magic byte of Retry token. -// Sent in plaintext. -constexpr uint8_t SHRPX_QUIC_RETRY_TOKEN_MAGIC = 0xb6; -constexpr size_t SHRPX_QUIC_MAX_RETRY_TOKENLEN = - /* magic */ 1 + sizeof(uint64_t) + NGTCP2_MAX_CIDLEN + - /* aead tag */ 16 + SHRPX_QUIC_TOKEN_RAND_DATALEN; - -// SHRPX_QUIC_TOKEN_MAGIC is the magic byte of token which is sent in -// NEW_TOKEN frame. Sent in plaintext. -constexpr uint8_t SHRPX_QUIC_TOKEN_MAGIC = 0x36; -constexpr size_t SHRPX_QUIC_MAX_TOKENLEN = - /* magic */ 1 + sizeof(uint64_t) + /* aead tag */ 16 + - SHRPX_QUIC_TOKEN_RAND_DATALEN; - ngtcp2_tstamp quic_timestamp(); int quic_send_packet(const UpstreamAddr *faddr, const sockaddr *remote_sa, diff --git a/src/shrpx_quic_connection_handler.cc b/src/shrpx_quic_connection_handler.cc index 1d1fae40..f7341913 100644 --- a/src/shrpx_quic_connection_handler.cc +++ b/src/shrpx_quic_connection_handler.cc @@ -152,7 +152,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr, auto &secret = quic_secret->token_secret; switch (hd.token.base[0]) { - case SHRPX_QUIC_RETRY_TOKEN_MAGIC: + case NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY: if (verify_retry_token(&odcid, hd.token.base, hd.token.len, &hd.dcid, &remote_addr.su.sa, remote_addr.len, secret.data()) != 0) { @@ -178,7 +178,7 @@ int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr, tokenlen = hd.token.len; break; - case SHRPX_QUIC_TOKEN_MAGIC: + case NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR: if (verify_token(hd.token.base, hd.token.len, &remote_addr.su.sa, remote_addr.len, secret.data()) != 0) { if (LOG_ENABLED(INFO)) { @@ -356,8 +356,8 @@ int QUICConnectionHandler::send_retry( return -1; } - std::array token; - size_t tokenlen = token.size(); + std::array token; + size_t tokenlen; ngtcp2_cid idcid, iscid; ngtcp2_cid_init(&idcid, ini_dcid, ini_dcidlen);