2021-08-23 11:15:11 +00:00
|
|
|
/**
|
|
|
|
|
* parser-quic.c
|
|
|
|
|
*
|
|
|
|
|
* Created on 2020-11-26
|
|
|
|
|
* @author: qyc
|
|
|
|
|
*
|
|
|
|
|
* @explain: QUIC解析
|
|
|
|
|
*/
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "parser-quic.h"
|
|
|
|
|
#include "wsgcrypt.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
#include "pint.h"
|
|
|
|
|
#include "gcrypt.h"
|
|
|
|
|
|
|
|
|
|
// #define DEBUG_PARSER_QUIC
|
|
|
|
|
|
|
|
|
|
int gcry_init()
|
|
|
|
|
{
|
|
|
|
|
//const char * tmp = gcry_check_version("1.8.7");
|
|
|
|
|
//gcry_control(GCRYCTL_SET_THREAD_CBS,&gcry_threads_pthread);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define QUIC_LPT_INITIAL 0x0
|
|
|
|
|
#define QUIC_LPT_0RTT 0x1
|
|
|
|
|
#define QUIC_LPT_HANDSHAKE 0x2
|
|
|
|
|
#define QUIC_LPT_RETRY 0x3
|
|
|
|
|
/* Version Negotiation packets don't have any real packet type */
|
|
|
|
|
#define QUIC_LPT_VER_NEG 0xfe
|
|
|
|
|
/* dummy value that is definitely not LPT */
|
|
|
|
|
#define QUIC_SHORT_PACKET 0xff
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Although the QUIC SCID/DCID length field can store at most 255, v1 limits the
|
|
|
|
|
* CID length to 20.
|
|
|
|
|
*/
|
|
|
|
|
#define QUIC_MAX_CID_LENGTH 20
|
|
|
|
|
typedef struct _quic_cid {
|
|
|
|
|
unsigned char len;
|
|
|
|
|
unsigned char cid[QUIC_MAX_CID_LENGTH];
|
|
|
|
|
} quic_cid_t;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* PROTECTED PAYLOAD DECRYPTION (done in first pass)
|
|
|
|
|
*
|
|
|
|
|
* Long packet types always use a single cipher depending on packet type.
|
|
|
|
|
* Short packet types always use 1-RTT secrets for packet protection (pp).
|
|
|
|
|
*
|
|
|
|
|
* Considerations:
|
|
|
|
|
* - QUIC packets might appear out-of-order (short packets before handshake
|
|
|
|
|
* message is captured), lost or retransmitted/duplicated.
|
|
|
|
|
* - During live capture, keys might not be immediately be available. 1-RTT
|
|
|
|
|
* client keys will be ready while client proceses Server Hello (Handshake).
|
|
|
|
|
* 1-RTT server keys will be ready while server creates Handshake message in
|
|
|
|
|
* response to Initial Handshake.
|
|
|
|
|
* - So delay cipher creation until first short packet is received.
|
|
|
|
|
*
|
|
|
|
|
* Required input from TLS dissector: TLS-Exporter 0-RTT/1-RTT secrets and
|
|
|
|
|
* cipher/hash algorithms.
|
|
|
|
|
*
|
|
|
|
|
* QUIC payload decryption requires proper reconstruction of the packet number
|
|
|
|
|
* which requires proper header decryption. The different states are:
|
|
|
|
|
*
|
|
|
|
|
* Packet type Packet number space Secrets
|
|
|
|
|
* Long: Initial Initial Initial secrets
|
|
|
|
|
* Long: Handshake Handshake Handshake
|
|
|
|
|
* Long: 0-RTT 0/1-RTT (appdata) 0-RTT
|
|
|
|
|
* Short header 0/1-RTT (appdata) 1-RTT (KP0 / KP1)
|
|
|
|
|
*
|
|
|
|
|
* Important to note is that Short Header decryption requires TWO ciphers (one
|
|
|
|
|
* for each key phase), but that header protection uses only KP0. Total state
|
|
|
|
|
* needed for each peer (client and server):
|
|
|
|
|
* - 3 packet number spaces: Initial, Handshake, 0/1-RTT (appdata).
|
|
|
|
|
* - 4 header protection ciphers: initial, 0-RTT, HS, 1-RTT.
|
|
|
|
|
* - 5 payload protection ciphers: initial, 0-RTT, HS, 1-RTT (KP0), 1-RTT (KP1).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef struct _quic_decrypt_result {
|
|
|
|
|
// Error message or NULL for success.
|
|
|
|
|
const guchar *error;
|
|
|
|
|
// Decrypted result on success (file-scoped).
|
|
|
|
|
const guint8 *data;
|
|
|
|
|
// Size of decrypted data.
|
|
|
|
|
guint data_len;
|
|
|
|
|
} quic_decrypt_result_t;
|
|
|
|
|
|
|
|
|
|
/** QUIC decryption context. */
|
|
|
|
|
|
|
|
|
|
typedef struct _quic_hp_cipher {
|
|
|
|
|
// Header protection cipher.
|
|
|
|
|
gcry_cipher_hd_t hp_cipher;
|
|
|
|
|
} quic_hp_cipher;
|
|
|
|
|
|
|
|
|
|
typedef struct _quic_pp_cipher {
|
|
|
|
|
// Packet protection cipher.
|
|
|
|
|
gcry_cipher_hd_t pp_cipher;
|
|
|
|
|
guint8 pp_iv[TLS13_AEAD_NONCE_LENGTH];
|
|
|
|
|
} quic_pp_cipher;
|
|
|
|
|
|
|
|
|
|
typedef struct _quic_ciphers {
|
|
|
|
|
quic_hp_cipher hp_cipher;
|
|
|
|
|
quic_pp_cipher pp_cipher;
|
|
|
|
|
} quic_ciphers;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* State for a single QUIC connection, identified by one or more Destination
|
|
|
|
|
* Connection IDs (DCID).
|
|
|
|
|
*/
|
|
|
|
|
typedef struct _quic_info_data {
|
|
|
|
|
guint32 version;
|
|
|
|
|
quic_ciphers client_initial_ciphers;
|
|
|
|
|
quic_ciphers server_initial_ciphers;
|
|
|
|
|
// Packet number spaces for Initial, Handshake and appdata.
|
|
|
|
|
guint64 max_client_pkn[3];
|
|
|
|
|
guint64 max_server_pkn[3];
|
|
|
|
|
} quic_info_data_t;
|
|
|
|
|
|
|
|
|
|
/** Per-packet information about QUIC, populated on the first pass. */
|
|
|
|
|
typedef struct _quic_packet_info {
|
|
|
|
|
// Reconstructed full packet number.
|
|
|
|
|
guint64 packet_number;
|
|
|
|
|
quic_decrypt_result_t decryption;
|
|
|
|
|
// Length of PKN (1/2/3/4) or unknown (0).
|
|
|
|
|
guint8 pkn_len;
|
|
|
|
|
// Decrypted flag byte, valid only if pkn_len is non-zero.
|
|
|
|
|
guint8 first_byte;
|
|
|
|
|
} quic_packet_info_t;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Given a QUIC message (header + non-empty payload), the actual packet number,
|
|
|
|
|
* try to decrypt it using the PP cipher.
|
|
|
|
|
* As the header points to the original buffer with an encrypted packet number,
|
|
|
|
|
* the (encrypted) packet number length is also included.
|
|
|
|
|
*
|
|
|
|
|
* The actual packet number must be constructed according to
|
|
|
|
|
* https://tools.ietf.org/html/draft-ietf-quic-transport-22#section-12.3
|
|
|
|
|
*/
|
|
|
|
|
static void quic_decrypt_message(quic_pp_cipher *pp_cipher, const char *payload, guint length, guint header_length,
|
|
|
|
|
guint8 first_byte, guint pkn_len, guint64 packet_number, quic_decrypt_result_t *result)
|
|
|
|
|
{
|
|
|
|
|
gcry_error_t err;
|
|
|
|
|
guint8 *header;
|
|
|
|
|
guint8 nonce[TLS13_AEAD_NONCE_LENGTH];
|
|
|
|
|
guint8 *buffer;
|
|
|
|
|
guint8 atag[16];
|
|
|
|
|
guint buffer_length;
|
|
|
|
|
const guchar **error = &result->error;
|
|
|
|
|
|
|
|
|
|
g_assert(pp_cipher != NULL);
|
|
|
|
|
g_assert(pp_cipher->pp_cipher != NULL);
|
|
|
|
|
g_assert(pkn_len < header_length);
|
|
|
|
|
g_assert(1 <= pkn_len && pkn_len <= 4);
|
|
|
|
|
// copy header, but replace encrypted first byte and PKN by plaintext.
|
|
|
|
|
header = (guint8 *)g_malloc(header_length);
|
|
|
|
|
memcpy(header, payload, header_length);
|
|
|
|
|
header[0] = first_byte;
|
|
|
|
|
guint i;
|
|
|
|
|
for (i = 0; i < pkn_len; i++)
|
|
|
|
|
header[header_length - 1 - i] = (guint8)(packet_number >> (8 * i));
|
|
|
|
|
|
|
|
|
|
// Input is "header || ciphertext (buffer) || auth tag (16 bytes)"
|
|
|
|
|
// buffer_length = length - (header_length + 16);
|
|
|
|
|
// buffer_length = 297 - (2 + 16);
|
|
|
|
|
buffer_length = length - (pkn_len + 16);
|
|
|
|
|
if (buffer_length == 0) {
|
|
|
|
|
*error = (const guchar *)"Decryption not possible, ciphertext is too short";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
buffer = (guint8 *)g_malloc(buffer_length);
|
|
|
|
|
memcpy(buffer, payload + header_length, buffer_length);
|
|
|
|
|
memcpy(atag, payload + header_length + buffer_length, 16);
|
|
|
|
|
|
|
|
|
|
memcpy(nonce, pp_cipher->pp_iv, TLS13_AEAD_NONCE_LENGTH);
|
|
|
|
|
// Packet number is left-padded with zeroes and XORed with write_iv
|
|
|
|
|
phton64(nonce + sizeof(nonce) - 8, pntoh64(nonce + sizeof(nonce) - 8) ^ packet_number);
|
|
|
|
|
|
|
|
|
|
gcry_cipher_reset(pp_cipher->pp_cipher);
|
|
|
|
|
err = gcry_cipher_setiv(pp_cipher->pp_cipher, nonce, TLS13_AEAD_NONCE_LENGTH);
|
|
|
|
|
if (err) {
|
|
|
|
|
//printf("Decryption (setiv) failed: %s\n", gcry_strerror(err));
|
|
|
|
|
*error = (const guchar *)"Decryption (setiv) failed";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// associated data (A) is the contents of QUIC header
|
|
|
|
|
err = gcry_cipher_authenticate(pp_cipher->pp_cipher, header, header_length);
|
|
|
|
|
if (err) {
|
|
|
|
|
//printf("Decryption (authenticate) failed: %s\n", gcry_strerror(err));
|
|
|
|
|
*error = (const guchar *)"Decryption (authenticate) failed";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Output ciphertext (C)
|
|
|
|
|
err = gcry_cipher_decrypt(pp_cipher->pp_cipher, buffer, buffer_length, NULL, 0);
|
|
|
|
|
if (err) {
|
|
|
|
|
//printf("Decryption (decrypt) failed: %s\n", gcry_strerror(err));
|
|
|
|
|
*error = (const guchar *)"Decryption (decrypt) failed";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = gcry_cipher_checktag(pp_cipher->pp_cipher, atag, 16);
|
|
|
|
|
if (err) {
|
|
|
|
|
//printf("Decryption (checktag) failed: %s\n", gcry_strerror(err));
|
|
|
|
|
*error = (const guchar *)"Decryption (checktag) failed";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free(header);
|
|
|
|
|
|
|
|
|
|
result->error = NULL;
|
|
|
|
|
result->data = buffer;
|
|
|
|
|
result->data_len = buffer_length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean quic_is_pp_cipher_initialized(quic_pp_cipher *pp_cipher)
|
|
|
|
|
{
|
|
|
|
|
return pp_cipher && pp_cipher->pp_cipher;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Process (protected) payload, adding the encrypted payload to the tree. If
|
|
|
|
|
* decryption is possible, frame dissection is also attempted.
|
|
|
|
|
*
|
|
|
|
|
* The given offset must correspond to the end of the QUIC header and begin of
|
|
|
|
|
* the (protected) payload. Dissected frames are appended to "tree" and expert
|
|
|
|
|
* info is attached to "ti" (the field with the encrypted payload).
|
|
|
|
|
*/
|
|
|
|
|
static void quic_process_payload(const char *payload, guint length, guint offset, quic_info_data_t *quic_info,
|
|
|
|
|
quic_packet_info_t *quic_packet, gboolean from_server, quic_pp_cipher *pp_cipher, guint8 first_byte, guint pkn_len)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If no decryption error has occurred yet, try decryption on the first
|
|
|
|
|
* pass and store the result for later use.
|
|
|
|
|
*/
|
|
|
|
|
if (quic_is_pp_cipher_initialized(pp_cipher))
|
|
|
|
|
quic_decrypt_message(pp_cipher, payload, length, offset, first_byte, pkn_len, quic_packet->packet_number, &quic_packet->decryption);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Inspired from ngtcp2 */
|
|
|
|
|
static guint64 quic_pkt_adjust_pkt_num(guint64 max_pkt_num, guint64 pkt_num, size_t n)
|
|
|
|
|
{
|
|
|
|
|
guint64 k = max_pkt_num == G_MAXUINT64 ? max_pkt_num : max_pkt_num + 1;
|
|
|
|
|
guint64 u = k & ~((G_GUINT64_CONSTANT(1) << n) - 1);
|
|
|
|
|
guint64 a = u | pkt_num;
|
|
|
|
|
guint64 b = (u + (G_GUINT64_CONSTANT(1) << n)) | pkt_num;
|
|
|
|
|
guint64 a1 = k < a ? a - k : k - a;
|
|
|
|
|
guint64 b1 = k < b ? b - k : k - b;
|
|
|
|
|
|
|
|
|
|
if (a1 < b1)
|
|
|
|
|
return a;
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Retrieve the maximum valid packet number space for a peer.
|
|
|
|
|
*/
|
|
|
|
|
static guint64 *quic_max_packet_number(quic_info_data_t *quic_info, gboolean from_server, guint8 first_byte)
|
|
|
|
|
{
|
|
|
|
|
int pkn_space;
|
|
|
|
|
if ((first_byte & 0x80) && (first_byte & 0x30) >> 4 == QUIC_LPT_INITIAL)
|
|
|
|
|
// Long header, Initial
|
|
|
|
|
pkn_space = 0;
|
|
|
|
|
else if ((first_byte & 0x80) && (first_byte & 0x30) >> 4 == QUIC_LPT_HANDSHAKE)
|
|
|
|
|
// Long header, Handshake
|
|
|
|
|
pkn_space = 1;
|
|
|
|
|
else
|
|
|
|
|
// Long header (0-RTT) or Short Header (1-RTT appdata).
|
|
|
|
|
pkn_space = 2;
|
|
|
|
|
if (from_server)
|
|
|
|
|
return &quic_info->max_server_pkn[pkn_space];
|
|
|
|
|
else
|
|
|
|
|
return &quic_info->max_client_pkn[pkn_space];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Calculate the full packet number and store it for later use.
|
|
|
|
|
*/
|
|
|
|
|
static void quic_set_full_packet_number(quic_info_data_t *quic_info, quic_packet_info_t *quic_packet, gboolean from_server, guint8 first_byte, guint32 pkn32)
|
|
|
|
|
{
|
|
|
|
|
guint pkn_len = (first_byte & 3) + 1;
|
|
|
|
|
guint64 pkn_full;
|
|
|
|
|
guint64 max_pn = *quic_max_packet_number(quic_info, from_server, first_byte);
|
|
|
|
|
|
|
|
|
|
// Sequential first pass, try to reconstruct full packet number.
|
|
|
|
|
pkn_full = quic_pkt_adjust_pkt_num(max_pn, pkn32, 8 * pkn_len);
|
|
|
|
|
quic_packet->pkn_len = pkn_len;
|
|
|
|
|
quic_packet->packet_number = pkn_full;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Given a header protection cipher, a buffer and the packet number offset,
|
|
|
|
|
* return the unmasked first byte and packet number.
|
|
|
|
|
*/
|
|
|
|
|
static gboolean quic_decrypt_header(const char *payload, guint pn_offset, quic_hp_cipher *hp_cipher, int hp_cipher_algo, guint8 *first_byte, guint32 *pn)
|
|
|
|
|
{
|
|
|
|
|
if (!hp_cipher->hp_cipher)
|
|
|
|
|
// need to know the cipher.
|
|
|
|
|
return FALSE;
|
|
|
|
|
gcry_cipher_hd_t h = hp_cipher->hp_cipher;
|
|
|
|
|
|
|
|
|
|
// Sample is always 16 bytes and starts after PKN (assuming length 4).
|
|
|
|
|
// https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.2
|
|
|
|
|
guint8 sample[16];
|
|
|
|
|
memcpy(sample, payload + pn_offset + 4, 16);
|
|
|
|
|
|
|
|
|
|
guint8 mask[5] = { 0 };
|
|
|
|
|
switch (hp_cipher_algo) {
|
|
|
|
|
case GCRY_CIPHER_AES128:
|
|
|
|
|
case GCRY_CIPHER_AES256:
|
|
|
|
|
// Encrypt in-place with AES-ECB and extract the mask.
|
|
|
|
|
if (gcry_cipher_encrypt(h, sample, sizeof(sample), NULL, 0))
|
|
|
|
|
return FALSE;
|
|
|
|
|
memcpy(mask, sample, sizeof(mask));
|
|
|
|
|
break;
|
|
|
|
|
#ifdef HAVE_LIBGCRYPT_CHACHA20
|
|
|
|
|
case GCRY_CIPHER_CHACHA20:
|
|
|
|
|
// If Gcrypt receives a 16 byte IV, it will assume the buffer to be
|
|
|
|
|
// counter || nonce (in little endian), as desired. */
|
|
|
|
|
if (gcry_cipher_setiv(h, sample, 16))
|
|
|
|
|
return FALSE;
|
|
|
|
|
// Apply ChaCha20, encrypt in-place five zero bytes.
|
|
|
|
|
if (gcry_cipher_encrypt(h, mask, sizeof(mask), NULL, 0))
|
|
|
|
|
return FALSE;
|
|
|
|
|
break;
|
|
|
|
|
#endif // HAVE_LIBGCRYPT_CHACHA20
|
|
|
|
|
default:
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.1
|
|
|
|
|
guint8 packet0 = payload[0];
|
|
|
|
|
if ((packet0 & 0x80) == 0x80)
|
|
|
|
|
// Long header: 4 bits masked
|
|
|
|
|
packet0 ^= mask[0] & 0x0f;
|
|
|
|
|
else
|
|
|
|
|
// Short header: 5 bits masked
|
|
|
|
|
packet0 ^= mask[0] & 0x1f;
|
|
|
|
|
guint pkn_len = (packet0 & 0x03) + 1;
|
|
|
|
|
|
|
|
|
|
guint8 pkn_bytes[4];
|
|
|
|
|
memcpy(pkn_bytes, payload + pn_offset, pkn_len);
|
|
|
|
|
guint32 pkt_pkn = 0;
|
|
|
|
|
guint i;
|
|
|
|
|
for (i = 0; i < pkn_len; i++)
|
|
|
|
|
pkt_pkn |= (pkn_bytes[i] ^ mask[1 + i]) << (8 * (pkn_len - 1 - i));
|
|
|
|
|
*first_byte = packet0;
|
|
|
|
|
*pn = pkt_pkn;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean quic_hkdf_expand_label(int hash_algo, guint8 *secret, guint secret_len, const char *label, guint8 *out, guint out_len)
|
|
|
|
|
{
|
|
|
|
|
const StringInfo secret_si = { secret, secret_len };
|
|
|
|
|
guchar *out_mem = NULL;
|
|
|
|
|
|
|
|
|
|
if (tls13_hkdf_expand_label(hash_algo, &secret_si, "tls13 ", label, out_len, &out_mem)) {
|
|
|
|
|
memcpy(out, out_mem, out_len);
|
|
|
|
|
g_free(out_mem);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Expands the secret (length MUST be the same as the "hash_algo" digest size)
|
|
|
|
|
* and initialize cipher with the new key.
|
|
|
|
|
*/
|
|
|
|
|
static gboolean quic_hp_cipher_init(quic_hp_cipher *hp_cipher, int hash_algo, guint8 key_length, guint8 *secret)
|
|
|
|
|
{
|
|
|
|
|
guchar hp_key[256/8];
|
|
|
|
|
guint hash_len = gcry_md_get_algo_dlen(hash_algo);
|
|
|
|
|
|
|
|
|
|
if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic hp", hp_key, key_length))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return gcry_cipher_setkey(hp_cipher->hp_cipher, hp_key, key_length) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean quic_pp_cipher_init(quic_pp_cipher *pp_cipher, int hash_algo, guint8 key_length, guint8 *secret)
|
|
|
|
|
{
|
|
|
|
|
// Maximum key size is for AES256 cipher.
|
|
|
|
|
guchar write_key[256/8];
|
|
|
|
|
guint hash_len = gcry_md_get_algo_dlen(hash_algo);
|
|
|
|
|
|
|
|
|
|
if (key_length > sizeof(write_key))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic key", write_key, key_length) ||
|
|
|
|
|
!quic_hkdf_expand_label(hash_algo, secret, hash_len, "quic iv", pp_cipher->pp_iv, sizeof(pp_cipher->pp_iv)))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return gcry_cipher_setkey(pp_cipher->pp_cipher, write_key, key_length) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void quic_hp_cipher_reset(quic_hp_cipher *hp_cipher)
|
|
|
|
|
{
|
|
|
|
|
gcry_cipher_close(hp_cipher->hp_cipher);
|
|
|
|
|
memset(hp_cipher, 0, sizeof(*hp_cipher));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void quic_pp_cipher_reset(quic_pp_cipher *pp_cipher)
|
|
|
|
|
{
|
|
|
|
|
gcry_cipher_close(pp_cipher->pp_cipher);
|
|
|
|
|
memset(pp_cipher, 0, sizeof(*pp_cipher));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Maps a Packet Protection cipher to the Packet Number protection cipher.
|
|
|
|
|
* See https://tools.ietf.org/html/draft-ietf-quic-tls-22#section-5.4.3
|
|
|
|
|
*/
|
|
|
|
|
static gboolean quic_get_pn_cipher_algo(int cipher_algo, int *hp_cipher_mode)
|
|
|
|
|
{
|
|
|
|
|
switch (cipher_algo) {
|
|
|
|
|
case GCRY_CIPHER_AES128:
|
|
|
|
|
case GCRY_CIPHER_AES256:
|
|
|
|
|
*hp_cipher_mode = GCRY_CIPHER_MODE_ECB;
|
|
|
|
|
return TRUE;
|
|
|
|
|
#ifdef HAVE_LIBGCRYPT_CHACHA20
|
|
|
|
|
case GCRY_CIPHER_CHACHA20:
|
|
|
|
|
*hp_cipher_mode = GCRY_CIPHER_MODE_STREAM;
|
|
|
|
|
return TRUE;
|
|
|
|
|
#endif // HAVE_LIBGCRYPT_CHACHA20
|
|
|
|
|
default:
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (Re)initialize the PNE/PP ciphers using the given cipher algorithm.
|
|
|
|
|
* If the optional base secret is given, then its length MUST match the hash
|
|
|
|
|
* algorithm output.
|
|
|
|
|
*/
|
|
|
|
|
static gboolean quic_hp_cipher_prepare(quic_hp_cipher *hp_cipher, int hash_algo, int cipher_algo, guint8 *secret, const char **error)
|
|
|
|
|
{
|
|
|
|
|
// Clear previous state (if any).
|
|
|
|
|
quic_hp_cipher_reset(hp_cipher);
|
|
|
|
|
|
|
|
|
|
int hp_cipher_mode;
|
|
|
|
|
if (!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) {
|
|
|
|
|
*error = "Unsupported cipher algorithm";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gcry_cipher_open(&hp_cipher->hp_cipher, cipher_algo, hp_cipher_mode, 0)) {
|
|
|
|
|
quic_hp_cipher_reset(hp_cipher);
|
|
|
|
|
*error = "Failed to create HP cipher";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (secret) {
|
|
|
|
|
guint cipher_keylen = (guint8)gcry_cipher_get_algo_keylen(cipher_algo);
|
|
|
|
|
if (!quic_hp_cipher_init(hp_cipher, hash_algo, cipher_keylen, secret)) {
|
|
|
|
|
quic_hp_cipher_reset(hp_cipher);
|
|
|
|
|
*error = "Failed to derive key material for HP cipher";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean quic_pp_cipher_prepare(quic_pp_cipher *pp_cipher, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error)
|
|
|
|
|
{
|
|
|
|
|
// Clear previous state (if any).
|
|
|
|
|
quic_pp_cipher_reset(pp_cipher);
|
|
|
|
|
|
|
|
|
|
int hp_cipher_mode;
|
|
|
|
|
if (!quic_get_pn_cipher_algo(cipher_algo, &hp_cipher_mode)) {
|
|
|
|
|
*error = "Unsupported cipher algorithm";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gcry_cipher_open(&pp_cipher->pp_cipher, cipher_algo,cipher_mode, 0)) {
|
|
|
|
|
quic_pp_cipher_reset(pp_cipher);
|
|
|
|
|
*error = "Failed to create PP cipher";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (secret) {
|
|
|
|
|
guint cipher_keylen = (guint8) gcry_cipher_get_algo_keylen(cipher_algo);
|
|
|
|
|
if (!quic_pp_cipher_init(pp_cipher, hash_algo, cipher_keylen, secret)) {
|
|
|
|
|
quic_pp_cipher_reset(pp_cipher);
|
|
|
|
|
*error = "Failed to derive key material for PP cipher";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean quic_ciphers_prepare(quic_ciphers *ciphers, int hash_algo, int cipher_algo, int cipher_mode, guint8 *secret, const char **error)
|
|
|
|
|
{
|
|
|
|
|
return quic_hp_cipher_prepare(&ciphers->hp_cipher, hash_algo, cipher_algo, secret, error) &&
|
|
|
|
|
quic_pp_cipher_prepare(&ciphers->pp_cipher, hash_algo, cipher_algo, cipher_mode, secret, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the QUIC draft version or 0 if not applicable. */
|
|
|
|
|
static inline guint8 quic_draft_version(guint32 version) {
|
|
|
|
|
if ((version >> 8) == 0xff0000)
|
|
|
|
|
return (guint8) version;
|
|
|
|
|
|
|
|
|
|
// Facebook mvfst, based on draft -22.
|
|
|
|
|
if (version == 0xfaceb001)
|
|
|
|
|
return 22;
|
|
|
|
|
|
|
|
|
|
// Facebook mvfst, based on draft -27.
|
|
|
|
|
if (version == 0xfaceb002 || version == 0xfaceb00e)
|
|
|
|
|
return 27;
|
|
|
|
|
|
|
|
|
|
// GQUIC Q050, T050 and T051: they are not really based on any drafts,
|
|
|
|
|
// but we must return a sensible value
|
|
|
|
|
if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531)
|
|
|
|
|
return 27;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* https://tools.ietf.org/html/draft-ietf-quic-transport-32#section-15
|
|
|
|
|
* "Versions that follow the pattern 0x?a?a?a?a are reserved for use in
|
|
|
|
|
* forcing version negotiation to be exercised"
|
|
|
|
|
* It is tricky to return a correct draft version: such number is primarly
|
|
|
|
|
* used to select a proper salt (which depends on the version itself), but
|
|
|
|
|
* we don't have a real version here! Let's hope that we need to handle
|
|
|
|
|
* only latest drafts...
|
|
|
|
|
*/
|
|
|
|
|
if ((version & 0x0F0F0F0F) == 0x0a0a0a0a)
|
|
|
|
|
return 29;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline gboolean is_quic_draft_max(guint32 version, guint8 max_version) {
|
|
|
|
|
guint8 draft_version = quic_draft_version(version);
|
|
|
|
|
return draft_version && draft_version <= max_version;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Compute the client and server initial secrets given Connection ID "cid".
|
|
|
|
|
*
|
|
|
|
|
* On success TRUE is returned and the two initial secrets are set.
|
|
|
|
|
* FALSE is returned on error (see "error" parameter for the reason).
|
|
|
|
|
*/
|
|
|
|
|
static gboolean quic_derive_initial_secrets(const quic_cid_t *cid, guint8 client_initial_secret[HASH_SHA2_256_LENGTH], guint8 server_initial_secret[HASH_SHA2_256_LENGTH], guint32 version, const gchar **error)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* https://tools.ietf.org/html/draft-ietf-quic-tls-29#section-5.2
|
|
|
|
|
*
|
|
|
|
|
* initial_salt = 0xafbfec289993d24c9e9786f19c6111e04390a899
|
|
|
|
|
* initial_secret = HKDF-Extract(initial_salt, client_dst_connection_id)
|
|
|
|
|
*
|
|
|
|
|
* client_initial_secret = HKDF-Expand-Label(initial_secret,
|
|
|
|
|
* "client in", "", Hash.length)
|
|
|
|
|
* server_initial_secret = HKDF-Expand-Label(initial_secret,
|
|
|
|
|
* "server in", "", Hash.length)
|
|
|
|
|
*
|
|
|
|
|
* Hash for handshake packets is SHA-256 (output size 32).
|
|
|
|
|
*/
|
|
|
|
|
static const guint8 handshake_salt_draft_22[20] = {
|
|
|
|
|
0x7f, 0xbc, 0xdb, 0x0e, 0x7c, 0x66, 0xbb, 0xe9, 0x19, 0x3a,
|
|
|
|
|
0x96, 0xcd, 0x21, 0x51, 0x9e, 0xbd, 0x7a, 0x02, 0x64, 0x4a
|
|
|
|
|
};
|
|
|
|
|
static const guint8 handshake_salt_draft_23[20] = {
|
|
|
|
|
0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7,
|
|
|
|
|
0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02,
|
|
|
|
|
};
|
|
|
|
|
static const guint8 handshake_salt_draft_29[20] = {
|
|
|
|
|
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97,
|
|
|
|
|
0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99
|
|
|
|
|
};
|
2021-08-26 13:19:01 +08:00
|
|
|
static const guint8 handshake_salt_v1[20] = {
|
|
|
|
|
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
|
|
|
|
|
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
|
|
|
|
|
};
|
2021-08-23 11:15:11 +00:00
|
|
|
static const guint8 hanshake_salt_draft_q50[20] = {
|
|
|
|
|
0x50, 0x45, 0x74, 0xEF, 0xD0, 0x66, 0xFE, 0x2F, 0x9D, 0x94,
|
|
|
|
|
0x5C, 0xFC, 0xDB, 0xD3, 0xA7, 0xF0, 0xD3, 0xB5, 0x6B, 0x45
|
|
|
|
|
};
|
|
|
|
|
static const guint8 hanshake_salt_draft_t50[20] = {
|
|
|
|
|
0x7f, 0xf5, 0x79, 0xe5, 0xac, 0xd0, 0x72, 0x91, 0x55, 0x80,
|
|
|
|
|
0x30, 0x4c, 0x43, 0xa2, 0x36, 0x7c, 0x60, 0x48, 0x83, 0x10
|
|
|
|
|
};
|
|
|
|
|
static const guint8 hanshake_salt_draft_t51[20] = {
|
|
|
|
|
0x7a, 0x4e, 0xde, 0xf4, 0xe7, 0xcc, 0xee, 0x5f, 0xa4, 0x50,
|
|
|
|
|
0x6c, 0x19, 0x12, 0x4f, 0xc8, 0xcc, 0xda, 0x6e, 0x03, 0x3d
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
gcry_error_t err;
|
|
|
|
|
guint8 secret[HASH_SHA2_256_LENGTH];
|
|
|
|
|
|
|
|
|
|
if (version == 0x51303530)
|
2021-08-26 13:19:01 +08:00
|
|
|
{
|
2021-08-23 11:15:11 +00:00
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_q50, sizeof(hanshake_salt_draft_q50), cid->cid, cid->len, secret);
|
2021-08-26 13:19:01 +08:00
|
|
|
}
|
2021-08-23 11:15:11 +00:00
|
|
|
else if (version == 0x54303530)
|
2021-08-26 13:19:01 +08:00
|
|
|
{
|
2021-08-23 11:15:11 +00:00
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t50, sizeof(hanshake_salt_draft_t50), cid->cid, cid->len, secret);
|
2021-08-26 13:19:01 +08:00
|
|
|
}
|
2021-08-23 11:15:11 +00:00
|
|
|
else if (version == 0x54303531)
|
2021-08-26 13:19:01 +08:00
|
|
|
{
|
2021-08-23 11:15:11 +00:00
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, hanshake_salt_draft_t51, sizeof(hanshake_salt_draft_t51), cid->cid, cid->len, secret);
|
2021-08-26 13:19:01 +08:00
|
|
|
}
|
2021-08-23 11:15:11 +00:00
|
|
|
else if (is_quic_draft_max(version, 22))
|
2021-08-26 13:19:01 +08:00
|
|
|
{
|
2021-08-23 11:15:11 +00:00
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_22, sizeof(handshake_salt_draft_22), cid->cid, cid->len, secret);
|
2021-08-26 13:19:01 +08:00
|
|
|
}
|
2021-08-23 11:15:11 +00:00
|
|
|
else if (is_quic_draft_max(version, 28))
|
2021-08-26 13:19:01 +08:00
|
|
|
{
|
2021-08-23 11:15:11 +00:00
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_23, sizeof(handshake_salt_draft_23), cid->cid, cid->len, secret);
|
2021-08-26 13:19:01 +08:00
|
|
|
}
|
|
|
|
|
else if (is_quic_draft_max(version, 32))
|
|
|
|
|
{
|
2021-08-23 11:15:11 +00:00
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_draft_29, sizeof(handshake_salt_draft_29), cid->cid, cid->len, secret);
|
2021-08-26 13:19:01 +08:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
err = hkdf_extract(GCRY_MD_SHA256, handshake_salt_v1, sizeof(handshake_salt_v1), cid->cid, cid->len, secret);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-23 11:15:11 +00:00
|
|
|
if (err) {
|
|
|
|
|
//printf("Failed to extract secrets: %s\n", gcry_strerror(err));
|
|
|
|
|
*error = "Failed to extract secrets";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "client in", client_initial_secret, HASH_SHA2_256_LENGTH)) {
|
|
|
|
|
*error = "Key expansion (client) failed";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!quic_hkdf_expand_label(GCRY_MD_SHA256, secret, sizeof(secret), "server in", server_initial_secret, HASH_SHA2_256_LENGTH)) {
|
|
|
|
|
*error = "Key expansion (server) failed";
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*error = NULL;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean quic_create_initial_decoders(const quic_cid_t *cid, const gchar **error, quic_info_data_t *quic_info)
|
|
|
|
|
{
|
|
|
|
|
unsigned char client_secret[HASH_SHA2_256_LENGTH];
|
|
|
|
|
unsigned char server_secret[HASH_SHA2_256_LENGTH];
|
|
|
|
|
|
|
|
|
|
if (!quic_derive_initial_secrets(cid, client_secret, server_secret, quic_info->version, error))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
// Packet numbers are protected with AES128-CTR,
|
|
|
|
|
// initial packets are protected with AEAD_AES_128_GCM.
|
|
|
|
|
if (!quic_ciphers_prepare(&quic_info->client_initial_ciphers, GCRY_MD_SHA256, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, client_secret, error))
|
|
|
|
|
{
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!quic_ciphers_prepare(&quic_info->server_initial_ciphers, GCRY_MD_SHA256, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, server_secret, error))
|
|
|
|
|
{
|
|
|
|
|
quic_hp_cipher_reset(&quic_info->client_initial_ciphers.hp_cipher);
|
|
|
|
|
quic_pp_cipher_reset(&quic_info->client_initial_ciphers.pp_cipher);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int quic_extract_header(const char *payload, unsigned char *long_packet_type, unsigned int *version, quic_cid_t *dcid, quic_cid_t *scid)
|
|
|
|
|
{
|
|
|
|
|
unsigned int offset = 0;
|
|
|
|
|
|
|
|
|
|
unsigned char packet_type = payload[offset];
|
|
|
|
|
unsigned char is_long_header = packet_type & 0x80;
|
|
|
|
|
if (is_long_header)
|
|
|
|
|
// long header form
|
|
|
|
|
*long_packet_type = (packet_type & 0x30) >> 4;
|
|
|
|
|
else
|
|
|
|
|
// short header form, store dummy value that is not a long packet type.
|
|
|
|
|
*long_packet_type = QUIC_SHORT_PACKET;
|
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
|
|
*version = pntoh32((unsigned int *)&payload[offset]);
|
|
|
|
|
|
|
|
|
|
if (is_long_header) {
|
|
|
|
|
// VN packets don't have any real packet type field,
|
|
|
|
|
// even if they have a long header: use a dummy value */
|
|
|
|
|
if (*version == 0x00000000)
|
|
|
|
|
*long_packet_type = QUIC_LPT_VER_NEG;
|
|
|
|
|
|
|
|
|
|
// skip version
|
|
|
|
|
offset += 4;
|
|
|
|
|
|
|
|
|
|
// read DCID and SCID (both are prefixed by a length byte).
|
|
|
|
|
unsigned char dcil = payload[offset];
|
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
|
|
if (dcil && dcil <= QUIC_MAX_CID_LENGTH) {
|
|
|
|
|
memcpy(dcid->cid, &payload[offset], dcil);
|
|
|
|
|
dcid->len = dcil;
|
|
|
|
|
}
|
|
|
|
|
offset += dcil;
|
|
|
|
|
|
|
|
|
|
unsigned char scil = payload[offset];
|
|
|
|
|
offset++;
|
|
|
|
|
|
|
|
|
|
if (scil && scil <= QUIC_MAX_CID_LENGTH) {
|
|
|
|
|
memcpy(scid->cid, &payload[offset], scil);
|
|
|
|
|
scid->len = scil;
|
|
|
|
|
}
|
|
|
|
|
offset += scil;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Definitely not draft -10, set version to dummy value.
|
|
|
|
|
*version = 0;
|
|
|
|
|
// For short headers, the DCID length is unknown and could be 0 or
|
|
|
|
|
// anything from 1 to 20 bytes. Copy the maximum possible and let the
|
|
|
|
|
// consumer truncate it as necessary.
|
|
|
|
|
memcpy(dcid->cid, &payload[offset], QUIC_MAX_CID_LENGTH);
|
|
|
|
|
dcid->len = QUIC_MAX_CID_LENGTH;
|
|
|
|
|
offset += QUIC_MAX_CID_LENGTH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int dissect_quic(const char *payload, unsigned int length, unsigned char *out, unsigned int *out_length)
|
|
|
|
|
{
|
|
|
|
|
guint offset = 0;
|
|
|
|
|
quic_packet_info_t quic_packet;
|
|
|
|
|
quic_info_data_t conn;
|
|
|
|
|
unsigned char long_packet_type;
|
|
|
|
|
quic_cid_t dcid = {.len=0}, scid = {.len=0};
|
|
|
|
|
guint64 token_length, payload_length;
|
|
|
|
|
const char *error = NULL;
|
|
|
|
|
guint8 first_byte = 0;
|
|
|
|
|
const gboolean from_server = FALSE;
|
|
|
|
|
quic_ciphers *ciphers = NULL;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
memset(&quic_packet, 0, sizeof(quic_packet_info_t));
|
|
|
|
|
memset(&conn, 0, sizeof(quic_info_data_t));
|
|
|
|
|
|
|
|
|
|
ret = quic_extract_header(payload, &long_packet_type, &conn.version, &dcid, &scid);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (long_packet_type == QUIC_LPT_INITIAL)
|
|
|
|
|
{
|
|
|
|
|
// Create new decryption context based on the Client Connection ID
|
|
|
|
|
// from the *very first* Client Initial packet.
|
|
|
|
|
quic_create_initial_decoders(&dcid, &error, &conn);
|
|
|
|
|
if (!error)
|
|
|
|
|
{
|
|
|
|
|
guint32 pkn32 = 0;
|
|
|
|
|
// PKN is after type(1) + version(4) + DCIL+DCID + SCIL+SCID
|
|
|
|
|
guint pn_offset = 1 + 4 + 1 + dcid.len + 1 + scid.len;
|
|
|
|
|
pn_offset += tvb_get_varint(payload, pn_offset, 8, &token_length, ENC_VARINT_QUIC);
|
|
|
|
|
pn_offset += (guint)token_length;
|
|
|
|
|
// printf("%d\n", token_length);
|
|
|
|
|
|
|
|
|
|
pn_offset += tvb_get_varint(payload, pn_offset, 8, &payload_length, ENC_VARINT_QUIC);
|
|
|
|
|
// printf("%d\n", payload_length);
|
|
|
|
|
|
|
|
|
|
// Assume failure unless proven otherwise.
|
|
|
|
|
ciphers = &conn.client_initial_ciphers;
|
|
|
|
|
error = "Header deprotection failed";
|
|
|
|
|
if (quic_decrypt_header(payload, pn_offset, &ciphers->hp_cipher, GCRY_CIPHER_AES128, &first_byte, &pkn32))
|
|
|
|
|
error = NULL;
|
|
|
|
|
if (!error) {
|
|
|
|
|
quic_set_full_packet_number(&conn, &quic_packet, from_server, first_byte, pkn32);
|
|
|
|
|
quic_packet.first_byte = first_byte;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Payload
|
|
|
|
|
// skip type(1) + version(4) + DCIL+DCID + SCIL+SCID + len_token_length + token_length + len_payload_length + len_packet_number
|
|
|
|
|
offset = pn_offset + quic_packet.pkn_len;
|
|
|
|
|
//quic_process_payload(payload, length, offset, &conn, &quic_packet, from_server, &ciphers->pp_cipher, first_byte, quic_packet.pkn_len);
|
|
|
|
|
quic_process_payload(payload, payload_length, offset, &conn, &quic_packet, from_server, &ciphers->pp_cipher, first_byte, quic_packet.pkn_len);
|
|
|
|
|
|
|
|
|
|
// Out
|
|
|
|
|
if (!quic_packet.decryption.error)
|
|
|
|
|
{
|
|
|
|
|
memcpy(out, quic_packet.decryption.data, quic_packet.decryption.data_len);
|
|
|
|
|
*out_length = quic_packet.decryption.data_len;
|
|
|
|
|
|
|
|
|
|
g_free((gpointer)quic_packet.decryption.data);
|
|
|
|
|
quic_packet.decryption.data = NULL;
|
|
|
|
|
|
|
|
|
|
ret=1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret=0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quic_hp_cipher_reset(&conn.client_initial_ciphers.hp_cipher);
|
|
|
|
|
quic_pp_cipher_reset(&conn.client_initial_ciphers.pp_cipher);
|
|
|
|
|
quic_hp_cipher_reset(&conn.server_initial_ciphers.hp_cipher);
|
|
|
|
|
quic_pp_cipher_reset(&conn.server_initial_ciphers.pp_cipher);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|