Skip to content

Commit c7532b8

Browse files
abakiaydinfacebook-github-bot
authored andcommitted
Update get retry config API.
Summary: Adds `sni` parameter to `getRetryConfigs` api. Returns configs matching the sni in the front of retry config list, keeps the relative order of the configs. Reviewed By: mingtaoy Differential Revision: D73597518 fbshipit-source-id: 29042607e20064b99d740575ad910c6609097c08
1 parent f1645ff commit c7532b8

File tree

6 files changed

+129
-15
lines changed

6 files changed

+129
-15
lines changed

third-party/fizz/src/fizz/protocol/ech/Decrypter.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#include <fizz/protocol/ech/Decrypter.h>
1010

11+
#include <iterator>
12+
1113
namespace fizz {
1214
namespace ech {
1315

@@ -180,11 +182,30 @@ ClientHello ECHConfigManager::decryptClientHelloHRR(
180182
*factory_, chlo, encapsulatedKey, dummy, configs_);
181183
}
182184

183-
std::vector<ech::ECHConfig> ECHConfigManager::getRetryConfigs() const {
185+
std::vector<ech::ECHConfig> ECHConfigManager::getRetryConfigs(
186+
const folly::Optional<std::string>& maybeSni) const {
184187
std::vector<ech::ECHConfig> retryConfigs;
185-
for (const auto& cfg : configs_) {
186-
retryConfigs.push_back(cfg.echConfig);
188+
std::vector<ech::ECHConfig> nonMatchingConfigs;
189+
for (const auto& config : configs_) {
190+
switch (config.echConfig.version) {
191+
case ECHVersion::Draft15: {
192+
auto cursor =
193+
folly::io::Cursor(config.echConfig.ech_config_content.get());
194+
auto currentConfigDraft = decode<ECHConfigContentDraft>(cursor);
195+
if (maybeSni.hasValue() &&
196+
maybeSni.value() ==
197+
currentConfigDraft.public_name->to<std::string>()) {
198+
retryConfigs.push_back(config.echConfig);
199+
} else {
200+
nonMatchingConfigs.push_back(config.echConfig);
201+
}
202+
} break;
203+
}
187204
}
205+
retryConfigs.insert(
206+
retryConfigs.end(),
207+
std::make_move_iterator(nonMatchingConfigs.begin()),
208+
std::make_move_iterator(nonMatchingConfigs.end()));
188209
return retryConfigs;
189210
}
190211

third-party/fizz/src/fizz/protocol/ech/Decrypter.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class Decrypter {
4242
virtual ClientHello decryptClientHelloHRR(
4343
const ClientHello& chlo,
4444
const std::unique_ptr<folly::IOBuf>& encapsulatedKey) = 0;
45-
virtual std::vector<ech::ECHConfig> getRetryConfigs() const = 0;
45+
virtual std::vector<ech::ECHConfig> getRetryConfigs(
46+
const folly::Optional<std::string>& maybeSni) const = 0;
4647
};
4748

4849
class ECHConfigManager : public Decrypter {
@@ -58,9 +59,10 @@ class ECHConfigManager : public Decrypter {
5859
ClientHello decryptClientHelloHRR(
5960
const ClientHello& chlo,
6061
const std::unique_ptr<folly::IOBuf>& encapsulatedKey) override;
61-
std::vector<ech::ECHConfig> getRetryConfigs() const override;
62+
std::vector<ech::ECHConfig> getRetryConfigs(
63+
const folly::Optional<std::string>& maybeSni) const override;
6264

63-
private:
65+
protected:
6466
std::shared_ptr<Factory> factory_;
6567
std::vector<DecrypterParams> configs_;
6668
};

third-party/fizz/src/fizz/protocol/ech/test/DecrypterTest.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <fizz/protocol/ech/test/TestUtil.h>
1717
#include <fizz/protocol/test/TestUtil.h>
1818

19+
using namespace std::string_literals;
1920
using namespace fizz::test;
2021

2122
namespace fizz {
@@ -102,6 +103,36 @@ ClientHello getChloOuterHRRWithExt(
102103
return chloOuter;
103104
}
104105

106+
ECHConfig makeDummyConfig(uint8_t configId, const std::string& publicName) {
107+
auto content = ECHConfigContentDraft{};
108+
content.key_config.config_id = configId;
109+
content.public_name = folly::IOBuf::copyBuffer(publicName);
110+
content.key_config.public_key = folly::IOBuf::copyBuffer("public");
111+
auto config = ECHConfig{};
112+
config.version = ECHVersion::Draft15;
113+
config.ech_config_content = fizz::encode(std::move(content));
114+
return config;
115+
}
116+
117+
struct RetryConfigExpectation {
118+
uint8_t id;
119+
std::string name;
120+
};
121+
122+
void checkRetryConfigExpectation(
123+
const std::vector<RetryConfigExpectation>& expectations,
124+
const std::vector<ECHConfig>& configs) {
125+
ASSERT_EQ(expectations.size(), configs.size());
126+
for (size_t i = 0; i < expectations.size(); ++i) {
127+
const auto& config = configs[i];
128+
auto cursor = folly::io::Cursor(config.ech_config_content.get());
129+
auto configContent = decode<ECHConfigContentDraft>(cursor);
130+
EXPECT_EQ(expectations[i].id, configContent.key_config.config_id);
131+
EXPECT_EQ(
132+
expectations[i].name, configContent.public_name->to<std::string>());
133+
}
134+
}
135+
105136
TEST(DecrypterTest, TestDecodeSuccess) {
106137
auto kex = openssl::makeOpenSSLECKeyExchange<fizz::P256>();
107138
kex->setPrivateKey(getPrivateKey(kP256Key));
@@ -254,6 +285,57 @@ TEST(DecrypterTest, TestDecodeMultipleDecrypterParam) {
254285
EXPECT_TRUE(result->configId == 2);
255286
}
256287

288+
TEST(DecrypterTest, TestGetRetryConfigs) {
289+
auto decrypter = ECHConfigManager(std::make_shared<DefaultFactory>());
290+
auto publicNames = {"a.com", "b.com", "a.com", "c.com", "b.com", "a.com"};
291+
uint8_t configId = 0;
292+
for (const auto& name : publicNames) {
293+
decrypter.addDecryptionConfig(
294+
{.echConfig = makeDummyConfig(configId++, name)});
295+
}
296+
297+
checkRetryConfigExpectation(
298+
{{0, "a.com"},
299+
{2, "a.com"},
300+
{5, "a.com"},
301+
{1, "b.com"},
302+
{3, "c.com"},
303+
{4, "b.com"}},
304+
decrypter.getRetryConfigs("a.com"s));
305+
checkRetryConfigExpectation(
306+
{{1, "b.com"},
307+
{4, "b.com"},
308+
{0, "a.com"},
309+
{2, "a.com"},
310+
{3, "c.com"},
311+
{5, "a.com"}},
312+
decrypter.getRetryConfigs("b.com"s));
313+
checkRetryConfigExpectation(
314+
{{3, "c.com"},
315+
{0, "a.com"},
316+
{1, "b.com"},
317+
{2, "a.com"},
318+
{4, "b.com"},
319+
{5, "a.com"}},
320+
decrypter.getRetryConfigs("c.com"s));
321+
checkRetryConfigExpectation(
322+
{{0, "a.com"},
323+
{1, "b.com"},
324+
{2, "a.com"},
325+
{3, "c.com"},
326+
{4, "b.com"},
327+
{5, "a.com"}},
328+
decrypter.getRetryConfigs("d.com"s));
329+
checkRetryConfigExpectation(
330+
{{0, "a.com"},
331+
{1, "b.com"},
332+
{2, "a.com"},
333+
{3, "c.com"},
334+
{4, "b.com"},
335+
{5, "a.com"}},
336+
decrypter.getRetryConfigs(folly::none));
337+
}
338+
257339
} // namespace test
258340
} // namespace ech
259341
} // namespace fizz

third-party/fizz/src/fizz/protocol/test/Mocks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ class MockECHDecrypter : public ech::Decrypter {
483483
MOCK_METHOD(
484484
std::vector<ech::ECHConfig>,
485485
getRetryConfigs,
486-
(),
486+
(const folly::Optional<std::string>& maybeSni),
487487
(const, override));
488488
};
489489
} // namespace test

third-party/fizz/src/fizz/server/ServerProtocol.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,9 +1073,9 @@ static std::pair<std::vector<ExtensionType>, Buf> getCertificateRequest(
10731073
std::move(certReqExtensions), std::move(encodedCertificateRequest));
10741074
}
10751075

1076-
static std::string getSNI(const ClientHello& chlo) {
1076+
static folly::Optional<std::string> getSNI(const ClientHello& chlo) {
1077+
folly::Optional<std::string> sni;
10771078
auto serverNameList = getExtension<ServerNameList>(chlo.extensions);
1078-
std::string sni;
10791079
if (serverNameList && !serverNameList->server_name_list.empty()) {
10801080
sni = serverNameList->server_name_list.front().hostname->to<std::string>();
10811081
}
@@ -1185,6 +1185,7 @@ static std::pair<ECHStatus, folly::Optional<ECHState>> processECH(
11851185
if (state.echState().has_value()) {
11861186
echState->hpkeContext = std::move(state.echState()->hpkeContext);
11871187
echState->cipherSuite = state.echState()->cipherSuite;
1188+
echState->outerSni = state.echState()->outerSni;
11881189
}
11891190
} else {
11901191
bool requestedECH =
@@ -1196,14 +1197,12 @@ static std::pair<ECHStatus, folly::Optional<ECHState>> processECH(
11961197
if (requestedECH) {
11971198
auto echExt = getExtension<ech::OuterECHClientHello>(chlo.extensions);
11981199
echState = ECHState{
1199-
echExt->cipher_suite, echExt->config_id, nullptr, folly::none};
1200+
echExt->cipher_suite, echExt->config_id, nullptr, getSNI(chlo)};
12001201
if (decrypter) {
12011202
auto gotChlo = decrypter->decryptClientHello(chlo);
12021203
if (gotChlo.has_value()) {
1203-
auto outerSni = getSNI(chlo);
12041204
echStatus = ECHStatus::Accepted;
12051205
echState->hpkeContext = std::move(gotChlo->context);
1206-
echState->outerSni = outerSni;
12071206
chlo = std::move(gotChlo->chlo);
12081207
} else {
12091208
echStatus = ECHStatus::Rejected;
@@ -1636,7 +1635,10 @@ EventHandler<ServerTypes, StateEnum::ExpectingClientHello, Event::ClientHello>::
16361635
std::move(echScheduler));
16371636
} else if (echStatus == ECHStatus::Rejected) {
16381637
auto decrypter = state.context()->getECHDecrypter();
1639-
echRetryConfigs = decrypter->getRetryConfigs();
1638+
DCHECK(decrypter);
1639+
DCHECK(echState);
1640+
echRetryConfigs =
1641+
decrypter->getRetryConfigs(echState->outerSni);
16401642
}
16411643

16421644
auto encodedServerHello = encodeHandshake(std::move(serverHello));

third-party/fizz/src/fizz/server/test/ServerProtocolTest.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,7 @@ TEST_F(ServerProtocolTest, TestECHDecryptionFailure) {
14881488
auto decrypter = std::make_shared<MockECHDecrypter>();
14891489
EXPECT_CALL(*decrypter, decryptClientHello(_))
14901490
.WillOnce(InvokeWithoutArgs([=]() { return folly::none; }));
1491-
EXPECT_CALL(*decrypter, getRetryConfigs())
1491+
EXPECT_CALL(*decrypter, getRetryConfigs(_))
14921492
.WillOnce(InvokeWithoutArgs([]() -> std::vector<ech::ECHConfig> {
14931493
ech::ECHConfig cfg;
14941494
cfg.version = ech::ECHVersion::Draft15;
@@ -3343,8 +3343,15 @@ TEST_F(ServerProtocolTest, TestRetryECHMissingInnerExtension) {
33433343
TEST_F(ServerProtocolTest, TestRetryClientHelloECHRejectedFlow) {
33443344
setUpExpectingClientHelloRetry();
33453345
state_.echStatus() = ECHStatus::Rejected;
3346+
fizz::server::ECHState echState;
3347+
echState.cipherSuite.kdf_id = fizz::hpke::KDFId::Sha256;
3348+
echState.cipherSuite.aead_id = fizz::hpke::AeadId::TLS_AES_128_GCM_SHA256;
3349+
echState.configId = 0xFB;
3350+
echState.hpkeContext = nullptr;
3351+
echState.outerSni = "test.net";
3352+
state_.echState() = std::move(echState);
33463353
auto decrypter = std::make_shared<MockECHDecrypter>();
3347-
EXPECT_CALL(*decrypter, getRetryConfigs())
3354+
EXPECT_CALL(*decrypter, getRetryConfigs(_))
33483355
.WillOnce(InvokeWithoutArgs([]() -> std::vector<ech::ECHConfig> {
33493356
ech::ECHConfig cfg;
33503357
cfg.version = ech::ECHVersion::Draft15;

0 commit comments

Comments
 (0)