From 43327f1aa33d7b4a072445bbf9e4603173fbde5e Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> Date: Thu, 24 May 2018 18:11:27 +0900 Subject: [PATCH] Packet number encryption --- examples/client.cc | 125 ++++++++++-- examples/client.h | 7 + examples/crypto.cc | 21 ++ examples/crypto.h | 21 ++ examples/crypto_openssl.cc | 44 ++++- examples/debug.cc | 8 + examples/debug.h | 3 + examples/server.cc | 125 ++++++++++-- examples/server.h | 7 + lib/includes/ngtcp2/ngtcp2.h | 79 ++++---- lib/ngtcp2_conn.c | 366 +++++++++++++++++++++-------------- lib/ngtcp2_conn.h | 3 - lib/ngtcp2_conv.c | 61 ++++++ lib/ngtcp2_conv.h | 19 ++ lib/ngtcp2_crypto.c | 9 +- lib/ngtcp2_crypto.h | 9 +- lib/ngtcp2_log.c | 22 +-- lib/ngtcp2_pkt.c | 117 ++++------- lib/ngtcp2_pkt.h | 17 +- lib/ngtcp2_ppe.c | 51 +++-- lib/ngtcp2_ppe.h | 12 +- tests/ngtcp2_conn_test.c | 157 +++++++++------ tests/ngtcp2_pkt_test.c | 103 +++++----- tests/ngtcp2_rtb_test.c | 32 +-- tests/ngtcp2_test_helper.c | 91 ++++++++- tests/ngtcp2_test_helper.h | 24 +++ 26 files changed, 1065 insertions(+), 468 deletions(-) diff --git a/examples/client.cc b/examples/client.cc index 5a071e61..ecf7b66a 100644 --- a/examples/client.cc +++ b/examples/client.cc @@ -335,21 +335,16 @@ void Client::close() { namespace { ssize_t send_client_initial(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, const uint8_t **pdest, + const uint8_t **pdest, int initial, void *user_data) { auto c = static_cast<Client *>(user_data); - if (c->tls_handshake(ppkt_num) != 0) { + if (c->tls_handshake(initial) != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } c->handle_early_data(); - if (ppkt_num) { - *ppkt_num = std::uniform_int_distribution<uint64_t>( - 0, NGTCP2_MAX_INITIAL_PKT_NUM)(randgen); - } - auto len = c->read_client_handshake(pdest); return len; @@ -539,6 +534,41 @@ ssize_t do_decrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, } } // namespace +namespace { +ssize_t do_hs_encrypt_pn(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen, + void *user_data) { + auto c = static_cast<Client *>(user_data); + + auto nwrite = c->hs_encrypt_pn(dest, destlen, plaintext, plaintextlen, key, + keylen, nonce, noncelen); + if (nwrite < 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return nwrite; +} +} // namespace + +namespace { +ssize_t do_encrypt_pn(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, size_t keylen, const uint8_t *nonce, + size_t noncelen, void *user_data) { + auto c = static_cast<Client *>(user_data); + + auto nwrite = c->encrypt_pn(dest, destlen, plaintext, plaintextlen, key, + keylen, nonce, noncelen); + if (nwrite < 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return nwrite; +} +} // namespace + int Client::init(int fd, const Address &remote_addr, const char *addr, int datafd, uint32_t version) { int rv; @@ -626,6 +656,8 @@ int Client::init(int fd, const Address &remote_addr, const char *addr, do_hs_decrypt, do_encrypt, do_decrypt, + do_hs_encrypt_pn, + do_encrypt_pn, recv_stream_data, acked_stream_data_offset, stream_close, @@ -709,7 +741,7 @@ int Client::setup_handshake_crypto_context() { return -1; } - std::array<uint8_t, 16> key, iv; + std::array<uint8_t, 16> key, iv, pn; auto keylen = crypto::derive_packet_protection_key( key.data(), key.size(), secret.data(), secret.size(), hs_crypto_ctx_); @@ -723,14 +755,21 @@ int Client::setup_handshake_crypto_context() { return -1; } + auto pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), secret.data(), secret.size(), hs_crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_client_hs_secret(secret.data(), secret.size()); debug::print_client_pp_key(key.data(), keylen); debug::print_client_pp_iv(iv.data(), ivlen); + debug::print_client_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_set_handshake_tx_keys(conn_, key.data(), keylen, iv.data(), - ivlen); + ngtcp2_conn_set_handshake_tx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); rv = crypto::derive_server_handshake_secret(secret.data(), secret.size(), handshake_secret.data(), @@ -752,14 +791,21 @@ int Client::setup_handshake_crypto_context() { return -1; } + pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), secret.data(), secret.size(), hs_crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_server_hs_secret(secret.data(), secret.size()); debug::print_server_pp_key(key.data(), keylen); debug::print_server_pp_iv(iv.data(), ivlen); + debug::print_server_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_set_handshake_rx_keys(conn_, key.data(), keylen, iv.data(), - ivlen); + ngtcp2_conn_set_handshake_rx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); return 0; } @@ -893,6 +939,8 @@ ssize_t Client::do_handshake_once(const uint8_t *data, size_t datalen) { if (nwrite < 0) { switch (nwrite) { case NGTCP2_ERR_TLS_DECRYPT: + std::cerr << "ngtcp2_conn_handshake: " << ngtcp2_strerror(nwrite) + << std::endl; case NGTCP2_ERR_NOBUF: return 0; } @@ -1255,7 +1303,7 @@ int Client::setup_early_crypto_context() { return -1; } - std::array<uint8_t, 64> key, iv; + std::array<uint8_t, 64> key, iv, pn; auto keylen = crypto::derive_packet_protection_key( key.data(), key.size(), crypto_ctx_.tx_secret.data(), @@ -1271,14 +1319,23 @@ int Client::setup_early_crypto_context() { return -1; } + auto pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), crypto_ctx_.tx_secret.data(), crypto_ctx_.secretlen, + crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_client_0rtt_secret(crypto_ctx_.tx_secret.data(), crypto_ctx_.secretlen); debug::print_client_pp_key(key.data(), keylen); debug::print_client_pp_iv(iv.data(), ivlen); + debug::print_client_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_update_early_keys(conn_, key.data(), keylen, iv.data(), ivlen); + ngtcp2_conn_update_early_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); ngtcp2_conn_set_aead_overhead(conn_, crypto::aead_max_overhead(crypto_ctx_)); @@ -1307,7 +1364,7 @@ int Client::setup_crypto_context() { return -1; } - std::array<uint8_t, 64> key{}, iv{}; + std::array<uint8_t, 64> key, iv, pn; auto keylen = crypto::derive_packet_protection_key( key.data(), key.size(), crypto_ctx_.tx_secret.data(), @@ -1323,14 +1380,23 @@ int Client::setup_crypto_context() { return -1; } + auto pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), crypto_ctx_.tx_secret.data(), crypto_ctx_.secretlen, + crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_client_1rtt_secret(crypto_ctx_.tx_secret.data(), crypto_ctx_.secretlen); debug::print_client_pp_key(key.data(), keylen); debug::print_client_pp_iv(iv.data(), ivlen); + debug::print_client_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_update_tx_keys(conn_, key.data(), keylen, iv.data(), ivlen); + ngtcp2_conn_update_tx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); rv = crypto::export_server_secret(crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen, ssl_); @@ -1352,14 +1418,23 @@ int Client::setup_crypto_context() { return -1; } + pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen, + crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_server_1rtt_secret(crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen); debug::print_server_pp_key(key.data(), keylen); debug::print_server_pp_iv(iv.data(), ivlen); + debug::print_server_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_update_rx_keys(conn_, key.data(), keylen, iv.data(), ivlen); + ngtcp2_conn_update_rx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); ngtcp2_conn_set_aead_overhead(conn_, crypto::aead_max_overhead(crypto_ctx_)); @@ -1403,6 +1478,22 @@ ssize_t Client::decrypt_data(uint8_t *dest, size_t destlen, key, keylen, nonce, noncelen, ad, adlen); } +ssize_t Client::hs_encrypt_pn(uint8_t *dest, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen) { + return crypto::encrypt_pn(dest, destlen, ciphertext, ciphertextlen, + hs_crypto_ctx_, key, keylen, nonce, noncelen); +} + +ssize_t Client::encrypt_pn(uint8_t *dest, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen) { + return crypto::encrypt_pn(dest, destlen, ciphertext, ciphertextlen, + crypto_ctx_, key, keylen, nonce, noncelen); +} + ngtcp2_conn *Client::conn() const { return conn_; } int Client::send_packet() { diff --git a/examples/client.h b/examples/client.h index a69dd783..23bf63d6 100644 --- a/examples/client.h +++ b/examples/client.h @@ -182,6 +182,13 @@ public: size_t ciphertextlen, const uint8_t *key, size_t keylen, const uint8_t *nonce, size_t noncelen, const uint8_t *ad, size_t adlen); + ssize_t hs_encrypt_pn(uint8_t *data, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, const uint8_t *nonce, + size_t noncelen); + ssize_t encrypt_pn(uint8_t *data, size_t destlen, const uint8_t *ciphertext, + size_t ciphertextlen, const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen); ngtcp2_conn *conn() const; int send_packet(); int start_interactive_input(); diff --git a/examples/crypto.cc b/examples/crypto.cc index c961d99c..7ad7d2aa 100644 --- a/examples/crypto.cc +++ b/examples/crypto.cc @@ -148,6 +148,27 @@ ssize_t derive_packet_protection_iv(uint8_t *dest, size_t destlen, return ivlen; } +ssize_t derive_pkt_num_protection_key(uint8_t *dest, size_t destlen, + const uint8_t *secret, size_t secretlen, + const Context &ctx) { + int rv; + static constexpr uint8_t LABEL_PKNKEY[] = "pn"; + + auto keylen = aead_key_length(ctx); + if (keylen > destlen) { + return -1; + } + + rv = crypto::qhkdf_expand(dest, keylen, secret, secretlen, LABEL_PKNKEY, + str_size(LABEL_PKNKEY), ctx); + + if (rv != 0) { + return -1; + } + + return keylen; +} + int qhkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *secret, size_t secretlen, const uint8_t *qlabel, size_t qlabellen, const Context &ctx) { diff --git a/examples/crypto.h b/examples/crypto.h index ddf36b18..615074e6 100644 --- a/examples/crypto.h +++ b/examples/crypto.h @@ -45,6 +45,7 @@ struct Context { #else // !OPENSSL_IS_BORINGSSL const EVP_CIPHER *aead; #endif // !OPENSSL_IS_BORINGSSL + const EVP_CIPHER *pn; const EVP_MD *prf; std::array<uint8_t, 64> tx_secret, rx_secret; size_t secretlen; @@ -110,6 +111,14 @@ ssize_t derive_packet_protection_iv(uint8_t *dest, size_t destlen, const uint8_t *secret, size_t secretlen, const Context &ctx); +// derive_pkt_num_protection_key derives and stores the packet number +// protection key in the buffer pointed by |dest| of length |destlen|, +// and the key size is returned. This function returns the key length +// if it succeeds, or -1. +ssize_t derive_pkt_num_protection_key(uint8_t *dest, size_t destlen, + const uint8_t *secret, size_t secretlen, + const Context &ctx); + // encrypt encrypts |plaintext| of length |plaintextlen| and writes // the encrypted data in the buffer pointed by |dest| of length // |destlen|. This function can encrypt data in-place. In other @@ -139,6 +148,18 @@ size_t aead_key_length(const Context &ctx); // aead_nonce_length returns the nonce size of ctx.aead. size_t aead_nonce_length(const Context &ctx); +// encrypt_pn encrypts |plaintext| of length |plaintextlen| and writes +// the encrypted data in the buffer pointed by |dest| of length +// |destlen|. This function can encrypt data in-place. In other +// words, |dest| == |plaintext| is allowed. This function returns the +// number of bytes written if it succeeds, or -1. +// +// Use this function to decrypt ciphertext. Just pass ciphertext as +// |plaintext|. +ssize_t encrypt_pn(uint8_t *dest, size_t destlen, const uint8_t *plaintext, + size_t plaintextlen, const Context &ctx, const uint8_t *key, + size_t keylen, const uint8_t *nonce, size_t noncelen); + // hkdf_expand performs HKDF-expand. This function returns 0 if it // succeeds, or -1. int hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *secret, diff --git a/examples/crypto_openssl.cc b/examples/crypto_openssl.cc index 7050cad5..1495c1a2 100644 --- a/examples/crypto_openssl.cc +++ b/examples/crypto_openssl.cc @@ -55,12 +55,15 @@ int negotiated_aead(Context &ctx, SSL *ssl) { switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { case 0x03001301u: // TLS_AES_128_GCM_SHA256 ctx.aead = EVP_aes_128_gcm(); + ctx.pn = EVP_aes_128_ctr(); return 0; case 0x03001302u: // TLS_AES_256_GCM_SHA384 ctx.aead = EVP_aes_256_gcm(); + ctx.pn = EVP_aes_256_ctr(); return 0; case 0x03001303u: // TLS_CHACHA20_POLY1305_SHA256 ctx.aead = EVP_chacha20_poly1305(); + ctx.pn = EVP_chacha20(); return 0; default: return -1; @@ -208,6 +211,42 @@ size_t aead_nonce_length(const Context &ctx) { return EVP_CIPHER_iv_length(ctx.aead); } +ssize_t encrypt_pn(uint8_t *dest, size_t destlen, const uint8_t *plaintext, + size_t plaintextlen, const Context &ctx, const uint8_t *key, + size_t keylen, const uint8_t *nonce, size_t noncelen) { + auto actx = EVP_CIPHER_CTX_new(); + if (actx == nullptr) { + return -1; + } + + auto actx_d = defer(EVP_CIPHER_CTX_free, actx); + + if (EVP_EncryptInit_ex(actx, ctx.pn, nullptr, key, nonce) != 1) { + return -1; + } + + size_t outlen = 0; + int len; + + if (EVP_EncryptUpdate(actx, dest, &len, plaintext, plaintextlen) != 1) { + return -1; + } + + assert(len > 0); + + outlen = len; + + if (EVP_EncryptFinal_ex(actx, dest + outlen, &len) != 1) { + return -1; + } + + assert(len == 0); + + /* outlen += len; */ + + return outlen; +} + int hkdf_expand(uint8_t *dest, size_t destlen, const uint8_t *secret, size_t secretlen, const uint8_t *info, size_t infolen, const Context &ctx) { @@ -288,7 +327,10 @@ int hkdf_extract(uint8_t *dest, size_t destlen, const uint8_t *secret, void prf_sha256(Context &ctx) { ctx.prf = EVP_sha256(); } -void aead_aes_128_gcm(Context &ctx) { ctx.aead = EVP_aes_128_gcm(); } +void aead_aes_128_gcm(Context &ctx) { + ctx.aead = EVP_aes_128_gcm(); + ctx.pn = EVP_aes_128_ctr(); +} } // namespace crypto diff --git a/examples/debug.cc b/examples/debug.cc index 3fc7b634..c1b73e1b 100644 --- a/examples/debug.cc +++ b/examples/debug.cc @@ -108,6 +108,14 @@ void print_server_pp_iv(const uint8_t *data, size_t len) { fprintf(outfile, "+ server_pp_iv=%s\n", util::format_hex(data, len).c_str()); } +void print_client_pp_pn(const uint8_t *data, size_t len) { + fprintf(outfile, "+ client_pp_pn=%s\n", util::format_hex(data, len).c_str()); +} + +void print_server_pp_pn(const uint8_t *data, size_t len) { + fprintf(outfile, "+ server_pp_pn=%s\n", util::format_hex(data, len).c_str()); +} + void log_printf(void *user_data, const char *fmt, ...) { va_list ap; diff --git a/examples/debug.h b/examples/debug.h index 3483722f..dd22a62d 100644 --- a/examples/debug.h +++ b/examples/debug.h @@ -65,6 +65,9 @@ void print_server_pp_key(const uint8_t *data, size_t len); void print_client_pp_iv(const uint8_t *data, size_t len); void print_server_pp_iv(const uint8_t *data, size_t len); +void print_client_pp_pn(const uint8_t *data, size_t len); +void print_server_pp_pn(const uint8_t *data, size_t len); + void log_printf(void *user_data, const char *fmt, ...); } // namespace debug diff --git a/examples/server.cc b/examples/server.cc index f2b9d9e7..14f9556b 100644 --- a/examples/server.cc +++ b/examples/server.cc @@ -595,20 +595,15 @@ int recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid, namespace { ssize_t send_server_handshake(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, const uint8_t **pdest, + const uint8_t **pdest, int initial, void *user_data) { auto h = static_cast<Handler *>(user_data); - if (ppkt_num) { - *ppkt_num = std::uniform_int_distribution<uint64_t>( - 0, NGTCP2_MAX_INITIAL_PKT_NUM)(randgen); - } - auto len = h->read_server_handshake(pdest); // If Initial packet does not have complete ClientHello, then drop // connection. - if (ppkt_num && len == 0) { + if (initial && len == 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -702,6 +697,41 @@ ssize_t do_decrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, } } // namespace +namespace { +ssize_t do_hs_encrypt_pn(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen, + void *user_data) { + auto h = static_cast<Handler *>(user_data); + + auto nwrite = h->hs_encrypt_pn(dest, destlen, plaintext, plaintextlen, key, + keylen, nonce, noncelen); + if (nwrite < 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return nwrite; +} +} // namespace + +namespace { +ssize_t do_encrypt_pn(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, size_t keylen, const uint8_t *nonce, + size_t noncelen, void *user_data) { + auto h = static_cast<Handler *>(user_data); + + auto nwrite = h->encrypt_pn(dest, destlen, plaintext, plaintextlen, key, + keylen, nonce, noncelen); + if (nwrite < 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return nwrite; +} +} // namespace + namespace { int recv_stream0_data(ngtcp2_conn *conn, uint64_t offset, const uint8_t *data, size_t datalen, void *user_data) { @@ -810,6 +840,8 @@ int Handler::init(int fd, const sockaddr *sa, socklen_t salen, do_hs_decrypt, do_encrypt, do_decrypt, + do_hs_encrypt_pn, + do_encrypt_pn, ::recv_stream_data, acked_stream_data_offset, stream_close, @@ -1045,7 +1077,7 @@ int Handler::recv_client_initial(const ngtcp2_cid *dcid) { return -1; } - std::array<uint8_t, 16> key, iv; + std::array<uint8_t, 16> key, iv, pn; auto keylen = crypto::derive_packet_protection_key( key.data(), key.size(), secret.data(), secret.size(), hs_crypto_ctx_); if (keylen < 0) { @@ -1058,14 +1090,21 @@ int Handler::recv_client_initial(const ngtcp2_cid *dcid) { return -1; } + auto pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), secret.data(), secret.size(), hs_crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_server_hs_secret(secret.data(), secret.size()); debug::print_server_pp_key(key.data(), keylen); debug::print_server_pp_iv(iv.data(), ivlen); + debug::print_server_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_set_handshake_tx_keys(conn_, key.data(), keylen, iv.data(), - ivlen); + ngtcp2_conn_set_handshake_tx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); rv = crypto::derive_client_handshake_secret(secret.data(), secret.size(), handshake_secret.data(), @@ -1087,14 +1126,21 @@ int Handler::recv_client_initial(const ngtcp2_cid *dcid) { return -1; } + pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), secret.data(), secret.size(), hs_crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_client_hs_secret(secret.data(), secret.size()); debug::print_client_pp_key(key.data(), keylen); debug::print_client_pp_iv(iv.data(), ivlen); + debug::print_client_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_set_handshake_rx_keys(conn_, key.data(), keylen, iv.data(), - ivlen); + ngtcp2_conn_set_handshake_rx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); return 0; } @@ -1121,7 +1167,7 @@ int Handler::setup_early_crypto_context() { return -1; } - std::array<uint8_t, 64> key, iv; + std::array<uint8_t, 64> key, iv, pn; auto keylen = crypto::derive_packet_protection_key( key.data(), key.size(), crypto_ctx_.rx_secret.data(), @@ -1137,14 +1183,23 @@ int Handler::setup_early_crypto_context() { return -1; } + auto pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen, + crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_client_0rtt_secret(crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen); debug::print_client_pp_key(key.data(), keylen); debug::print_client_pp_iv(iv.data(), ivlen); + debug::print_client_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_update_early_keys(conn_, key.data(), keylen, iv.data(), ivlen); + ngtcp2_conn_update_early_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); ngtcp2_conn_set_aead_overhead(conn_, crypto::aead_max_overhead(crypto_ctx_)); @@ -1173,7 +1228,7 @@ int Handler::setup_crypto_context() { return -1; } - std::array<uint8_t, 64> key{}, iv{}; + std::array<uint8_t, 64> key, iv, pn; auto keylen = crypto::derive_packet_protection_key( key.data(), key.size(), crypto_ctx_.tx_secret.data(), @@ -1189,14 +1244,23 @@ int Handler::setup_crypto_context() { return -1; } + auto pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), crypto_ctx_.tx_secret.data(), crypto_ctx_.secretlen, + crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_server_1rtt_secret(crypto_ctx_.tx_secret.data(), crypto_ctx_.secretlen); debug::print_server_pp_key(key.data(), keylen); debug::print_server_pp_iv(iv.data(), ivlen); + debug::print_server_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_update_tx_keys(conn_, key.data(), keylen, iv.data(), ivlen); + ngtcp2_conn_update_tx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); rv = crypto::export_client_secret(crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen, ssl_); @@ -1218,14 +1282,23 @@ int Handler::setup_crypto_context() { return -1; } + pnlen = crypto::derive_pkt_num_protection_key( + pn.data(), pn.size(), crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen, + crypto_ctx_); + if (pnlen < 0) { + return -1; + } + if (!config.quiet && config.show_secret) { debug::print_client_1rtt_secret(crypto_ctx_.rx_secret.data(), crypto_ctx_.secretlen); debug::print_client_pp_key(key.data(), keylen); debug::print_client_pp_iv(iv.data(), ivlen); + debug::print_client_pp_pn(pn.data(), pnlen); } - ngtcp2_conn_update_rx_keys(conn_, key.data(), keylen, iv.data(), ivlen); + ngtcp2_conn_update_rx_keys(conn_, key.data(), keylen, iv.data(), ivlen, + pn.data(), pnlen); ngtcp2_conn_set_aead_overhead(conn_, crypto::aead_max_overhead(crypto_ctx_)); @@ -1270,12 +1343,30 @@ ssize_t Handler::decrypt_data(uint8_t *dest, size_t destlen, key, keylen, nonce, noncelen, ad, adlen); } +ssize_t Handler::hs_encrypt_pn(uint8_t *dest, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen) { + return crypto::encrypt_pn(dest, destlen, ciphertext, ciphertextlen, + hs_crypto_ctx_, key, keylen, nonce, noncelen); +} + +ssize_t Handler::encrypt_pn(uint8_t *dest, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen) { + return crypto::encrypt_pn(dest, destlen, ciphertext, ciphertextlen, + crypto_ctx_, key, keylen, nonce, noncelen); +} + ssize_t Handler::do_handshake_once(const uint8_t *data, size_t datalen) { auto nwrite = ngtcp2_conn_handshake(conn_, sendbuf_.wpos(), max_pktlen_, data, datalen, util::timestamp(loop_)); if (nwrite < 0) { switch (nwrite) { case NGTCP2_ERR_TLS_DECRYPT: + std::cerr << "ngtcp2_conn_handshake: " << ngtcp2_strerror(nwrite) + << std::endl; case NGTCP2_ERR_NOBUF: return 0; } diff --git a/examples/server.h b/examples/server.h index f33147e5..04fe6c5f 100644 --- a/examples/server.h +++ b/examples/server.h @@ -199,6 +199,13 @@ public: size_t ciphertextlen, const uint8_t *key, size_t keylen, const uint8_t *nonce, size_t noncelen, const uint8_t *ad, size_t adlen); + ssize_t hs_encrypt_pn(uint8_t *dest, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, const uint8_t *nonce, + size_t noncelen); + ssize_t encrypt_pn(uint8_t *dest, size_t destlen, const uint8_t *ciphertext, + size_t ciphertextlen, const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen); Server *server() const; const Address &remote_addr() const; ngtcp2_conn *conn() const; diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h index a9ef0ec2..2d512ee7 100644 --- a/lib/includes/ngtcp2/ngtcp2.h +++ b/lib/includes/ngtcp2/ngtcp2.h @@ -236,9 +236,8 @@ typedef enum { NGTCP2_PKT_RETRY = 0x7E, NGTCP2_PKT_HANDSHAKE = 0x7D, NGTCP2_PKT_0RTT_PROTECTED = 0x7C, - NGTCP2_PKT_01 = 0x00, - NGTCP2_PKT_02 = 0x01, - NGTCP2_PKT_03 = 0x02 + /* NGTCP2_PKT_SHORT is defined by libngtcp2 for convenience. */ + NGTCP2_PKT_SHORT = 0x00 } ngtcp2_pkt_type; typedef enum { @@ -316,7 +315,15 @@ typedef struct { ngtcp2_cid dcid; ngtcp2_cid scid; uint64_t pkt_num; - size_t payloadlen; + /** + * pkt_numlen is the number of bytes spent to encode pkt_num. + */ + size_t pkt_numlen; + /** + * len is the sum of pkt_numlen and the length of QUIC packet + * payload. + */ + size_t len; uint32_t version; uint8_t type; uint8_t flags; @@ -640,11 +647,11 @@ ngtcp2_decode_transport_params(ngtcp2_transport_params *params, uint8_t exttype, * @function * * `ngtcp2_pkt_decode_hd_long` decodes QUIC long packet header in - * |pkt| of length |pktlen|. + * |pkt| of length |pktlen|. This function only parses the input just + * before packet number field. * - * This function does not verify that payload length is correct. In - * other words, this function succeeds even if payload length > - * |pktlen|. + * This function does not verify that length field is correct. In + * other words, this function succeeds even if length > |pktlen|. * * This function handles Version Negotiation specially. If version * field is 0, |pkt| must contain Version Negotiation packet. Version @@ -673,10 +680,11 @@ NGTCP2_EXTERN ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in * packet header. Short packet does not encode the length of - * connection ID, thus we need the input from the outside. It stores - * the result in the object pointed by |dest|, and returns the number - * of bytes decoded to read the packet header if it succeeds, or one - * of the following error codes: + * connection ID, thus we need the input from the outside. This + * function only parses the input just before packet number field. It + * stores the result in the object pointed by |dest|, and returns the + * number of bytes decoded to read the packet header if it succeeds, + * or one of the following error codes: * * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` * Packet is too short; or it is not a short header @@ -772,9 +780,8 @@ struct ngtcp2_conn; typedef struct ngtcp2_conn ngtcp2_conn; typedef ssize_t (*ngtcp2_send_client_initial)(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, const uint8_t **pdest, - void *user_data); + int initial, void *user_data); typedef ssize_t (*ngtcp2_send_client_handshake)(ngtcp2_conn *conn, uint32_t flags, @@ -804,9 +811,8 @@ typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, typedef ssize_t (*ngtcp2_send_server_handshake)(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, const uint8_t **pdest, - void *user_data); + int initial, void *user_data); /** * @functypedef @@ -874,6 +880,12 @@ typedef ssize_t (*ngtcp2_decrypt)(ngtcp2_conn *conn, uint8_t *dest, size_t noncelen, const uint8_t *ad, size_t adlen, void *user_data); +typedef ssize_t (*ngtcp2_encrypt_pn)(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, const uint8_t *plaintext, + size_t plaintextlen, const uint8_t *key, + size_t keylen, const uint8_t *nonce, + size_t noncelen, void *user_data); + typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint64_t stream_id, uint8_t fin, uint64_t offset, const uint8_t *data, size_t datalen, @@ -952,6 +964,8 @@ typedef struct { ngtcp2_decrypt hs_decrypt; ngtcp2_encrypt encrypt; ngtcp2_decrypt decrypt; + ngtcp2_encrypt_pn hs_encrypt_pn; + ngtcp2_encrypt_pn encrypt_pn; ngtcp2_recv_stream_data recv_stream_data; ngtcp2_acked_stream_data_offset acked_stream_data_offset; ngtcp2_stream_close stream_close; @@ -1154,8 +1168,8 @@ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); /** * @function * - * `ngtcp2_conn_set_handshake_tx_keys` sets key and iv to encrypt - * handshake packets. If key and iv have already been set, they are + * `ngtcp2_conn_set_handshake_tx_keys` sets key, iv, and pn to encrypt + * handshake packets. If they have already been set, they are * overwritten. * * This function returns 0 if it succeeds, or one of the following @@ -1164,17 +1178,15 @@ NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); * :enum:`NGTCP2_ERR_NOMEM` * Out of memory. */ -NGTCP2_EXTERN int ngtcp2_conn_set_handshake_tx_keys(ngtcp2_conn *conn, - const uint8_t *key, - size_t keylen, - const uint8_t *iv, - size_t ivlen); +NGTCP2_EXTERN int ngtcp2_conn_set_handshake_tx_keys( + ngtcp2_conn *conn, const uint8_t *key, size_t keylen, const uint8_t *iv, + size_t ivlen, const uint8_t *pn, size_t pnlen); /** * @function * - * `ngtcp2_conn_set_handshake_rx_keys` sets key and iv to decrypt - * handshake packets. If key and iv have already been set, they are + * `ngtcp2_conn_set_handshake_rx_keys` sets key, iv and pn to decrypt + * handshake packets. If they have already been set, they are * overwritten. * * This function returns 0 if it succeeds, or one of the following @@ -1183,26 +1195,27 @@ NGTCP2_EXTERN int ngtcp2_conn_set_handshake_tx_keys(ngtcp2_conn *conn, * :enum:`NGTCP2_ERR_NOMEM` * Out of memory. */ -NGTCP2_EXTERN int ngtcp2_conn_set_handshake_rx_keys(ngtcp2_conn *conn, - const uint8_t *key, - size_t keylen, - const uint8_t *iv, - size_t ivlen); +NGTCP2_EXTERN int ngtcp2_conn_set_handshake_rx_keys( + ngtcp2_conn *conn, const uint8_t *key, size_t keylen, const uint8_t *iv, + size_t ivlen, const uint8_t *pn, size_t pnlen); NGTCP2_EXTERN void ngtcp2_conn_set_aead_overhead(ngtcp2_conn *conn, size_t aead_overhead); NGTCP2_EXTERN int ngtcp2_conn_update_early_keys(ngtcp2_conn *conn, const uint8_t *key, - size_t keylen, const uint8_t *iv, size_t ivlen); + size_t keylen, const uint8_t *iv, size_t ivlen, + const uint8_t *pn, size_t pnlen); NGTCP2_EXTERN int ngtcp2_conn_update_tx_keys(ngtcp2_conn *conn, const uint8_t *key, size_t keylen, - const uint8_t *iv, size_t ivlen); + const uint8_t *iv, size_t ivlen, + const uint8_t *pn, size_t pnlen); NGTCP2_EXTERN int ngtcp2_conn_update_rx_keys(ngtcp2_conn *conn, const uint8_t *key, size_t keylen, - const uint8_t *iv, size_t ivlen); + const uint8_t *iv, size_t ivlen, + const uint8_t *pn, size_t pnlen); /** * @function diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c index 44392883..85e15481 100644 --- a/lib/ngtcp2_conn.c +++ b/lib/ngtcp2_conn.c @@ -237,6 +237,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->version = version; (*pconn)->mem = mem; (*pconn)->user_data = user_data; + (*pconn)->last_tx_pkt_num = (uint64_t)-1; (*pconn)->largest_ack = -1; (*pconn)->local_settings = *settings; (*pconn)->unsent_max_rx_offset = (*pconn)->max_rx_offset = settings->max_data; @@ -651,6 +652,7 @@ static ssize_t conn_retransmit_unprotected_once(ngtcp2_conn *conn, ctx.ckm = conn->hs_tx_ckm; ctx.aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; ctx.encrypt = conn->callbacks.hs_encrypt; + ctx.encrypt_pn = conn->callbacks.hs_encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -826,25 +828,27 @@ static ssize_t conn_retransmit_unprotected(ngtcp2_conn *conn, uint8_t *dest, } /* - * conn_select_pkt_type selects shorted short packet type based on the - * next packet number |pkt_num|. + * conn_select_pkt_numlen selects shortest packet number encoding + * based on the next packet number |pkt_num| and the largest + * acknowledged packet number. It returns the number of bytes to + * encode the packet number. */ -static uint8_t conn_select_pkt_type(ngtcp2_conn *conn, uint64_t pkt_num) { +static size_t conn_select_pkt_numlen(ngtcp2_conn *conn, uint64_t pkt_num) { uint64_t n = (uint64_t)((int64_t)pkt_num - conn->rtb.largest_acked_tx_pkt_num); if (UINT64_MAX / 2 <= pkt_num) { - return NGTCP2_PKT_03; + return 4; } n = n * 2 + 1; - if (n > 0xffff) { - return NGTCP2_PKT_03; + if (n > 0x3fff) { + return 4; } - if (n > 0xff) { - return NGTCP2_PKT_02; + if (n > 0x7f) { + return 2; } - return NGTCP2_PKT_01; + return 1; } /* @@ -880,11 +884,13 @@ static ssize_t conn_retransmit_protected_once(ngtcp2_conn *conn, uint8_t *dest, hd.version = conn->version; hd.dcid = conn->dcid; hd.pkt_num = conn->last_tx_pkt_num + 1; - hd.type = conn_select_pkt_type(conn, hd.pkt_num); + hd.pkt_numlen = conn_select_pkt_numlen(conn, hd.pkt_num); + hd.type = NGTCP2_PKT_SHORT; ctx.ckm = conn->tx_ckm; ctx.aead_overhead = conn->aead_overhead; ctx.encrypt = conn->callbacks.encrypt; + ctx.encrypt_pn = conn->callbacks.encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -1036,17 +1042,9 @@ static ssize_t conn_retransmit_protected(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_rtb_lost_protected_pop(&conn->rtb); assert(!(ent->hd.flags & NGTCP2_PKT_FLAG_LONG_FORM)); - switch (ent->hd.type) { - case NGTCP2_PKT_01: - case NGTCP2_PKT_02: - case NGTCP2_PKT_03: - nwrite = conn_retransmit_protected_once(conn, dest, destlen, ent, ts); - break; - default: - /* TODO fix this */ - ngtcp2_rtb_entry_del(ent, conn->mem); - return NGTCP2_ERR_INVALID_ARGUMENT; - } + assert(ent->hd.type == NGTCP2_PKT_SHORT); + + nwrite = conn_retransmit_protected_once(conn, dest, destlen, ent, ts); if (nwrite <= 0) { if (nwrite == 0) { @@ -1201,11 +1199,14 @@ static ssize_t conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, pfrc = &frc_head; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type, &conn->dcid, - &conn->scid, conn->last_tx_pkt_num + 1, conn->version, 0); + &conn->scid, conn->last_tx_pkt_num + 1, + conn_select_pkt_numlen(conn, conn->last_tx_pkt_num + 1), + conn->version, 0); ctx.ckm = conn->hs_tx_ckm; ctx.aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; ctx.encrypt = conn->callbacks.hs_encrypt; + ctx.encrypt_pn = conn->callbacks.hs_encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -1354,6 +1355,7 @@ static ssize_t conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, if (type == NGTCP2_PKT_INITIAL && (!conn->early_ckm || require_padding || ngtcp2_ppe_left(&ppe) < + /* TODO Assuming that pkt_num is encoded in 1 byte. */ NGTCP2_MIN_LONG_HEADERLEN + conn->rcid.datalen + conn->scid.datalen + 1 /* payloadlen bytes - 1 */ + 128 /* minimum data */ + NGTCP2_MAX_AEAD_OVERHEAD)) { @@ -1459,11 +1461,13 @@ static ssize_t conn_write_handshake_ack_pkt(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE, &conn->dcid, &conn->scid, conn->last_tx_pkt_num + 1, + conn_select_pkt_numlen(conn, conn->last_tx_pkt_num + 1), conn->version, 0); ctx.ckm = conn->hs_tx_ckm; ctx.aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; ctx.encrypt = conn->callbacks.hs_encrypt; + ctx.encrypt_pn = conn->callbacks.hs_encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -1511,15 +1515,13 @@ fail: static ssize_t conn_write_client_initial(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, int require_padding, ngtcp2_tstamp ts) { - uint64_t pkt_num = 0; const uint8_t *payload; ssize_t payloadlen; ngtcp2_buf *tx_buf = &conn->strm0->tx_buf; payloadlen = conn->callbacks.send_client_initial( - conn, NGTCP2_CONN_FLAG_NONE, - (conn->flags & NGTCP2_CONN_FLAG_STATELESS_RETRY) ? NULL : &pkt_num, - &payload, conn->user_data); + conn, NGTCP2_CONN_FLAG_NONE, &payload, + !(conn->flags & NGTCP2_CONN_FLAG_STATELESS_RETRY), conn->user_data); if (payloadlen <= 0) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -1528,11 +1530,6 @@ static ssize_t conn_write_client_initial(ngtcp2_conn *conn, uint8_t *dest, ngtcp2_buf_init(tx_buf, (uint8_t *)payload, (size_t)payloadlen); tx_buf->last += payloadlen; - if (!(conn->flags & NGTCP2_CONN_FLAG_STATELESS_RETRY)) { - conn->last_tx_pkt_num = pkt_num - 1; - conn->rtb.largest_acked_tx_pkt_num = (int64_t)conn->last_tx_pkt_num; - } - return conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, tx_buf, require_padding, ts); } @@ -1602,7 +1599,6 @@ static ssize_t conn_write_protected_ack_pkt(ngtcp2_conn *conn, uint8_t *dest, static ssize_t conn_write_server_handshake(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, int initial, ngtcp2_tstamp ts) { - uint64_t pkt_num = 0; const uint8_t *payload; ssize_t payloadlen; ngtcp2_buf *tx_buf = &conn->strm0->tx_buf; @@ -1610,8 +1606,7 @@ static ssize_t conn_write_server_handshake(ngtcp2_conn *conn, uint8_t *dest, if (ngtcp2_buf_len(tx_buf) == 0) { payloadlen = conn->callbacks.send_server_handshake( - conn, NGTCP2_CONN_FLAG_NONE, initial ? &pkt_num : NULL, &payload, - conn->user_data); + conn, NGTCP2_CONN_FLAG_NONE, &payload, initial, conn->user_data); if (payloadlen < 0) { return NGTCP2_ERR_CALLBACK_FAILURE; @@ -1641,11 +1636,6 @@ static ssize_t conn_write_server_handshake(ngtcp2_conn *conn, uint8_t *dest, tx_buf->last += payloadlen; } - if (initial) { - conn->last_tx_pkt_num = pkt_num - 1; - conn->rtb.largest_acked_tx_pkt_num = (int64_t)conn->last_tx_pkt_num; - } - return conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, tx_buf, 0 /*require_padding */, ts); } @@ -1778,14 +1768,15 @@ static ssize_t conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, return 0; } - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, - conn_select_pkt_type(conn, conn->last_tx_pkt_num + 1), - &conn->dcid, &conn->scid, conn->last_tx_pkt_num + 1, + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &conn->dcid, + &conn->scid, conn->last_tx_pkt_num + 1, + conn_select_pkt_numlen(conn, conn->last_tx_pkt_num + 1), conn->version, 0); ctx.ckm = conn->tx_ckm; ctx.aead_overhead = conn->aead_overhead; ctx.encrypt = conn->callbacks.encrypt; + ctx.encrypt_pn = conn->callbacks.encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -2011,14 +2002,15 @@ static ssize_t conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, ssize_t nwrite; ngtcp2_crypto_ctx ctx; - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, - conn_select_pkt_type(conn, conn->last_tx_pkt_num + 1), - &conn->dcid, &conn->scid, conn->last_tx_pkt_num + 1, + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &conn->dcid, + &conn->scid, conn->last_tx_pkt_num + 1, + conn_select_pkt_numlen(conn, conn->last_tx_pkt_num + 1), conn->version, 0); ctx.ckm = conn->tx_ckm; ctx.aead_overhead = conn->aead_overhead; ctx.encrypt = conn->callbacks.encrypt; + ctx.encrypt_pn = conn->callbacks.encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -2077,11 +2069,13 @@ static ssize_t conn_write_single_frame_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE, &conn->dcid, &conn->scid, conn->last_tx_pkt_num + 1, + conn_select_pkt_numlen(conn, conn->last_tx_pkt_num + 1), conn->version, 0); ctx.ckm = conn->hs_tx_ckm; ctx.aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; ctx.encrypt = conn->callbacks.hs_encrypt; + ctx.encrypt_pn = conn->callbacks.hs_encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -2645,6 +2639,45 @@ static ssize_t conn_decrypt_pkt(ngtcp2_conn *conn, uint8_t *dest, return nwrite; } +/* + * conn_decrypt_pn decryptes packet number which starts at |pkt| + + * |pkt_num_offset|. The entire plaintext QUIC packer header will be + * written to the buffer pointed by |dest|. This function assumes + * that |dest| has enough capacity to store the entire packet header. + */ +static ssize_t conn_decrypt_pn(ngtcp2_conn *conn, ngtcp2_pkt_hd *hd, + uint8_t *dest, const uint8_t *pkt, size_t pktlen, + size_t pkt_num_offset, ngtcp2_crypto_km *ckm, + ngtcp2_encrypt_pn enc, size_t aead_overhead) { + ssize_t nwrite; + size_t sample_offset; + uint8_t *p = dest; + + assert(enc); + assert(ckm); + assert(aead_overhead >= NGTCP2_PN_SAMPLELEN); + + if (pkt_num_offset + 1 + aead_overhead > pktlen) { + return NGTCP2_ERR_PROTO; + } + + p = ngtcp2_cpymem(p, pkt, pkt_num_offset); + + sample_offset = ngtcp2_min(pkt_num_offset + 4, pktlen - aead_overhead); + + nwrite = enc(conn, p, 4, pkt + pkt_num_offset, 4, ckm->pn, ckm->pnlen, + pkt + sample_offset, NGTCP2_PN_SAMPLELEN, conn->user_data); + if (nwrite != 4) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + hd->pkt_num = ngtcp2_get_pkt_num(&hd->pkt_numlen, p); + + p += hd->pkt_numlen; + + return p - dest; +} + static void conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm, size_t datalen) { if (strm->unsent_max_rx_offset <= NGTCP2_MAX_VARINT - datalen) { @@ -2822,6 +2855,23 @@ static int conn_ensure_retry_ack_initial(ngtcp2_conn *conn, return 0; } +/* + * pkt_num_bits returns the number of bits available when packet + * number is encoded in |pkt_numlen| bytes. + */ +static size_t pkt_num_bits(size_t pkt_numlen) { + switch (pkt_numlen) { + case 1: + return 7; + case 2: + return 14; + case 4: + return 30; + default: + assert(0); + } +} + /* * conn_recv_handshake_pkt processes received packet |pkt| whose * length if |pktlen| during handshake period. The buffer pointed by @@ -2866,11 +2916,11 @@ static ssize_t conn_recv_handshake_pkt(ngtcp2_conn *conn, const uint8_t *pkt, uint64_t rx_offset; int handshake_failed = 0; uint64_t fr_end_offset; - const uint8_t *hdpkt = pkt; size_t hdpktlen; const uint8_t *payload; size_t payloadlen; ssize_t nwrite; + uint8_t plain_hdpkt[256]; if (pktlen == 0) { return 0; @@ -2894,11 +2944,11 @@ static ssize_t conn_recv_handshake_pkt(ngtcp2_conn *conn, const uint8_t *pkt, return (int)nread; } - ngtcp2_log_pkt_hd(&conn->log, &hd); + if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) { + hdpktlen = (size_t)nread; - hdpktlen = (size_t)nread; + ngtcp2_log_pkt_hd(&conn->log, &hd); - if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) { if (conn->server) { return NGTCP2_ERR_PROTO; } @@ -2921,60 +2971,79 @@ static ssize_t conn_recv_handshake_pkt(ngtcp2_conn *conn, const uint8_t *pkt, return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; } - if (conn->version != hd.version) { + if (pktlen < (size_t)nread + hd.len) { return (ssize_t)pktlen; } - if (pktlen < hdpktlen + hd.payloadlen) { + pktlen = (size_t)nread + hd.len; + + if (conn->version != hd.version) { return (ssize_t)pktlen; } - payload = pkt + hdpktlen; - pktlen = hdpktlen + hd.payloadlen; - payloadlen = hd.payloadlen; + if (hd.type == NGTCP2_PKT_0RTT_PROTECTED) { + if (!conn->server) { + /* TODO protocol violation? */ + return (ssize_t)pktlen; + } + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + if (conn->early_ckm) { + ssize_t nread2; + /* TODO Avoid to parse header twice. */ + nread2 = conn_recv_pkt(conn, pkt, pktlen, ts); + if (nread2 < 0) { + return nread2; + } + } - if (conn->server && conn->early_ckm && ngtcp2_cid_eq(&conn->rcid, &hd.dcid) && - hd.type == NGTCP2_PKT_0RTT_PROTECTED) { - ssize_t nread2; - /* TODO Avoid to parse header twice. */ - nread2 = conn_recv_pkt(conn, pkt, pktlen, ts); - if (nread2 < 0) { - return nread2; + /* Discard 0-RTT packet if we don't have a key to decrypt it. */ + return (ssize_t)pktlen; + } else { + /* Buffer re-ordered 0-RTT Protected packet. */ + rv = conn_buffer_protected_pkt(conn, pkt, pktlen, ts); + if (rv != 0) { + return rv; + } + return (ssize_t)pktlen; } - return (ssize_t)pktlen; } - hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(conn->max_rx_pkt_num, hd.pkt_num, 32); + if (conn->server && hd.type == NGTCP2_PKT_INITIAL && + (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) { + conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED; + conn->rcid = hd.dcid; + + rv = conn_call_recv_client_initial(conn); + if (rv != 0) { + return rv; + } + } + + nwrite = conn_decrypt_pn(conn, &hd, plain_hdpkt, pkt, pktlen, (size_t)nread, + conn->hs_rx_ckm, conn->callbacks.hs_encrypt_pn, + NGTCP2_HANDSHAKE_AEAD_OVERHEAD); + if (nwrite < 0) { + return (ssize_t)nwrite; + } + + hdpktlen = (size_t)nwrite; + payload = pkt + hdpktlen; + payloadlen = hd.len - hd.pkt_numlen; + + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(conn->max_rx_pkt_num, hd.pkt_num, + pkt_num_bits(hd.pkt_numlen)); + + ngtcp2_log_pkt_hd(&conn->log, &hd); if (conn->server) { switch (hd.type) { case NGTCP2_PKT_INITIAL: - if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) { - conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED; - conn->rcid = hd.dcid; - - rv = conn_call_recv_client_initial(conn); - if (rv != 0) { - return rv; - } - } break; case NGTCP2_PKT_HANDSHAKE: if (!ngtcp2_cid_eq(&conn->scid, &hd.dcid)) { return (ssize_t)pktlen; } break; - case NGTCP2_PKT_0RTT_PROTECTED: - if (!(conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED)) { - /* Buffer re-ordered 0-RTT Protected packet. */ - rv = conn_buffer_protected_pkt(conn, pkt, pktlen, ts); - if (rv != 0) { - return rv; - } - return (ssize_t)pktlen; - } - /* Discard 0-RTT packet if we don't have a key to decrypt it. */ - return (ssize_t)pktlen; default: return NGTCP2_ERR_PROTO; } @@ -3019,7 +3088,7 @@ static ssize_t conn_recv_handshake_pkt(ngtcp2_conn *conn, const uint8_t *pkt, } nwrite = conn_decrypt_pkt(conn, conn->decrypt_buf.base, payloadlen, payload, - payloadlen, hdpkt, hdpktlen, hd.pkt_num, + payloadlen, plain_hdpkt, hdpktlen, hd.pkt_num, conn->hs_rx_ckm, conn->callbacks.hs_decrypt); if (nwrite < 0) { return (int)nwrite; @@ -3880,7 +3949,6 @@ static int conn_recv_max_stream_id(ngtcp2_conn *conn, static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) { ngtcp2_pkt_hd hd; - size_t pkt_num_bits; int rv = 0; const uint8_t *hdpkt = pkt; size_t hdpktlen; @@ -3891,6 +3959,9 @@ static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const uint8_t *pkt, ngtcp2_frame *fr = &mfr.fr; int require_ack = 0; ngtcp2_crypto_km *ckm; + uint8_t plain_hdpkt[256]; + ngtcp2_encrypt_pn encrypt_pn; + size_t aead_overhead; if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); @@ -3898,42 +3969,34 @@ static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const uint8_t *pkt, return nread; } - ngtcp2_log_pkt_hd(&conn->log, &hd); - if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) { + ngtcp2_log_pkt_hd(&conn->log, &hd); + /* Ignore late VN. */ return (ssize_t)pktlen; } - if (pktlen < (size_t)nread + hd.payloadlen) { + if (pktlen < (size_t)nread + hd.len) { return (ssize_t)pktlen; } - pktlen = (size_t)nread + hd.payloadlen; + pktlen = (size_t)nread + hd.len; if (conn->version != hd.version) { return (ssize_t)pktlen; } - switch (hd.type) { - case NGTCP2_PKT_INITIAL: - case NGTCP2_PKT_HANDSHAKE: - /* Ignore incoming unprotected packet after we get all - acknowledgements to unprotected packet we sent so far. */ - if (conn->final_hs_tx_offset && - ngtcp2_gaptr_first_gap_offset(&conn->strm0->acked_tx_offset) >= - conn->final_hs_tx_offset && - (!conn->server || - (conn->acktr.flags & NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK))) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_CON, - "unprotected packet %" PRIu64 - " is ignored because handshake has finished", - ngtcp2_pkt_adjust_pkt_num(conn->max_rx_pkt_num, hd.pkt_num, 32)); - + if (hd.type == NGTCP2_PKT_0RTT_PROTECTED) { + if (!conn->early_ckm) { return (ssize_t)pktlen; } - break; + ckm = conn->early_ckm; + encrypt_pn = conn->callbacks.encrypt_pn; + aead_overhead = conn->aead_overhead; + } else { + ckm = conn->hs_rx_ckm; + encrypt_pn = conn->callbacks.hs_encrypt_pn; + aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; } } else { nread = ngtcp2_pkt_decode_hd_short(&hd, pkt, pktlen, conn->scid.datalen); @@ -3941,38 +4004,45 @@ static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const uint8_t *pkt, return (int)nread; } - ngtcp2_log_pkt_hd(&conn->log, &hd); + ckm = conn->rx_ckm; + encrypt_pn = conn->callbacks.encrypt_pn; + aead_overhead = conn->aead_overhead; } - hdpktlen = (size_t)nread; + nwrite = conn_decrypt_pn(conn, &hd, plain_hdpkt, pkt, pktlen, (size_t)nread, + ckm, encrypt_pn, aead_overhead); + if (nwrite < 0) { + return (ssize_t)nwrite; + } + + hdpktlen = (size_t)nwrite; payload = pkt + hdpktlen; payloadlen = pktlen - hdpktlen; - if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { - pkt_num_bits = 32; - } else { - switch (hd.type) { - case NGTCP2_PKT_01: - pkt_num_bits = 8; - break; - case NGTCP2_PKT_02: - pkt_num_bits = 16; - break; - case NGTCP2_PKT_03: - pkt_num_bits = 32; - break; - default: - assert(0); - } - } + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(conn->max_rx_pkt_num, hd.pkt_num, + pkt_num_bits(hd.pkt_numlen)); - hd.pkt_num = - ngtcp2_pkt_adjust_pkt_num(conn->max_rx_pkt_num, hd.pkt_num, pkt_num_bits); + ngtcp2_log_pkt_hd(&conn->log, &hd); if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { switch (hd.type) { case NGTCP2_PKT_INITIAL: case NGTCP2_PKT_HANDSHAKE: + /* Ignore incoming unprotected packet after we get all + acknowledgements to unprotected packet we sent so far. */ + if (conn->final_hs_tx_offset && + ngtcp2_gaptr_first_gap_offset(&conn->strm0->acked_tx_offset) >= + conn->final_hs_tx_offset && + (!conn->server || + (conn->acktr.flags & NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK))) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "unprotected packet %" PRIu64 + " is ignored because handshake has finished", + hd.pkt_num); + + return (ssize_t)pktlen; + } + rv = conn_recv_delayed_handshake_pkt(conn, &hd, payload, payloadlen, hdpkt, hdpktlen, ts); if (rv != 0) { @@ -4003,7 +4073,7 @@ static ssize_t conn_recv_pkt(ngtcp2_conn *conn, const uint8_t *pkt, } nwrite = conn_decrypt_pkt(conn, conn->decrypt_buf.base, payloadlen, payload, - payloadlen, hdpkt, hdpktlen, hd.pkt_num, ckm, + payloadlen, plain_hdpkt, hdpktlen, hd.pkt_num, ckm, conn->callbacks.decrypt); if (nwrite < 0) { if (nwrite != NGTCP2_ERR_TLS_DECRYPT || @@ -4513,10 +4583,13 @@ static ssize_t conn_write_stream_early(ngtcp2_conn *conn, uint8_t *dest, ctx.ckm = conn->early_ckm; ngtcp2_pkt_hd_init(&hd, pkt_flags, pkt_type, &conn->rcid, &conn->scid, - conn->last_tx_pkt_num + 1, conn->version, 0); + conn->last_tx_pkt_num + 1, + conn_select_pkt_numlen(conn, conn->last_tx_pkt_num + 1), + conn->version, 0); ctx.aead_overhead = conn->aead_overhead; ctx.encrypt = conn->callbacks.encrypt; + ctx.encrypt_pn = conn->callbacks.encrypt_pn; ctx.user_data = conn; ngtcp2_ppe_init(&ppe, dest, destlen, &ctx); @@ -4588,7 +4661,7 @@ static ssize_t conn_write_stream_early(ngtcp2_conn *conn, uint8_t *dest, completes. This covers the case that 0-RTT data is rejected by the peer. 0-RTT packet is retransmitted as a Short packet. */ ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; - ent->hd.type = NGTCP2_PKT_01; + ent->hd.type = NGTCP2_PKT_SHORT; ngtcp2_list_insert(ent, &conn->early_rtb); @@ -4753,55 +4826,62 @@ void ngtcp2_conn_set_aead_overhead(ngtcp2_conn *conn, size_t aead_overhead) { int ngtcp2_conn_set_handshake_tx_keys(ngtcp2_conn *conn, const uint8_t *key, size_t keylen, const uint8_t *iv, - size_t ivlen) { + size_t ivlen, const uint8_t *pn, + size_t pnlen) { if (conn->hs_tx_ckm) { ngtcp2_crypto_km_del(conn->hs_tx_ckm, conn->mem); conn->hs_tx_ckm = NULL; } - return ngtcp2_crypto_km_new(&conn->hs_tx_ckm, key, keylen, iv, ivlen, - conn->mem); + return ngtcp2_crypto_km_new(&conn->hs_tx_ckm, key, keylen, iv, ivlen, pn, + pnlen, conn->mem); } int ngtcp2_conn_set_handshake_rx_keys(ngtcp2_conn *conn, const uint8_t *key, size_t keylen, const uint8_t *iv, - size_t ivlen) { + size_t ivlen, const uint8_t *pn, + size_t pnlen) { if (conn->hs_rx_ckm) { ngtcp2_crypto_km_del(conn->hs_rx_ckm, conn->mem); conn->hs_rx_ckm = NULL; } - return ngtcp2_crypto_km_new(&conn->hs_rx_ckm, key, keylen, iv, ivlen, - conn->mem); + return ngtcp2_crypto_km_new(&conn->hs_rx_ckm, key, keylen, iv, ivlen, pn, + pnlen, conn->mem); } int ngtcp2_conn_update_early_keys(ngtcp2_conn *conn, const uint8_t *key, size_t keylen, const uint8_t *iv, - size_t ivlen) { + size_t ivlen, const uint8_t *pn, + size_t pnlen) { if (conn->early_ckm) { return NGTCP2_ERR_INVALID_STATE; } - return ngtcp2_crypto_km_new(&conn->early_ckm, key, keylen, iv, ivlen, - conn->mem); + return ngtcp2_crypto_km_new(&conn->early_ckm, key, keylen, iv, ivlen, pn, + pnlen, conn->mem); } int ngtcp2_conn_update_tx_keys(ngtcp2_conn *conn, const uint8_t *key, - size_t keylen, const uint8_t *iv, size_t ivlen) { + size_t keylen, const uint8_t *iv, size_t ivlen, + const uint8_t *pn, size_t pnlen) { if (conn->tx_ckm) { return NGTCP2_ERR_INVALID_STATE; } - return ngtcp2_crypto_km_new(&conn->tx_ckm, key, keylen, iv, ivlen, conn->mem); + return ngtcp2_crypto_km_new(&conn->tx_ckm, key, keylen, iv, ivlen, pn, pnlen, + conn->mem); } int ngtcp2_conn_update_rx_keys(ngtcp2_conn *conn, const uint8_t *key, - size_t keylen, const uint8_t *iv, size_t ivlen) { + size_t keylen, const uint8_t *iv, size_t ivlen, + const uint8_t *pn, size_t pnlen) { if (conn->rx_ckm) { return NGTCP2_ERR_INVALID_STATE; } - return ngtcp2_crypto_km_new(&conn->rx_ckm, key, keylen, iv, ivlen, conn->mem); + return ngtcp2_crypto_km_new(&conn->rx_ckm, key, keylen, iv, ivlen, pn, pnlen, + conn->mem); } ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) { diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h index 36f6bd36..0f9f1a2c 100644 --- a/lib/ngtcp2_conn.h +++ b/lib/ngtcp2_conn.h @@ -99,9 +99,6 @@ typedef enum { from flow control during handshake. */ #define NGTCP2_MAX_HS_STREAM0_OFFSET 65536 -/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */ -#define NGTCP2_MAX_PKT_NUM ((1llu << 62) - 1) - struct ngtcp2_pkt_chain; typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain; diff --git a/lib/ngtcp2_conv.c b/lib/ngtcp2_conv.c index 81b76e7e..7101c655 100644 --- a/lib/ngtcp2_conv.c +++ b/lib/ngtcp2_conv.c @@ -97,6 +97,34 @@ uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p) { assert(0); } +uint64_t ngtcp2_get_pkt_num(size_t *plen, const uint8_t *p) { + union { + char b[4]; + uint16_t n16; + uint32_t n32; + } n; + + if ((*p >> 7) == 0) { + *plen = 1; + return *p; + } + + switch (*p >> 6) { + case 2: + *plen = 2; + memcpy(&n, p, 2); + n.b[0] &= 0x3fu; + return ntohs(n.n16); + case 3: + *plen = 4; + memcpy(&n, p, 4); + n.b[0] &= 0x3fu; + return ntohl(n.n32); + } + + assert(0); +} + uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n) { n = bswap64(n); return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); @@ -155,10 +183,43 @@ uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { return rv; } +uint8_t *ngtcp2_put_pkt_num(uint8_t *p, uint64_t pkt_num, size_t len) { + switch (len) { + case 1: + *p++ = (uint8_t)(pkt_num & ~0x80u); + return p; + case 2: + ngtcp2_put_uint16be(p, (uint16_t)pkt_num); + *p = (uint8_t)((*p & ~0xc0u) | 0x80u); + return p + 2; + case 4: + ngtcp2_put_uint32be(p, (uint32_t)pkt_num); + *p |= 0xc0u; + return p + 4; + default: + assert(0); + } +} + size_t ngtcp2_get_varint_len(const uint8_t *p) { return varintlen_def[*p >> 6]; } +size_t ngtcp2_get_pkt_num_len(const uint8_t *p) { + if ((*p >> 7) == 0) { + return 1; + } + + switch (*p >> 6) { + case 2: + return 2; + case 3: + return 4; + default: + assert(0); + } +} + size_t ngtcp2_put_varint_len(uint64_t n) { if (n < 64) { return 1; diff --git a/lib/ngtcp2_conv.h b/lib/ngtcp2_conv.h index b6907908..4a15b18a 100644 --- a/lib/ngtcp2_conv.h +++ b/lib/ngtcp2_conv.h @@ -84,6 +84,13 @@ uint16_t ngtcp2_get_uint16(const uint8_t *p); */ uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p); +/* + * ngtcp2_get_pkt_num reads packet number encoded in variable-length + * encoding from |p|, and returns it in host byte order. The number + * of bytes read is stored in |*plen|. + */ +uint64_t ngtcp2_get_pkt_num(size_t *plen, const uint8_t *p); + /* * ngtcp2_put_uint64be writes |n| in host byte order in |p| in network * byte order. It returns the one beyond of the last written @@ -133,12 +140,24 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); */ uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); +/* + * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It + * returns the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_pkt_num(uint8_t *p, uint64_t pkt_num, size_t len); + /* * ngtcp2_get_varint_len returns the required number of bytes to read * variable-length integer starting at |p|. */ size_t ngtcp2_get_varint_len(const uint8_t *p); +/* + * ngtcp2_get_pkt_num_len returns the required number of bytes to read + * variable-length packet number starting at |p|. + */ +size_t ngtcp2_get_pkt_num_len(const uint8_t *p); + /* * ngtcp2_put_varint_len returns the required number of bytes to * encode |n|. diff --git a/lib/ngtcp2_crypto.c b/lib/ngtcp2_crypto.c index 6181d8a2..4706951b 100644 --- a/lib/ngtcp2_crypto.c +++ b/lib/ngtcp2_crypto.c @@ -32,11 +32,11 @@ int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *key, size_t keylen, const uint8_t *iv, size_t ivlen, - ngtcp2_mem *mem) { + const uint8_t *pn, size_t pnlen, ngtcp2_mem *mem) { size_t len; uint8_t *p; - len = sizeof(ngtcp2_crypto_km) + keylen + ivlen; + len = sizeof(ngtcp2_crypto_km) + keylen + ivlen + pnlen; *pckm = ngtcp2_mem_malloc(mem, len); if (*pckm == NULL) { @@ -49,7 +49,10 @@ int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *key, p = ngtcp2_cpymem(p, key, keylen); (*pckm)->iv = p; (*pckm)->ivlen = ivlen; - /*p = */ ngtcp2_cpymem(p, iv, ivlen); + p = ngtcp2_cpymem(p, iv, ivlen); + (*pckm)->pn = p; + (*pckm)->pnlen = pnlen; + /* p = */ ngtcp2_cpymem(p, pn, pnlen); return 0; } diff --git a/lib/ngtcp2_crypto.h b/lib/ngtcp2_crypto.h index ce3fa2f8..33e52c33 100644 --- a/lib/ngtcp2_crypto.h +++ b/lib/ngtcp2_crypto.h @@ -40,16 +40,22 @@ /* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */ #define NGTCP2_MAX_AEAD_OVERHEAD 16 +/* NGTCP2_PN_SAMPLELEN is the number bytes sampled when encoding a + packet number. */ +#define NGTCP2_PN_SAMPLELEN 16 + typedef struct { const uint8_t *key; size_t keylen; const uint8_t *iv; size_t ivlen; + const uint8_t *pn; + size_t pnlen; } ngtcp2_crypto_km; int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *key, size_t keylen, const uint8_t *iv, size_t ivlen, - ngtcp2_mem *mem); + const uint8_t *pn, size_t pnlen, ngtcp2_mem *mem); void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, ngtcp2_mem *mem); @@ -58,6 +64,7 @@ typedef struct { size_t aead_overhead; ngtcp2_encrypt encrypt; ngtcp2_decrypt decrypt; + ngtcp2_encrypt_pn encrypt_pn; void *user_data; } ngtcp2_crypto_ctx; diff --git a/lib/ngtcp2_log.c b/lib/ngtcp2_log.c index 5c696288..2b98e6ce 100644 --- a/lib/ngtcp2_log.c +++ b/lib/ngtcp2_log.c @@ -170,24 +170,11 @@ static const char *strpkttype_long(uint8_t type) { } } -static const char *strpkttype_short(uint8_t type) { - switch (type) { - case NGTCP2_PKT_01: - return "S01"; - case NGTCP2_PKT_02: - return "S02"; - case NGTCP2_PKT_03: - return "S03"; - default: - return "(unknown)"; - } -} - static const char *strpkttype(const ngtcp2_pkt_hd *hd) { if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { return strpkttype_long(hd->type); } - return strpkttype_short(hd->type); + return "Short"; } static const char *strevent(ngtcp2_log_event ev) { @@ -584,12 +571,13 @@ void ngtcp2_log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { ngtcp2_log_info( log, NGTCP2_LOG_EVENT_PKT, - "rx pkt dcid=0x%s scid=0x%s type=%s(0x%02x) payloadlen=%zu", + "rx pkt %" PRIu64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu", + hd->pkt_num, (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) - : strpkttype_short(hd->type), - hd->type, hd->payloadlen); + : "Short", + hd->type, hd->len); } void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, diff --git a/lib/ngtcp2_pkt.c b/lib/ngtcp2_pkt.c index dbddad87..a5c234bd 100644 --- a/lib/ngtcp2_pkt.c +++ b/lib/ngtcp2_pkt.c @@ -34,7 +34,8 @@ void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - uint64_t pkt_num, uint32_t version, size_t payloadlen) { + uint64_t pkt_num, size_t pkt_numlen, uint32_t version, + size_t len) { hd->flags = flags; hd->type = type; if (dcid) { @@ -48,8 +49,9 @@ void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, ngtcp2_cid_zero(&hd->scid); } hd->pkt_num = pkt_num; + hd->pkt_numlen = pkt_numlen; hd->version = version; - hd->payloadlen = payloadlen; + hd->len = len; } ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, @@ -87,7 +89,7 @@ ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, default: return NGTCP2_ERR_UNKNOWN_PKT_TYPE; } - len = NGTCP2_MIN_LONG_HEADERLEN; + len = NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ } if (pktlen < len) { @@ -111,9 +113,11 @@ ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } if (type != NGTCP2_PKT_VERSION_NEGOTIATION) { + /* Length */ p = &pkt[6 + dcil + scil]; - len += ngtcp2_get_varint_len(p) - 1; + n = ngtcp2_get_varint_len(p); + len += n - 1; if (pktlen < len) { return NGTCP2_ERR_INVALID_ARGUMENT; @@ -123,6 +127,8 @@ ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, dest->flags = NGTCP2_PKT_FLAG_LONG_FORM; dest->type = type; dest->version = version; + dest->pkt_num = 0; + dest->pkt_numlen = 0; p = &pkt[6]; @@ -131,16 +137,11 @@ ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, ngtcp2_cid_init(&dest->scid, p, scil); p += scil; - if (type != NGTCP2_PKT_VERSION_NEGOTIATION) { - dest->payloadlen = ngtcp2_get_varint(&n, p); - p += n; - - dest->pkt_num = ngtcp2_get_uint32(p); - - p += sizeof(uint32_t); + if (type == NGTCP2_PKT_VERSION_NEGOTIATION) { + dest->len = 0; } else { - dest->payloadlen = 0; - dest->pkt_num = 0; + dest->len = ngtcp2_get_varint(&n, p); + p += n; } assert((size_t)(p - pkt) == len); @@ -151,10 +152,13 @@ ssize_t ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, ssize_t ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen, size_t dcidlen) { uint8_t flags = 0; - uint8_t type; size_t len = 1 + dcidlen; const uint8_t *p = pkt; + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { return NGTCP2_ERR_INVALID_ARGUMENT; } @@ -168,28 +172,9 @@ ssize_t ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, return NGTCP2_ERR_INVALID_ARGUMENT; } - type = pkt[0] & NGTCP2_SHORT_TYPE_MASK; - switch (type) { - case NGTCP2_PKT_01: - ++len; - break; - case NGTCP2_PKT_02: - len += 2; - break; - case NGTCP2_PKT_03: - len += 4; - break; - default: - return NGTCP2_ERR_UNKNOWN_PKT_TYPE; - } + p = &pkt[1]; - if (pktlen < len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } - - ++p; - - dest->type = type; + dest->type = NGTCP2_PKT_SHORT; ngtcp2_cid_init(&dest->dcid, p, dcidlen); p += dcidlen; @@ -198,21 +183,13 @@ ssize_t ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, garbage. */ ngtcp2_cid_zero(&dest->scid); - switch (type) { - case NGTCP2_PKT_01: - dest->pkt_num = *p; - break; - case NGTCP2_PKT_02: - dest->pkt_num = ngtcp2_get_uint16(p); - break; - case NGTCP2_PKT_03: - dest->pkt_num = ngtcp2_get_uint32(p); - break; - } - dest->flags = flags; dest->version = 0; - dest->payloadlen = 0; + dest->len = 0; + dest->pkt_num = 0; + dest->pkt_numlen = 0; + + assert((size_t)(p - pkt) == len); return (ssize_t)len; } @@ -221,8 +198,9 @@ ssize_t ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, const ngtcp2_pkt_hd *hd) { uint8_t *p; size_t len = NGTCP2_MIN_LONG_HEADERLEN + hd->dcid.datalen + hd->scid.datalen + - 2 - 1 /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for - payloadlen */; + 2 + hd->pkt_numlen - + 2 /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for len + and 1 byte for packet number. */; if (outlen < len) { return NGTCP2_ERR_NOBUF; @@ -248,8 +226,8 @@ ssize_t ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, if (hd->scid.datalen) { p = ngtcp2_cpymem(p, hd->scid.data, hd->scid.datalen); } - p = ngtcp2_put_varint14(p, (uint16_t)hd->payloadlen); - p = ngtcp2_put_uint32be(p, (uint32_t)hd->pkt_num); + p = ngtcp2_put_varint14(p, (uint16_t)hd->len); + p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); assert((size_t)(p - out) == len); @@ -259,21 +237,7 @@ ssize_t ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, ssize_t ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, const ngtcp2_pkt_hd *hd) { uint8_t *p; - size_t len = 1 + hd->dcid.datalen; - - switch (hd->type) { - case NGTCP2_PKT_01: - ++len; - break; - case NGTCP2_PKT_02: - len += 2; - break; - case NGTCP2_PKT_03: - len += 4; - break; - default: - return NGTCP2_ERR_INVALID_ARGUMENT; - } + size_t len = 1 + hd->dcid.datalen + hd->pkt_numlen; if (outlen < len) { return NGTCP2_ERR_NOBUF; @@ -281,7 +245,7 @@ ssize_t ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, p = out; - *p = NGTCP2_THIRD_BIT | NGTCP2_FOURTH_BIT | hd->type; + *p = NGTCP2_THIRD_BIT | NGTCP2_FOURTH_BIT; if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) { *p |= NGTCP2_KEY_PHASE_BIT; } @@ -292,19 +256,7 @@ ssize_t ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); } - switch (hd->type) { - case NGTCP2_PKT_01: - *p++ = (uint8_t)hd->pkt_num; - break; - case NGTCP2_PKT_02: - p = ngtcp2_put_uint16be(p, (uint16_t)hd->pkt_num); - break; - case NGTCP2_PKT_03: - p = ngtcp2_put_uint32be(p, (uint32_t)hd->pkt_num); - break; - default: - assert(0); - } + p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); assert((size_t)(p - out) == len); @@ -1570,7 +1522,8 @@ int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, uint64_t ngtcp2_pkt_adjust_pkt_num(uint64_t max_pkt_num, uint64_t pkt_num, size_t n) { - uint64_t k = max_pkt_num == UINT64_MAX ? max_pkt_num : max_pkt_num + 1; + uint64_t k = + max_pkt_num == NGTCP2_MAX_PKT_NUM ? max_pkt_num : max_pkt_num + 1; uint64_t u = k & ~((1llu << n) - 1); uint64_t a = u | pkt_num; uint64_t b = (u + (1llu << n)) | pkt_num; diff --git a/lib/ngtcp2_pkt.h b/lib/ngtcp2_pkt.h index cfcb4602..eafe3126 100644 --- a/lib/ngtcp2_pkt.h +++ b/lib/ngtcp2_pkt.h @@ -37,18 +37,14 @@ #define NGTCP2_FOURTH_BIT 0x10 #define NGTCP2_GQUIC_BIT 0x08 #define NGTCP2_LONG_TYPE_MASK 0x7f -#define NGTCP2_SHORT_TYPE_MASK 0x03 /* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */ #define NGTCP2_SR_TYPE 0x1f -/* NGTCP2_LONG_HEADERLEN is the length of long header */ -#define NGTCP2_LONG_HEADERLEN 17 - /* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header. That is (1|TYPE)<1> + VERSION<4> + (DCIL|SCIL)<1> + PAYLOADLEN<1> + - PKN<4> */ -#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 4) + PKN<1> */ +#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 1) #define NGTCP2_STREAM_FIN_BIT 0x01 #define NGTCP2_STREAM_LEN_BIT 0x02 @@ -67,14 +63,19 @@ blocks which this library can create, or decode. */ #define NGTCP2_MAX_ACK_BLKS 255 +/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */ +#define NGTCP2_MAX_PKT_NUM ((1llu << 62) - 1) + /* * ngtcp2_pkt_hd_init initializes |hd| with the given values. If * |dcid| and/or |scid| is NULL, DCID and SCID of |hd| is empty - * respectively. + * respectively. |pkt_numlen| is the number of bytes used to encode + * |pkt_num| and either 1, 2, or 4. */ void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - uint64_t pkt_num, uint32_t version, size_t payloadlen); + uint64_t pkt_num, size_t pkt_numlen, uint32_t version, + size_t len); /* * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into diff --git a/lib/ngtcp2_ppe.c b/lib/ngtcp2_ppe.c index e1fe74ba..f3f7552b 100644 --- a/lib/ngtcp2_ppe.c +++ b/lib/ngtcp2_ppe.c @@ -31,14 +31,18 @@ #include "ngtcp2_str.h" #include "ngtcp2_conv.h" #include "ngtcp2_conn.h" +#include "ngtcp2_macro.h" void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, ngtcp2_crypto_ctx *cctx) { ngtcp2_buf_init(&ppe->buf, out, outlen); ppe->hdlen = 0; - ppe->payloadlen_offset = 0; + ppe->len_offset = 0; + ppe->pkt_num_offset = 0; + ppe->pkt_numlen = 0; ppe->pkt_num = 0; + ppe->sample_offset = 0; ppe->ctx = cctx; } @@ -52,10 +56,15 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { } if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { - ppe->payloadlen_offset = 1 + 4 + 1 + hd->dcid.datalen + hd->scid.datalen; + ppe->len_offset = 1 + 4 + 1 + hd->dcid.datalen + hd->scid.datalen; + ppe->pkt_num_offset = ppe->len_offset + 2; + ppe->sample_offset = + 1 + 4 + 1 + hd->dcid.datalen + hd->scid.datalen + 2 + 4; rv = ngtcp2_pkt_encode_hd_long( buf->last, ngtcp2_buf_left(buf) - ctx->aead_overhead, hd); } else { + ppe->pkt_num_offset = 1 + hd->dcid.datalen; + ppe->sample_offset = 1 + hd->dcid.datalen + 4; rv = ngtcp2_pkt_encode_hd_short( buf->last, ngtcp2_buf_left(buf) - ctx->aead_overhead, hd); } @@ -65,6 +74,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { buf->last += rv; + ppe->pkt_numlen = hd->pkt_numlen; ppe->hdlen = (size_t)rv; ppe->pkt_num = hd->pkt_num; @@ -93,7 +103,7 @@ int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) { } ssize_t ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { - ssize_t rv; + ssize_t nwrite; ngtcp2_buf *buf = &ppe->buf; ngtcp2_crypto_ctx *ctx = ppe->ctx; ngtcp2_conn *conn = ctx->user_data; @@ -101,23 +111,40 @@ ssize_t ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen; size_t destlen = (size_t)(buf->end - buf->begin) - ppe->hdlen; - if (ppe->payloadlen_offset) { - ngtcp2_put_varint14(buf->begin + ppe->payloadlen_offset, - (uint16_t)(payloadlen + ctx->aead_overhead)); + assert(ppe->ctx->encrypt); + assert(ppe->ctx->encrypt_pn); + + if (ppe->len_offset) { + ngtcp2_put_varint14( + buf->begin + ppe->len_offset, + (uint16_t)(payloadlen + ppe->pkt_numlen + ctx->aead_overhead)); } ngtcp2_crypto_create_nonce(ppe->nonce, ctx->ckm->iv, ctx->ckm->ivlen, ppe->pkt_num); - rv = ppe->ctx->encrypt(conn, payload, destlen, payload, payloadlen, - ctx->ckm->key, ctx->ckm->keylen, ppe->nonce, - ctx->ckm->ivlen, buf->begin, ppe->hdlen, - conn->user_data); - if (rv < 0) { + nwrite = ppe->ctx->encrypt(conn, payload, destlen, payload, payloadlen, + ctx->ckm->key, ctx->ckm->keylen, ppe->nonce, + ctx->ckm->ivlen, buf->begin, ppe->hdlen, + conn->user_data); + if (nwrite < 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } - buf->last = payload + rv; + buf->last = payload + nwrite; + + ppe->sample_offset = + ngtcp2_min(ppe->sample_offset, ngtcp2_buf_len(buf) - ctx->aead_overhead); + + nwrite = ppe->ctx->encrypt_pn( + conn, buf->begin + ppe->pkt_num_offset, ppe->pkt_numlen, + buf->begin + ppe->pkt_num_offset, ppe->pkt_numlen, ctx->ckm->pn, + ctx->ckm->pnlen, buf->begin + ppe->sample_offset, NGTCP2_PN_SAMPLELEN, + conn->user_data); + + if (nwrite < 0) { + return nwrite; + } if (ppkt != NULL) { *ppkt = buf->begin; diff --git a/lib/ngtcp2_ppe.h b/lib/ngtcp2_ppe.h index 842776ec..b569d0a8 100644 --- a/lib/ngtcp2_ppe.h +++ b/lib/ngtcp2_ppe.h @@ -42,8 +42,16 @@ typedef struct { ngtcp2_crypto_ctx *ctx; /* hdlen is the number of bytes for packet header written in buf. */ size_t hdlen; - /* payloadlen_offset is the offset to payload length field. */ - size_t payloadlen_offset; + /* len_offset is the offset to Length field. */ + size_t len_offset; + /* pkt_num_offset is the offset to packet number field. */ + size_t pkt_num_offset; + /* pkt_numlen is the number of bytes used to encode a packet + number */ + size_t pkt_numlen; + /* sample_offset is the offset to sample for packet number + encryption. */ + size_t sample_offset; /* pkt_num is the packet number written in buf. */ uint64_t pkt_num; /* nonce is the buffer to store nonce. It should be equal or longer diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c index b7d1181d..2130f685 100644 --- a/tests/ngtcp2_conn_test.c +++ b/tests/ngtcp2_conn_test.c @@ -51,7 +51,7 @@ static ssize_t null_encrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, (void)ad; (void)adlen; (void)user_data; - return (ssize_t)plaintextlen; + return (ssize_t)plaintextlen + NGTCP2_FAKE_AEAD_OVERHEAD; } static ssize_t null_decrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, @@ -71,8 +71,9 @@ static ssize_t null_decrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, (void)adlen; (void)user_data; assert(destlen >= ciphertextlen); - memcpy(dest, ciphertext, ciphertextlen); - return (ssize_t)ciphertextlen; + assert(ciphertextlen >= NGTCP2_FAKE_AEAD_OVERHEAD); + memmove(dest, ciphertext, ciphertextlen - NGTCP2_FAKE_AEAD_OVERHEAD); + return (ssize_t)ciphertextlen - NGTCP2_FAKE_AEAD_OVERHEAD; } static ssize_t fail_decrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, @@ -95,8 +96,28 @@ static ssize_t fail_decrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, return NGTCP2_ERR_TLS_DECRYPT; } +static ssize_t null_encrypt_pn(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen, + void *user_data) { + (void)conn; + (void)dest; + (void)destlen; + (void)ciphertext; + (void)key; + (void)keylen; + (void)nonce; + (void)noncelen; + (void)user_data; + assert(destlen >= ciphertextlen); + memmove(dest, ciphertext, ciphertextlen); + return (ssize_t)ciphertextlen; +} + static uint8_t null_key[16]; static uint8_t null_iv[16]; +static uint8_t null_pn[16]; static uint8_t null_data[4096]; typedef struct { @@ -111,22 +132,14 @@ typedef struct { } my_user_data; static ssize_t send_client_initial(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, const uint8_t **pdest, + const uint8_t **pdest, int initial, void *user_data) { - my_user_data *ud = user_data; (void)conn; (void)flags; - + (void)initial; + (void)user_data; *pdest = null_data; - if (ppkt_num) { - if (ud) { - *ppkt_num = ++ud->pkt_num; - } else { - *ppkt_num = 1000000007; - } - } - return 217; } @@ -149,27 +162,24 @@ static int recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid, } static ssize_t send_server_handshake(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, const uint8_t **pdest, + const uint8_t **pdest, int initial, void *user_data) { (void)conn; (void)flags; + (void)initial; (void)user_data; *pdest = null_data; - if (ppkt_num) { - *ppkt_num = 1000000009; - } return 218; } static ssize_t send_server_handshake_zero(ngtcp2_conn *conn, uint32_t flags, - uint64_t *ppkt_num, - const uint8_t **pdest, + const uint8_t **pdest, int initial, void *user_data) { (void)conn; (void)flags; - (void)ppkt_num; (void)pdest; + (void)initial; (void)user_data; return 0; } @@ -278,21 +288,24 @@ static void setup_default_server(ngtcp2_conn **pconn) { memset(&cb, 0, sizeof(cb)); cb.hs_decrypt = null_decrypt; cb.hs_encrypt = null_encrypt; + cb.hs_encrypt_pn = null_encrypt_pn; cb.decrypt = null_decrypt; cb.encrypt = null_encrypt; + cb.encrypt_pn = null_encrypt_pn; cb.recv_stream0_data = recv_stream0_data; server_default_settings(&settings); ngtcp2_conn_server_new(pconn, &dcid, &scid, NGTCP2_PROTO_VER_MAX, &cb, &settings, NULL); ngtcp2_conn_set_handshake_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_set_handshake_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_update_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_update_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); + ngtcp2_conn_set_aead_overhead(*pconn, NGTCP2_FAKE_AEAD_OVERHEAD); (*pconn)->state = NGTCP2_CS_POST_HANDSHAKE; (*pconn)->remote_settings.max_stream_data = 64 * 1024; (*pconn)->remote_settings.max_streams_bidi = 0; @@ -316,21 +329,24 @@ static void setup_default_client(ngtcp2_conn **pconn) { memset(&cb, 0, sizeof(cb)); cb.hs_decrypt = null_decrypt; cb.hs_encrypt = null_encrypt; + cb.hs_encrypt_pn = null_encrypt_pn; cb.decrypt = null_decrypt; cb.encrypt = null_encrypt; + cb.encrypt_pn = null_encrypt_pn; cb.recv_stream0_data = recv_stream0_data; client_default_settings(&settings); ngtcp2_conn_client_new(pconn, &dcid, &scid, NGTCP2_PROTO_VER_MAX, &cb, &settings, NULL); ngtcp2_conn_set_handshake_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_set_handshake_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_update_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_update_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); + ngtcp2_conn_set_aead_overhead(*pconn, NGTCP2_FAKE_AEAD_OVERHEAD); (*pconn)->state = NGTCP2_CS_POST_HANDSHAKE; (*pconn)->remote_settings.max_stream_data = 64 * 1024; (*pconn)->remote_settings.max_streams_bidi = 1; @@ -357,15 +373,16 @@ static void setup_handshake_server(ngtcp2_conn **pconn) { cb.recv_stream0_data = recv_stream0_data; cb.hs_decrypt = null_decrypt; cb.hs_encrypt = null_encrypt; + cb.hs_encrypt_pn = null_encrypt_pn; cb.rand = genrand; server_default_settings(&settings); ngtcp2_conn_server_new(pconn, &dcid, &scid, NGTCP2_PROTO_VER_MAX, &cb, &settings, NULL); ngtcp2_conn_set_handshake_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_set_handshake_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); } static void setup_handshake_client(ngtcp2_conn **pconn) { @@ -381,14 +398,15 @@ static void setup_handshake_client(ngtcp2_conn **pconn) { cb.recv_stream0_data = recv_stream0_data; cb.hs_decrypt = null_decrypt; cb.hs_encrypt = null_encrypt; + cb.hs_encrypt_pn = null_encrypt_pn; client_default_settings(&settings); ngtcp2_conn_client_new(pconn, &rcid, &scid, NGTCP2_PROTO_VER_MAX, &cb, &settings, NULL); ngtcp2_conn_set_handshake_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_set_handshake_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); } static void setup_early_server(ngtcp2_conn **pconn) { @@ -405,19 +423,22 @@ static void setup_early_server(ngtcp2_conn **pconn) { cb.recv_stream0_data = recv_stream0_data; cb.hs_decrypt = null_decrypt; cb.hs_encrypt = null_encrypt; + cb.hs_encrypt_pn = null_encrypt_pn; cb.decrypt = null_decrypt; cb.encrypt = null_encrypt; + cb.encrypt_pn = null_encrypt_pn; cb.rand = genrand; server_default_settings(&settings); ngtcp2_conn_server_new(pconn, &dcid, &scid, NGTCP2_PROTO_VER_MAX, &cb, &settings, NULL); ngtcp2_conn_set_handshake_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_set_handshake_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_update_early_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); + ngtcp2_conn_set_aead_overhead(*pconn, NGTCP2_FAKE_AEAD_OVERHEAD); (*pconn)->remote_settings.max_stream_data = 64 * 1024; (*pconn)->remote_settings.max_streams_bidi = 0; (*pconn)->remote_settings.max_streams_uni = 1; @@ -443,18 +464,21 @@ static void setup_early_client(ngtcp2_conn **pconn) { cb.recv_stream0_data = recv_stream0_data; cb.hs_decrypt = null_decrypt; cb.hs_encrypt = null_encrypt; + cb.hs_encrypt_pn = null_encrypt_pn; cb.decrypt = null_decrypt; cb.encrypt = null_encrypt; + cb.encrypt_pn = null_encrypt_pn; client_default_settings(&settings); ngtcp2_conn_client_new(pconn, &dcid, &scid, NGTCP2_PROTO_VER_MAX, &cb, &settings, NULL); ngtcp2_conn_set_handshake_tx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_set_handshake_rx_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); ngtcp2_conn_update_early_keys(*pconn, null_key, sizeof(null_key), null_iv, - sizeof(null_iv)); + sizeof(null_iv), null_pn, sizeof(null_pn)); + ngtcp2_conn_set_aead_overhead(*pconn, NGTCP2_FAKE_AEAD_OVERHEAD); params.initial_max_stream_data = 64 * 1024; params.initial_max_streams_bidi = 1; @@ -1487,6 +1511,7 @@ void test_ngtcp2_conn_recv_conn_id_omitted(void) { void test_ngtcp2_conn_short_pkt_type(void) { ngtcp2_conn *conn; + ngtcp2_pkt_hd hd; uint8_t buf[2048]; ssize_t spktlen; uint64_t stream_id; @@ -1499,25 +1524,29 @@ void test_ngtcp2_conn_short_pkt_type(void) { null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_01 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(1 == hd.pkt_numlen); ngtcp2_conn_del(conn); - /* 2 octet pkt num */ + /* 2 octets pkt num */ setup_default_client(&conn); conn->rtb.largest_acked_tx_pkt_num = 0x6afa2f; - conn->last_tx_pkt_num = 0x6b4263; + conn->last_tx_pkt_num = 0x6afd78; ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, buf, sizeof(buf), NULL, stream_id, 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_02 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(2 == hd.pkt_numlen); ngtcp2_conn_del(conn); - /* 3 octet pkt num */ + /* 4 octets pkt num */ setup_default_client(&conn); conn->rtb.largest_acked_tx_pkt_num = 0x6afa2f; conn->last_tx_pkt_num = 0x6bc106; @@ -1527,63 +1556,73 @@ void test_ngtcp2_conn_short_pkt_type(void) { null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_03 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(4 == hd.pkt_numlen); ngtcp2_conn_del(conn); /* 1 octet pkt num (largest)*/ setup_default_client(&conn); conn->rtb.largest_acked_tx_pkt_num = 1; - conn->last_tx_pkt_num = 127; + conn->last_tx_pkt_num = 63; ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, buf, sizeof(buf), NULL, stream_id, 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_01 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(1 == hd.pkt_numlen); ngtcp2_conn_del(conn); /* 2 octet pkt num (shortest)*/ setup_default_client(&conn); conn->rtb.largest_acked_tx_pkt_num = 1; - conn->last_tx_pkt_num = 128; + conn->last_tx_pkt_num = 64; ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, buf, sizeof(buf), NULL, stream_id, 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_02 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(2 == hd.pkt_numlen); ngtcp2_conn_del(conn); /* 2 octet pkt num (largest)*/ setup_default_client(&conn); conn->rtb.largest_acked_tx_pkt_num = 1; - conn->last_tx_pkt_num = 32767; + conn->last_tx_pkt_num = 8191; ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, buf, sizeof(buf), NULL, stream_id, 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_02 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(2 == hd.pkt_numlen); ngtcp2_conn_del(conn); - /* 3 octet pkt num (shortest)*/ + /* 4 octet pkt num (shortest)*/ setup_default_client(&conn); conn->rtb.largest_acked_tx_pkt_num = 1; - conn->last_tx_pkt_num = 32768; + conn->last_tx_pkt_num = 8192; ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); spktlen = ngtcp2_conn_write_stream(conn, buf, sizeof(buf), NULL, stream_id, 0, null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_03 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(4 == hd.pkt_numlen); ngtcp2_conn_del(conn); @@ -1597,7 +1636,9 @@ void test_ngtcp2_conn_short_pkt_type(void) { null_data, 19, 1); CU_ASSERT(spktlen > 0); - CU_ASSERT(NGTCP2_PKT_03 == (buf[0] & NGTCP2_SHORT_TYPE_MASK)); + CU_ASSERT(pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->scid.datalen) > + 0); + CU_ASSERT(4 == hd.pkt_numlen); ngtcp2_conn_del(conn); } @@ -1618,8 +1659,8 @@ void test_ngtcp2_conn_recv_stateless_reset(void) { token[i] = (uint8_t)~i; } - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_01, &dcid, NULL, - 0xe1, 0, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 0xe1, 1, 0, 0); /* server: Just ignore SR */ setup_default_server(&conn); @@ -1717,7 +1758,7 @@ void test_ngtcp2_conn_recv_server_stateless_retry(void) { spktlen = ngtcp2_conn_handshake(conn, buf, sizeof(buf), buf, pktlen, 2); CU_ASSERT(spktlen > 0); - CU_ASSERT(2 == conn->last_tx_pkt_num); + CU_ASSERT(1 == conn->last_tx_pkt_num); ngtcp2_conn_del(conn); } diff --git a/tests/ngtcp2_pkt_test.c b/tests/ngtcp2_pkt_test.c index 48ac343e..1d1c8b38 100644 --- a/tests/ngtcp2_pkt_test.c +++ b/tests/ngtcp2_pkt_test.c @@ -45,7 +45,7 @@ void test_ngtcp2_pkt_decode_hd_long(void) { /* Handshake */ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE, - &dcid, &scid, 0xe1e2e3e4u, 0x000000ff, 16383); + &dcid, &scid, 0xe1e2e3e4u, 4, 0x000000ff, 16383); rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd); @@ -53,21 +53,21 @@ void test_ngtcp2_pkt_decode_hd_long(void) { CU_ASSERT((ssize_t)len == rv); - rv = ngtcp2_pkt_decode_hd_long(&nhd, buf, len); + rv = pkt_decode_hd_long(&nhd, buf, len); CU_ASSERT((ssize_t)len == rv); CU_ASSERT(hd.type == nhd.type); CU_ASSERT(hd.flags == nhd.flags); CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid)); CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid)); - CU_ASSERT(hd.pkt_num == nhd.pkt_num); + CU_ASSERT((hd.pkt_num & 0x3fffffffu) == nhd.pkt_num); CU_ASSERT(hd.version == nhd.version); - CU_ASSERT(hd.payloadlen == nhd.payloadlen); + CU_ASSERT(hd.len == nhd.len); /* VN */ /* Set random packet type */ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE, - &dcid, &scid, 0, 0, 0); + &dcid, &scid, 0, 4, 0, 0); rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd); @@ -75,7 +75,7 @@ void test_ngtcp2_pkt_decode_hd_long(void) { CU_ASSERT((ssize_t)len == rv - 2 /* payloadlen */ - 4 /* pkt_num */); - rv = ngtcp2_pkt_decode_hd_long(&nhd, buf, len); + rv = pkt_decode_hd_long(&nhd, buf, len); CU_ASSERT((ssize_t)len == rv); CU_ASSERT(NGTCP2_PKT_VERSION_NEGOTIATION == nhd.type); @@ -84,7 +84,7 @@ void test_ngtcp2_pkt_decode_hd_long(void) { CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid)); CU_ASSERT(hd.pkt_num == nhd.pkt_num); CU_ASSERT(hd.version == nhd.version); - CU_ASSERT(hd.payloadlen == nhd.payloadlen); + CU_ASSERT(hd.len == nhd.len); } void test_ngtcp2_pkt_decode_hd_short(void) { @@ -97,9 +97,9 @@ void test_ngtcp2_pkt_decode_hd_short(void) { dcid_init(&dcid); ngtcp2_cid_zero(&zcid); - /* NGTCP2_PKT_03 */ - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_03, &dcid, NULL, - 0xe1e2e3e4u, 0xd1d2d3d4u, 0); + /* 4 bytes packet number */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); expectedlen = 1 + dcid.datalen + 4; @@ -107,20 +107,21 @@ void test_ngtcp2_pkt_decode_hd_short(void) { CU_ASSERT((ssize_t)expectedlen == rv); - rv = ngtcp2_pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); CU_ASSERT((ssize_t)expectedlen == rv); CU_ASSERT(hd.flags == nhd.flags); - CU_ASSERT(NGTCP2_PKT_03 == nhd.type); + CU_ASSERT(NGTCP2_PKT_SHORT == nhd.type); CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); - CU_ASSERT(hd.pkt_num == nhd.pkt_num); + CU_ASSERT((hd.pkt_num & 0x3fffffffu) == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); CU_ASSERT(0 == nhd.version); - CU_ASSERT(0 == nhd.payloadlen); + CU_ASSERT(0 == nhd.len); - /* NGTCP2_PKT_02 */ - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_02, &dcid, NULL, - 0xe1e2e3e4u, 0xd1d2d3d4u, 0); + /* 2 bytes packet number */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 0xe1e2e3e4u, 2, 0xd1d2d3d4u, 0); expectedlen = 1 + dcid.datalen + 2; @@ -128,20 +129,21 @@ void test_ngtcp2_pkt_decode_hd_short(void) { CU_ASSERT((ssize_t)expectedlen == rv); - rv = ngtcp2_pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); CU_ASSERT((ssize_t)expectedlen == rv); CU_ASSERT(hd.flags == nhd.flags); - CU_ASSERT(NGTCP2_PKT_02 == nhd.type); + CU_ASSERT(NGTCP2_PKT_SHORT == nhd.type); CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); - CU_ASSERT((hd.pkt_num & 0xffff) == nhd.pkt_num); + CU_ASSERT((hd.pkt_num & 0x3fff) == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); CU_ASSERT(0 == nhd.version); - CU_ASSERT(0 == nhd.payloadlen); + CU_ASSERT(0 == nhd.len); - /* NGTCP2_PKT_01 */ - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_01, &dcid, NULL, - 0xe1e2e3e4u, 0xd1d2d3d4u, 0); + /* 1 byte packet number */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 0xe1e2e3e4u, 1, 0xd1d2d3d4u, 0); expectedlen = 1 + dcid.datalen + 1; @@ -149,20 +151,21 @@ void test_ngtcp2_pkt_decode_hd_short(void) { CU_ASSERT((ssize_t)expectedlen == rv); - rv = ngtcp2_pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); CU_ASSERT((ssize_t)expectedlen == rv); CU_ASSERT(hd.flags == nhd.flags); - CU_ASSERT(NGTCP2_PKT_01 == nhd.type); + CU_ASSERT(NGTCP2_PKT_SHORT == nhd.type); CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); - CU_ASSERT((hd.pkt_num & 0xff) == nhd.pkt_num); + CU_ASSERT((hd.pkt_num & 0x7f) == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); CU_ASSERT(0 == nhd.version); - CU_ASSERT(0 == nhd.payloadlen); + CU_ASSERT(0 == nhd.len); /* With Key Phase */ - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_03, &dcid, NULL, - 0xe1e2e3e4u, 0xd1d2d3d4u, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_SHORT, &dcid, + NULL, 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); expectedlen = 1 + dcid.datalen + 4; @@ -170,20 +173,21 @@ void test_ngtcp2_pkt_decode_hd_short(void) { CU_ASSERT((ssize_t)expectedlen == rv); - rv = ngtcp2_pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); CU_ASSERT((ssize_t)expectedlen == rv); CU_ASSERT(hd.flags == nhd.flags); - CU_ASSERT(NGTCP2_PKT_03 == nhd.type); + CU_ASSERT(NGTCP2_PKT_SHORT == nhd.type); CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); - CU_ASSERT(hd.pkt_num == nhd.pkt_num); + CU_ASSERT((hd.pkt_num & 0x3fffffff) == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); CU_ASSERT(0 == nhd.version); - CU_ASSERT(0 == nhd.payloadlen); + CU_ASSERT(0 == nhd.len); /* With empty DCID */ - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_03, NULL, NULL, - 0xe1e2e3e4u, 0xd1d2d3d4u, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, NULL, NULL, + 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); expectedlen = 1 + 4; @@ -191,16 +195,17 @@ void test_ngtcp2_pkt_decode_hd_short(void) { CU_ASSERT((ssize_t)expectedlen == rv); - rv = ngtcp2_pkt_decode_hd_short(&nhd, buf, expectedlen, 0); + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, 0); CU_ASSERT((ssize_t)expectedlen == rv); CU_ASSERT(hd.flags == nhd.flags); - CU_ASSERT(NGTCP2_PKT_03 == nhd.type); + CU_ASSERT(NGTCP2_PKT_SHORT == nhd.type); CU_ASSERT(ngtcp2_cid_empty(&nhd.dcid)); CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); - CU_ASSERT(hd.pkt_num == nhd.pkt_num); + CU_ASSERT((hd.pkt_num & 0x3fffffff) == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); CU_ASSERT(0 == nhd.version); - CU_ASSERT(0 == nhd.payloadlen); + CU_ASSERT(0 == nhd.len); } @@ -884,8 +889,8 @@ void test_ngtcp2_pkt_adjust_pkt_num(void) { CU_ASSERT(0x01ff == ngtcp2_pkt_adjust_pkt_num(0x0100, 0xff, 8)); CU_ASSERT(0x02ff == ngtcp2_pkt_adjust_pkt_num(0x01ff, 0xff, 8)); - CU_ASSERT(0xffffffffffffffabllu == - ngtcp2_pkt_adjust_pkt_num(0xffffffffffffffffllu, 0xab, 8)); + CU_ASSERT(0x3fffffffffffffabllu == + ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0xab, 8)); } void test_ngtcp2_pkt_validate_ack(void) { @@ -945,16 +950,16 @@ void test_ngtcp2_pkt_write_stateless_reset(void) { token[i] = (uint8_t)(i + 1); } - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_01, &dcid, NULL, - 0xf1, 0, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_SHORT, &dcid, + NULL, 0xf1, 1, 0, 0); spktlen = ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), &hd, token, rand, sizeof(rand)); p = buf; CU_ASSERT(256 == spktlen); - CU_ASSERT((NGTCP2_KEY_PHASE_BIT | NGTCP2_THIRD_BIT | NGTCP2_FOURTH_BIT | - NGTCP2_PKT_01) == *p); + CU_ASSERT((NGTCP2_KEY_PHASE_BIT | NGTCP2_THIRD_BIT | NGTCP2_FOURTH_BIT) == + *p); ++p; @@ -962,7 +967,7 @@ void test_ngtcp2_pkt_write_stateless_reset(void) { p += dcid.datalen; - CU_ASSERT(0xf1 == *p); + CU_ASSERT((0xf1 & 0x7f) == *p); ++p; @@ -979,8 +984,8 @@ void test_ngtcp2_pkt_write_stateless_reset(void) { CU_ASSERT(spktlen == p - buf); /* Not enough buffer */ - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_02, &dcid, NULL, - 0xf1, 0, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 0xf1, 2, 0, 0); spktlen = ngtcp2_pkt_write_stateless_reset( buf, 1 + dcid.datalen + 2 + NGTCP2_STATELESS_RESET_TOKENLEN - 1, &hd, token, rand, sizeof(rand)); diff --git a/tests/ngtcp2_rtb_test.c b/tests/ngtcp2_rtb_test.c index 13a1801a..0325c182 100644 --- a/tests/ngtcp2_rtb_test.c +++ b/tests/ngtcp2_rtb_test.c @@ -45,8 +45,8 @@ void test_ngtcp2_rtb_add(void) { ngtcp2_log_init(&log, NULL, NULL, 0, NULL); ngtcp2_rtb_init(&rtb, &log, mem); - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_01, &dcid, NULL, - 1000000007, NGTCP2_PROTO_VER_MAX, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 1000000007, 1, NGTCP2_PROTO_VER_MAX, 0); rv = ngtcp2_rtb_entry_new(&ent, &hd, NULL, 10, 0, NGTCP2_RTB_FLAG_NONE, mem); @@ -54,8 +54,8 @@ void test_ngtcp2_rtb_add(void) { ngtcp2_rtb_add(&rtb, ent); - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_02, &dcid, NULL, - 1000000008, NGTCP2_PROTO_VER_MAX, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 1000000008, 2, NGTCP2_PROTO_VER_MAX, 0); rv = ngtcp2_rtb_entry_new(&ent, &hd, NULL, 9, 0, NGTCP2_RTB_FLAG_NONE, mem); @@ -63,8 +63,8 @@ void test_ngtcp2_rtb_add(void) { ngtcp2_rtb_add(&rtb, ent); - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_03, &dcid, NULL, - 1000000009, NGTCP2_PROTO_VER_MAX, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + 1000000009, 4, NGTCP2_PROTO_VER_MAX, 0); rv = ngtcp2_rtb_entry_new(&ent, &hd, NULL, 11, 0, NGTCP2_RTB_FLAG_NONE, mem); @@ -105,8 +105,8 @@ static void add_rtb_entry_range(ngtcp2_rtb *rtb, uint64_t base_pkt_num, dcid_init(&dcid); for (i = base_pkt_num; i < base_pkt_num + len; ++i) { - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_01, &dcid, NULL, i, - NGTCP2_PROTO_VER_MAX, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, &dcid, NULL, + i, 1, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent, &hd, NULL, 0, 0, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_rtb_add(rtb, ent); } @@ -276,23 +276,23 @@ void test_ngtcp2_rtb_insert_range(void) { ngtcp2_rtb_init(&rtb, &log, mem); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 900, NGTCP2_PROTO_VER_MAX, 0); + &scid, 900, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent1, &hd, NULL, 0, 1, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 898, NGTCP2_PROTO_VER_MAX, 0); + &scid, 898, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent2, &hd, NULL, 0, 2, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 897, NGTCP2_PROTO_VER_MAX, 0); + &scid, 897, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent3, &hd, NULL, 0, 4, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 790, NGTCP2_PROTO_VER_MAX, 0); + &scid, 790, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent4, &hd, NULL, 0, 8, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 788, NGTCP2_PROTO_VER_MAX, 0); + &scid, 788, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent5, &hd, NULL, 0, 16, NGTCP2_RTB_FLAG_NONE, mem); head = ent1; @@ -302,17 +302,17 @@ void test_ngtcp2_rtb_insert_range(void) { ent4->next = ent5; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 896, NGTCP2_PROTO_VER_MAX, 0); + &scid, 896, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent, &hd, NULL, 0, 0, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_rtb_add(&rtb, ent); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 899, NGTCP2_PROTO_VER_MAX, 0); + &scid, 899, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent, &hd, NULL, 0, 0, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_rtb_add(&rtb, ent); ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_HANDSHAKE, &dcid, - &scid, 901, NGTCP2_PROTO_VER_MAX, 0); + &scid, 901, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_rtb_entry_new(&ent, &hd, NULL, 0, 0, NGTCP2_RTB_FLAG_NONE, mem); ngtcp2_rtb_add(&rtb, ent); diff --git a/tests/ngtcp2_test_helper.c b/tests/ngtcp2_test_helper.c index 2b55dfdc..e218ab19 100644 --- a/tests/ngtcp2_test_helper.c +++ b/tests/ngtcp2_test_helper.c @@ -97,6 +97,23 @@ static ssize_t null_encrypt(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, (void)ad; (void)adlen; (void)user_data; + return (ssize_t)plaintextlen + NGTCP2_FAKE_AEAD_OVERHEAD; +} + +static ssize_t null_encrypt_pn(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, size_t keylen, + const uint8_t *nonce, size_t noncelen, + void *user_data) { + (void)conn; + (void)dest; + (void)destlen; + (void)plaintext; + (void)key; + (void)keylen; + (void)nonce; + (void)noncelen; + (void)user_data; return (ssize_t)plaintextlen; } @@ -111,11 +128,13 @@ size_t write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *out, size_t outlen, memset(&ctx, 0, sizeof(ctx)); ctx.encrypt = null_encrypt; + ctx.encrypt_pn = null_encrypt_pn; ctx.ckm = conn->rx_ckm; + ctx.aead_overhead = NGTCP2_FAKE_AEAD_OVERHEAD; ctx.user_data = conn; - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_03, dcid, NULL, - pkt_num, NGTCP2_PROTO_VER_MAX, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, dcid, NULL, + pkt_num, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_ppe_init(&ppe, out, outlen, &ctx); rv = ngtcp2_ppe_encode_hd(&ppe, &hd); @@ -138,11 +157,13 @@ size_t write_single_frame_pkt_without_conn_id(ngtcp2_conn *conn, uint8_t *out, memset(&ctx, 0, sizeof(ctx)); ctx.encrypt = null_encrypt; + ctx.encrypt_pn = null_encrypt_pn; ctx.ckm = conn->rx_ckm; + ctx.aead_overhead = NGTCP2_FAKE_AEAD_OVERHEAD; ctx.user_data = conn; - ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_03, NULL, NULL, - pkt_num, NGTCP2_PROTO_VER_MAX, 0); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_SHORT, NULL, NULL, + pkt_num, 4, NGTCP2_PROTO_VER_MAX, 0); ngtcp2_ppe_init(&ppe, out, outlen, &ctx); rv = ngtcp2_ppe_encode_hd(&ppe, &hd); @@ -168,11 +189,13 @@ size_t write_single_frame_handshake_pkt(ngtcp2_conn *conn, uint8_t *out, memset(&ctx, 0, sizeof(ctx)); ctx.encrypt = null_encrypt; + ctx.encrypt_pn = null_encrypt_pn; ctx.ckm = conn->hs_rx_ckm; + ctx.aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; ctx.user_data = conn; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, pkt_type, dcid, scid, - pkt_num, version, 0); + pkt_num, 4, version, 0); ngtcp2_ppe_init(&ppe, out, outlen, &ctx); rv = ngtcp2_ppe_encode_hd(&ppe, &hd); @@ -198,11 +221,13 @@ size_t write_handshake_pkt(ngtcp2_conn *conn, uint8_t *out, size_t outlen, memset(&ctx, 0, sizeof(ctx)); ctx.encrypt = null_encrypt; + ctx.encrypt_pn = null_encrypt_pn; ctx.ckm = conn->hs_rx_ckm; + ctx.aead_overhead = NGTCP2_HANDSHAKE_AEAD_OVERHEAD; ctx.user_data = conn; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, pkt_type, dcid, scid, - pkt_num, version, 0); + pkt_num, 4, version, 0); ngtcp2_ppe_init(&ppe, out, outlen, &ctx); rv = ngtcp2_ppe_encode_hd(&ppe, &hd); @@ -274,3 +299,57 @@ void write_pkt_payloadlen(uint8_t *pkt, const ngtcp2_cid *dcid, ngtcp2_put_varint14(&pkt[1 + 4 + 1 + dcid->datalen + scid->datalen], (uint16_t)payloadlen); } + +ssize_t pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen) { + const uint8_t *p; + size_t n; + ssize_t nread; + + nread = ngtcp2_pkt_decode_hd_long(dest, pkt, pktlen); + if (nread < 0 || dest->type == NGTCP2_PKT_VERSION_NEGOTIATION) { + return nread; + } + + if ((size_t)nread == pktlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = pkt + nread; + + n = ngtcp2_get_pkt_num_len(p); + if (pktlen < (size_t)nread + n) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dest->pkt_num = ngtcp2_get_pkt_num(&dest->pkt_numlen, p); + + return nread + (ssize_t)n; +} + +ssize_t pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen) { + const uint8_t *p; + size_t n; + ssize_t nread; + + nread = ngtcp2_pkt_decode_hd_short(dest, pkt, pktlen, dcidlen); + if (nread < 0) { + return nread; + } + + if ((size_t)nread == pktlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = pkt + nread; + + n = ngtcp2_get_pkt_num_len(p); + if (pktlen < (size_t)nread + n) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dest->pkt_num = ngtcp2_get_pkt_num(&dest->pkt_numlen, p); + + return nread + (ssize_t)n; +} diff --git a/tests/ngtcp2_test_helper.h b/tests/ngtcp2_test_helper.h index 2d32475c..f1bad175 100644 --- a/tests/ngtcp2_test_helper.h +++ b/tests/ngtcp2_test_helper.h @@ -50,6 +50,14 @@ #define NGTCP2_APP_ERR01 0xff01u #define NGTCP2_APP_ERR02 0xff02u +/* + * NGTCP2_FAKE_AEAD_OVERHEAD is AEAD overhead used in unit tests. + * Because we use the same encryption/decryption function for both + * handshake and post handshake packets, we have to use AEAD overhead + * used in handshake packets. + */ +#define NGTCP2_FAKE_AEAD_OVERHEAD NGTCP2_HANDSHAKE_AEAD_OVERHEAD + /* * ngtcp2_t_encode_stream_frame encodes STREAM frame into |out| with * the given parameters. If NGTCP2_STREAM_LEN_BIT is set in |flags|, @@ -146,4 +154,20 @@ uint64_t read_pkt_payloadlen(const uint8_t *pkt, const ngtcp2_cid *dcid, void write_pkt_payloadlen(uint8_t *pkt, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, uint64_t payloadlen); +/* + * pkt_decode_hd_long decodes long packet header from |pkt| of length + * |pktlen|. This function assumes that packte number field has been + * decrypted. + */ +ssize_t pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen); + +/* + * pkt_decode_hd_short decodes long packet header from |pkt| of length + * |pktlen|. This function assumes that packte number field has been + * decrypted. + */ +ssize_t pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen); + #endif /* NGTCP2_TEST_HELPER_H */ -- GitLab