blob: f8757e10b11316559e3efcb91c0d07254b5be07a [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/third_party/quiche/src/quic/core/stateless_rejector.h"
6
7#include <memory>
vasilvv872e7a32019-03-12 16:42:44 -07008#include <string>
QUICHE teama6ef0a62019-03-07 20:34:33 -05009#include <vector>
10
11#include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h"
12#include "net/third_party/quiche/src/quic/core/crypto/proof_source.h"
13#include "net/third_party/quiche/src/quic/core/quic_utils.h"
14#include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h"
15#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
16#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
17#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
18#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050019#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
20#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
21#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
22#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
23#include "net/third_party/quiche/src/quic/test_tools/quic_crypto_server_config_peer.h"
24#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
25
26namespace quic {
27namespace test {
28namespace {
29
30QuicConnectionId TestServerDesignatedConnectionId() {
31 return TestConnectionId(24);
32}
33
34// All four combinations of the two flags involved.
35enum FlagsMode { ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED };
36
37const char* FlagsModeToString(FlagsMode mode) {
38 switch (mode) {
39 case ENABLED:
40 return "ENABLED";
41 case STATELESS_DISABLED:
42 return "STATELESS_DISABLED";
43 case CHEAP_DISABLED:
44 return "CHEAP_DISABLED";
45 case BOTH_DISABLED:
46 return "BOTH_DISABLED";
47 default:
48 QUIC_DLOG(FATAL) << "Unexpected FlagsMode";
49 return nullptr;
50 }
51}
52
53// Test various combinations of QUIC version and flag state.
54struct TestParams {
55 ParsedQuicVersion version = UnsupportedQuicVersion();
56 FlagsMode flags;
57};
58
vasilvvc48c8712019-03-11 13:38:16 -070059std::string TestParamToString(
60 const testing::TestParamInfo<TestParams>& params) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050061 return QuicStrCat("v", ParsedQuicVersionToString(params.param.version), "_",
62 FlagsModeToString(params.param.flags));
63}
64
65std::vector<TestParams> GetTestParams() {
66 std::vector<TestParams> params;
67 for (FlagsMode flags :
68 {ENABLED, STATELESS_DISABLED, CHEAP_DISABLED, BOTH_DISABLED}) {
69 for (ParsedQuicVersion version : AllSupportedVersions()) {
70 TestParams param;
71 param.version = version;
72 param.flags = flags;
73 params.push_back(param);
74 }
75 }
76 return params;
77}
78
79class StatelessRejectorTest : public QuicTestWithParam<TestParams> {
80 public:
81 StatelessRejectorTest()
82 : proof_source_(crypto_test_utils::ProofSourceForTesting()),
83 config_(QuicCryptoServerConfig::TESTING,
84 QuicRandom::GetInstance(),
85 crypto_test_utils::ProofSourceForTesting(),
86 KeyExchangeSource::Default(),
87 TlsServerHandshaker::CreateSslCtx()),
88 config_peer_(&config_),
89 compressed_certs_cache_(
90 QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
91 rejector_(QuicMakeUnique<StatelessRejector>(
92 GetParam().version,
93 AllSupportedVersions(),
94 &config_,
95 &compressed_certs_cache_,
96 &clock_,
97 QuicRandom::GetInstance(),
98 kDefaultMaxPacketSize,
99 QuicSocketAddress(QuicIpAddress::Loopback4(), 12345),
100 QuicSocketAddress(QuicIpAddress::Loopback4(), 443))) {
101 SetQuicReloadableFlag(
102 enable_quic_stateless_reject_support,
103 GetParam().flags == ENABLED || GetParam().flags == CHEAP_DISABLED);
104 SetQuicReloadableFlag(
105 quic_use_cheap_stateless_rejects,
106 GetParam().flags == ENABLED || GetParam().flags == STATELESS_DISABLED);
107
108 // Add a new primary config.
109 std::unique_ptr<CryptoHandshakeMessage> msg(config_.AddDefaultConfig(
110 QuicRandom::GetInstance(), &clock_, config_options_));
111
112 // Save the server config.
113 scid_hex_ =
114 "#" + QuicTextUtils::HexEncode(config_peer_.GetPrimaryConfig()->id);
115
116 // Encode the QUIC version.
117 ver_hex_ = ParsedQuicVersionToString(GetParam().version);
118
119 // Generate a public value.
120 char public_value[32];
121 memset(public_value, 42, sizeof(public_value));
122 pubs_hex_ =
123 "#" + QuicTextUtils::HexEncode(public_value, sizeof(public_value));
124
125 // Generate a client nonce.
vasilvvc48c8712019-03-11 13:38:16 -0700126 std::string nonce;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500127 CryptoUtils::GenerateNonce(
128 clock_.WallNow(), QuicRandom::GetInstance(),
129 QuicStringPiece(
130 reinterpret_cast<char*>(config_peer_.GetPrimaryConfig()->orbit),
131 kOrbitSize),
132 &nonce);
133 nonc_hex_ = "#" + QuicTextUtils::HexEncode(nonce);
134
135 // Generate a source address token.
136 SourceAddressTokens previous_tokens;
137 QuicIpAddress ip = QuicIpAddress::Loopback4();
138 MockRandom rand;
vasilvvc48c8712019-03-11 13:38:16 -0700139 std::string stk = config_peer_.NewSourceAddressToken(
QUICHE teama6ef0a62019-03-07 20:34:33 -0500140 config_peer_.GetPrimaryConfig()->id, previous_tokens, ip, &rand,
141 clock_.WallNow(), nullptr);
142 stk_hex_ = "#" + QuicTextUtils::HexEncode(stk);
143 }
144
145 protected:
146 class ProcessDoneCallback : public StatelessRejector::ProcessDoneCallback {
147 public:
148 explicit ProcessDoneCallback(StatelessRejectorTest* test) : test_(test) {}
149 void Run(std::unique_ptr<StatelessRejector> rejector) override {
150 test_->rejector_ = std::move(rejector);
151 }
152
153 private:
154 StatelessRejectorTest* test_;
155 };
156
157 std::unique_ptr<ProofSource> proof_source_;
158 MockClock clock_;
159 QuicCryptoServerConfig config_;
160 QuicCryptoServerConfigPeer config_peer_;
161 QuicCompressedCertsCache compressed_certs_cache_;
162 QuicCryptoServerConfig::ConfigOptions config_options_;
163 std::unique_ptr<StatelessRejector> rejector_;
164
165 // Values used in CHLO messages
vasilvvc48c8712019-03-11 13:38:16 -0700166 std::string scid_hex_;
167 std::string nonc_hex_;
168 std::string pubs_hex_;
169 std::string ver_hex_;
170 std::string stk_hex_;
QUICHE teama6ef0a62019-03-07 20:34:33 -0500171};
172
vasilvvc48c8712019-03-11 13:38:16 -0700173INSTANTIATE_TEST_SUITE_P(Flags,
174 StatelessRejectorTest,
QUICHE teama6ef0a62019-03-07 20:34:33 -0500175 ::testing::ValuesIn(GetTestParams()),
176 TestParamToString);
177
178TEST_P(StatelessRejectorTest, InvalidChlo) {
179 // clang-format off
180 const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
181 {{"PDMD", "X509"},
182 {"COPT", "SREJ"}});
183 // clang-format on
184 rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
185 TestServerDesignatedConnectionId(), client_hello);
186
187 if (GetParam().flags != ENABLED) {
188 EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
189 return;
190 }
191
192 // The StatelessRejector is undecided - proceed with async processing
193 ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
194 StatelessRejector::Process(std::move(rejector_),
195 QuicMakeUnique<ProcessDoneCallback>(this));
196
197 EXPECT_EQ(StatelessRejector::FAILED, rejector_->state());
198 EXPECT_EQ(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, rejector_->error());
199}
200
201TEST_P(StatelessRejectorTest, ValidChloWithoutSrejSupport) {
202 // clang-format off
203 const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
204 {{"PDMD", "X509"},
205 {"AEAD", "AESG"},
206 {"KEXS", "C255"},
207 {"PUBS", pubs_hex_},
208 {"NONC", nonc_hex_},
209 {"VER\0", ver_hex_}},
210 kClientHelloMinimumSize);
211 // clang-format on
212
213 rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
214 TestServerDesignatedConnectionId(), client_hello);
215 EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
216}
217
218TEST_P(StatelessRejectorTest, RejectChlo) {
219 // clang-format off
220 const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
221 {{"PDMD", "X509"},
222 {"AEAD", "AESG"},
223 {"KEXS", "C255"},
224 {"COPT", "SREJ"},
225 {"SCID", scid_hex_},
226 {"PUBS", pubs_hex_},
227 {"NONC", nonc_hex_},
228 {"#004b5453", stk_hex_},
229 {"VER\0", ver_hex_}},
230 kClientHelloMinimumSize);
231 // clang-format on
232
233 rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
234 TestServerDesignatedConnectionId(), client_hello);
235 if (GetParam().flags != ENABLED) {
236 EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
237 return;
238 }
239
240 // The StatelessRejector is undecided - proceed with async processing
241 ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
242 StatelessRejector::Process(std::move(rejector_),
243 QuicMakeUnique<ProcessDoneCallback>(this));
244
245 ASSERT_EQ(StatelessRejector::REJECTED, rejector_->state());
246 const CryptoHandshakeMessage& reply = rejector_->reply();
247 EXPECT_EQ(kSREJ, reply.tag());
248 QuicTagVector reject_reasons;
249 EXPECT_EQ(QUIC_NO_ERROR, reply.GetTaglist(kRREJ, &reject_reasons));
250 EXPECT_EQ(1u, reject_reasons.size());
251 EXPECT_EQ(INVALID_EXPECTED_LEAF_CERTIFICATE,
252 static_cast<HandshakeFailureReason>(reject_reasons[0]));
253}
254
255TEST_P(StatelessRejectorTest, AcceptChlo) {
256 const uint64_t xlct = crypto_test_utils::LeafCertHashForTesting();
vasilvvc48c8712019-03-11 13:38:16 -0700257 const std::string xlct_hex =
QUICHE teama6ef0a62019-03-07 20:34:33 -0500258 "#" + QuicTextUtils::HexEncode(reinterpret_cast<const char*>(&xlct),
259 sizeof(xlct));
260 // clang-format off
261 const CryptoHandshakeMessage client_hello = crypto_test_utils::CreateCHLO(
262 {{"PDMD", "X509"},
263 {"AEAD", "AESG"},
264 {"KEXS", "C255"},
265 {"COPT", "SREJ"},
266 {"SCID", scid_hex_},
267 {"PUBS", pubs_hex_},
268 {"NONC", nonc_hex_},
269 {"#004b5453", stk_hex_},
270 {"VER\0", ver_hex_},
271 {"XLCT", xlct_hex}},
272 kClientHelloMinimumSize);
273 // clang-format on
274
275 rejector_->OnChlo(GetParam().version.transport_version, TestConnectionId(),
276 TestServerDesignatedConnectionId(), client_hello);
277 if (GetParam().flags != ENABLED) {
278 EXPECT_EQ(StatelessRejector::UNSUPPORTED, rejector_->state());
279 return;
280 }
281
282 // The StatelessRejector is undecided - proceed with async processing
283 ASSERT_EQ(StatelessRejector::UNKNOWN, rejector_->state());
284 StatelessRejector::Process(std::move(rejector_),
285 QuicMakeUnique<ProcessDoneCallback>(this));
286
287 EXPECT_EQ(StatelessRejector::ACCEPTED, rejector_->state());
288}
289
290} // namespace
291} // namespace test
292} // namespace quic