Skip to content

Commit

Permalink
Add grease ECH generation
Browse files Browse the repository at this point in the history
Summary: Adds grease ECH extension preparation.

Reviewed By: mingtaoy

Differential Revision: D65239683

fbshipit-source-id: 8485d318daa65beae0f451f172d0bd7b2b48ac9a
  • Loading branch information
abakiaydin authored and facebook-github-bot committed Nov 7, 2024
1 parent ea1e6dd commit 812149c
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 1 deletion.
5 changes: 4 additions & 1 deletion fizz/cmake/FizzSources.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @generated SignedSource<<b886bc306435900c000f58aa3c6387f5>>
# @generated SignedSource<<6739c8027c4f0f51ab7bf5dcb36c5d96>>
#
# This file is generated file from `fizz/facebook/boilerplate.sh`.
# All manual changes will be lost.
Expand Down Expand Up @@ -79,6 +79,7 @@ set(
protocol/clock/SystemClock.cpp
protocol/ech/Decrypter.cpp
protocol/ech/Encryption.cpp
protocol/ech/GreaseECH.cpp
record/BufAndPaddingPolicy.cpp
record/EncryptedRecordLayer.cpp
record/PlaintextRecordLayer.cpp
Expand Down Expand Up @@ -225,6 +226,8 @@ set(
protocol/ech/ECHExtensions-inl.h
protocol/ech/ECHExtensions.h
protocol/ech/Encryption.h
protocol/ech/GreaseECH.h
protocol/ech/GreaseECHSetting.h
protocol/ech/Types-inl.h
protocol/ech/Types.h
record/BufAndPaddingPolicy.h
Expand Down
28 changes: 28 additions & 0 deletions fizz/protocol/ech/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,31 @@ cpp_library(
"//fizz/protocol:factory",
],
)

cpp_library(
name = "grease_ech_setting",
headers = [
"GreaseECHSetting.h",
],
exported_deps = [
"//fizz/crypto/hpke:hpke",
],
)

cpp_library(
name = "grease_ech",
srcs = [
"GreaseECH.cpp",
],
headers = [
"GreaseECH.h",
],
deps = [
"//fizz/crypto/hpke:utils",
],
exported_deps = [
":encrypted_client_hello",
":grease_ech_setting",
"//fizz/protocol:factory",
],
)
89 changes: 89 additions & 0 deletions fizz/protocol/ech/GreaseECH.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <random>

#include <fizz/protocol/ech/GreaseECH.h>

#include <fizz/crypto/hpke/Utils.h>

namespace fizz {
namespace ech {
namespace {

/**
* A random number generator adaptor based on the Factory.
* Current usage is limited to type size_t.
*/
class RandomNumberGenerator {
public:
typedef size_t result_type;

static constexpr size_t min() {
return 0;
}

static constexpr size_t max() {
return std::numeric_limits<size_t>::max();
}

explicit RandomNumberGenerator(const Factory& factory) : factory_{factory} {}

size_t operator()() const {
size_t number = 0;
factory_.makeRandomBytes(
reinterpret_cast<unsigned char*>(&number), sizeof(number));
return number;
}

private:
const Factory& factory_;
};

class RandomSelector {
public:
explicit RandomSelector(RandomNumberGenerator&& generator)
: generator_{std::move(generator)} {}

size_t genNumber(size_t min, size_t max) const {
std::uniform_int_distribution<size_t> distribution(min, max);
return distribution(generator_);
}

template <typename T>
T select(const std::vector<T>& elems) const {
return elems[genNumber(0, elems.size() - 1)];
}

private:
const RandomNumberGenerator generator_;
};
} // namespace

OuterECHClientHello generateGreaseECH(
const GreaseECHSetting& setting,
const Factory& factory,
size_t encodedChloSize) {
RandomSelector selector{RandomNumberGenerator{factory}};
OuterECHClientHello echExtension;
echExtension.cipher_suite = HpkeSymmetricCipherSuite{
selector.select(setting.kdfs), selector.select(setting.aeads)};
echExtension.config_id =
selector.genNumber(setting.minConfigId, setting.maxConfigId);
echExtension.enc = factory.makeRandomIOBuf(selector.select(setting.keySizes));
size_t payloadSize =
selector.genNumber(setting.minPayloadSize, setting.maxPayloadSize);
if (setting.payloadStrategy == PayloadGenerationStrategy::Computed) {
payloadSize += encodedChloSize +
hpke::getCipherOverhead(echExtension.cipher_suite.aead_id);
}
echExtension.payload = factory.makeRandomIOBuf(payloadSize);
return echExtension;
}
} // namespace ech
} // namespace fizz
22 changes: 22 additions & 0 deletions fizz/protocol/ech/GreaseECH.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <fizz/protocol/Factory.h>
#include <fizz/protocol/ech/ECHExtensions.h>
#include <fizz/protocol/ech/GreaseECHSetting.h>

namespace fizz {
namespace ech {
OuterECHClientHello generateGreaseECH(
const GreaseECHSetting& setting,
const Factory& factory,
size_t encodedChloSize);
} // namespace ech
} // namespace fizz
50 changes: 50 additions & 0 deletions fizz/protocol/ech/GreaseECHSetting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#pragma once

#include <fizz/crypto/hpke/Hpke.h>

namespace fizz {
namespace ech {

enum class PayloadGenerationStrategy : uint8_t {
/**
* Generates a payload with a size P where P is in the range
* [minPayloadSize, maxPayloadSize].
*/
UniformRandom = 0,

/**
* Generates a payload with size L + C + P bytes where L is the size of the
* encoded inner client hello, C is the ciphertext expansion of the selected
* AEAD schema, and P is the expected padding within the range
* [minPayloadSize, maxPayloadSize].
*/
Computed
};

struct GreaseECHSetting {
uint8_t minConfigId{0};
uint8_t maxConfigId{std::numeric_limits<uint8_t>::max()};
PayloadGenerationStrategy payloadStrategy{
PayloadGenerationStrategy::UniformRandom};
size_t minPayloadSize{0};
size_t maxPayloadSize{0};
std::vector<uint16_t> keySizes{32, 48, 64};
std::vector<hpke::KDFId> kdfs{
hpke::KDFId::Sha256,
hpke::KDFId::Sha384,
hpke::KDFId::Sha512};
std::vector<hpke::AeadId> aeads{
hpke::AeadId::TLS_AES_128_GCM_SHA256,
hpke::AeadId::TLS_AES_256_GCM_SHA384,
hpke::AeadId::TLS_CHACHA20_POLY1305_SHA256};
};
} // namespace ech
} // namespace fizz
13 changes: 13 additions & 0 deletions fizz/protocol/ech/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,16 @@ cpp_unittest(
"//folly/portability:gtest",
],
)

cpp_unittest(
name = "grease_ech_test",
srcs = [
"GreaseECHTest.cpp",
],
deps = [
"//fizz/crypto/hpke:utils",
"//fizz/protocol/ech:grease_ech",
"//fizz/protocol/test:mocks",
"//fizz/protocol/test:test_util",
],
)
90 changes: 90 additions & 0 deletions fizz/protocol/ech/test/GreaseECHTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <gtest/gtest.h>

#include <fizz/protocol/ech/GreaseECH.h>

#include <fizz/crypto/hpke/Utils.h>
#include <fizz/protocol/test/Mocks.h>
#include <fizz/protocol/test/TestUtil.h>

using namespace fizz::test;

namespace fizz {
namespace ech {
namespace test {

TEST(GreaseECHTest, TestGenerateRandomGreaseECH) {
MockFactory factory;
factory.setDefaults();

auto chlo = TestMessages::clientHelloPsk();
auto sni = getExtension<ServerNameList>(chlo.extensions);

GreaseECHSetting setting{};
setting.maxPayloadSize = 100;
auto greaseEch = generateGreaseECH(setting, factory, 0);

std::array<hpke::KDFId, 3> kdfs{
hpke::KDFId::Sha256, hpke::KDFId::Sha384, hpke::KDFId::Sha512};
std::array<hpke::AeadId, 3> aeads{
hpke::AeadId::TLS_AES_128_GCM_SHA256,
hpke::AeadId::TLS_AES_256_GCM_SHA384,
hpke::AeadId::TLS_CHACHA20_POLY1305_SHA256};

EXPECT_NE(
kdfs.end(),
std::find(kdfs.begin(), kdfs.end(), greaseEch.cipher_suite.kdf_id));
EXPECT_NE(
aeads.end(),
std::find(aeads.begin(), aeads.end(), greaseEch.cipher_suite.aead_id));

std::array<size_t, 3> keyLengths{32, 48, 64};
EXPECT_NE(
keyLengths.end(),
std::find(
keyLengths.begin(),
keyLengths.end(),
greaseEch.enc->computeChainDataLength()));

EXPECT_LE(greaseEch.payload->computeChainDataLength(), 100);
}

TEST(GreaseECHTest, TestGenerateComputedGreaseECH) {
MockFactory factory;
factory.setDefaults();

auto chlo = TestMessages::clientHelloPsk();
auto sni = getExtension<ServerNameList>(chlo.extensions);
auto encodedChlo = encode(chlo);

size_t encodedChloSize = encodedChlo->computeChainDataLength();
GreaseECHSetting setting{
/* minConfigId = */ 0,
/* maxConfigId = */ 0,
PayloadGenerationStrategy::Computed,
/* minPayloadSize = */ 100,
/* maxPayloadSize = */ 100,
/* keySizes = */ {32},
/* kdfs = */ {hpke::KDFId::Sha256},
/* aeads = */ {hpke::AeadId::TLS_AES_128_GCM_SHA256}};
auto greaseEch = generateGreaseECH(setting, factory, encodedChloSize);

EXPECT_EQ(hpke::KDFId::Sha256, greaseEch.cipher_suite.kdf_id);
EXPECT_EQ(
hpke::AeadId::TLS_AES_128_GCM_SHA256, greaseEch.cipher_suite.aead_id);
EXPECT_EQ(32, greaseEch.enc->computeChainDataLength());

size_t expectedPayloadSize = encodedChloSize +
hpke::getCipherOverhead(greaseEch.cipher_suite.aead_id) + 100;
EXPECT_EQ(expectedPayloadSize, greaseEch.payload->computeChainDataLength());
}
} // namespace test
} // namespace ech
} // namespace fizz

0 comments on commit 812149c

Please sign in to comment.