1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-04-18 17:24:03 +03:00

Implement Fizz ClientHello padding client extension

Summary: Implement TLS padding extension in QUIC `FizzClientExtensions`

Reviewed By: mjoras

Differential Revision: D69134166

fbshipit-source-id: 8ef10d92b0f8ff33940807f44bf3f8940b795ae3
This commit is contained in:
Jolene Tan 2025-02-12 10:35:12 -08:00 committed by Facebook GitHub Bot
parent dd947c79d9
commit 9b44e8dac0
6 changed files with 178 additions and 103 deletions

View File

@ -8,6 +8,7 @@
#pragma once
#include <fizz/client/ClientExtensions.h>
#include <fizz/extensions/clientpadding/Types.h>
#include <quic/client/handshake/ClientTransportParametersExtension.h>
#include <quic/fizz/handshake/FizzTransportParameters.h>
@ -16,8 +17,10 @@ namespace quic {
class FizzClientExtensions : public fizz::ClientExtensions {
public:
FizzClientExtensions(
std::shared_ptr<ClientTransportParametersExtension> clientParameters)
: clientParameters_(std::move(clientParameters)) {}
std::shared_ptr<ClientTransportParametersExtension> clientParameters,
uint16_t chloPaddingBytes)
: clientParameters_(std::move(clientParameters)),
chloPaddingBytes_(chloPaddingBytes) {}
~FizzClientExtensions() override = default;
@ -29,6 +32,11 @@ class FizzClientExtensions : public fizz::ClientExtensions {
exts.push_back(
encodeExtension(params, clientParameters_->encodingVersion_));
if (chloPaddingBytes_ > 0) {
fizz::extensions::Padding padding{chloPaddingBytes_};
exts.push_back(fizz::encodeExtension(padding));
}
return exts;
}
@ -48,5 +56,6 @@ class FizzClientExtensions : public fizz::ClientExtensions {
private:
std::shared_ptr<ClientTransportParametersExtension> clientParameters_;
uint16_t chloPaddingBytes_{0};
};
} // namespace quic

View File

@ -63,7 +63,8 @@ Optional<CachedServerTransportParameters> FizzClientHandshake::connectImpl(
fizzContext_->getCertificateVerifier(),
std::move(hostname),
std::move(cachedPsk),
std::make_shared<FizzClientExtensions>(getClientTransportParameters()),
std::make_shared<FizzClientExtensions>(
getClientTransportParameters(), fizzContext_->getChloPaddingBytes()),
std::move(echConfigs)));
return transportParams;

View File

@ -18,12 +18,14 @@ FizzClientQuicHandshakeContext::FizzClientQuicHandshakeContext(
std::shared_ptr<const fizz::CertificateVerifier> verifier,
std::shared_ptr<QuicPskCache> pskCache,
std::shared_ptr<fizz::client::ECHPolicy> echPolicy,
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback_)
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback_,
uint16_t chloPaddingBytes)
: context_(std::move(context)),
verifier_(std::move(verifier)),
pskCache_(std::move(pskCache)),
echPolicy_(std::move(echPolicy)),
echRetryCallback_(std::move(echRetryCallback_)) {}
echRetryCallback_(std::move(echRetryCallback_)),
chloPaddingBytes_(chloPaddingBytes) {}
FizzClientQuicHandshakeContext::FizzClientQuicHandshakeContext(
std::shared_ptr<const fizz::client::FizzClientContext> context,
@ -31,13 +33,15 @@ FizzClientQuicHandshakeContext::FizzClientQuicHandshakeContext(
std::shared_ptr<QuicPskCache> pskCache,
std::unique_ptr<FizzCryptoFactory> cryptoFactory,
std::shared_ptr<fizz::client::ECHPolicy> echPolicy,
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback)
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback,
uint16_t chloPaddingBytes)
: context_(std::move(context)),
verifier_(std::move(verifier)),
pskCache_(std::move(pskCache)),
echPolicy_(std::move(echPolicy)),
echRetryCallback_(std::move(echRetryCallback)),
cryptoFactory_(std::move(cryptoFactory)) {}
cryptoFactory_(std::move(cryptoFactory)),
chloPaddingBytes_(chloPaddingBytes) {}
std::unique_ptr<ClientHandshake>
FizzClientQuicHandshakeContext::makeClientHandshake(
@ -102,7 +106,8 @@ FizzClientQuicHandshakeContext::Builder::build() && {
std::move(pskCache_),
std::move(cryptoFactory_),
std::move(echPolicy_),
std::move(echRetryCallback_)));
std::move(echRetryCallback_),
chloPaddingBytes_));
}
} // namespace quic

View File

@ -53,6 +53,10 @@ class FizzClientQuicHandshakeContext
echRetryCallback_ = callback;
}
uint16_t getChloPaddingBytes() const {
return chloPaddingBytes_;
}
private:
/**
* We make the constructor private so that users have to use the Builder
@ -67,7 +71,8 @@ class FizzClientQuicHandshakeContext
std::shared_ptr<const fizz::CertificateVerifier> verifier,
std::shared_ptr<QuicPskCache> pskCache,
std::shared_ptr<fizz::client::ECHPolicy> echPolicy,
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback);
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback,
uint16_t chloPaddingBytes);
FizzClientQuicHandshakeContext(
std::shared_ptr<const fizz::client::FizzClientContext> context,
@ -75,7 +80,8 @@ class FizzClientQuicHandshakeContext
std::shared_ptr<QuicPskCache> pskCache,
std::unique_ptr<FizzCryptoFactory> cryptoFactory,
std::shared_ptr<fizz::client::ECHPolicy> echPolicy,
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback);
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback,
uint16_t chloPaddingBytes);
std::shared_ptr<const fizz::client::FizzClientContext> context_;
std::shared_ptr<const fizz::CertificateVerifier> verifier_;
@ -83,6 +89,7 @@ class FizzClientQuicHandshakeContext
std::shared_ptr<fizz::client::ECHPolicy> echPolicy_;
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback_;
std::unique_ptr<FizzCryptoFactory> cryptoFactory_;
uint16_t chloPaddingBytes_{0};
public:
class Builder {
@ -121,6 +128,11 @@ class FizzClientQuicHandshakeContext
return std::move(*this);
}
Builder&& setChloPaddingBytes(uint16_t chloPaddingBytes) && {
chloPaddingBytes_ = chloPaddingBytes;
return std::move(*this);
}
std::shared_ptr<FizzClientQuicHandshakeContext> build() &&;
private:
@ -130,6 +142,7 @@ class FizzClientQuicHandshakeContext
std::shared_ptr<fizz::client::ECHPolicy> echPolicy_;
std::shared_ptr<fizz::client::ECHRetryCallback> echRetryCallback_;
std::unique_ptr<FizzCryptoFactory> cryptoFactory_;
uint16_t chloPaddingBytes_{0};
};
};

View File

@ -25,19 +25,21 @@ static EncryptedExtensions getEncryptedExtensions() {
}
TEST(FizzClientHandshakeTest, TestGetChloExtensionsMvfst) {
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
0);
auto extensions = ext.getClientHelloExtensions();
EXPECT_EQ(extensions.size(), 1);
@ -48,19 +50,21 @@ TEST(FizzClientHandshakeTest, TestGetChloExtensionsMvfst) {
}
TEST(FizzClientHandshakeTest, TestGetChloExtensionsV1) {
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
0);
auto extensions = ext.getClientHelloExtensions();
EXPECT_EQ(extensions.size(), 1);
@ -70,19 +74,21 @@ TEST(FizzClientHandshakeTest, TestGetChloExtensionsV1) {
}
TEST(FizzClientHandshakeTest, TestGetChloExtensionsV1Alias) {
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1_ALIAS,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1_ALIAS,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
0);
auto extensions = ext.getClientHelloExtensions();
EXPECT_EQ(extensions.size(), 1);
@ -93,37 +99,41 @@ TEST(FizzClientHandshakeTest, TestGetChloExtensionsV1Alias) {
}
TEST(FizzClientHandshakeTest, TestOnEE) {
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
0);
ext.getClientHelloExtensions();
ext.onEncryptedExtensions(getEncryptedExtensions().extensions);
}
TEST(FizzClientHandshakeTest, TestV1RejectExtensionNumberMismatch) {
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
0);
ext.getClientHelloExtensions();
auto ee = TestMessages::encryptedExt();
@ -141,19 +151,21 @@ TEST(FizzClientHandshakeTest, TestV1RejectExtensionNumberMismatch) {
}
TEST(FizzClientHandshakeTest, TestOnEEMissingServerParams) {
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
0);
ext.getClientHelloExtensions();
EXPECT_THROW(
ext.onEncryptedExtensions(TestMessages::encryptedExt().extensions),
@ -168,20 +180,22 @@ TEST(FizzClientHandshakeTest, TestGetChloExtensionsCustomParams) {
customTransportParameters.push_back(
encodeIntegerParameter(static_cast<TransportParameterId>(0x4000), 12));
FizzClientExtensions ext(std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>()),
customTransportParameters));
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::QUIC_V1,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>()),
customTransportParameters),
0);
auto extensions = ext.getClientHelloExtensions();
EXPECT_EQ(extensions.size(), 1);
@ -203,4 +217,37 @@ TEST(FizzClientHandshakeTest, TestGetChloExtensionsCustomParams) {
auto val = decodeQuicInteger(cursor1);
EXPECT_EQ(val->first, 12);
}
TEST(FizzClientHandshakeTest, TestGetChloExtensionsChloPadding) {
uint16_t chloPaddingBytes = 1234;
FizzClientExtensions ext(
std::make_shared<ClientTransportParametersExtension>(
QuicVersion::MVFST,
kDefaultConnectionFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultStreamFlowControlWindow,
kDefaultMaxStreamsBidirectional,
kDefaultMaxStreamsUnidirectional,
kDefaultIdleTimeout,
kDefaultAckDelayExponent,
kDefaultUDPSendPacketLen,
kDefaultActiveConnectionIdLimit,
ConnectionId(std::vector<uint8_t>())),
chloPaddingBytes);
auto extensions = ext.getClientHelloExtensions();
EXPECT_EQ(extensions.size(), 2);
auto clientParams = getClientExtension(extensions, QuicVersion::MVFST);
ASSERT_TRUE(clientParams.has_value());
// Size == 10 to check that initial_source_connection_id is not included
EXPECT_EQ(clientParams->parameters.size(), 10);
auto paddingIt = findExtension(extensions, ExtensionType::padding);
EXPECT_NE(paddingIt, extensions.end());
EXPECT_EQ(paddingIt->extension_type, ExtensionType::padding);
EXPECT_EQ(
paddingIt->extension_data->computeChainDataLength(),
chloPaddingBytes - 4);
}
} // namespace quic::test

View File

@ -143,7 +143,7 @@ class ServerHandshakeTest : public Test {
hostname,
cachedPsk,
Optional<std::vector<fizz::ech::ECHConfig>>(none),
std::make_shared<FizzClientExtensions>(clientExtensions));
std::make_shared<FizzClientExtensions>(clientExtensions, 0));
}
void processCryptoEvents() {