haproxy/include/haproxy/quic_tls.h
Frédéric Lécaille 55367c8679 MINOR: quic_tls: Add quic_tls_decrypt2() implementation
This function does exactly the same thing as quic_tls_decrypt(), except that
it does reuse its input buffer as output buffer. This is needed
to decrypt the Retry token without modifying the packet buffer which
contains this token. Indeed, this would prevent us from decryption
the packet itself as the token belong to the AEAD AAD for the packet.
2022-05-20 16:57:12 +02:00

626 lines
19 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
#define TRACE_SOURCE &trace_quic
#include <stdlib.h>
#include <openssl/ssl.h>
#include <haproxy/dynbuf.h>
#include <haproxy/quic_tls-t.h>
#include <haproxy/trace.h>
#include <haproxy/xprt_quic.h>
/* Initial salt depending on QUIC version to derive client/server initial secrets.
* This one is for draft-29 QUIC version.
*/
unsigned char initial_salt_draft_29[20] = {
0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c,
0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0,
0x43, 0x90, 0xa8, 0x99
};
unsigned char initial_salt_v1[20] = {
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3,
0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad,
0xcc, 0xbb, 0x7f, 0x0a
};
void quic_tls_keys_hexdump(struct buffer *buf,
const struct quic_tls_secrets *secs);
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 *key, 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, size_t odcid_len,
unsigned char *buf, size_t len);
int quic_tls_derive_keys(const EVP_CIPHER *aead, const EVP_CIPHER *hp,
const EVP_MD *md,
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_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,
unsigned char *new_sec, size_t new_seclen,
const unsigned char *sec, size_t seclen);
int quic_aead_iv_build(unsigned char *iv, size_t ivlen,
unsigned char *aead_iv, size_t aead_ivlen, uint64_t pn);
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();
#ifndef OPENSSL_IS_BORINGSSL
/* XXX TO DO XXX */
/* Note that for chacha20_poly1305, there exists EVP_AEAD_chacha20_poly135() function
* which returns a pointer to const EVP_AEAD.
*/
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return EVP_chacha20_poly1305();
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:
#ifndef OPENSSL_IS_BORINGSSL
/* XXX TO DO XXX */
/* Note that for chacha20_poly1305, there exists EVP_AEAD_chacha20_poly135() function
* which returns a pointer to const EVP_AEAD.
*/
case TLS1_3_CK_AES_128_CCM_SHA256:
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
#endif
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)) {
#ifndef OPENSSL_IS_BORINGSSL
/* XXX TO DO XXX */
/* Note that for chacha20_poly1305, there exists EVP_AEAD_chacha20_poly135() function
* which returns a pointer to const EVP_AEAD.
*/
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
return EVP_chacha20();
case TLS1_3_CK_AES_128_CCM_SHA256:
#endif
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 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;
}
}
/* 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";
#ifndef OPENSSL_IS_BORINGSSL
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->els[QUIC_TLS_ENC_LEVEL_INITIAL])
return 'I';
else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_EARLY_DATA])
return 'E';
else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_HANDSHAKE])
return 'H';
else if (qel == &qc->els[QUIC_TLS_ENC_LEVEL_APP])
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 '-';
}
}
/* 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;
}
}
/* 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->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;
}
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);
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);
ctx->rx.iv = ctx->tx.iv = NULL;
ctx->rx.key = ctx->tx.key = NULL;
}
/* 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.iv = pool_alloc(pool_head_quic_tls_iv)) ||
!(ctx->rx.key = pool_alloc(pool_head_quic_tls_key)) ||
!(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;
return 1;
err:
quic_tls_ctx_secs_free(ctx);
return 0;
}
/* 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();
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;
}
}
/* Set <*level> and <*next_level> depending on <state> QUIC handshake state. */
static inline int quic_get_tls_enc_levels(enum quic_tls_enc_level *level,
enum quic_tls_enc_level *next_level,
enum quic_handshake_state state, int zero_rtt)
{
switch (state) {
case QUIC_HS_ST_SERVER_INITIAL:
case QUIC_HS_ST_CLIENT_INITIAL:
*level = QUIC_TLS_ENC_LEVEL_INITIAL;
if (zero_rtt)
*next_level = QUIC_TLS_ENC_LEVEL_EARLY_DATA;
else
*next_level = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
break;
case QUIC_HS_ST_SERVER_HANDSHAKE:
case QUIC_HS_ST_CLIENT_HANDSHAKE:
*level = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
*next_level = QUIC_TLS_ENC_LEVEL_APP;
break;
case QUIC_HS_ST_COMPLETE:
case QUIC_HS_ST_CONFIRMED:
*level = QUIC_TLS_ENC_LEVEL_APP;
*next_level = QUIC_TLS_ENC_LEVEL_NONE;
break;
default:
return 0;
}
return 1;
}
/* Flag the keys at <qel> encryption level as discarded.
* Note that this function is called only for Initial or Handshake encryption levels.
*/
static inline void quic_tls_discard_keys(struct quic_enc_level *qel)
{
qel->tls_ctx.flags |= QUIC_FL_TLS_SECRETS_DCD;
}
/* 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,
const unsigned char *salt, size_t salt_len,
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;
struct quic_tls_ctx *ctx;
TRACE_ENTER(QUIC_EV_CONN_ISEC);
ctx = &qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx;
if (!quic_initial_tls_ctx_init(ctx))
goto err;
if (!quic_derive_initial_secret(ctx->rx.md,
salt, 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,
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_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md,
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;
ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
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;
}
/* 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);
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);
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);
}
/* 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.
*/
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:
quic_tls_ku_free(qc);
return 0;
}
#endif /* USE_QUIC */
#endif /* _PROTO_QUIC_TLS_H */