Skip to content

Commit 1cdcebd

Browse files
author
bneradt
committed
Make QUIC connection ID creation explicit
Default-constructing a QUIC connection ID generates random bytes, even when the value is only a placeholder that will be overwritten. Several generation paths also randomize the same ID a second time. This patch makes empty, decoded, and newly generated IDs explicit. It deletes default construction, adds a checked CSPRNG-backed factory, and adds coverage for the connection ID representation and initialization contract. Fixes: #5504
1 parent 46be2f5 commit 1cdcebd

10 files changed

Lines changed: 120 additions & 58 deletions

File tree

include/iocore/net/quic/QUICTypes.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ class QUICConnectionId
230230
static constexpr int MAX_LENGTH = 20;
231231
static constexpr size_t MAX_HEX_STR_LENGTH = MAX_LENGTH * 2 + 1;
232232
static QUICConnectionId ZERO();
233-
QUICConnectionId();
233+
static QUICConnectionId random();
234+
/// Force callers to explicitly choose zero, random, or byte-based initialization.
235+
QUICConnectionId() = delete;
234236
QUICConnectionId(const uint8_t *buf, uint8_t len);
235237

236238
explicit
@@ -269,12 +271,11 @@ class QUICConnectionId
269271

270272
uint8_t length() const;
271273
bool is_zero() const;
272-
void randomize();
273274

274275
private:
275276
uint64_t _hashcode() const;
276-
uint8_t _id[MAX_LENGTH];
277-
uint8_t _len = 0;
277+
uint8_t _id[MAX_LENGTH] = {0};
278+
uint8_t _len = 0;
278279
};
279280

280281
class QUICStatelessResetToken
@@ -432,7 +433,7 @@ class QUICPreferredAddress
432433
private:
433434
IpEndpoint _endpoint_ipv4 = {};
434435
IpEndpoint _endpoint_ipv6 = {};
435-
QUICConnectionId _cid;
436+
QUICConnectionId _cid = QUICConnectionId::ZERO();
436437
QUICStatelessResetToken _token;
437438
bool _valid = false;
438439
};

src/iocore/net/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ if(BUILD_TESTING)
156156
if(SSLLIB_IS_AT_LEAST_OPENSSL3)
157157
target_sources(test_net PRIVATE unit_tests/test_SSLDHParams.cc)
158158
endif()
159+
if(TS_USE_QUIC OR TS_USE_QMUX)
160+
target_sources(test_net PRIVATE unit_tests/test_QUICConnectionId.cc)
161+
endif()
159162
if(TS_USE_QUIC)
160163
target_sources(test_net PRIVATE unit_tests/test_QUICTokenKeyConfig.cc)
161164
endif()

src/iocore/net/OpenSSLQUICNetVConnection.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ QUICNetVConnection::init(SSL *ssl, QUICPacketHandler *packet_handler)
101101
{
102102
SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent);
103103

104-
this->_ssl = ssl;
105-
this->_packet_handler = packet_handler;
106-
this->_quic_connection_id.randomize();
104+
this->_ssl = ssl;
105+
this->_packet_handler = packet_handler;
106+
this->_quic_connection_id = QUICConnectionId::random();
107107
this->_initial_source_connection_id = this->_quic_connection_id;
108108
this->_cid_text = this->_quic_connection_id.hex();
109109

@@ -477,25 +477,25 @@ QUICNetVConnection::ping()
477477
QUICConnectionId
478478
QUICNetVConnection::peer_connection_id() const
479479
{
480-
return {};
480+
return QUICConnectionId::ZERO();
481481
}
482482

483483
QUICConnectionId
484484
QUICNetVConnection::original_connection_id() const
485485
{
486-
return {};
486+
return QUICConnectionId::ZERO();
487487
}
488488

489489
QUICConnectionId
490490
QUICNetVConnection::first_connection_id() const
491491
{
492-
return {};
492+
return QUICConnectionId::ZERO();
493493
}
494494

495495
QUICConnectionId
496496
QUICNetVConnection::retry_source_connection_id() const
497497
{
498-
return {};
498+
return QUICConnectionId::ZERO();
499499
}
500500

501501
QUICConnectionId

src/iocore/net/P_QUICNetVConnection.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,14 @@ class QUICNetVConnection : public UnixNetVConnection,
213213
SSL *_ssl;
214214
QUICConfig::scoped_config _quic_config;
215215

216-
QUICConnectionId _peer_quic_connection_id; // dst cid in local
217-
QUICConnectionId _peer_old_quic_connection_id; // dst previous cid in local
218-
QUICConnectionId _original_quic_connection_id; // dst cid of initial packet from client
219-
QUICConnectionId _first_quic_connection_id; // dst cid of initial packet from client that doesn't have retry token
220-
QUICConnectionId _retry_source_connection_id; // src cid used for sending Retry packet
221-
QUICConnectionId _initial_source_connection_id; // src cid used for Initial packet
222-
QUICConnectionId _quic_connection_id; // src cid in local
216+
QUICConnectionId _peer_quic_connection_id = QUICConnectionId::ZERO(); // dst cid in local
217+
QUICConnectionId _peer_old_quic_connection_id = QUICConnectionId::ZERO(); // dst previous cid in local
218+
QUICConnectionId _original_quic_connection_id = QUICConnectionId::ZERO(); // dst cid of initial packet from client
219+
QUICConnectionId _first_quic_connection_id =
220+
QUICConnectionId::ZERO(); // dst cid of initial packet from client without retry token
221+
QUICConnectionId _retry_source_connection_id = QUICConnectionId::ZERO(); // src cid used for sending Retry packet
222+
QUICConnectionId _initial_source_connection_id = QUICConnectionId::ZERO(); // src cid used for Initial packet
223+
QUICConnectionId _quic_connection_id = QUICConnectionId::ZERO(); // src cid in local
223224

224225
#if TS_HAS_QUICHE
225226
QUICConnectionTable *_ctable = nullptr;

src/iocore/net/QUICNetProcessor.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,7 @@ QUICNetProcessor::connect_re(Continuation *cont, sockaddr const *remote_addr, Ne
185185
}
186186

187187
// Setup QUICNetVConnection
188-
QUICConnectionId client_dst_cid;
189-
client_dst_cid.randomize();
188+
QUICConnectionId client_dst_cid = QUICConnectionId::random();
190189
// vc->init set handler of vc `QUICNetVConnection::startEvent`
191190
vc->init(QUIC_SUPPORTED_VERSIONS[0], client_dst_cid, client_dst_cid, con, packet_handler);
192191
packet_handler->init(vc);

src/iocore/net/QUICNetVConnection.cc

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ QUICNetVConnection::init(QUICVersion /* version ATS_UNUSED */, QUICConnectionId
7979
QUICPacketHandler *packet_handler, QUICConnectionTable *ctable, SSL *ssl)
8080
{
8181
SET_HANDLER((NetVConnHandler)&QUICNetVConnection::acceptEvent);
82-
this->_udp_con = udp_con;
83-
this->_quiche_con = quiche_con;
84-
this->_packet_handler = packet_handler;
85-
this->_original_quic_connection_id = original_cid;
86-
this->_quic_connection_id.randomize();
82+
this->_udp_con = udp_con;
83+
this->_quiche_con = quiche_con;
84+
this->_packet_handler = packet_handler;
85+
this->_original_quic_connection_id = original_cid;
86+
this->_quic_connection_id = QUICConnectionId::random();
8787
this->_initial_source_connection_id = this->_quic_connection_id;
8888

8989
if (ctable) {
@@ -448,37 +448,37 @@ QUICNetVConnection::ping()
448448
QUICConnectionId
449449
QUICNetVConnection::peer_connection_id() const
450450
{
451-
return {};
451+
return QUICConnectionId::ZERO();
452452
}
453453

454454
QUICConnectionId
455455
QUICNetVConnection::original_connection_id() const
456456
{
457-
return {};
457+
return QUICConnectionId::ZERO();
458458
}
459459

460460
QUICConnectionId
461461
QUICNetVConnection::first_connection_id() const
462462
{
463-
return {};
463+
return QUICConnectionId::ZERO();
464464
}
465465

466466
QUICConnectionId
467467
QUICNetVConnection::retry_source_connection_id() const
468468
{
469-
return {};
469+
return QUICConnectionId::ZERO();
470470
}
471471

472472
QUICConnectionId
473473
QUICNetVConnection::initial_source_connection_id() const
474474
{
475-
return {};
475+
return QUICConnectionId::ZERO();
476476
}
477477

478478
QUICConnectionId
479479
QUICNetVConnection::connection_id() const
480480
{
481-
return {};
481+
return QUICConnectionId::ZERO();
482482
}
483483

484484
std::string_view

src/iocore/net/QUICPacketHandler.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,8 @@ QUICPacketHandlerIn::_recv_packet(int /* event ATS_UNUSED */, UDPPacket *udp_pac
240240

241241
QUICConfig::scoped_config params;
242242
if (params->stateless_retry() && token_len == 0) {
243-
QUICConnectionId new_cid;
244-
new_cid.randomize();
245-
QUICRetryToken retry_token = {
243+
QUICConnectionId new_cid = QUICConnectionId::random();
244+
QUICRetryToken retry_token = {
246245
udp_packet->from,
247246
{dcid, static_cast<uint8_t>(dcid_len)},
248247
new_cid
@@ -278,7 +277,7 @@ QUICPacketHandlerIn::_recv_packet(int /* event ATS_UNUSED */, UDPPacket *udp_pac
278277
return;
279278
}
280279

281-
QUICConnectionId new_cid;
280+
QUICConnectionId new_cid = QUICConnectionId::random();
282281

283282
QUICCertConfig::scoped_config server_cert;
284283
auto default_ctx = server_cert->defaultContext();

src/iocore/net/qmux/QMuxConnection.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,11 @@ QMuxConnection::_init_shared_config()
9292
});
9393
}
9494

95-
QMuxConnection::QMuxConnection(NetVConnection *netvc) : Continuation(netvc->mutex)
95+
QMuxConnection::QMuxConnection(NetVConnection *netvc) : Continuation(netvc->mutex), _synthetic_cid(QUICConnectionId::random())
9696
{
9797
_init_shared_config();
9898
SET_HANDLER(&QMuxConnection::main_event);
9999

100-
_synthetic_cid.randomize();
101100
_cids_str = _synthetic_cid.hex();
102101

103102
auto *local_ep = netvc->get_local_addr();

src/iocore/net/quic/QUICTypes.cc

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
#include "iocore/net/quic/QUICTypes.h"
3030
#include "iocore/net/quic/QUICConfig.h"
3131
#include "iocore/net/quic/QUICIntUtil.h"
32-
#include <random>
3332
#include <openssl/crypto.h>
3433
#include <openssl/hmac.h>
34+
#include <openssl/rand.h>
3535

3636
uint8_t QUICConnectionId::SCID_LEN = 0;
3737

@@ -723,17 +723,22 @@ QUICConnectionId::ZERO()
723723
return QUICConnectionId(zero, 0);
724724
}
725725

726-
QUICConnectionId::QUICConnectionId()
727-
{
728-
this->randomize();
729-
}
730-
731726
QUICConnectionId::QUICConnectionId(const uint8_t *buf, uint8_t len) : _len(len)
732727
{
733728
ink_assert(len <= QUICConnectionId::MAX_LENGTH);
734729
memcpy(this->_id, buf, std::min(static_cast<int>(len), QUICConnectionId::MAX_LENGTH));
735730
}
736731

732+
QUICConnectionId
733+
QUICConnectionId::random()
734+
{
735+
uint8_t id[MAX_LENGTH] = {0};
736+
737+
ink_release_assert(SCID_LEN <= MAX_LENGTH);
738+
ink_release_assert(RAND_bytes(id, SCID_LEN) == 1);
739+
return {id, SCID_LEN};
740+
}
741+
737742
uint8_t
738743
QUICConnectionId::length() const
739744
{
@@ -751,20 +756,6 @@ QUICConnectionId::is_zero() const
751756
return true;
752757
}
753758

754-
void
755-
QUICConnectionId::randomize()
756-
{
757-
std::random_device rnd;
758-
uint32_t x = rnd();
759-
for (int i = QUICConnectionId::SCID_LEN - 1; i >= 0; --i) {
760-
if (i % 4 == 0) {
761-
x = rnd();
762-
}
763-
this->_id[i] = (x >> (8 * (i % 4))) & 0xFF;
764-
}
765-
this->_len = QUICConnectionId::SCID_LEN;
766-
}
767-
768759
uint64_t
769760
QUICConnectionId::_hashcode() const
770761
{
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/** @file
2+
3+
Tests for QUIC connection ID initialization.
4+
5+
@section license License
6+
7+
Licensed to the Apache Software Foundation (ASF) under one
8+
or more contributor license agreements. See the NOTICE file
9+
distributed with this work for additional information
10+
regarding copyright ownership. The ASF licenses this file
11+
to you under the Apache License, Version 2.0 (the
12+
"License"); you may not use this file except in compliance
13+
with the License. You may obtain a copy of the License at
14+
15+
http://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
See the License for the specific language governing permissions and
21+
limitations under the License.
22+
*/
23+
24+
#include "iocore/net/quic/QUICTypes.h"
25+
#include "tsutil/PostScript.h"
26+
27+
#include <catch2/catch_test_macros.hpp>
28+
29+
#include <cstdint>
30+
#include <type_traits>
31+
32+
static_assert(!std::is_default_constructible_v<QUICConnectionId>);
33+
34+
TEST_CASE("QUICConnectionId requires explicit initialization", "[quic]")
35+
{
36+
SECTION("empty connection ID")
37+
{
38+
QUICConnectionId cid = QUICConnectionId::ZERO();
39+
40+
CHECK(cid.length() == 0);
41+
CHECK(cid.is_zero());
42+
CHECK(cid.hex() == "0x");
43+
CHECK(static_cast<uint64_t>(cid) == 0);
44+
}
45+
46+
SECTION("connection ID from bytes")
47+
{
48+
uint8_t const raw[] = {0x01, 0x02, 0x03, 0x04};
49+
QUICConnectionId cid{raw, static_cast<uint8_t>(sizeof(raw))};
50+
51+
CHECK(cid.length() == sizeof(raw));
52+
CHECK_FALSE(cid.is_zero());
53+
CHECK(cid.h32() == 0x01020304);
54+
CHECK(static_cast<uint64_t>(cid) == 0x0102030400000000ULL);
55+
CHECK(cid.hex() == "0x01020304");
56+
}
57+
58+
SECTION("random connection ID")
59+
{
60+
uint8_t const previous_scid_len = QUICConnectionId::SCID_LEN;
61+
ts::PostScript restore_scid_len([previous_scid_len]() -> void { QUICConnectionId::SCID_LEN = previous_scid_len; });
62+
63+
QUICConnectionId::SCID_LEN = 18;
64+
QUICConnectionId cid = QUICConnectionId::random();
65+
66+
CHECK(cid.length() == 18);
67+
CHECK(cid.hex().size() == 2 + 18 * 2);
68+
}
69+
}

0 commit comments

Comments
 (0)