Skip to content

Commit

Permalink
Support decryption of a few more versions
Browse files Browse the repository at this point in the history
  • Loading branch information
awelzel committed Jan 8, 2024
1 parent 58cba10 commit 164d580
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 22 deletions.
41 changes: 38 additions & 3 deletions analyzer/QUIC.spicy
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ function can_decrypt(long_header: LongHeaderPacket, context: ConnectionIDInfo, i
if ( ! long_header.is_initial )
return False;

if ( long_header.version != Version1 && long_header.version != Version2 )
return False;

if ( is_client )
return ! context.client_initial_processed;

Expand Down Expand Up @@ -80,6 +77,25 @@ type ConnectionIDInfo = struct {
##############
# Definitions
##############
const VersionDraft22: uint32 = 0xff000016;
const VersionDraft23: uint32 = 0xff000017;
const VersionDraft24: uint32 = 0xff000018;
const VersionDraft25: uint32 = 0xff000019;
const VersionDraft26: uint32 = 0xff00001a;
const VersionDraft27: uint32 = 0xff00001b;
const VersionDraft28: uint32 = 0xff00001c;
const VersionDraft29: uint32 = 0xff00001d;
const VersionDraft30: uint32 = 0xff00001e;
const VersionDraft31: uint32 = 0xff00001f;
const VersionDraft32: uint32 = 0xff000020;
const VersionDraft33: uint32 = 0xff000021;
const VersionDraft34: uint32 = 0xff000022;
const VersionFace001: uint32 = 0xfaceb001;
const VersionFace002: uint32 = 0xfaceb002;
const VersionFace00e: uint32 = 0xfaceb00e;
const VersionFace011: uint32 = 0xfaceb011;
const VersionFace012: uint32 = 0xfaceb012;
const VersionFace013: uint32 = 0xfaceb013;
const Version1: uint32 = 0x00000001;
const Version2: uint32 = 0x6b3343cf;

Expand Down Expand Up @@ -236,6 +252,25 @@ public type LongHeaderPacket = unit {
src_conn_id: bytes &size=self.client_conn_id_length;

switch ( self.version ) {
VersionDraft22,
VersionDraft23,
VersionDraft24,
VersionDraft25,
VersionDraft26,
VersionDraft27,
VersionDraft28,
VersionDraft29,
VersionDraft30,
VersionDraft31,
VersionDraft32,
VersionDraft33,
VersionDraft34 -> d: LongHeaderPacketV1(self);
VersionFace001,
VersionFace002,
VersionFace00e,
VersionFace011,
VersionFace012,
VersionFace013 -> d: LongHeaderPacketV1(self);
Version1 -> v1: LongHeaderPacketV1(self);
Version2 -> v2: LongHeaderPacketV2(self);
* -> unknown: UnhandledVersion(self) {
Expand Down
92 changes: 73 additions & 19 deletions analyzer/decrypt_crypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ hilti::rt::Bytes decrypt(const std::vector<uint8_t>& client_key, const hilti::rt
// Pre-initialized SSL contexts for re-use. Not thread-safe. These are only used in expand-only mode
// and have a fixed HKDF info set.
struct HkdfCtx {
bool initialized = false;
EVP_PKEY_CTX* client_in_ctx = nullptr;
EVP_PKEY_CTX* server_in_ctx = nullptr;
EVP_PKEY_CTX* key_info_ctx = nullptr;
Expand Down Expand Up @@ -251,9 +250,9 @@ std::vector<uint8_t> hkdf_expand(EVP_PKEY_CTX* ctx, size_t out_len, const std::v

class QuicPacketProtection {
public:
std::vector<uint8_t> GetSecret(bool is_orig, const hilti::rt::Bytes& connection_id) {
std::vector<uint8_t> GetSecret(bool is_orig, uint32_t version, const hilti::rt::Bytes& connection_id) {
const auto& ctxs = GetHkdfCtxs();
const auto initial_secret = hkdf_extract(GetInitialSalt(), connection_id);
const auto initial_secret = hkdf_extract(GetInitialSalt(version), connection_id);
EVP_PKEY_CTX* ctx = is_orig ? ctxs.client_in_ctx : ctxs.server_in_ctx;
return hkdf_expand(ctx, INITIAL_SECRET_LEN, initial_secret);
}
Expand All @@ -273,7 +272,8 @@ class QuicPacketProtection {
return hkdf_expand(ctxs.hp_info_ctx, AEAD_HP_LEN, secret);
}

virtual const std::vector<uint8_t>& GetInitialSalt() = 0;
virtual bool Supports(uint32_t version) = 0;
virtual const std::vector<uint8_t>& GetInitialSalt(uint32_t version) = 0;
virtual HkdfCtx& GetHkdfCtxs() = 0;

virtual ~QuicPacketProtection() = default;
Expand All @@ -296,6 +296,16 @@ class QuicPacketProtection {
std::vector<uint8_t> INITIAL_SALT_V1 = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};

// https://insights.sei.cmu.edu/documents/4499/2023_017_001_890985.pdf
std::vector<uint8_t> INITIAL_SALT_D22 = {0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a,
0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a};

std::vector<uint8_t> INITIAL_SALT_D23_D28 = {0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7,
0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02};

std::vector<uint8_t> INITIAL_SALT_D29_D32 = {0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97,
0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99};

std::vector<uint8_t> CLIENT_INITIAL_INFO = {0x00, 0x20, 0x0f, 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x00};

Expand All @@ -314,14 +324,50 @@ std::vector<uint8_t> HP_INFO = {0x00, 0x10, 0x0d, 0x74, 0x6c, 0x73, 0x31, 0x33,

class QuicPacketProtectionV1 : public QuicPacketProtection {
public:
virtual std::vector<uint8_t>& GetInitialSalt() override { return INITIAL_SALT_V1; }
virtual bool Supports(uint32_t version) override {
// Quic V1
if ( version == 0x00000001 )
return true;

// Draft 22 through 34
if ( version >= 0xff000016 && version <= 0xff000022 )
return true;

// mvfst from facebook
if ( version == 0xfaceb001 || version == 0xfaceb002 || version == 0xfaceb00e || version == 0xfaceb011 ||
version == 0xfaceb012 || version == 0xfaceb013 )
return true;

return false;
};

virtual std::vector<uint8_t>& GetInitialSalt(uint32_t version) override {
if ( version == 0x00000001 || version == 0xff000021 || version == 0xff000022 )
return INITIAL_SALT_V1;

if ( version == 0xff000016 )
return INITIAL_SALT_D22;

if ( version >= 0xff000017 && version <= 0xff00001c )
return INITIAL_SALT_D23_D28;

if ( version >= 0xff00001d && version <= 0xff000020 )
return INITIAL_SALT_D29_D32;

if ( version == 0xfaceb001 )
return INITIAL_SALT_D22;

if ( version >= 0xfaceb002 && version <= 0xfaceb013 )
return INITIAL_SALT_D23_D28;

assert(false);
return INITIAL_SALT_V1;
}

virtual HkdfCtx& GetHkdfCtxs() override { return hkdf_ctxs; }

// Pre-initialize SSL context for reuse with HKDF info set to version specific values.
static void Initialize() {
if ( hkdf_ctxs.initialized )
return;

std::vector<HkdfCtxParam> hkdf_ctx_params = {
{&hkdf_ctxs.client_in_ctx, CLIENT_INITIAL_INFO},
{&hkdf_ctxs.server_in_ctx, SERVER_INITIAL_INFO},
Expand All @@ -333,7 +379,6 @@ class QuicPacketProtectionV1 : public QuicPacketProtection {
QuicPacketProtection::Initialize(hkdf_ctx_params);

instance = std::make_unique<QuicPacketProtectionV1>();
hkdf_ctxs.initialized = true;
}

static HkdfCtx hkdf_ctxs;
Expand Down Expand Up @@ -367,12 +412,16 @@ std::vector<uint8_t> HP_INFO_V2 = {0x00, 0x10, 0x0f, 0x74, 0x6c, 0x73, 0x31, 0x3

class QuicPacketProtectionV2 : public QuicPacketProtection {
public:
virtual std::vector<uint8_t>& GetInitialSalt() override { return INITIAL_SALT_V2; }
virtual bool Supports(uint32_t version) override { return version == 0x6b3343cf; }

virtual std::vector<uint8_t>& GetInitialSalt(uint32_t version) override {
assert(version == 0x6b3343cf);
return INITIAL_SALT_V2;
}

virtual HkdfCtx& GetHkdfCtxs() override { return hkdf_ctxs; }

static void Initialize() {
if ( hkdf_ctxs.initialized )
return;
std::vector<HkdfCtxParam> hkdf_ctx_params = {
{&hkdf_ctxs.client_in_ctx, CLIENT_INITIAL_INFO_V2},
{&hkdf_ctxs.server_in_ctx, SERVER_INITIAL_INFO_V2},
Expand All @@ -383,7 +432,6 @@ class QuicPacketProtectionV2 : public QuicPacketProtection {

QuicPacketProtection::Initialize(hkdf_ctx_params);
instance = std::make_unique<QuicPacketProtectionV2>();
hkdf_ctxs.initialized = true;
}

static HkdfCtx hkdf_ctxs;
Expand All @@ -404,28 +452,34 @@ hilti::rt::Bytes QUIC_decrypt_crypto_payload(const hilti::rt::integer::safe<uint
const hilti::rt::integer::safe<uint64_t>& encrypted_offset,
const hilti::rt::integer::safe<uint64_t>& payload_length,
const hilti::rt::Bool& from_client) {
static bool initialized = false;
if ( ! initialized ) {
QuicPacketProtectionV1::Initialize();
QuicPacketProtectionV2::Initialize();
initialized = true;
}

if ( payload_length < 20 )
throw hilti::rt::RuntimeError(hilti::rt::fmt("payload too small %ld < 20", payload_length));

if ( (all_data.size() < encrypted_offset + payload_length) )
throw hilti::rt::RuntimeError(
hilti::rt::fmt("packet too small %ld %ld", all_data.size(), encrypted_offset + payload_length));

uint32_t v = version;
QuicPacketProtection* qpp = nullptr;

if ( version == 0x00000001 ) { // quicv1
QuicPacketProtectionV1::Initialize();
if ( QuicPacketProtectionV1::instance->Supports(v) ) {
qpp = QuicPacketProtectionV1::instance.get();
}
else if ( version == 0x6b3343cf ) { // quicv2
QuicPacketProtectionV2::Initialize();
else if ( QuicPacketProtectionV2::instance->Supports(v) ) {
qpp = QuicPacketProtectionV2::instance.get();
}
else {
throw hilti::rt::RuntimeError(hilti::rt::fmt("unable to handle version %lx", version));
throw hilti::rt::RuntimeError(hilti::rt::fmt("unable to decrypt QUIC version 0x%lx", version));
}

const auto& secret = qpp->GetSecret(from_client, connection_id);
const auto& secret = qpp->GetSecret(from_client, v, connection_id);
std::vector<uint8_t> key = qpp->GetKey(secret);
std::vector<uint8_t> iv = qpp->GetIv(secret);
std::vector<uint8_t> hp = qpp->GetHp(secret);
Expand Down
19 changes: 19 additions & 0 deletions scripts/consts.zeek
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,24 @@ export {
const version_strings: table[count] of string = {
[0x00000001] = "1",
[0x6b3343cf] = "quicv2",
[0xff000016] = "draft-22",
[0xff000017] = "draft-23",
[0xff000018] = "draft-24",
[0xff000019] = "draft-25",
[0xff00001a] = "draft-26",
[0xff00001b] = "draft-27",
[0xff00001c] = "draft-28",
[0xff00001d] = "draft-29",
[0xff00001e] = "draft-30",
[0xff00001f] = "draft-30",
[0xff000020] = "draft-32",
[0xff000021] = "draft-33",
[0xff000022] = "draft-34",
[0xfaceb001] = "mvfst (faceb001)",
[0xfaceb002] = "mvfst (faceb002)",
[0xfaceb00e] = "mvfst (faceb00e)",
[0xfaceb011] = "mvfst (faceb011)",
[0xfaceb012] = "mvfst (faceb012)",
[0xfaceb013] = "mvfst (faceb013)",
} &default=function(version: count): string { return fmt("unknown-%x", version); };
}

0 comments on commit 164d580

Please sign in to comment.