* receive packet

* populated the packet struct

* added ACK frame sending

* Initial ACK frame response sent

* added handshake decryption

* Completed handshake decryption

* finshed processing handshake packet

* fuzzed handshake packet and sent

* fixed Read key not available bug

* fixed a bug

* Testing to see if pipelines passes

* pop packet when decryption fail

* minor change

* only free packet after we are done processing

* attempt to fix the pipeline

* packet copy is sent

* recv event modified

* zeroed out send buffer

* minor change

* minor

* free packet event

* redefined few variables to fix memory leak

* minor change

* fixed recv datagram bug

* attempt to fix the pipeline

* test

* added few checks

* minor change

* minor change

* debug statement

* some debug statements for the pipeling

* minor change

* Few fixes

* minor

* added allocated check

* chain processing changed

* cleaned up code

* minor change

* minor change

* minor change

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* minor code cleanups:

* mode changes

* minor

* made initial packet and handshake packet fuzzing work together

* minor change

* debug

* minor change

* minor change

* minor change

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* minor change

* few cxplatevent changes

* few minor changes

* handle failure

* minor

* few minor things

* delete state buffer after every iteration

* minor

* minor

* test change

* minor change

* minor change

* added startms

* added result flag

* cleanup code and few debug statements

* cleanup

* Made packet as stack variable

* reverted change

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* resolved comments

* fixed potential memory leak

* Update src/tools/recvfuzz/recvfuzz.cpp

Co-authored-by: Nick Banks <nibanks@microsoft.com>

* resolved comments

* New handshake each iteration

* removed unnecessary comment

* minor change

* minor change

* Some modifications

* fixed memory leaks

* minor change

* freed up send data

* revert

* revert

* minor change

* minor changes

* test

* add free send data

* rest

* modified implementation

* minor change

* few optimisations

* attempt to fix

* added ASAN option

* resolved comments

* minor refactoring

* minor change

---------

Co-authored-by: Nick Banks <nibanks@microsoft.com>
This commit is contained in:
Gaurav Singh 2024-03-12 18:39:38 +05:30 коммит произвёл GitHub
Родитель 55830d38c8
Коммит 1a32822791
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
2 изменённых файлов: 509 добавлений и 106 удалений

Просмотреть файл

@ -98,6 +98,7 @@ param (
[switch]$UseXdp
)
$env:ASAN_OPTIONS = "allocator_may_return_null=1"
Set-StrictMode -Version 'Latest'
$PSDefaultParameterValues['*:ErrorAction'] = 'Stop'

Просмотреть файл

@ -14,6 +14,7 @@ Abstract:
#include <map>
#include <mutex>
#include <algorithm>
#include <list>
#define QUIC_TEST_APIS 1 // Needed for self signed cert API
#define QUIC_API_ENABLE_INSECURE_FEATURES 1 // Needed for disabling 1-RTT encryption
@ -28,13 +29,30 @@ const MsQuicApi* MsQuic;
uint64_t MagicCid = 0x989898989898989ull;
const QUIC_HKDF_LABELS HkdfLabels = { "quic key", "quic iv", "quic hp", "quic ku" };
uint64_t RunTimeMs = 60000;
CxPlatEvent RecvPacketEvent(true);
std::list<QUIC_RX_PACKET*> PacketQueue;
uint64_t CurrSrcCid = 0;
static const char* Alpn = "fuzz";
static uint32_t Version = QUIC_VERSION_DRAFT_29;
static uint32_t Version = QUIC_VERSION_1;
const char* Sni = "localhost";
#define QUIC_MIN_INITIAL_LENGTH 1200
struct PacketParams {
uint8_t DestCidLen;
uint8_t SourceCidLen;
uint8_t* DestCid;
uint8_t* SourceCid;
uint64_t packetNumber;
uint8_t NumFrames;
uint8_t NumPackets;
QUIC_LONG_HEADER_TYPE_V1 PacketType;
uint8_t Mode;
QUIC_FRAME_TYPE FrameTypes[2];
uint64_t LargestAcknowledge; // For ACK Frame
};
struct StrBuffer {
uint8_t* Data;
uint16_t Length;
@ -54,6 +72,8 @@ struct StrBuffer {
~StrBuffer() { delete [] Data; }
};
const StrBuffer InitialSalt("38762cf7f55934b34d179ae6a4c80cadccbb7f0a");
class FuzzingData {
const uint8_t* data {nullptr};
const size_t size {0};
@ -105,11 +125,73 @@ _IRQL_requires_max_(DISPATCH_LEVEL)
_Function_class_(CXPLAT_DATAPATH_RECEIVE_CALLBACK)
void
UdpRecvCallback(
_In_ CXPLAT_SOCKET* /* Binding */,
_In_ void* /* Context */,
_In_ CXPLAT_SOCKET* Binding,
_In_ void* Context,
_In_ CXPLAT_RECV_DATA* RecvBufferChain
)
{
CXPLAT_RECV_DATA* Datagram = RecvBufferChain;
while (Datagram != NULL) {
uint8_t DestCidLen, SourceCidLen;
const uint8_t* DestCid, *SourceCid;
QUIC_RX_PACKET Packet;
Packet.AvailBuffer = Datagram->Buffer;
do {
Packet.AvailBufferLength = Datagram->BufferLength;
DestCidLen = Packet.Invariant->LONG_HDR.DestCidLength;
DestCid = Packet.Invariant->LONG_HDR.DestCid;
SourceCidLen = *(DestCid + DestCidLen);
SourceCid = DestCid + sizeof(uint8_t) + DestCidLen;
uint16_t Offset = MIN_INV_LONG_HDR_LENGTH + DestCidLen + SourceCidLen;
Packet.DestCidLen = DestCidLen;
Packet.SourceCidLen = SourceCidLen;
Packet.DestCid = DestCid;
Packet.SourceCid = SourceCid;
if (Packet.LH->Type == QUIC_INITIAL_V1) {
QUIC_VAR_INT TokenLengthVarInt;
QuicVarIntDecode(
Packet.AvailBufferLength,
Packet.AvailBuffer,
&Offset,
&TokenLengthVarInt);
Offset += (uint16_t)TokenLengthVarInt;
}
QUIC_VAR_INT LengthVarInt;
QuicVarIntDecode(
Packet.AvailBufferLength,
Packet.AvailBuffer,
&Offset,
&LengthVarInt);
Packet.HeaderLength = Offset;
Packet.PayloadLength = (uint16_t)LengthVarInt;
Packet.ValidatedHeaderVer = TRUE;
if (Packet.LH->Version == QUIC_VERSION_2) {
Packet.KeyType = QuicPacketTypeToKeyTypeV2(Packet.LH->Type);
} else {
Packet.KeyType = QuicPacketTypeToKeyTypeV1(Packet.LH->Type);
}
Packet.Encrypted = TRUE;
if (Packet.AvailBufferLength >= Packet.HeaderLength &&
(memcmp(Packet.DestCid, &CurrSrcCid, sizeof(uint64_t)) == 0) &&
(Packet.LH->Type == QUIC_INITIAL_V1 || Packet.LH->Type == QUIC_HANDSHAKE_V1)) {
Packet.AvailBufferLength = Packet.HeaderLength + Packet.PayloadLength;
QUIC_RX_PACKET* PacketCopy = (QUIC_RX_PACKET *)CXPLAT_ALLOC_NONPAGED(sizeof(QUIC_RX_PACKET) + Packet.AvailBufferLength + Packet.DestCidLen + Packet.SourceCidLen, QUIC_POOL_TOOL);
memcpy(PacketCopy, &Packet, sizeof(QUIC_RX_PACKET));
PacketCopy->AvailBuffer = (uint8_t*)(PacketCopy + 1);
memcpy((void *)PacketCopy->AvailBuffer, Packet.AvailBuffer, Packet.AvailBufferLength);
PacketCopy->DestCid = PacketCopy->AvailBuffer + Packet.AvailBufferLength;
memcpy((void *)PacketCopy->DestCid, Packet.DestCid, Packet.DestCidLen);
PacketCopy->SourceCid = PacketCopy->DestCid + Packet.DestCidLen;
memcpy((void *)PacketCopy->SourceCid, Packet.SourceCid, Packet.SourceCidLen);
PacketQueue.push_back(PacketCopy);
}
Packet.AvailBuffer += Packet.AvailBufferLength;
} while (Packet.AvailBuffer - Datagram->Buffer < Datagram->BufferLength);
Datagram = Datagram->Next;
}
if (!PacketQueue.empty()) {
RecvPacketEvent.Set();
}
CxPlatRecvDataReturn(RecvBufferChain);
}
@ -127,18 +209,21 @@ UdpUnreachCallback(
struct TlsContext
{
CXPLAT_TLS* Ptr {nullptr};
CXPLAT_SEC_CONFIG* SecConfig {nullptr};
CXPLAT_SEC_CONFIG* ClientSecConfig {nullptr};
CXPLAT_TLS_PROCESS_STATE State;
uint8_t AlpnListBuffer[256];
TlsContext() {
TlsContext() {
AlpnListBuffer[0] = (uint8_t)strlen(Alpn);
memcpy(&AlpnListBuffer[1], Alpn, AlpnListBuffer[0]);
CxPlatZeroMemory(&State, sizeof(State));
State.Buffer = (uint8_t*)CXPLAT_ALLOC_NONPAGED(8000, QUIC_POOL_TOOL);
State.BufferAllocLength = 8000;
}
void CreateContext(uint64_t initSrcCid = MagicCid) {
uint8_t *stateBuffer = State.Buffer;
CxPlatZeroMemory(&State, sizeof(State));
State.Buffer = stateBuffer;
State.BufferAllocLength = 8000;
QUIC_CREDENTIAL_CONFIG CredConfig = {
QUIC_CREDENTIAL_TYPE_NONE,
QUIC_CREDENTIAL_FLAG_CLIENT | QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION,
@ -154,7 +239,7 @@ struct TlsContext
&CredConfig,
CXPLAT_TLS_CREDENTIAL_FLAG_NONE,
&TlsCallbacks,
&SecConfig,
&ClientSecConfig,
OnSecConfigCreateComplete))) {
printf("Failed to create sec config!\n");
}
@ -174,11 +259,11 @@ struct TlsContext
TP.InitialMaxUniStreams = 3;
TP.Flags |= QUIC_TP_FLAG_INITIAL_SOURCE_CONNECTION_ID;
TP.InitialSourceConnectionIDLength = sizeof(uint64_t);
*(uint64_t*)&TP.InitialSourceConnectionID[0] = MagicCid;
*(uint64_t*)&TP.InitialSourceConnectionID[0] = initSrcCid;
CXPLAT_TLS_CONFIG Config = {0};
Config.IsServer = FALSE;
Config.SecConfig = SecConfig;
Config.SecConfig = ClientSecConfig;
Config.HkdfLabels = &HkdfLabels;
Config.AlpnBuffer = AlpnListBuffer;
Config.AlpnBufferLength = AlpnListBuffer[0] + 1;
@ -202,13 +287,22 @@ struct TlsContext
~TlsContext() {
CxPlatTlsUninitialize(Ptr);
if (SecConfig) {
CxPlatTlsSecConfigDelete(SecConfig);
if (ClientSecConfig) {
CxPlatTlsSecConfigDelete(ClientSecConfig);
}
if (State.Buffer != nullptr) {
CXPLAT_FREE(State.Buffer, QUIC_POOL_TOOL);
State.Buffer = nullptr;
}
CXPLAT_FREE(State.Buffer, QUIC_POOL_TOOL);
for (uint8_t i = 0; i < QUIC_PACKET_KEY_COUNT; ++i) {
QuicPacketKeyFree(State.ReadKeys[i]);
QuicPacketKeyFree(State.WriteKeys[i]);
if (State.ReadKeys[i] != nullptr) {
QuicPacketKeyFree(State.ReadKeys[i]);
State.ReadKeys[i] = nullptr;
}
if (State.WriteKeys[i] != nullptr) {
QuicPacketKeyFree(State.WriteKeys[i]);
State.WriteKeys[i] = nullptr;
}
}
}
@ -244,7 +338,6 @@ private:
if (Result & CXPLAT_TLS_RESULT_ERROR) {
printf("Failed to process data!\n");
exit(0);
}
return Result;
@ -318,18 +411,56 @@ private:
return TRUE;
}
};
void WriteInitialCryptoFrame(
bool WriteAckFrame(
_In_ uint64_t LargestAcknowledge,
_Inout_ uint16_t* Offset,
_In_ uint16_t BufferLength,
_Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
)
{
QUIC_RANGE AckRange;
QuicRangeInitialize(QUIC_MAX_RANGE_DECODE_ACKS, &AckRange);
BOOLEAN RangeUpdated;
QuicRangeAddRange(&AckRange, LargestAcknowledge, 1, &RangeUpdated);
uint64_t AckDelay = 40;
if (!QuicAckFrameEncode(
&AckRange,
AckDelay,
nullptr,
Offset,
BufferLength,
Buffer)) {
printf("QuicAckFrameEncode failure!\n");
return false;
}
return true;
}
bool WriteCryptoFrame(
_Inout_ uint16_t* Offset,
_In_ uint16_t BufferLength,
_Out_writes_to_(BufferLength, *Offset)
uint8_t* Buffer)
uint8_t* Buffer,
_In_ TlsContext* ClientContext,
_In_ PacketParams* PacketParams
)
{
TlsContext ClientContext;
ClientContext.ProcessData();
if (PacketParams->Mode == 0) {
uint64_t SrcCid;
CxPlatRandom(sizeof(uint64_t), &SrcCid);
if (ClientContext == nullptr) {
ClientContext = new TlsContext();
PacketParams->SourceCid = (uint8_t *)&SrcCid;
ClientContext->CreateContext(SrcCid);
auto Result = ClientContext->ProcessData();
if (!(Result & CXPLAT_TLS_RESULT_DATA)) {
return false;
}
}
}
QUIC_CRYPTO_EX Frame = {
0, ClientContext.State.BufferLength, ClientContext.State.Buffer
0, ClientContext->State.BufferLength, ClientContext->State.Buffer
};
if (!QuicCryptoFrameEncode(
@ -338,31 +469,76 @@ void WriteInitialCryptoFrame(
BufferLength,
Buffer)) {
printf("QuicCryptoFrameEncode failure!\n");
exit(0);
return false;
}
if (PacketParams->Mode == 0) {
CxPlatTlsUninitialize(ClientContext->Ptr);
if (ClientContext->ClientSecConfig) {
CxPlatTlsSecConfigDelete(ClientContext->ClientSecConfig);
}
if (ClientContext->State.Buffer != nullptr) {
CXPLAT_FREE(ClientContext->State.Buffer, QUIC_POOL_TOOL);
}
for (uint8_t i = 0; i < QUIC_PACKET_KEY_COUNT; ++i) {
if (ClientContext->State.ReadKeys[i] != nullptr) {
QuicPacketKeyFree(ClientContext->State.ReadKeys[i]);
}
if (ClientContext->State.WriteKeys[i] != nullptr) {
QuicPacketKeyFree(ClientContext->State.WriteKeys[i]);
}
}
}
return true;
}
void WriteClientInitialPacket(
bool WriteClientPacket(
_In_ uint32_t PacketNumber,
_In_ uint8_t CidLength,
_In_ uint16_t BufferLength,
_Out_writes_to_(BufferLength, *PacketLength)
uint8_t* Buffer,
_Out_ uint16_t* PacketLength,
_Out_ uint16_t* HeaderLength
_Out_ uint16_t* HeaderLength,
_In_ TlsContext* ClientContext,
_In_ PacketParams* PacketParams
)
{
uint32_t QuicVersion = Version;
uint8_t CryptoBuffer[4096];
uint16_t BufferSize = sizeof(CryptoBuffer);
uint16_t CryptoBufferLength = 0;
uint8_t FrameBuffer[4096];
uint16_t BufferSize = sizeof(FrameBuffer);
uint16_t FrameBufferLength = 0;
for (int i = 0; i < PacketParams->NumFrames; i++) {
if (PacketParams->FrameTypes[i] == QUIC_FRAME_ACK) {
if (!WriteAckFrame(
PacketParams->LargestAcknowledge,
&FrameBufferLength,
BufferSize,
FrameBuffer)) {
return false;
}
}
WriteInitialCryptoFrame(
&CryptoBufferLength, BufferSize, CryptoBuffer);
uint8_t CidBuffer[sizeof(QUIC_CID) + 256] = {0};
QUIC_CID* Cid = (QUIC_CID*)CidBuffer;
Cid->IsInitial = TRUE;
Cid->Length = CidLength;
if (PacketParams->FrameTypes[i] == QUIC_FRAME_CRYPTO) {
if (!WriteCryptoFrame(
&FrameBufferLength,
BufferSize,
FrameBuffer,
ClientContext,
PacketParams)) {
return false;
}
}
}
uint8_t DestCidBuffer[sizeof(QUIC_CID) + 256] = {0};
uint8_t SourceCidBuffer[sizeof(QUIC_CID) + 256] = {0};
QUIC_CID* DestCid = (QUIC_CID*)DestCidBuffer;
QUIC_CID* SourceCid = (QUIC_CID*)SourceCidBuffer;
DestCid->IsInitial = TRUE;
DestCid->Length = PacketParams->DestCidLen;
SourceCid->IsInitial = TRUE;
SourceCid->Length = PacketParams->SourceCidLen;
uint16_t PayloadLengthOffset = 0;
uint8_t PacketNumberLength;
@ -370,10 +546,10 @@ void WriteClientInitialPacket(
*PacketLength =
QuicPacketEncodeLongHeaderV1(
QuicVersion,
QUIC_INITIAL_V1,
(uint8_t)PacketParams->PacketType,
1, // Fixed bit must be 1 in this case
Cid,
Cid,
DestCid,
SourceCid,
0,
nullptr,
PacketNumber,
@ -381,19 +557,21 @@ void WriteClientInitialPacket(
Buffer,
&PayloadLengthOffset,
&PacketNumberLength);
if (*PacketLength + CryptoBufferLength > BufferLength) {
if (*PacketLength + FrameBufferLength > BufferLength) {
printf("Crypto Too Big!\n");
exit(0);
return false;
}
QuicVarIntEncode2Bytes(
PacketNumberLength + CryptoBufferLength + CXPLAT_ENCRYPTION_OVERHEAD,
PacketNumberLength + FrameBufferLength + CXPLAT_ENCRYPTION_OVERHEAD,
Buffer + PayloadLengthOffset);
*HeaderLength = *PacketLength;
CxPlatCopyMemory(Buffer + *PacketLength, CryptoBuffer, CryptoBufferLength);
*PacketLength += CryptoBufferLength;
CxPlatCopyMemory(Buffer + *PacketLength, FrameBuffer, FrameBufferLength);
*PacketLength += FrameBufferLength;
*PacketLength += CXPLAT_ENCRYPTION_OVERHEAD;
return true;
}
void fuzzPacket(uint8_t* Packet, uint16_t PacketLength) {
@ -403,45 +581,50 @@ void fuzzPacket(uint8_t* Packet, uint16_t PacketLength) {
}
}
void buildInitialPacket(CXPLAT_SOCKET* Binding, CXPLAT_ROUTE Route, int64_t* PacketCount, int64_t* TotalByteCount, bool fuzzing = true) {
const StrBuffer InitialSalt("afbfec289993d24c9e9786f19c6111e04390a899");
void sendPacket(
CXPLAT_SOCKET* Binding,
CXPLAT_ROUTE Route,
int64_t* PacketCount,
int64_t* TotalByteCount,
PacketParams* PacketParams,
bool fuzzing = true,
TlsContext* ClientContext = nullptr) {
const uint16_t DatagramLength = QUIC_MIN_INITIAL_LENGTH;
CXPLAT_SEND_CONFIG SendConfig = { &Route, DatagramLength, CXPLAT_ECN_NON_ECT, 0 };
CXPLAT_SEND_DATA* SendData = CxPlatSendDataAlloc(Binding, &SendConfig);
if (!SendData) {
printf("CxPlatSendDataAlloc failed\n");
return;
}
while (!CxPlatSendDataIsFull(SendData)) {
const uint64_t PacketNumber = GetRandom(1000);
uint8_t numPacketsSent = 0;
while (!CxPlatSendDataIsFull(SendData) && numPacketsSent <= PacketParams->NumPackets) {
uint8_t Packet[512] = {0};
uint16_t PacketLength, HeaderLength;
WriteClientInitialPacket(
(uint32_t)PacketNumber,
sizeof(uint64_t),
sizeof(Packet),
Packet,
&PacketLength,
&HeaderLength);
uint16_t PacketNumberOffset = HeaderLength - sizeof(uint32_t);
uint64_t* DestCid = (uint64_t*)(Packet + sizeof(QUIC_LONG_HEADER_V1));
uint64_t* SrcCid = (uint64_t*)(Packet + sizeof(QUIC_LONG_HEADER_V1) + sizeof(uint64_t) + sizeof(uint8_t));
uint64_t* OrigSrcCid = nullptr;
for (uint16_t i = HeaderLength; i < PacketLength; ++i) {
if (!memcmp(&MagicCid, Packet+i, sizeof(MagicCid))) {
OrigSrcCid = (uint64_t*)&Packet[i];
}
}
if (!OrigSrcCid) {
printf("Failed to find OrigSrcCid!\n");
uint64_t packetNum = PacketParams->packetNumber++;
if (!WriteClientPacket(
(uint32_t)packetNum,
sizeof(Packet),
Packet,
&PacketLength,
&HeaderLength,
ClientContext,
PacketParams)) {
CxPlatSendDataFree(SendData);
return;
}
uint16_t PacketNumberOffset = HeaderLength - sizeof(uint32_t);
CxPlatRandom(sizeof(uint64_t), DestCid); //fuzz
CxPlatRandom(sizeof(uint64_t), SrcCid); //fuzz
uint8_t* DestCid = (uint8_t*)(Packet + sizeof(QUIC_LONG_HEADER_V1));
uint8_t* SrcCid = (uint8_t*)(Packet + sizeof(QUIC_LONG_HEADER_V1) + PacketParams->DestCidLen + sizeof(uint8_t));
if (PacketParams->DestCid == nullptr) {
CxPlatRandom(sizeof(uint64_t), DestCid);
} else {
memcpy(DestCid, PacketParams->DestCid, PacketParams->DestCidLen);
}
memcpy(SrcCid, PacketParams->SourceCid, PacketParams->SourceCidLen);
if (fuzzing) {
fuzzPacket(Packet, sizeof(Packet));
}
@ -449,27 +632,50 @@ void buildInitialPacket(CXPLAT_SOCKET* Binding, CXPLAT_ROUTE Route, int64_t* Pac
CxPlatSendDataAllocBuffer(SendData, DatagramLength);
if (!SendBuffer) {
printf("CxPlatSendDataAllocBuffer failed\n");
CxPlatSendDataFree(SendData);
return;
}
*OrigSrcCid = *SrcCid;
CxPlatZeroMemory(SendBuffer->Buffer, DatagramLength);
memcpy(SendBuffer->Buffer, Packet, PacketLength);
QUIC_PACKET_KEY* WriteKey;
if (QUIC_FAILED(
QuicPacketKeyCreateInitial(
FALSE,
&HkdfLabels,
InitialSalt.Data,
sizeof(uint64_t),
(uint8_t*)DestCid,
nullptr,
&WriteKey))) {
printf("QuicPacketKeyCreateInitial failed\n");
return;
QUIC_PACKET_KEY* WriteKey = nullptr;
QUIC_PACKET_KEY_TYPE KeyType = QuicPacketTypeToKeyTypeV1((uint8_t)PacketParams->PacketType);
if (PacketParams->Mode == 0) {
if (QUIC_FAILED(
QuicPacketKeyCreateInitial(
FALSE,
&HkdfLabels,
InitialSalt.Data,
PacketParams->DestCidLen,
(uint8_t*)DestCid,
nullptr,
&WriteKey))) {
printf("QuicPacketKeyCreateInitial failed\n");
CxPlatSendDataFree(SendData);
return;
}
} else {
if (ClientContext->State.WriteKeys[0] == nullptr) {
if (QUIC_FAILED(
QuicPacketKeyCreateInitial(
FALSE,
&HkdfLabels,
InitialSalt.Data,
PacketParams->DestCidLen,
(uint8_t*)DestCid,
&ClientContext->State.ReadKeys[0],
&ClientContext->State.WriteKeys[0]))) {
printf("QuicPacketKeyCreateInitial failed\n");
CxPlatSendDataFree(SendData);
return;
}
ClientContext->State.ReadKey = QUIC_PACKET_KEY_INITIAL;
ClientContext->State.WriteKey = QUIC_PACKET_KEY_INITIAL;
}
WriteKey = ClientContext->State.WriteKeys[KeyType];
}
uint8_t Iv[CXPLAT_IV_LENGTH];
QuicCryptoCombineIvAndPacketNumber(
WriteKey->Iv, (uint8_t*)&PacketNumber, Iv);
WriteKey->Iv, (uint8_t*)&packetNum, Iv);
CxPlatEncrypt(
WriteKey->PacketKey,
@ -486,13 +692,16 @@ void buildInitialPacket(CXPLAT_SOCKET* Binding, CXPLAT_ROUTE Route, int64_t* Pac
SendBuffer->Buffer + HeaderLength,
HpMask);
QuicPacketKeyFree(WriteKey);
SendBuffer->Buffer[0] ^= HpMask[0] & 0x0F;
for (uint8_t i = 0; i < 4; ++i) {
SendBuffer->Buffer[PacketNumberOffset + i] ^= HpMask[i + 1];
}
InterlockedExchangeAdd64(PacketCount, 1);
InterlockedExchangeAdd64(TotalByteCount, DatagramLength);
numPacketsSent++;
if (!fuzzing) {
break;
}
}
if (QUIC_FAILED(
@ -505,25 +714,223 @@ void buildInitialPacket(CXPLAT_SOCKET* Binding, CXPLAT_ROUTE Route, int64_t* Pac
}
}
void fuzzHandshakePacket() {
// TODO
}
void fuzz(CXPLAT_SOCKET* Binding, CXPLAT_ROUTE Route) {
int64_t PacketCount = 0;
int64_t InitialPacketCount = 0;
int64_t HandshakePacketCount = 0;
int64_t TotalByteCount = 0;
uint8_t mode;
uint64_t StartTimeMs = CxPlatTimeMs64();
uint8_t recvBuffer[8192];
uint32_t bufferoffset = 0;
bool handshakeComplete = FALSE;
TlsContext HandshakeClientContext;
while (CxPlatTimeDiff64(StartTimeMs, CxPlatTimeMs64()) < RunTimeMs) {
mode = 0; //(uint8_t)GetRandom(2);
if (mode == 0) {
buildInitialPacket(Binding, Route, &PacketCount, &TotalByteCount);
} else if (mode == 1) {
fuzzHandshakePacket();
mode = (uint8_t)GetRandom(10);
if (mode < 1) {
PacketParams InitialPacketParams = {
sizeof(uint64_t),
sizeof(uint64_t),
nullptr,
nullptr,
0,
1,
100
};
InitialPacketParams.PacketType = QUIC_INITIAL_V1;
InitialPacketParams.FrameTypes[0] = QUIC_FRAME_CRYPTO;
InitialPacketParams.Mode = 0;
sendPacket(Binding, Route, &InitialPacketCount, &TotalByteCount, &InitialPacketParams, true);
} else if (mode >= 1) {
PacketParams HandshakePacketParams = {
sizeof(uint64_t),
sizeof(uint64_t),
nullptr,
nullptr,
0,
1
};
HandshakePacketParams.FrameTypes[0] = QUIC_FRAME_CRYPTO;
RecvPacketEvent.Reset();
bool FirstPacket = TRUE;
do {
CxPlatRandom(sizeof(uint64_t), &CurrSrcCid);
HandshakePacketParams.SourceCid = (uint8_t *)&CurrSrcCid;
HandshakeClientContext.CreateContext(CurrSrcCid);
HandshakeClientContext.ProcessData();
HandshakePacketParams.PacketType = QUIC_INITIAL_V1;
HandshakePacketParams.Mode = 1;
sendPacket(Binding, Route, &InitialPacketCount, &TotalByteCount, &HandshakePacketParams, false, &HandshakeClientContext);
FirstPacket = FALSE;
} while (!RecvPacketEvent.WaitTimeout(400) && CxPlatTimeDiff64(StartTimeMs, CxPlatTimeMs64()) < RunTimeMs);
while (!PacketQueue.empty()) {
QUIC_RX_PACKET* packet = PacketQueue.front();
if (!packet->DestCidLen ||
!packet->DestCid || packet->PayloadLength < 4 + CXPLAT_HP_SAMPLE_LENGTH ||
(memcmp(packet->DestCid, &CurrSrcCid, sizeof(uint64_t)) != 0)) {
CXPLAT_FREE(packet, QUIC_POOL_TOOL);
PacketQueue.pop_front();
continue;
}
if (packet->LH->Type == QUIC_INITIAL_V1) {
bufferoffset = 0;
}
uint8_t Cipher[CXPLAT_HP_SAMPLE_LENGTH];
uint8_t HpMask[16];
CxPlatCopyMemory(
Cipher,
packet->AvailBuffer + packet->HeaderLength + 4,
CXPLAT_HP_SAMPLE_LENGTH);
// same step for all long header packets
QUIC_PACKET_KEY_TYPE KeyType = packet->KeyType;
if (HandshakeClientContext.State.ReadKeys[KeyType] == nullptr) {
CXPLAT_FREE(packet, QUIC_POOL_TOOL);
PacketQueue.pop_front();
continue;
}
if (QUIC_FAILED(
CxPlatHpComputeMask(
HandshakeClientContext.State.ReadKeys[KeyType]->HeaderKey,
1,
Cipher,
HpMask))) {
printf("Failed to Compute Mask\n");
}
uint8_t CompressedPacketNumberLength = 0;
((uint8_t*)packet->AvailBuffer)[0] ^= HpMask[0] & 0x0F;
CompressedPacketNumberLength = packet->LH->PnLength + 1;
for (uint8_t i = 0; i < CompressedPacketNumberLength; i++) {
((uint8_t*)packet->AvailBuffer)[packet->HeaderLength + i] ^= HpMask[1 + i];
}
uint64_t CompressedPacketNumber = 0;
QuicPktNumDecode(
CompressedPacketNumberLength,
packet->AvailBuffer + packet->HeaderLength,
&CompressedPacketNumber);
packet->HeaderLength += CompressedPacketNumberLength;
packet->PayloadLength -= CompressedPacketNumberLength;
QUIC_ENCRYPT_LEVEL EncryptLevel = QuicKeyTypeToEncryptLevel(packet->KeyType);
packet->PacketNumber =
QuicPktNumDecompress(
HandshakePacketParams.packetNumber + 1,
CompressedPacketNumber,
CompressedPacketNumberLength);
packet->PacketNumberSet = TRUE;
const uint8_t* Payload = packet->AvailBuffer + packet->HeaderLength;
uint8_t Iv[CXPLAT_MAX_IV_LENGTH];
QuicCryptoCombineIvAndPacketNumber(
HandshakeClientContext.State.ReadKeys[KeyType]->Iv,
(uint8_t*)&packet->PacketNumber,
Iv);
if (QUIC_FAILED(
CxPlatDecrypt(
HandshakeClientContext.State.ReadKeys[KeyType]->PacketKey,
Iv,
packet->HeaderLength, // HeaderLength
packet->AvailBuffer, // Header
packet->PayloadLength, // BufferLength
(uint8_t*)Payload))) { // Buffer
printf("CxPlatDecrypt failed\n");
CXPLAT_FREE(packet, QUIC_POOL_TOOL);
PacketQueue.pop_front();
continue;
}
packet->PayloadLength -= CXPLAT_ENCRYPTION_OVERHEAD;
QUIC_VAR_INT FrameType INIT_NO_SAL(0);
uint16_t offset = 0;
uint16_t PayloadLength = packet->PayloadLength;
while (offset < PayloadLength) {
QuicVarIntDecode(PayloadLength, Payload, &offset, &FrameType);
if (FrameType == QUIC_FRAME_ACK) {
QUIC_VAR_INT temp INIT_NO_SAL(0);
for (int i=0; i < 4; i++) {
QuicVarIntDecode(PayloadLength, Payload, &offset, &temp);
}
}
if (FrameType == QUIC_FRAME_CRYPTO) {
QUIC_CRYPTO_EX Frame;
QuicCryptoFrameDecode(packet->PayloadLength, Payload, &offset, &Frame);
uint64_t FlowControlLimit = UINT16_MAX;
BOOLEAN DataReady;
DataReady = FALSE;
CxPlatCopyMemory(
recvBuffer + (uint32_t)Frame.Offset,
Frame.Data,
(uint32_t)Frame.Length);
uint32_t recvBufferLength = (uint32_t)Frame.Length + (uint32_t)Frame.Offset - bufferoffset;
recvBufferLength = QuicCryptoTlsGetCompleteTlsMessagesLength(
recvBuffer + bufferoffset, recvBufferLength);
auto Result = CxPlatTlsProcessData(
HandshakeClientContext.Ptr,
CXPLAT_TLS_CRYPTO_DATA,
recvBuffer + bufferoffset,
&recvBufferLength,
&HandshakeClientContext.State);
if (Result & CXPLAT_TLS_RESULT_ERROR) {
printf("Failed to process handshake data!\n");
}
bufferoffset += recvBufferLength;
HandshakePacketParams.LargestAcknowledge = packet->PacketNumber;
if (packet->LH->Type == QUIC_INITIAL_V1) {
bufferoffset = 0;
HandshakePacketParams.NumFrames = 1;
HandshakePacketParams.FrameTypes[0] = QUIC_FRAME_ACK;
HandshakePacketParams.LargestAcknowledge = packet->PacketNumber;
HandshakePacketParams.SourceCid = (uint8_t *)packet->DestCid;
HandshakePacketParams.SourceCidLen = packet->DestCidLen;
HandshakePacketParams.DestCid = (uint8_t *)packet->SourceCid;
HandshakePacketParams.DestCidLen = packet->SourceCidLen;
HandshakePacketParams.PacketType = QUIC_INITIAL_V1;
HandshakePacketParams.Mode = 1;
sendPacket(Binding, Route, &InitialPacketCount, &TotalByteCount, &HandshakePacketParams, false, &HandshakeClientContext);
}
}
}
if (HandshakeClientContext.State.HandshakeComplete) {
bufferoffset = 0;
HandshakePacketParams.PacketType = QUIC_HANDSHAKE_V1;
HandshakePacketParams.NumFrames = 2;
HandshakePacketParams.FrameTypes[0] = QUIC_FRAME_ACK;
HandshakePacketParams.FrameTypes[1] = QUIC_FRAME_CRYPTO;
HandshakePacketParams.SourceCid = (uint8_t *)packet->DestCid;
HandshakePacketParams.SourceCidLen = packet->DestCidLen;
HandshakePacketParams.DestCid = (uint8_t *)packet->SourceCid;
HandshakePacketParams.DestCidLen = packet->SourceCidLen;
HandshakePacketParams.Mode = 1;
HandshakePacketParams.NumPackets = (uint8_t)GetRandom(3) + 1;
sendPacket(Binding, Route, &HandshakePacketCount, &TotalByteCount, &HandshakePacketParams, true, &HandshakeClientContext);
handshakeComplete = FALSE;
CXPLAT_FREE(packet, QUIC_POOL_TOOL);
PacketQueue.pop_front();
break;
}
CXPLAT_FREE(packet, QUIC_POOL_TOOL);
PacketQueue.pop_front();
}
for (uint8_t i = 0; i < QUIC_PACKET_KEY_COUNT; ++i) {
if (HandshakeClientContext.State.ReadKeys[i] != nullptr) {
QuicPacketKeyFree(HandshakeClientContext.State.ReadKeys[i]);
HandshakeClientContext.State.ReadKeys[i] = nullptr;
}
if (HandshakeClientContext.State.WriteKeys[i] != nullptr) {
QuicPacketKeyFree(HandshakeClientContext.State.WriteKeys[i]);
HandshakeClientContext.State.WriteKeys[i] = nullptr;
}
}
}
}
printf("Total Packets sent: %lld\n", (long long)PacketCount);
printf("Total Bytes sent: %lld\n", (long long)TotalByteCount);
while (!PacketQueue.empty()) {
QUIC_RX_PACKET* packet = PacketQueue.front();
CXPLAT_FREE(packet, QUIC_POOL_TOOL);
PacketQueue.pop_front();
}
printf("Total Initial Packets sent: %lld\n", (long long)InitialPacketCount);
printf("Total Handshake Packets sent: %lld\n", (long long)HandshakePacketCount);
printf("Total Bytes sent: %lld\n", (long long)TotalByteCount);
}
void start() {
@ -576,10 +983,6 @@ void start() {
UdpConfig.Flags = 0;
UdpConfig.InterfaceIndex = 0;
UdpConfig.CallbackContext = nullptr;
QUIC_ADDR_STR str;
QuicAddrToString(&sockAddr, &str);
printf("Remote address: %s\n", str.Address);
Status =
CxPlatSocketCreateUdp(
Datapath,
@ -593,7 +996,6 @@ void start() {
CXPLAT_ROUTE Route = {0};
CxPlatSocketGetLocalAddress(Binding, &Route.LocalAddress);
Route.RemoteAddress = sockAddr;
// Fuzzing
fuzz(Binding, Route);
}