mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-22 14:21:25 +02:00
This bug could be reproduced with -dMfail option and detected by libasan. During the TLS secrets allocations, when failed, quic_tls_ctx_secs_free() is called. It resets the already initialized secrets. Some were detected as initialized when not, or with a non initialized length, which leads to big "memset(0)" detected by libsasan. Ensure that all the secrets are really initialized with correct lengths. No need to be backported.
1055 lines
31 KiB
C
1055 lines
31 KiB
C
/*
|
|
* include/proto/quic_tls.h
|
|
* This file provides definitions for QUIC-TLS.
|
|
*
|
|
* Copyright 2019 HAProxy Technologies, Frederic Lecaille <flecaille@haproxy.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#ifndef _PROTO_QUIC_TLS_H
|
|
#define _PROTO_QUIC_TLS_H
|
|
#ifdef USE_QUIC
|
|
#ifndef USE_OPENSSL
|
|
#error "Must define USE_OPENSSL"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <haproxy/dynbuf.h>
|
|
#include <haproxy/pool.h>
|
|
#include <haproxy/openssl-compat.h>
|
|
#include <haproxy/quic_conn.h>
|
|
#include <haproxy/quic_frame.h>
|
|
#include <haproxy/quic_tls-t.h>
|
|
#include <haproxy/quic_tx.h>
|
|
#include <haproxy/quic_trace.h>
|
|
#include <haproxy/trace.h>
|
|
|
|
int quic_tls_finalize(struct quic_conn *qc, int server);
|
|
void quic_tls_ctx_free(struct quic_tls_ctx **ctx);
|
|
void quic_pktns_release(struct quic_conn *qc, struct quic_pktns **pktns);
|
|
int qc_enc_level_alloc(struct quic_conn *qc, struct quic_pktns **pktns,
|
|
struct quic_enc_level **qel, enum ssl_encryption_level_t level);
|
|
void qc_enc_level_free(struct quic_conn *qc, struct quic_enc_level **qel);
|
|
|
|
void quic_tls_keys_hexdump(struct buffer *buf,
|
|
const struct quic_tls_secrets *secs);
|
|
void quic_tls_kp_keys_hexdump(struct buffer *buf,
|
|
const struct quic_tls_kp *kp);
|
|
|
|
void quic_conn_enc_level_uninit(struct quic_conn *qc, struct quic_enc_level *qel);
|
|
void quic_tls_secret_hexdump(struct buffer *buf,
|
|
const unsigned char *secret, size_t secret_len);
|
|
|
|
int quic_derive_initial_secret(const EVP_MD *md,
|
|
const unsigned char *initial_salt, size_t initial_salt_sz,
|
|
unsigned char *initial_secret, size_t initial_secret_sz,
|
|
const unsigned char *secret, size_t secret_sz);
|
|
|
|
int quic_tls_derive_initial_secrets(const EVP_MD *md,
|
|
unsigned char *rx, size_t rx_sz,
|
|
unsigned char *tx, size_t tx_sz,
|
|
const unsigned char *secret, size_t secret_sz,
|
|
int server);
|
|
|
|
int quic_tls_encrypt(unsigned char *buf, size_t len,
|
|
const unsigned char *aad, size_t aad_len,
|
|
EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
|
|
const unsigned char *iv);
|
|
|
|
int quic_tls_decrypt2(unsigned char *out,
|
|
unsigned char *in, size_t ilen,
|
|
unsigned char *aad, size_t aad_len,
|
|
EVP_CIPHER_CTX *ctx, const EVP_CIPHER *aead,
|
|
const unsigned char *key, const unsigned char *iv);
|
|
|
|
int quic_tls_decrypt(unsigned char *buf, size_t len,
|
|
unsigned char *aad, size_t aad_len,
|
|
EVP_CIPHER_CTX *tls_ctx, const EVP_CIPHER *aead,
|
|
const unsigned char *key, const unsigned char *iv);
|
|
|
|
int quic_tls_generate_retry_integrity_tag(unsigned char *odcid, unsigned char odcid_len,
|
|
unsigned char *buf, size_t len,
|
|
const struct quic_version *qv);
|
|
|
|
int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
|
|
const EVP_MD *md, const struct quic_version *qv,
|
|
unsigned char *key, size_t keylen,
|
|
unsigned char *iv, size_t ivlen,
|
|
unsigned char *hp_key, size_t hp_keylen,
|
|
const unsigned char *secret, size_t secretlen);
|
|
|
|
int quic_tls_derive_retry_token_secret(const EVP_MD *md,
|
|
unsigned char *key, size_t keylen,
|
|
unsigned char *iv, size_t ivlen,
|
|
const unsigned char *salt, size_t saltlen,
|
|
const unsigned char *secret, size_t secretlen);
|
|
|
|
int quic_hkdf_expand(const EVP_MD *md,
|
|
unsigned char *buf, size_t buflen,
|
|
const unsigned char *key, size_t keylen,
|
|
const unsigned char *label, size_t labellen);
|
|
|
|
int quic_hkdf_expand_label(const EVP_MD *md,
|
|
unsigned char *buf, size_t buflen,
|
|
const unsigned char *key, size_t keylen,
|
|
const unsigned char *label, size_t labellen);
|
|
|
|
int quic_hkdf_extract_and_expand(const EVP_MD *md,
|
|
unsigned char *buf, size_t buflen,
|
|
const unsigned char *key, size_t keylen,
|
|
const unsigned char *salt, size_t saltlen,
|
|
const unsigned char *label, size_t labellen);
|
|
|
|
int quic_tls_rx_ctx_init(EVP_CIPHER_CTX **rx_ctx,
|
|
const EVP_CIPHER *aead, unsigned char *key);
|
|
int quic_tls_tx_ctx_init(EVP_CIPHER_CTX **tx_ctx,
|
|
const EVP_CIPHER *aead, unsigned char *key);
|
|
|
|
int quic_tls_sec_update(const EVP_MD *md, const struct quic_version *qv,
|
|
unsigned char *new_sec, size_t new_seclen,
|
|
const unsigned char *sec, size_t seclen);
|
|
|
|
void quic_aead_iv_build(unsigned char *iv, size_t ivlen,
|
|
unsigned char *aead_iv, size_t aead_ivlen, uint64_t pn);
|
|
|
|
/* HP protection (AES) */
|
|
int quic_tls_dec_aes_ctx_init(EVP_CIPHER_CTX **aes_ctx,
|
|
const EVP_CIPHER *aes, unsigned char *key);
|
|
int quic_tls_enc_aes_ctx_init(EVP_CIPHER_CTX **aes_ctx,
|
|
const EVP_CIPHER *aes, unsigned char *key);
|
|
int quic_tls_aes_decrypt(unsigned char *out,
|
|
const unsigned char *in, size_t inlen,
|
|
EVP_CIPHER_CTX *ctx);
|
|
int quic_tls_aes_encrypt(unsigned char *out,
|
|
const unsigned char *in, size_t inlen,
|
|
EVP_CIPHER_CTX *ctx);
|
|
|
|
int quic_tls_key_update(struct quic_conn *qc);
|
|
void quic_tls_rotate_keys(struct quic_conn *qc);
|
|
|
|
static inline const EVP_CIPHER *tls_aead(const SSL_CIPHER *cipher)
|
|
{
|
|
switch (SSL_CIPHER_get_id(cipher)) {
|
|
case TLS1_3_CK_AES_128_GCM_SHA256:
|
|
return EVP_aes_128_gcm();
|
|
case TLS1_3_CK_AES_256_GCM_SHA384:
|
|
return EVP_aes_256_gcm();
|
|
#if !defined(OPENSSL_IS_AWSLC)
|
|
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
|
return EVP_chacha20_poly1305();
|
|
#endif
|
|
#if !defined(USE_OPENSSL_WOLFSSL) && !defined(OPENSSL_IS_AWSLC)
|
|
case TLS1_3_CK_AES_128_CCM_SHA256:
|
|
return EVP_aes_128_ccm();
|
|
#endif
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline const EVP_MD *tls_md(const SSL_CIPHER *cipher)
|
|
{
|
|
switch (SSL_CIPHER_get_id(cipher)) {
|
|
case TLS1_3_CK_AES_128_GCM_SHA256:
|
|
case TLS1_3_CK_AES_128_CCM_SHA256:
|
|
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
|
return EVP_sha256();
|
|
case TLS1_3_CK_AES_256_GCM_SHA384:
|
|
return EVP_sha384();
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline const EVP_CIPHER *tls_hp(const SSL_CIPHER *cipher)
|
|
{
|
|
switch (SSL_CIPHER_get_id(cipher)) {
|
|
#if !defined(OPENSSL_IS_AWSLC)
|
|
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
|
return EVP_chacha20();
|
|
#endif
|
|
case TLS1_3_CK_AES_128_CCM_SHA256:
|
|
case TLS1_3_CK_AES_128_GCM_SHA256:
|
|
return EVP_aes_128_ctr();
|
|
case TLS1_3_CK_AES_256_GCM_SHA384:
|
|
return EVP_aes_256_ctr();
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
/* These following functions map TLS implementation encryption level to ours */
|
|
static inline struct quic_pktns **ssl_to_quic_pktns(struct quic_conn *qc,
|
|
enum ssl_encryption_level_t level)
|
|
{
|
|
switch (level) {
|
|
case ssl_encryption_initial:
|
|
return &qc->ipktns;
|
|
case ssl_encryption_early_data:
|
|
return &qc->apktns;
|
|
case ssl_encryption_handshake:
|
|
return &qc->hpktns;
|
|
case ssl_encryption_application:
|
|
return &qc->apktns;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* These following functions map TLS implementation encryption level to ours */
|
|
static inline struct quic_pktns **qel_to_quic_pktns(struct quic_conn *qc,
|
|
enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return &qc->ipktns;
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
return &qc->apktns;
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return &qc->hpktns;
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return &qc->apktns;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Map <level> TLS stack encryption level to our internal QUIC TLS encryption level
|
|
* if succeded, or -1 if failed.
|
|
*/
|
|
static inline enum quic_tls_enc_level ssl_to_quic_enc_level(enum ssl_encryption_level_t level)
|
|
{
|
|
switch (level) {
|
|
case ssl_encryption_initial:
|
|
return QUIC_TLS_ENC_LEVEL_INITIAL;
|
|
case ssl_encryption_early_data:
|
|
return QUIC_TLS_ENC_LEVEL_EARLY_DATA;
|
|
case ssl_encryption_handshake:
|
|
return QUIC_TLS_ENC_LEVEL_HANDSHAKE;
|
|
case ssl_encryption_application:
|
|
return QUIC_TLS_ENC_LEVEL_APP;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Return the address of the QUIC TLS encryption level associated to <level> TLS
|
|
* stack encryption level and attached to <qc> QUIC connection if succeeded, or
|
|
* NULL if failed.
|
|
*/
|
|
static inline struct quic_enc_level **ssl_to_qel_addr(struct quic_conn *qc,
|
|
enum ssl_encryption_level_t level)
|
|
{
|
|
switch (level) {
|
|
case ssl_encryption_initial:
|
|
return &qc->iel;
|
|
case ssl_encryption_early_data:
|
|
return &qc->eel;
|
|
case ssl_encryption_handshake:
|
|
return &qc->hel;
|
|
case ssl_encryption_application:
|
|
return &qc->ael;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Return the address of the QUIC TLS encryption level associated to <level> internal
|
|
* encryption level and attached to <qc> QUIC connection if succeeded, or
|
|
* NULL if failed.
|
|
*/
|
|
static inline struct quic_enc_level **qel_to_qel_addr(struct quic_conn *qc,
|
|
enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return &qc->iel;
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
return &qc->eel;
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return &qc->hel;
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return &qc->ael;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Return the QUIC TLS encryption level associated to <level> internal encryption
|
|
* level attached to <qc> QUIC connection if succeeded, or NULL if failed.
|
|
*/
|
|
static inline struct quic_enc_level *qc_quic_enc_level(const struct quic_conn *qc,
|
|
enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return qc->iel;
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
return qc->eel;
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return qc->hel;
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return qc->ael;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* These two following functions map our encryption level to the TLS implementation ones. */
|
|
static inline enum ssl_encryption_level_t quic_to_ssl_enc_level(enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return ssl_encryption_initial;
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
return ssl_encryption_early_data;
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return ssl_encryption_handshake;
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return ssl_encryption_application;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Return a human readable string from <state> QUIC handshake state of NULL
|
|
* for unknown state values (for debug purpose).
|
|
*/
|
|
static inline char *quic_hdshk_state_str(const enum quic_handshake_state state)
|
|
{
|
|
switch (state) {
|
|
case QUIC_HS_ST_CLIENT_INITIAL:
|
|
return "CI";
|
|
case QUIC_HS_ST_CLIENT_HANDSHAKE:
|
|
return "CH";
|
|
case QUIC_HS_ST_CLIENT_HANDSHAKE_FAILED:
|
|
return "CF";
|
|
case QUIC_HS_ST_SERVER_INITIAL:
|
|
return "SI";
|
|
case QUIC_HS_ST_SERVER_HANDSHAKE:
|
|
return "SH";
|
|
case QUIC_HS_ST_SERVER_HANDSHAKE_FAILED:
|
|
return "SF";
|
|
case QUIC_HS_ST_COMPLETE:
|
|
return "HCP";
|
|
case QUIC_HS_ST_CONFIRMED:
|
|
return "HCF";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Return a human readable string from <err> SSL error (returned from
|
|
* SSL_get_error())
|
|
*/
|
|
static inline const char *ssl_error_str(int err)
|
|
{
|
|
switch (err) {
|
|
case SSL_ERROR_NONE:
|
|
return "NONE";
|
|
case SSL_ERROR_SSL:
|
|
return "SSL";
|
|
case SSL_ERROR_WANT_READ:
|
|
return "WANT_READ";
|
|
case SSL_ERROR_WANT_WRITE:
|
|
return "WANT_WRITE";
|
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
|
return "X509_LOOKUP";
|
|
case SSL_ERROR_SYSCALL:
|
|
return "SYSCALL";
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
return "ZERO_RETURN";
|
|
case SSL_ERROR_WANT_CONNECT:
|
|
return "WANT_CONNECT";
|
|
case SSL_ERROR_WANT_ACCEPT:
|
|
return "WANT_ACCEPT";
|
|
#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(USE_OPENSSL_WOLFSSL) && !defined(OPENSSL_IS_AWSLC)
|
|
case SSL_ERROR_WANT_ASYNC:
|
|
return "WANT_ASYNC";
|
|
case SSL_ERROR_WANT_ASYNC_JOB:
|
|
return "WANT_ASYNC_JOB";
|
|
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
|
|
return "WANT_CLIENT_HELLO_CB";
|
|
#endif
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
|
|
/* Return a character identifying the encryption level from <level> QUIC TLS
|
|
* encryption level (for debug purpose).
|
|
* Initial -> 'I', Early Data -> 'E', Handshake -> 'H', Application -> 'A' and
|
|
* '-' if undefined.
|
|
*/
|
|
static inline char quic_enc_level_char(enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return 'I';
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
return 'E';
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return 'H';
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return 'A';
|
|
default:
|
|
return '-';
|
|
}
|
|
}
|
|
|
|
/* Return a character identifying <qel> encryption level from <qc> QUIC connection
|
|
* (for debug purpose).
|
|
* Initial -> 'I', Early Data -> 'E', Handshake -> 'H', Application -> 'A' and
|
|
* '-' if undefined.
|
|
*/
|
|
static inline char quic_enc_level_char_from_qel(const struct quic_enc_level *qel,
|
|
const struct quic_conn *qc)
|
|
{
|
|
if (qel == qc->iel)
|
|
return 'I';
|
|
else if (qel == qc->eel)
|
|
return 'E';
|
|
else if (qel == qc->hel)
|
|
return 'H';
|
|
else if (qel == qc->ael)
|
|
return 'A';
|
|
return '-';
|
|
}
|
|
|
|
/* Return a character identifying the encryption level of a packet depending on
|
|
* its <type> type, and its <long_header> header length (for debug purpose).
|
|
* Initial -> 'I', ORTT -> '0', Handshake -> 'H', Application -> 'A' and
|
|
* '-' if undefined.
|
|
*/
|
|
static inline char quic_packet_type_enc_level_char(int packet_type)
|
|
{
|
|
switch (packet_type) {
|
|
case QUIC_PACKET_TYPE_INITIAL:
|
|
return 'I';
|
|
case QUIC_PACKET_TYPE_0RTT:
|
|
return '0';
|
|
case QUIC_PACKET_TYPE_HANDSHAKE:
|
|
return 'H';
|
|
case QUIC_PACKET_TYPE_SHORT:
|
|
return 'A';
|
|
default:
|
|
return '-';
|
|
}
|
|
}
|
|
|
|
/* Initialize a QUIC packet number space.
|
|
* Never fails.
|
|
*/
|
|
static inline int quic_pktns_init(struct quic_conn *qc, struct quic_pktns **p)
|
|
{
|
|
struct quic_pktns *pktns;
|
|
|
|
pktns = pool_alloc(pool_head_quic_pktns);
|
|
if (!pktns)
|
|
return 0;
|
|
|
|
LIST_INIT(&pktns->tx.frms);
|
|
pktns->tx.next_pn = -1;
|
|
pktns->tx.pkts = EB_ROOT_UNIQUE;
|
|
pktns->tx.time_of_last_eliciting = 0;
|
|
pktns->tx.loss_time = TICK_ETERNITY;
|
|
pktns->tx.pto_probe = 0;
|
|
pktns->tx.in_flight = 0;
|
|
pktns->tx.ack_delay = 0;
|
|
|
|
pktns->rx.largest_pn = -1;
|
|
pktns->rx.largest_acked_pn = -1;
|
|
pktns->rx.arngs.root = EB_ROOT_UNIQUE;
|
|
pktns->rx.arngs.sz = 0;
|
|
pktns->rx.arngs.enc_sz = 0;
|
|
pktns->rx.nb_aepkts_since_last_ack = 0;
|
|
pktns->rx.largest_time_received = 0;
|
|
|
|
pktns->flags = 0;
|
|
if (p == &qc->hpktns && qc->apktns)
|
|
LIST_INSERT(&qc->ipktns->list, &pktns->list);
|
|
else
|
|
LIST_APPEND(&qc->pktns_list, &pktns->list);
|
|
*p = pktns;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static inline void quic_pktns_tx_pkts_release(struct quic_pktns *pktns, struct quic_conn *qc)
|
|
{
|
|
struct eb64_node *node;
|
|
|
|
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
|
|
|
|
node = eb64_first(&pktns->tx.pkts);
|
|
while (node) {
|
|
struct quic_tx_packet *pkt;
|
|
struct quic_frame *frm, *frmbak;
|
|
|
|
pkt = eb64_entry(node, struct quic_tx_packet, pn_node);
|
|
node = eb64_next(node);
|
|
if (pkt->flags & QUIC_FL_TX_PACKET_ACK_ELICITING)
|
|
qc->path->ifae_pkts--;
|
|
list_for_each_entry_safe(frm, frmbak, &pkt->frms, list) {
|
|
TRACE_DEVEL("freeing frame from packet",
|
|
QUIC_EV_CONN_PRSAFRM, qc, frm, &pkt->pn_node.key);
|
|
qc_frm_unref(frm, qc);
|
|
LIST_DEL_INIT(&frm->list);
|
|
quic_tx_packet_refdec(frm->pkt);
|
|
qc_frm_free(qc, &frm);
|
|
}
|
|
eb64_delete(&pkt->pn_node);
|
|
quic_tx_packet_refdec(pkt);
|
|
}
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
|
|
}
|
|
|
|
/* Discard <pktns> packet number space attached to <qc> QUIC connection.
|
|
* Its loss information are reset. Deduce the outstanding bytes for this
|
|
* packet number space from the outstanding bytes for the path of this
|
|
* connection.
|
|
* Note that all the non acknowledged TX packets and their frames are freed.
|
|
* Always succeeds.
|
|
*/
|
|
static inline void quic_pktns_discard(struct quic_pktns *pktns,
|
|
struct quic_conn *qc)
|
|
{
|
|
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
|
|
|
|
if (pktns == qc->ipktns)
|
|
qc->flags |= QUIC_FL_CONN_IPKTNS_DCD;
|
|
else if (pktns == qc->hpktns)
|
|
qc->flags |= QUIC_FL_CONN_HPKTNS_DCD;
|
|
qc->path->in_flight -= pktns->tx.in_flight;
|
|
qc->path->prep_in_flight -= pktns->tx.in_flight;
|
|
qc->path->loss.pto_count = 0;
|
|
|
|
pktns->tx.time_of_last_eliciting = 0;
|
|
pktns->tx.loss_time = TICK_ETERNITY;
|
|
pktns->tx.pto_probe = 0;
|
|
pktns->tx.in_flight = 0;
|
|
quic_pktns_tx_pkts_release(pktns, qc);
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
|
|
}
|
|
|
|
|
|
/* Release all the frames attached to <pktns> packet number space */
|
|
static inline void qc_release_pktns_frms(struct quic_conn *qc,
|
|
struct quic_pktns *pktns)
|
|
{
|
|
struct quic_frame *frm, *frmbak;
|
|
|
|
TRACE_ENTER(QUIC_EV_CONN_PHPKTS, qc);
|
|
|
|
if (!pktns)
|
|
goto leave;
|
|
|
|
list_for_each_entry_safe(frm, frmbak, &pktns->tx.frms, list)
|
|
qc_frm_free(qc, &frm);
|
|
|
|
leave:
|
|
TRACE_LEAVE(QUIC_EV_CONN_PHPKTS, qc);
|
|
}
|
|
|
|
/* Return 1 if <pktns> matches with the Application packet number space of
|
|
* <conn> connection which is common to the 0-RTT and 1-RTT encryption levels, 0
|
|
* if not (handshake packets).
|
|
*/
|
|
static inline int quic_application_pktns(struct quic_pktns *pktns, struct quic_conn *qc)
|
|
{
|
|
return pktns == qc->apktns;
|
|
}
|
|
|
|
/* Returns the current largest acknowledged packet number if exists, -1 if not */
|
|
static inline int64_t quic_pktns_get_largest_acked_pn(struct quic_pktns *pktns)
|
|
{
|
|
struct eb64_node *ar = eb64_last(&pktns->rx.arngs.root);
|
|
|
|
if (!ar)
|
|
return -1;
|
|
|
|
return eb64_entry(ar, struct quic_arng_node, first)->last;
|
|
}
|
|
|
|
/* Return a character to identify the packet number space <pktns> of <qc> QUIC
|
|
* connection. 'I' for Initial packet number space, 'H' for Handshake packet
|
|
* space, and 'A' for Application data number space, or '-' if not found.
|
|
*/
|
|
static inline char quic_pktns_char(const struct quic_conn *qc,
|
|
const struct quic_pktns *pktns)
|
|
{
|
|
if (pktns == qc->apktns)
|
|
return 'A';
|
|
else if (pktns == qc->hpktns)
|
|
return 'H';
|
|
else if (pktns == qc->ipktns)
|
|
return 'I';
|
|
|
|
return '-';
|
|
}
|
|
|
|
/* Return the TLS encryption level to be used for <packet_type>
|
|
* QUIC packet type.
|
|
* Returns -1 if there is no TLS encryption level for <packet_type>
|
|
* packet type.
|
|
*/
|
|
static inline enum quic_tls_enc_level quic_packet_type_enc_level(enum quic_pkt_type packet_type)
|
|
{
|
|
switch (packet_type) {
|
|
case QUIC_PACKET_TYPE_INITIAL:
|
|
return QUIC_TLS_ENC_LEVEL_INITIAL;
|
|
case QUIC_PACKET_TYPE_0RTT:
|
|
return QUIC_TLS_ENC_LEVEL_EARLY_DATA;
|
|
case QUIC_PACKET_TYPE_HANDSHAKE:
|
|
return QUIC_TLS_ENC_LEVEL_HANDSHAKE;
|
|
case QUIC_PACKET_TYPE_RETRY:
|
|
return QUIC_TLS_ENC_LEVEL_NONE;
|
|
case QUIC_PACKET_TYPE_SHORT:
|
|
return QUIC_TLS_ENC_LEVEL_APP;
|
|
default:
|
|
return QUIC_TLS_ENC_LEVEL_NONE;
|
|
}
|
|
}
|
|
|
|
static inline enum quic_tls_pktns quic_tls_pktns(enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return QUIC_TLS_PKTNS_INITIAL;
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return QUIC_TLS_PKTNS_01RTT;
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return QUIC_TLS_PKTNS_HANDSHAKE;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Return 1 if <pktns> packet number space attached to <qc> connection has been discarded,
|
|
* 0 if not.
|
|
*/
|
|
static inline int quic_tls_pktns_is_dcd(struct quic_conn *qc, struct quic_pktns *pktns)
|
|
{
|
|
if (pktns == qc->apktns)
|
|
return 0;
|
|
|
|
if ((pktns == qc->ipktns && (qc->flags & QUIC_FL_CONN_IPKTNS_DCD)) ||
|
|
(pktns == qc->hpktns && (qc->flags & QUIC_FL_CONN_HPKTNS_DCD)))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Return 1 the packet number space attached to <qc> connection with <type> associated
|
|
* packet type has been discarded, 0 if not.
|
|
*/
|
|
static inline int quic_tls_pkt_type_pktns_dcd(struct quic_conn *qc, unsigned char type)
|
|
{
|
|
if ((type == QUIC_PACKET_TYPE_INITIAL && (qc->flags & QUIC_FL_CONN_IPKTNS_DCD)) ||
|
|
(type == QUIC_PACKET_TYPE_HANDSHAKE && (qc->flags & QUIC_FL_CONN_HPKTNS_DCD)))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Select the correct TLS cipher context to used to decipher an RX packet
|
|
* with <type> as type and <version> as version and attached to <qc>
|
|
* connection from <qel> encryption level.
|
|
*/
|
|
static inline struct quic_tls_ctx *qc_select_tls_ctx(struct quic_conn *qc,
|
|
struct quic_enc_level *qel,
|
|
unsigned char type,
|
|
const struct quic_version *version)
|
|
{
|
|
return type != QUIC_PACKET_TYPE_INITIAL ? &qel->tls_ctx :
|
|
version == qc->negotiated_version ? qc->nictx : &qel->tls_ctx;
|
|
}
|
|
|
|
/* Reset all members of <ctx> to default values, ->hp_key[] excepted */
|
|
static inline void quic_tls_ctx_reset(struct quic_tls_ctx *ctx)
|
|
{
|
|
ctx->rx.ctx = NULL;
|
|
ctx->rx.aead = NULL;
|
|
ctx->rx.md = NULL;
|
|
ctx->rx.hp_ctx = NULL;
|
|
ctx->rx.hp = NULL;
|
|
ctx->rx.secret = NULL;
|
|
ctx->rx.secretlen = 0;
|
|
ctx->rx.iv = NULL;
|
|
ctx->rx.ivlen = 0;
|
|
ctx->rx.key = NULL;
|
|
ctx->rx.keylen = 0;
|
|
ctx->rx.pn = 0;
|
|
|
|
ctx->tx.ctx = NULL;
|
|
ctx->tx.aead = NULL;
|
|
ctx->tx.md = NULL;
|
|
ctx->tx.hp_ctx = NULL;
|
|
ctx->tx.hp = NULL;
|
|
ctx->tx.secret = NULL;
|
|
ctx->tx.secretlen = 0;
|
|
ctx->tx.iv = NULL;
|
|
ctx->tx.ivlen = 0;
|
|
ctx->tx.key = NULL;
|
|
ctx->tx.keylen = 0;
|
|
/* Not used on the TX path. */
|
|
ctx->tx.pn = 0;
|
|
|
|
ctx->flags = 0;
|
|
}
|
|
|
|
/* Erase and free the secrets for a QUIC encryption level with <ctx> as
|
|
* context.
|
|
* Always succeeds.
|
|
*/
|
|
static inline void quic_tls_ctx_secs_free(struct quic_tls_ctx *ctx)
|
|
{
|
|
if (!ctx)
|
|
return;
|
|
|
|
if (ctx->rx.iv) {
|
|
memset(ctx->rx.iv, 0, ctx->rx.ivlen);
|
|
ctx->rx.ivlen = 0;
|
|
}
|
|
if (ctx->rx.key) {
|
|
memset(ctx->rx.key, 0, ctx->rx.keylen);
|
|
ctx->rx.keylen = 0;
|
|
}
|
|
if (ctx->tx.iv) {
|
|
memset(ctx->tx.iv, 0, ctx->tx.ivlen);
|
|
ctx->tx.ivlen = 0;
|
|
}
|
|
if (ctx->tx.key) {
|
|
memset(ctx->tx.key, 0, ctx->tx.keylen);
|
|
ctx->tx.keylen = 0;
|
|
}
|
|
|
|
/* RX HP protection */
|
|
EVP_CIPHER_CTX_free(ctx->rx.hp_ctx);
|
|
/* RX AEAD decryption */
|
|
EVP_CIPHER_CTX_free(ctx->rx.ctx);
|
|
pool_free(pool_head_quic_tls_iv, ctx->rx.iv);
|
|
pool_free(pool_head_quic_tls_key, ctx->rx.key);
|
|
|
|
/* TX HP protection */
|
|
EVP_CIPHER_CTX_free(ctx->tx.hp_ctx);
|
|
/* TX AEAD encryption */
|
|
EVP_CIPHER_CTX_free(ctx->tx.ctx);
|
|
pool_free(pool_head_quic_tls_iv, ctx->tx.iv);
|
|
pool_free(pool_head_quic_tls_key, ctx->tx.key);
|
|
|
|
quic_tls_ctx_reset(ctx);
|
|
}
|
|
|
|
/* Allocate the secrete keys for a QUIC encryption level with <ctx> as context.
|
|
* Returns 1 if succeeded, 0 if not.
|
|
*/
|
|
static inline int quic_tls_ctx_keys_alloc(struct quic_tls_ctx *ctx)
|
|
{
|
|
if (ctx->rx.key)
|
|
goto write;
|
|
|
|
if (!(ctx->rx.iv = pool_alloc(pool_head_quic_tls_iv)) ||
|
|
!(ctx->rx.key = pool_alloc(pool_head_quic_tls_key)))
|
|
goto err;
|
|
|
|
write:
|
|
if (ctx->tx.key)
|
|
goto out;
|
|
|
|
if (!(ctx->tx.iv = pool_alloc(pool_head_quic_tls_iv)) ||
|
|
!(ctx->tx.key = pool_alloc(pool_head_quic_tls_key)))
|
|
goto err;
|
|
|
|
ctx->rx.ivlen = ctx->tx.ivlen = QUIC_TLS_IV_LEN;
|
|
ctx->rx.keylen = ctx->tx.keylen = QUIC_TLS_KEY_LEN;
|
|
out:
|
|
return 1;
|
|
|
|
err:
|
|
quic_tls_ctx_secs_free(ctx);
|
|
return 0;
|
|
}
|
|
|
|
/* Release the memory allocated for <secs> secrets */
|
|
static inline void quic_tls_secrets_keys_free(struct quic_tls_secrets *secs)
|
|
{
|
|
if (secs->iv) {
|
|
memset(secs->iv, 0, secs->ivlen);
|
|
secs->ivlen = 0;
|
|
}
|
|
|
|
if (secs->key) {
|
|
memset(secs->key, 0, secs->keylen);
|
|
secs->keylen = 0;
|
|
}
|
|
|
|
/* HP protection */
|
|
EVP_CIPHER_CTX_free(secs->hp_ctx);
|
|
/* AEAD decryption */
|
|
EVP_CIPHER_CTX_free(secs->ctx);
|
|
pool_free(pool_head_quic_tls_iv, secs->iv);
|
|
pool_free(pool_head_quic_tls_key, secs->key);
|
|
|
|
secs->iv = secs->key = NULL;
|
|
}
|
|
|
|
/* Allocate the memory for the <secs> secrets.
|
|
* Return 1 if succeeded, 0 if not.
|
|
*/
|
|
static inline int quic_tls_secrets_keys_alloc(struct quic_tls_secrets *secs)
|
|
{
|
|
if (!(secs->iv = pool_alloc(pool_head_quic_tls_iv)) ||
|
|
!(secs->key = pool_alloc(pool_head_quic_tls_key)))
|
|
goto err;
|
|
|
|
secs->ivlen = QUIC_TLS_IV_LEN;
|
|
secs->keylen = QUIC_TLS_KEY_LEN;
|
|
|
|
return 1;
|
|
|
|
err:
|
|
quic_tls_secrets_keys_free(secs);
|
|
return 0;
|
|
}
|
|
|
|
/* Release the memory allocated for the negotiated Initial QUIC TLS context
|
|
* attached to <qc> connection.
|
|
*/
|
|
static inline void quic_nictx_free(struct quic_conn *qc)
|
|
{
|
|
quic_tls_ctx_secs_free(qc->nictx);
|
|
pool_free(pool_head_quic_tls_ctx, qc->nictx);
|
|
qc->nictx = NULL;
|
|
}
|
|
|
|
/* Initialize a TLS cryptographic context for the Initial encryption level. */
|
|
static inline int quic_initial_tls_ctx_init(struct quic_tls_ctx *ctx)
|
|
{
|
|
ctx->rx.aead = ctx->tx.aead = EVP_aes_128_gcm();
|
|
ctx->rx.md = ctx->tx.md = EVP_sha256();
|
|
ctx->rx.hp = ctx->tx.hp = EVP_aes_128_ctr();
|
|
|
|
ctx->rx.iv = NULL;
|
|
ctx->rx.ivlen = 0;
|
|
ctx->rx.key = NULL;
|
|
ctx->rx.keylen = 0;
|
|
ctx->rx.secret = NULL;
|
|
ctx->rx.secretlen = 0;
|
|
|
|
ctx->tx.iv = NULL;
|
|
ctx->tx.ivlen = 0;
|
|
ctx->tx.key = NULL;
|
|
ctx->tx.keylen = 0;
|
|
ctx->tx.secret = NULL;
|
|
ctx->tx.secretlen = 0;
|
|
|
|
return quic_tls_ctx_keys_alloc(ctx);
|
|
}
|
|
|
|
static inline int quic_tls_level_pkt_type(enum quic_tls_enc_level level)
|
|
{
|
|
switch (level) {
|
|
case QUIC_TLS_ENC_LEVEL_INITIAL:
|
|
return QUIC_PACKET_TYPE_INITIAL;
|
|
case QUIC_TLS_ENC_LEVEL_EARLY_DATA:
|
|
return QUIC_PACKET_TYPE_0RTT;
|
|
case QUIC_TLS_ENC_LEVEL_HANDSHAKE:
|
|
return QUIC_PACKET_TYPE_HANDSHAKE;
|
|
case QUIC_TLS_ENC_LEVEL_APP:
|
|
return QUIC_PACKET_TYPE_SHORT;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Return the packet type associated to <qel> encryption for <qc> QUIC connection,
|
|
* or -1 if not found.
|
|
*/
|
|
static inline enum quic_pkt_type quic_enc_level_pkt_type(struct quic_conn *qc,
|
|
struct quic_enc_level *qel)
|
|
{
|
|
if (qel == qc->iel)
|
|
return QUIC_PACKET_TYPE_INITIAL;
|
|
else if (qel == qc->hel)
|
|
return QUIC_PACKET_TYPE_HANDSHAKE;
|
|
else if (qel == qc->eel)
|
|
return QUIC_PACKET_TYPE_0RTT;
|
|
else if (qel == qc->ael)
|
|
return QUIC_PACKET_TYPE_SHORT;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/* Derive the initial secrets with <ctx> as QUIC TLS context which is the
|
|
* cryptographic context for the first encryption level (Initial) from
|
|
* <cid> connection ID with <cidlen> as length (in bytes) for a server or not
|
|
* depending on <server> boolean value.
|
|
* Return 1 if succeeded or 0 if not.
|
|
*/
|
|
static inline int qc_new_isecs(struct quic_conn *qc,
|
|
struct quic_tls_ctx *ctx, const struct quic_version *ver,
|
|
const unsigned char *cid, size_t cidlen, int server)
|
|
{
|
|
unsigned char initial_secret[32];
|
|
/* Initial secret to be derived for incoming packets */
|
|
unsigned char rx_init_sec[32];
|
|
/* Initial secret to be derived for outgoing packets */
|
|
unsigned char tx_init_sec[32];
|
|
struct quic_tls_secrets *rx_ctx, *tx_ctx;
|
|
|
|
TRACE_ENTER(QUIC_EV_CONN_ISEC);
|
|
if (!quic_initial_tls_ctx_init(ctx))
|
|
goto err;
|
|
|
|
if (!quic_derive_initial_secret(ctx->rx.md,
|
|
ver->initial_salt, ver->initial_salt_len,
|
|
initial_secret, sizeof initial_secret,
|
|
cid, cidlen))
|
|
goto err;
|
|
|
|
if (!quic_tls_derive_initial_secrets(ctx->rx.md,
|
|
rx_init_sec, sizeof rx_init_sec,
|
|
tx_init_sec, sizeof tx_init_sec,
|
|
initial_secret, sizeof initial_secret, server))
|
|
goto err;
|
|
|
|
rx_ctx = &ctx->rx;
|
|
tx_ctx = &ctx->tx;
|
|
if (!quic_tls_derive_keys(ctx->rx.aead, ctx->rx.hp, ctx->rx.md, ver,
|
|
rx_ctx->key, rx_ctx->keylen,
|
|
rx_ctx->iv, rx_ctx->ivlen,
|
|
rx_ctx->hp_key, sizeof rx_ctx->hp_key,
|
|
rx_init_sec, sizeof rx_init_sec))
|
|
goto err;
|
|
|
|
if (!quic_tls_rx_ctx_init(&rx_ctx->ctx, rx_ctx->aead, rx_ctx->key))
|
|
goto err;
|
|
|
|
if (!quic_tls_enc_aes_ctx_init(&rx_ctx->hp_ctx, rx_ctx->hp, rx_ctx->hp_key))
|
|
goto err;
|
|
|
|
if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md, ver,
|
|
tx_ctx->key, tx_ctx->keylen,
|
|
tx_ctx->iv, tx_ctx->ivlen,
|
|
tx_ctx->hp_key, sizeof tx_ctx->hp_key,
|
|
tx_init_sec, sizeof tx_init_sec))
|
|
goto err;
|
|
|
|
if (!quic_tls_tx_ctx_init(&tx_ctx->ctx, tx_ctx->aead, tx_ctx->key))
|
|
goto err;
|
|
|
|
if (!quic_tls_enc_aes_ctx_init(&tx_ctx->hp_ctx, tx_ctx->hp, tx_ctx->hp_key))
|
|
goto err;
|
|
|
|
TRACE_LEAVE(QUIC_EV_CONN_ISEC, qc, rx_init_sec, tx_init_sec);
|
|
|
|
return 1;
|
|
|
|
err:
|
|
TRACE_DEVEL("leaving in error", QUIC_EV_CONN_ISEC);
|
|
return 0;
|
|
}
|
|
|
|
/* Reset all members of <tls_kp> to default values. */
|
|
static inline void quic_tls_ku_reset(struct quic_tls_kp *tls_kp)
|
|
{
|
|
tls_kp->ctx = NULL;
|
|
tls_kp->secret = NULL;
|
|
tls_kp->iv = NULL;
|
|
tls_kp->key = NULL;
|
|
}
|
|
|
|
/* Release the memory allocated for all the key update key phase
|
|
* structures for <qc> QUIC connection.
|
|
* Always succeeds.
|
|
*/
|
|
static inline void quic_tls_ku_free(struct quic_conn *qc)
|
|
{
|
|
EVP_CIPHER_CTX_free(qc->ku.prv_rx.ctx);
|
|
pool_free(pool_head_quic_tls_secret, qc->ku.prv_rx.secret);
|
|
pool_free(pool_head_quic_tls_iv, qc->ku.prv_rx.iv);
|
|
pool_free(pool_head_quic_tls_key, qc->ku.prv_rx.key);
|
|
quic_tls_ku_reset(&qc->ku.prv_rx);
|
|
EVP_CIPHER_CTX_free(qc->ku.nxt_rx.ctx);
|
|
pool_free(pool_head_quic_tls_secret, qc->ku.nxt_rx.secret);
|
|
pool_free(pool_head_quic_tls_iv, qc->ku.nxt_rx.iv);
|
|
pool_free(pool_head_quic_tls_key, qc->ku.nxt_rx.key);
|
|
quic_tls_ku_reset(&qc->ku.nxt_rx);
|
|
EVP_CIPHER_CTX_free(qc->ku.nxt_tx.ctx);
|
|
pool_free(pool_head_quic_tls_secret, qc->ku.nxt_tx.secret);
|
|
pool_free(pool_head_quic_tls_iv, qc->ku.nxt_tx.iv);
|
|
pool_free(pool_head_quic_tls_key, qc->ku.nxt_tx.key);
|
|
quic_tls_ku_reset(&qc->ku.nxt_tx);
|
|
}
|
|
|
|
/* Initialize <kp> key update secrets, allocating the required memory.
|
|
* Return 1 if all the secrets could be allocated, 0 if not.
|
|
* This is the responsibility of the caller to release the memory
|
|
* allocated by this function in case of failure.
|
|
*/
|
|
static inline int quic_tls_kp_init(struct quic_tls_kp *kp)
|
|
{
|
|
kp->count = 0;
|
|
kp->pn = 0;
|
|
kp->flags = 0;
|
|
kp->secret = pool_alloc(pool_head_quic_tls_secret);
|
|
kp->secretlen = QUIC_TLS_SECRET_LEN;
|
|
kp->iv = pool_alloc(pool_head_quic_tls_iv);
|
|
kp->ivlen = QUIC_TLS_IV_LEN;
|
|
kp->key = pool_alloc(pool_head_quic_tls_key);
|
|
kp->keylen = QUIC_TLS_KEY_LEN;
|
|
|
|
return kp->secret && kp->iv && kp->key;
|
|
}
|
|
|
|
/* Initialize all the key update key phase structures for <qc>
|
|
* QUIC connection, allocating the required memory.
|
|
*
|
|
* Returns 1 if succeeded, 0 if not. The caller is responsible to use
|
|
* quic_tls_ku_free() on error to cleanup partially allocated content.
|
|
*/
|
|
static inline int quic_tls_ku_init(struct quic_conn *qc)
|
|
{
|
|
struct quic_tls_kp *prv_rx = &qc->ku.prv_rx;
|
|
struct quic_tls_kp *nxt_rx = &qc->ku.nxt_rx;
|
|
struct quic_tls_kp *nxt_tx = &qc->ku.nxt_tx;
|
|
|
|
if (!quic_tls_kp_init(prv_rx) ||
|
|
!quic_tls_kp_init(nxt_rx) ||
|
|
!quic_tls_kp_init(nxt_tx))
|
|
goto err;
|
|
|
|
return 1;
|
|
|
|
err:
|
|
return 0;
|
|
}
|
|
|
|
/* Return 1 if <qel> has RX secrets, 0 if not. */
|
|
static inline int quic_tls_has_rx_sec(const struct quic_enc_level *qel)
|
|
{
|
|
return qel && !!qel->tls_ctx.rx.key;
|
|
}
|
|
|
|
/* Return 1 if <qel> has TX secrets, 0 if not. */
|
|
static inline int quic_tls_has_tx_sec(const struct quic_enc_level *qel)
|
|
{
|
|
return qel && !!qel->tls_ctx.tx.key;
|
|
}
|
|
|
|
#endif /* USE_QUIC */
|
|
#endif /* _PROTO_QUIC_TLS_H */
|
|
|