1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-01 01:44:22 +03:00

Remove throws from PacketNumberCipher

Summary: Continuing the theme.

Reviewed By: kvtsoy

Differential Revision: D74944002

fbshipit-source-id: 9f23780aada2841ee88a365b93850fde4bb27a70
This commit is contained in:
Matt Joras
2025-05-19 13:49:37 -07:00
committed by Facebook GitHub Bot
parent ab8a6386c2
commit 92c50dad6f
17 changed files with 161 additions and 62 deletions

View File

@ -309,13 +309,19 @@ continuousMemoryBuildScheduleEncrypt(
packetBuf->prepend(headerLen);
HeaderForm headerForm = packet->packet.header.getHeaderForm();
encryptPacketHeader(
auto headerEncryptResult = encryptPacketHeader(
headerForm,
packetBuf->writableData(),
headerLen,
packetBuf->data() + headerLen,
packetBuf->length() - headerLen,
headerCipher);
if (headerEncryptResult.hasError()) {
return folly::makeUnexpected(headerEncryptResult.error());
}
if (headerEncryptResult.hasError()) {
return folly::makeUnexpected(headerEncryptResult.error());
}
CHECK(!packetBuf->isChained());
auto encodedSize = packetBuf->length();
auto encodedBodySize = encodedSize - headerLen;
@ -413,13 +419,16 @@ iobufChainBasedBuildScheduleEncrypt(
packetBuf->append(headerLen + bodyLen + aead.getCipherOverhead());
HeaderForm headerForm = packet->packet.header.getHeaderForm();
encryptPacketHeader(
auto headerEncryptResult = encryptPacketHeader(
headerForm,
packetBuf->writableData(),
headerLen,
packetBuf->data() + headerLen,
packetBuf->length() - headerLen,
headerCipher);
if (headerEncryptResult.hasError()) {
return folly::makeUnexpected(headerEncryptResult.error());
}
auto encodedSize = packetBuf->computeChainDataLength();
auto encodedBodySize = encodedSize - headerLen;
if (encodedSize > connection.udpSendPacketLen) {
@ -1525,7 +1534,7 @@ void writeShortClose(
headerCipher);
}
void encryptPacketHeader(
folly::Expected<folly::Unit, QuicError> encryptPacketHeader(
HeaderForm headerForm,
uint8_t* header,
size_t headerLen,
@ -1546,12 +1555,19 @@ void encryptPacketHeader(
MutableByteRange packetNumByteRange(
header + headerLen - packetNumberLength, packetNumberLength);
if (headerForm == HeaderForm::Short) {
headerCipher.encryptShortHeader(
sample, initialByteRange, packetNumByteRange);
} else {
headerCipher.encryptLongHeader(
auto result = headerCipher.encryptShortHeader(
sample, initialByteRange, packetNumByteRange);
if (result.hasError()) {
return folly::makeUnexpected(result.error());
}
} else {
auto result = headerCipher.encryptLongHeader(
sample, initialByteRange, packetNumByteRange);
if (result.hasError()) {
return folly::makeUnexpected(result.error());
}
}
return folly::unit;
}
/**

View File

@ -268,7 +268,7 @@ void writeShortClose(
* whether or not there are enough bytes to sample for the header encryption
* from the encryptedBody via a CHECK.
*/
void encryptPacketHeader(
folly::Expected<folly::Unit, QuicError> encryptPacketHeader(
HeaderForm headerForm,
uint8_t* header,
size_t headerLen,

View File

@ -76,7 +76,10 @@ mvfst_cpp_library(
":types",
],
exported_deps = [
"//folly:expected",
"//folly:unit",
"//folly/io:iobuf",
"//quic:exception",
"//quic/common:buf_util",
"//quic/common:optional",
],

View File

@ -13,14 +13,18 @@
namespace quic {
void PacketNumberCipher::decipherHeader(
folly::Expected<folly::Unit, QuicError> PacketNumberCipher::decipherHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes,
uint8_t initialByteMask,
uint8_t /* packetNumLengthMask */) const {
CHECK_EQ(packetNumberBytes.size(), kMaxPacketNumEncodingSize);
HeaderProtectionMask headerMask = mask(sample);
auto maskResult = mask(sample);
if (maskResult.hasError()) {
return folly::makeUnexpected(maskResult.error());
}
HeaderProtectionMask headerMask = std::move(maskResult.value());
// Mask size should be > packet number length + 1.
DCHECK_GE(headerMask.size(), 5);
initialByte.data()[0] ^= headerMask.data()[0] & initialByteMask;
@ -28,15 +32,20 @@ void PacketNumberCipher::decipherHeader(
for (size_t i = 0; i < packetNumLength; ++i) {
packetNumberBytes.data()[i] ^= headerMask.data()[i + 1];
}
return folly::unit;
}
void PacketNumberCipher::cipherHeader(
folly::Expected<folly::Unit, QuicError> PacketNumberCipher::cipherHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes,
uint8_t initialByteMask,
uint8_t /* packetNumLengthMask */) const {
HeaderProtectionMask headerMask = mask(sample);
auto maskResult = mask(sample);
if (maskResult.hasError()) {
return folly::makeUnexpected(maskResult.error());
}
HeaderProtectionMask headerMask = std::move(maskResult.value());
// Mask size should be > packet number length + 1.
DCHECK_GE(headerMask.size(), kMaxPacketNumEncodingSize + 1);
size_t packetNumLength = parsePacketNumberLength(*initialByte.data());
@ -44,13 +53,14 @@ void PacketNumberCipher::cipherHeader(
for (size_t i = 0; i < packetNumLength; ++i) {
packetNumberBytes.data()[i] ^= headerMask.data()[i + 1];
}
return folly::unit;
}
void PacketNumberCipher::decryptLongHeader(
folly::Expected<folly::Unit, QuicError> PacketNumberCipher::decryptLongHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const {
decipherHeader(
return decipherHeader(
sample,
initialByte,
packetNumberBytes,
@ -58,11 +68,11 @@ void PacketNumberCipher::decryptLongHeader(
LongHeader::kPacketNumLenMask);
}
void PacketNumberCipher::decryptShortHeader(
folly::Expected<folly::Unit, QuicError> PacketNumberCipher::decryptShortHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const {
decipherHeader(
return decipherHeader(
sample,
initialByte,
packetNumberBytes,
@ -70,11 +80,11 @@ void PacketNumberCipher::decryptShortHeader(
ShortHeader::kPacketNumLenMask);
}
void PacketNumberCipher::encryptLongHeader(
folly::Expected<folly::Unit, QuicError> PacketNumberCipher::encryptLongHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const {
cipherHeader(
return cipherHeader(
sample,
initialByte,
packetNumberBytes,
@ -82,11 +92,11 @@ void PacketNumberCipher::encryptLongHeader(
LongHeader::kPacketNumLenMask);
}
void PacketNumberCipher::encryptShortHeader(
folly::Expected<folly::Unit, QuicError> PacketNumberCipher::encryptShortHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const {
cipherHeader(
return cipherHeader(
sample,
initialByte,
packetNumberBytes,

View File

@ -7,7 +7,10 @@
#pragma once
#include <folly/Expected.h>
#include <folly/Unit.h>
#include <folly/io/Cursor.h>
#include <quic/QuicException.h>
#include <quic/common/BufUtil.h>
#include <quic/common/Optional.h>
@ -20,9 +23,11 @@ class PacketNumberCipher {
public:
virtual ~PacketNumberCipher() = default;
virtual void setKey(ByteRange key) = 0;
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError> setKey(
ByteRange key) = 0;
virtual HeaderProtectionMask mask(ByteRange sample) const = 0;
[[nodiscard]] virtual folly::Expected<HeaderProtectionMask, QuicError> mask(
ByteRange sample) const = 0;
/**
* Decrypts a long header from a sample.
@ -30,7 +35,8 @@ class PacketNumberCipher {
* initialByte is the initial byte.
* packetNumberBytes should be supplied with at least 4 bytes.
*/
virtual void decryptLongHeader(
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError>
decryptLongHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const;
@ -41,7 +47,8 @@ class PacketNumberCipher {
* initialByte is the initial byte.
* packetNumberBytes should be supplied with at least 4 bytes.
*/
virtual void decryptShortHeader(
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError>
decryptShortHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const;
@ -51,7 +58,8 @@ class PacketNumberCipher {
* sample should be 16 bytes long.
* initialByte is the initial byte.
*/
virtual void encryptLongHeader(
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError>
encryptLongHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const;
@ -61,7 +69,8 @@ class PacketNumberCipher {
* sample should be 16 bytes long.
* initialByte is the initial byte.
*/
virtual void encryptShortHeader(
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError>
encryptShortHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes) const;
@ -69,22 +78,22 @@ class PacketNumberCipher {
/**
* Returns the length of key needed for the pn cipher.
*/
virtual size_t keyLength() const = 0;
[[nodiscard]] virtual size_t keyLength() const = 0;
/**
* Get the packet protection key
*/
virtual const BufPtr& getKey() const = 0;
[[nodiscard]] virtual const BufPtr& getKey() const = 0;
protected:
virtual void cipherHeader(
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError> cipherHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes,
uint8_t initialByteMask,
uint8_t packetNumLengthMask) const;
virtual void decipherHeader(
[[nodiscard]] virtual folly::Expected<folly::Unit, QuicError> decipherHeader(
ByteRange sample,
MutableByteRange initialByte,
MutableByteRange packetNumberBytes,

View File

@ -91,7 +91,7 @@ static PacketDropReason getDecryptErrorReason(ProtectionType protectionType) {
}
}
CodecResult QuicReadCodec::parseLongHeaderPacket(
folly::Expected<CodecResult, QuicError> QuicReadCodec::parseLongHeaderPacket(
BufQueue& queue,
const AckStates& ackStates) {
Cursor cursor(queue.front());
@ -213,8 +213,12 @@ CodecResult QuicReadCodec::parseLongHeaderPacket(
MutableByteRange packetNumberByteRange(
currentPacketData->writableData() + packetNumberOffset,
kMaxPacketNumEncodingSize);
headerCipher->decryptLongHeader(
auto decryptResult = headerCipher->decryptLongHeader(
folly::range(sample), initialByteRange, packetNumberByteRange);
if (decryptResult.hasError()) {
VLOG(4) << "Failed to decrypt long header " << connIdToHex();
return folly::makeUnexpected(decryptResult.error());
}
std::pair<PacketNum, size_t> packetNum = parsePacketNumber(
initialByteRange.data()[0], packetNumberByteRange, expectedNextPacketNum);
@ -260,7 +264,8 @@ CodecResult QuicReadCodec::parseLongHeaderPacket(
return CodecResult(std::move(*packetRes));
}
CodecResult QuicReadCodec::tryParseShortHeaderPacket(
folly::Expected<CodecResult, QuicError>
QuicReadCodec::tryParseShortHeaderPacket(
BufPtr data,
const AckStates& ackStates,
size_t dstConnIdSize,
@ -284,8 +289,12 @@ CodecResult QuicReadCodec::tryParseShortHeaderPacket(
data->writableData() + packetNumberOffset, kMaxPacketNumEncodingSize);
ByteRange sampleByteRange(data->writableData() + sampleOffset, sample.size());
oneRttHeaderCipher_->decryptShortHeader(
auto decryptResult = oneRttHeaderCipher_->decryptShortHeader(
sampleByteRange, initialByteRange, packetNumberByteRange);
if (decryptResult.hasError()) {
VLOG(4) << "Failed to decrypt short header " << connIdToHex();
return folly::makeUnexpected(decryptResult.error());
}
std::pair<PacketNum, size_t> packetNum = parsePacketNumber(
initialByteRange.data()[0], packetNumberByteRange, expectedNextPacketNum);
auto shortHeader =
@ -411,7 +420,11 @@ CodecResult QuicReadCodec::parsePacket(
uint8_t initialByte = cursor.readBE<uint8_t>();
auto headerForm = getHeaderForm(initialByte);
if (headerForm == HeaderForm::Long) {
return parseLongHeaderPacket(queue, ackStates);
auto result = parseLongHeaderPacket(queue, ackStates);
if (result.hasError()) {
return CodecResult(CodecError(std::move(result.error())));
}
return std::move(result.value());
}
// Missing 1-rtt header cipher is the only case we wouldn't consider reset
if (!currentOneRttReadCipher_ || !oneRttHeaderCipher_) {
@ -447,8 +460,12 @@ CodecResult QuicReadCodec::parsePacket(
}
}
}
return tryParseShortHeaderPacket(
auto result = tryParseShortHeaderPacket(
std::move(data), ackStates, dstConnIdSize, cursor);
if (result.hasError()) {
return CodecResult(CodecError(std::move(result.error())));
}
return std::move(result.value());
}
bool QuicReadCodec::canInitiateKeyUpdate() const {

View File

@ -198,12 +198,12 @@ class QuicReadCodec {
Optional<TimePoint> getHandshakeDoneTime();
private:
CodecResult tryParseShortHeaderPacket(
folly::Expected<CodecResult, QuicError> tryParseShortHeaderPacket(
BufPtr data,
const AckStates& ackStates,
size_t dstConnIdSize,
Cursor& cursor);
CodecResult parseLongHeaderPacket(
folly::Expected<CodecResult, QuicError> parseLongHeaderPacket(
BufQueue& queue,
const AckStates& ackStates);

View File

@ -95,13 +95,19 @@ bool PacketGroupWriter::writeSingleQuicPacket(
packetbuildBuf->prepend(headerLen);
HeaderForm headerForm = packet.packet.header.getHeaderForm();
encryptPacketHeader(
auto headerEncryptResult = encryptPacketHeader(
headerForm,
packetbuildBuf->writableData(),
headerLen,
packetbuildBuf->data() + headerLen,
packetbuildBuf->length() - headerLen,
headerCipher);
if (headerEncryptResult.hasError()) {
throw QuicInternalException(
"DSR Send failed: Header encryption error: " +
headerEncryptResult.error().message,
LocalErrorCode::INTERNAL_ERROR);
}
CHECK(!packetbuildBuf->isChained());
auto encodedSize = packetbuildBuf->length();
// Include previous packets back.

View File

@ -68,7 +68,9 @@ class CipherBuilder {
throw std::runtime_error("Failed to create header cipher");
}
auto headerCipher = std::move(headerCipherResult.value());
headerCipher->setKey(packetProtectionKey->coalesce());
if (headerCipher->setKey(packetProtectionKey->coalesce()).hasError()) {
throw std::runtime_error("Failed to set header cipher key");
}
return {std::move(aead), std::move(headerCipher)};
}

View File

@ -77,7 +77,10 @@ FizzCryptoFactory::makePacketNumberCipher(ByteRange baseSecret) const {
fizzFactory_->makeKeyDeriver(fizz::CipherSuite::TLS_AES_128_GCM_SHA256);
auto pnKey = deriver->expandLabel(
baseSecret, kQuicPNLabel, BufHelpers::create(0), pnCipher->keyLength());
pnCipher->setKey(pnKey->coalesce());
auto setKeyResult = pnCipher->setKey(pnKey->coalesce());
if (setKeyResult.hasError()) {
return folly::makeUnexpected(setKeyResult.error());
}
return pnCipher;
}

View File

@ -9,22 +9,26 @@
namespace quic {
static void setKeyImpl(
static folly::Expected<folly::Unit, QuicError> setKeyImpl(
folly::ssl::EvpCipherCtxUniquePtr& context,
const EVP_CIPHER* cipher,
ByteRange key) {
DCHECK_EQ(key.size(), EVP_CIPHER_key_length(cipher));
context.reset(EVP_CIPHER_CTX_new());
if (context == nullptr) {
throw std::runtime_error("Unable to allocate an EVP_CIPHER_CTX object");
return folly::makeUnexpected(QuicError(
TransportErrorCode::INTERNAL_ERROR,
"Unable to allocate an EVP_CIPHER_CTX object"));
}
if (EVP_EncryptInit_ex(context.get(), cipher, nullptr, key.data(), nullptr) !=
1) {
throw std::runtime_error("Init error");
return folly::makeUnexpected(
QuicError(TransportErrorCode::INTERNAL_ERROR, "Init error"));
}
return folly::unit;
}
static HeaderProtectionMask maskImpl(
static folly::Expected<HeaderProtectionMask, QuicError> maskImpl(
const folly::ssl::EvpCipherCtxUniquePtr& context,
ByteRange sample) {
HeaderProtectionMask outMask;
@ -35,19 +39,22 @@ static HeaderProtectionMask maskImpl(
outMask.data(),
&outLen,
sample.data(),
sample.size()) != 1 ||
static_cast<int>(sample.size())) != 1 ||
static_cast<HeaderProtectionMask::size_type>(outLen) != outMask.size()) {
throw std::runtime_error("Encryption error");
return folly::makeUnexpected(
QuicError(TransportErrorCode::INTERNAL_ERROR, "Encryption error"));
}
return outMask;
}
void Aes128PacketNumberCipher::setKey(ByteRange key) {
folly::Expected<folly::Unit, QuicError> Aes128PacketNumberCipher::setKey(
ByteRange key) {
pnKey_ = BufHelpers::copyBuffer(key);
return setKeyImpl(encryptCtx_, EVP_aes_128_ecb(), key);
}
void Aes256PacketNumberCipher::setKey(ByteRange key) {
folly::Expected<folly::Unit, QuicError> Aes256PacketNumberCipher::setKey(
ByteRange key) {
pnKey_ = BufHelpers::copyBuffer(key);
return setKeyImpl(encryptCtx_, EVP_aes_256_ecb(), key);
}
@ -60,11 +67,13 @@ const BufPtr& Aes256PacketNumberCipher::getKey() const {
return pnKey_;
}
HeaderProtectionMask Aes128PacketNumberCipher::mask(ByteRange sample) const {
folly::Expected<HeaderProtectionMask, QuicError> Aes128PacketNumberCipher::mask(
ByteRange sample) const {
return maskImpl(encryptCtx_, sample);
}
HeaderProtectionMask Aes256PacketNumberCipher::mask(ByteRange sample) const {
folly::Expected<HeaderProtectionMask, QuicError> Aes256PacketNumberCipher::mask(
ByteRange sample) const {
return maskImpl(encryptCtx_, sample);
}

View File

@ -17,13 +17,15 @@ class Aes128PacketNumberCipher : public PacketNumberCipher {
public:
~Aes128PacketNumberCipher() override = default;
void setKey(ByteRange key) override;
[[nodiscard]] folly::Expected<folly::Unit, QuicError> setKey(
ByteRange key) override;
const BufPtr& getKey() const override;
[[nodiscard]] const BufPtr& getKey() const override;
HeaderProtectionMask mask(ByteRange sample) const override;
[[nodiscard]] folly::Expected<HeaderProtectionMask, QuicError> mask(
ByteRange sample) const override;
size_t keyLength() const override;
[[nodiscard]] size_t keyLength() const override;
private:
folly::ssl::EvpCipherCtxUniquePtr encryptCtx_;
@ -35,13 +37,15 @@ class Aes256PacketNumberCipher : public PacketNumberCipher {
public:
~Aes256PacketNumberCipher() override = default;
void setKey(ByteRange key) override;
[[nodiscard]] folly::Expected<folly::Unit, QuicError> setKey(
ByteRange key) override;
const BufPtr& getKey() const override;
[[nodiscard]] const BufPtr& getKey() const override;
HeaderProtectionMask mask(ByteRange sample) const override;
[[nodiscard]] folly::Expected<HeaderProtectionMask, QuicError> mask(
ByteRange sample) const override;
size_t keyLength() const override;
[[nodiscard]] size_t keyLength() const override;
private:
folly::ssl::EvpCipherCtxUniquePtr encryptCtx_;

View File

@ -51,6 +51,7 @@ class FizzCryptoFactoryTest : public Test {
EXPECT_CALL(*mockPacketNumberCipher, setKey(_))
.WillOnce(Invoke([&](ByteRange key) {
packetCipherKey_ = folly::IOBuf::copyBuffer(key);
return folly::Expected<folly::Unit, QuicError>(folly::unit);
}));
EXPECT_CALL(*mockPacketNumberCipher, keyLength())
.WillRepeatedly(Return(fizz::AESGCM128::kKeyLength));

View File

@ -66,23 +66,26 @@ TEST_P(LongPacketNumberCipherTest, TestEncryptDecrypt) {
auto cipher = std::move(cipherResult.value());
auto key = folly::unhexlify(GetParam().key);
EXPECT_EQ(cipher->keyLength(), key.size());
cipher->setKey(folly::range(key));
auto setKeyResult = cipher->setKey(folly::range(key));
ASSERT_FALSE(setKeyResult.hasError());
EXPECT_TRUE(!memcmp(cipher->getKey()->data(), key.c_str(), key.size()));
CipherBytes cipherBytes(
GetParam().sample,
GetParam().decryptedInitialByte,
GetParam().decryptedPacketNumberBytes);
cipher->encryptLongHeader(
auto encryptResult = cipher->encryptLongHeader(
cipherBytes.sample,
folly::range(cipherBytes.initial),
folly::range(cipherBytes.packetNumber));
ASSERT_FALSE(encryptResult.hasError());
EXPECT_EQ(folly::hexlify(cipherBytes.initial), GetParam().initialByte);
EXPECT_EQ(
folly::hexlify(cipherBytes.packetNumber), GetParam().packetNumberBytes);
cipher->decryptLongHeader(
auto decryptResult = cipher->decryptLongHeader(
cipherBytes.sample,
folly::range(cipherBytes.initial),
folly::range(cipherBytes.packetNumber));
ASSERT_FALSE(decryptResult.hasError());
EXPECT_EQ(
folly::hexlify(cipherBytes.initial), GetParam().decryptedInitialByte);
EXPECT_EQ(

View File

@ -25,7 +25,9 @@ mvfst_cpp_library(
],
exported_deps = [
":aead",
"//folly:expected",
"//quic:constants",
"//quic:exception",
"//quic/codec:packet_number_cipher",
"//quic/codec:types",
],
@ -44,6 +46,7 @@ mvfst_cpp_library(
"//quic/state:quic_state_machine",
],
exported_deps = [
"//folly:expected",
"//quic:constants",
"//quic:exception",
"//quic/codec:types",

View File

@ -8,7 +8,10 @@ mvfst_cpp_library(
"Mocks.h",
],
exported_deps = [
"//folly:expected",
"//folly:unit",
"//folly/portability:gmock",
"//quic:exception",
"//quic/codec:packet_number_cipher",
"//quic/fizz/handshake:fizz_handshake",
"//quic/handshake:aead",

View File

@ -7,7 +7,10 @@
#pragma once
#include <folly/Expected.h>
#include <folly/Unit.h>
#include <folly/portability/GMock.h>
#include <quic/QuicException.h>
#include <quic/codec/PacketNumberCipher.h>
#include <quic/fizz/handshake/FizzCryptoFactory.h>
#include <quic/handshake/Aead.h>
@ -23,8 +26,15 @@ class MockPacketNumberCipher : public PacketNumberCipher {
public:
virtual ~MockPacketNumberCipher() = default;
MOCK_METHOD(void, setKey, (ByteRange key));
MOCK_METHOD(HeaderProtectionMask, mask, (ByteRange), (const));
MOCK_METHOD(
(folly::Expected<folly::Unit, QuicError>),
setKey,
(ByteRange key));
MOCK_METHOD(
(folly::Expected<HeaderProtectionMask, QuicError>),
mask,
(ByteRange),
(const));
MOCK_METHOD(size_t, keyLength, (), (const));
MOCK_METHOD(const BufPtr&, getKey, (), (const));