Skip to content

Commit

Permalink
Enable sending grease ECH
Browse files Browse the repository at this point in the history
Summary: Allows client protocol to send grease ECH given client context configuration.

Reviewed By: mingtaoy

Differential Revision: D65284753

fbshipit-source-id: 86a50ce096a53b296a60633aa30e8a85a4f66d5e
  • Loading branch information
abakiaydin authored and facebook-github-bot committed Nov 8, 2024
1 parent 812149c commit c4379b4
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 6 deletions.
2 changes: 2 additions & 0 deletions fizz/client/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cpp_library(
"//fizz/protocol:certificate",
"//fizz/protocol:factory",
"//fizz/protocol/clock:system_clock",
"//fizz/protocol/ech:grease_ech_setting",
"//fizz/record:record",
],
)
Expand Down Expand Up @@ -84,6 +85,7 @@ cpp_library(
"//fizz/protocol:protocol",
"//fizz/protocol:state_machine",
"//fizz/protocol/ech:encryption",
"//fizz/protocol/ech:grease_ech",
"//fizz/record:record",
],
exported_deps = [
Expand Down
32 changes: 26 additions & 6 deletions fizz/client/ClientProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <fizz/protocol/Protocol.h>
#include <fizz/protocol/StateMachine.h>
#include <fizz/protocol/ech/Encryption.h>
#include <fizz/protocol/ech/GreaseECH.h>
#include <fizz/record/Extensions.h>

using folly::Optional;
Expand Down Expand Up @@ -873,12 +874,20 @@ EventHandler<ClientTypes, StateEnum::Uninitialized, Event::Connect>::handle(
requestedExtensions.push_back(extension.extension_type);
}

if (echParams.has_value() &&
echParams.value().supportedECHConfig.config.version ==
ech::ECHVersion::Draft15) {
ech::InnerECHClientHello chloIsInnerExt;
chlo.extensions.push_back(encodeExtension(std::move(chloIsInnerExt)));
requestedExtensions.push_back(ExtensionType::encrypted_client_hello);
if (echParams.has_value()) {
if (echParams.value().supportedECHConfig.config.version ==
ech::ECHVersion::Draft15) {
ech::InnerECHClientHello chloIsInnerExt;
chlo.extensions.push_back(encodeExtension(std::move(chloIsInnerExt)));
requestedExtensions.push_back(ExtensionType::encrypted_client_hello);
}
} else if (
context->getGreaseECHSetting().hasValue() &&
context->getGreaseECHSetting()->payloadStrategy ==
ech::PayloadGenerationStrategy::UniformRandom) {
auto greaseECH = ech::generateGreaseECH(
*context->getGreaseECHSetting(), *context->getFactory(), 0);
chlo.extensions.push_back(encodeExtension(std::move(greaseECH)));
}

Buf encodedClientHello;
Expand Down Expand Up @@ -960,6 +969,17 @@ EventHandler<ClientTypes, StateEnum::Uninitialized, Event::Connect>::handle(

// Update the client hello with the ECH client hello outer
encodedClientHello = encodeHandshake(chlo);
} else if (
context->getGreaseECHSetting().hasValue() &&
context->getGreaseECHSetting()->payloadStrategy ==
ech::PayloadGenerationStrategy::Computed) {
auto greaseECH = ech::generateGreaseECH(
*context->getGreaseECHSetting(),
*context->getFactory(),
encodedClientHello->computeChainDataLength());
chlo.extensions.push_back(encodeExtension(std::move(greaseECH)));
// Update the client hello with the grease ECH extension
encodedClientHello = encodeHandshake(chlo);
}

auto readRecordLayer = context->getFactory()->makePlaintextReadRecordLayer();
Expand Down
13 changes: 13 additions & 0 deletions fizz/client/FizzClientContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <fizz/protocol/Certificate.h>
#include <fizz/protocol/Factory.h>
#include <fizz/protocol/clock/SystemClock.h>
#include <fizz/protocol/ech/GreaseECHSetting.h>
#include <fizz/record/Types.h>

namespace fizz {
Expand Down Expand Up @@ -321,6 +322,17 @@ class FizzClientContext {
return sniExtFirst_;
}

/**
* Sets the grease ECH setting. If not set, no grease ECH extension is send.
*/
void setGreaseECHSetting(ech::GreaseECHSetting setting) {
greaseECHSetting_ = std::move(setting);
}

const auto& getGreaseECHSetting() const {
return greaseECHSetting_;
}

private:
std::shared_ptr<Factory> factory_;

Expand Down Expand Up @@ -368,6 +380,7 @@ class FizzClientContext {
std::chrono::seconds maxPskHandshakeLife_{std::chrono::hours(168)};

bool sniExtFirst_{false};
folly::Optional<ech::GreaseECHSetting> greaseECHSetting_;
};
} // namespace client
} // namespace fizz
84 changes: 84 additions & 0 deletions fizz/client/test/ClientProtocolTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,8 @@ TEST_F(ClientProtocolTest, TestConnectCompat) {
}

TEST_F(ClientProtocolTest, TestConnectECH) {
// grease ech will be ignored
context_->setGreaseECHSetting(ech::GreaseECHSetting{});
auto echConfig = ech::test::getECHConfig();
Connect connect;
connect.context = context_;
Expand Down Expand Up @@ -1300,6 +1302,88 @@ TEST_F(ClientProtocolTest, TestConnectECHWithPSK) {
}
}

TEST_F(ClientProtocolTest, TestConnectWithRandomGreaseECH) {
auto setting = ech::GreaseECHSetting{};
setting.minPayloadSize = 100;
setting.maxPayloadSize = 1000;
context_->setGreaseECHSetting(setting);
Connect connect;
connect.context = context_;
connect.verifier = verifier_;
std::string sni = "www.hostname.com";
connect.sni = sni;
auto extensions = std::make_shared<MockClientExtensions>();
connect.extensions = extensions;

fizz::Param param = std::move(connect);
auto actions = detail::processEvent(state_, param);
expectActions<MutateState, WriteToSocket>(actions);
processStateMutations(actions);
EXPECT_EQ(state_.state(), StateEnum::ExpectingServerHello);
EXPECT_FALSE(state_.echState().hasValue());

auto& encodedHello = *state_.encodedClientHello();
// Get rid of handshake header (type + version)
encodedHello->trimStart(4);
auto decodedHello = decode<ClientHello>(std::move(encodedHello));
auto echExtension =
getExtension<ech::OuterECHClientHello>(decodedHello.extensions);
ASSERT_TRUE(echExtension.hasValue());
EXPECT_GT(echExtension->enc->computeChainDataLength(), 0);
EXPECT_GE(
echExtension->payload->computeChainDataLength(), setting.minPayloadSize);
EXPECT_LE(
echExtension->payload->computeChainDataLength(), setting.maxPayloadSize);
}

TEST_F(ClientProtocolTest, TestConnectWithComputedGreaseECH) {
ech::GreaseECHSetting setting{
/* minConfigId = */ 0,
/* maxConfigId = */ 0,
ech::PayloadGenerationStrategy::Computed,
/* minPayloadSize = */ 100,
/* maxPayloadSize = */ 100,
/* keySizes = */ {32},
/* kdfs = */ {hpke::KDFId::Sha256},
/* aeads = */ {hpke::AeadId::TLS_AES_128_GCM_SHA256}};
context_->setGreaseECHSetting(setting);
Connect connect;
connect.context = context_;
connect.verifier = verifier_;
std::string sni = "www.hostname.com";
connect.sni = sni;
auto extensions = std::make_shared<MockClientExtensions>();
connect.extensions = extensions;

fizz::Param param = std::move(connect);
auto actions = detail::processEvent(state_, param);
expectActions<MutateState, WriteToSocket>(actions);
processStateMutations(actions);
EXPECT_EQ(state_.state(), StateEnum::ExpectingServerHello);
EXPECT_FALSE(state_.echState().hasValue());

auto& encodedHello = *state_.encodedClientHello();
auto encodedChloSize = encodedHello->computeChainDataLength();
// Get rid of handshake header (type + version)
encodedHello->trimStart(4);
auto decodedHello = decode<ClientHello>(std::move(encodedHello));
auto echExtension =
getExtension<ech::OuterECHClientHello>(decodedHello.extensions);
ASSERT_TRUE(echExtension.hasValue());
EXPECT_EQ(hpke::KDFId::Sha256, echExtension->cipher_suite.kdf_id);
EXPECT_EQ(
hpke::AeadId::TLS_AES_128_GCM_SHA256, echExtension->cipher_suite.aead_id);
EXPECT_EQ(32, echExtension->enc->computeChainDataLength());

auto encodedEch = encodeExtension(*echExtension);
auto echOverhead = encodedEch.extension_data->computeChainDataLength() + 4;
size_t expectedPayloadSize = encodedChloSize +
hpke::getCipherOverhead(echExtension->cipher_suite.aead_id) + 100 -
echOverhead;
EXPECT_EQ(
expectedPayloadSize, echExtension->payload->computeChainDataLength());
}

TEST_F(ClientProtocolTest, TestConnectCompatEarly) {
context_->setCompatibilityMode(true);
Connect connect;
Expand Down

0 comments on commit c4379b4

Please sign in to comment.