-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
quic: enable certificate compression/decompression (#35999)
QUIC servers are limited in how much data they can send to clients in the ServerHello before the client is validated (see https://www.rfc-editor.org/rfc/rfc9000.html#section-8). If too much data needs to be sent, an extra network round trip is needed. One way to reduce the size of the ServerHello data is to compress the certificates. This can, in some situations, remove an extra round trip. Risk Level: Low Testing: Added unit and integration tests Docs Changes: Release Notes: Added Platform Specific Features: Runtime guard: `envoy.reloadable_features.quic_support_certificate_compression` --------- Signed-off-by: Greg Greenway <[email protected]>
- Loading branch information
Showing
12 changed files
with
264 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#include "source/common/quic/cert_compression.h" | ||
|
||
#include "source/common/common/assert.h" | ||
#include "source/common/runtime/runtime_features.h" | ||
|
||
#include "openssl/tls1.h" | ||
|
||
#define ZLIB_CONST | ||
#include "zlib.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
namespace { | ||
|
||
class ScopedZStream { | ||
public: | ||
using CleanupFunc = int (*)(z_stream*); | ||
|
||
ScopedZStream(z_stream& z, CleanupFunc cleanup) : z_(z), cleanup_(cleanup) {} | ||
~ScopedZStream() { cleanup_(&z_); } | ||
|
||
private: | ||
z_stream& z_; | ||
CleanupFunc cleanup_; | ||
}; | ||
|
||
} // namespace | ||
|
||
void CertCompression::registerSslContext(SSL_CTX* ssl_ctx) { | ||
if (Runtime::runtimeFeatureEnabled( | ||
"envoy.reloadable_features.quic_support_certificate_compression")) { | ||
auto ret = SSL_CTX_add_cert_compression_alg(ssl_ctx, TLSEXT_cert_compression_zlib, compressZlib, | ||
decompressZlib); | ||
ASSERT(ret == 1); | ||
} | ||
} | ||
|
||
int CertCompression::compressZlib(SSL*, CBB* out, const uint8_t* in, size_t in_len) { | ||
|
||
z_stream z = {}; | ||
int rv = deflateInit(&z, Z_DEFAULT_COMPRESSION); | ||
if (rv != Z_OK) { | ||
IS_ENVOY_BUG(fmt::format("Cert compression failure in deflateInit: {}", rv)); | ||
return FAILURE; | ||
} | ||
|
||
ScopedZStream deleter(z, deflateEnd); | ||
|
||
const auto upper_bound = deflateBound(&z, in_len); | ||
|
||
uint8_t* out_buf = nullptr; | ||
if (!CBB_reserve(out, &out_buf, upper_bound)) { | ||
IS_ENVOY_BUG(fmt::format("Cert compression failure in allocating output CBB buffer of size {}", | ||
upper_bound)); | ||
return FAILURE; | ||
} | ||
|
||
z.next_in = in; | ||
z.avail_in = in_len; | ||
z.next_out = out_buf; | ||
z.avail_out = upper_bound; | ||
|
||
rv = deflate(&z, Z_FINISH); | ||
if (rv != Z_STREAM_END) { | ||
IS_ENVOY_BUG(fmt::format( | ||
"Cert compression failure in deflate: {}, z.total_out {}, in_len {}, z.avail_in {}", rv, | ||
z.avail_in, in_len, z.avail_in)); | ||
return FAILURE; | ||
} | ||
|
||
if (!CBB_did_write(out, z.total_out)) { | ||
IS_ENVOY_BUG("CBB_did_write failed"); | ||
return FAILURE; | ||
} | ||
|
||
ENVOY_LOG(trace, "Cert compression successful"); | ||
|
||
return SUCCESS; | ||
} | ||
|
||
int CertCompression::decompressZlib(SSL*, CRYPTO_BUFFER** out, size_t uncompressed_len, | ||
const uint8_t* in, size_t in_len) { | ||
z_stream z = {}; | ||
int rv = inflateInit(&z); | ||
if (rv != Z_OK) { | ||
IS_ENVOY_BUG(fmt::format("Cert decompression failure in inflateInit: {}", rv)); | ||
return FAILURE; | ||
} | ||
|
||
ScopedZStream deleter(z, inflateEnd); | ||
|
||
z.next_in = in; | ||
z.avail_in = in_len; | ||
bssl::UniquePtr<CRYPTO_BUFFER> decompressed_data( | ||
CRYPTO_BUFFER_alloc(&z.next_out, uncompressed_len)); | ||
z.avail_out = uncompressed_len; | ||
|
||
rv = inflate(&z, Z_FINISH); | ||
if (rv != Z_STREAM_END) { | ||
ENVOY_LOG_PERIODIC(error, std::chrono::seconds(10), | ||
"Cert decompression failure in inflate, possibly caused by invalid " | ||
"compressed cert from peer: {}, z.total_out {}, uncompressed_len {}", | ||
rv, z.total_out, uncompressed_len); | ||
return FAILURE; | ||
} | ||
|
||
if (z.total_out != uncompressed_len) { | ||
ENVOY_LOG_PERIODIC(error, std::chrono::seconds(10), | ||
"Decompression length did not match peer provided uncompressed length, " | ||
"caused by either invalid peer handshake data or decompression error."); | ||
return FAILURE; | ||
} | ||
|
||
ENVOY_LOG(trace, "Cert decompression successful"); | ||
|
||
*out = decompressed_data.release(); | ||
return SUCCESS; | ||
} | ||
|
||
} // namespace Quic | ||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#pragma once | ||
|
||
#include "source/common/common/logger.h" | ||
|
||
#include "openssl/ssl.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
/** | ||
* Support for certificate compression and decompression in QUIC TLS handshakes. This often | ||
* needed for the ServerHello to fit in the initial response and not need an additional round trip | ||
* between client and server. | ||
*/ | ||
class CertCompression : protected Logger::Loggable<Logger::Id::quic> { | ||
public: | ||
// Registers compression and decompression functions on `ssl_ctx` if enabled. | ||
static void registerSslContext(SSL_CTX* ssl_ctx); | ||
|
||
// Callbacks for `SSL_CTX_add_cert_compression_alg`. | ||
static int compressZlib(SSL* ssl, CBB* out, const uint8_t* in, size_t in_len); | ||
static int decompressZlib(SSL*, CRYPTO_BUFFER** out, size_t uncompressed_len, const uint8_t* in, | ||
size_t in_len); | ||
|
||
// Defined return values for callbacks from `SSL_CTX_add_cert_compression_alg`. | ||
static constexpr int SUCCESS = 1; | ||
static constexpr int FAILURE = 0; | ||
}; | ||
|
||
} // namespace Quic | ||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#include "source/common/quic/cert_compression.h" | ||
|
||
#include "test/test_common/logging.h" | ||
|
||
#include "gtest/gtest.h" | ||
|
||
namespace Envoy { | ||
namespace Quic { | ||
|
||
TEST(CertCompressionZlibTest, DecompressBadData) { | ||
EXPECT_LOG_CONTAINS( | ||
"error", | ||
"Cert decompression failure in inflate, possibly caused by invalid compressed cert from peer", | ||
{ | ||
CRYPTO_BUFFER* out = nullptr; | ||
const uint8_t bad_compressed_data = 1; | ||
EXPECT_EQ(CertCompression::FAILURE, | ||
CertCompression::decompressZlib(nullptr, &out, 100, &bad_compressed_data, | ||
sizeof(bad_compressed_data))); | ||
}); | ||
} | ||
|
||
TEST(CertCompressionZlibTest, DecompressBadLength) { | ||
constexpr uint8_t the_data[] = {1, 2, 3, 4, 5, 6}; | ||
constexpr size_t uncompressed_len = 6; | ||
bssl::ScopedCBB compressed; | ||
ASSERT_EQ(1, CBB_init(compressed.get(), 0)); | ||
ASSERT_EQ(CertCompression::SUCCESS, | ||
CertCompression::compressZlib(nullptr, compressed.get(), the_data, uncompressed_len)); | ||
const auto compressed_len = CBB_len(compressed.get()); | ||
EXPECT_NE(0, compressed_len); | ||
|
||
EXPECT_LOG_CONTAINS("error", | ||
"Decompression length did not match peer provided uncompressed length, " | ||
"caused by either invalid peer handshake data or decompression error.", | ||
{ | ||
CRYPTO_BUFFER* out = nullptr; | ||
EXPECT_EQ(CertCompression::FAILURE, | ||
CertCompression::decompressZlib( | ||
nullptr, &out, | ||
uncompressed_len + 1 /* intentionally incorrect */, | ||
CBB_data(compressed.get()), compressed_len)); | ||
}); | ||
} | ||
} // namespace Quic | ||
} // namespace Envoy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters