You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
13 KiB
442 lines
13 KiB
5 months ago
|
/*
|
||
|
* include/proto/quic_tls.h
|
||
|
* This file provides definitions for QUIC-TLS.
|
||
|
*
|
||
|
* Copyright 2019 HAProxy Technologies, Frédéric Lécaille <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>
|
||
|
|
||
|
void quic_tls_keys_hexdump(struct buffer *buf, 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,
|
||
|
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,
|
||
|
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,
|
||
|
const EVP_CIPHER *aead,
|
||
|
const unsigned char *key, const unsigned char *iv);
|
||
|
|
||
|
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_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 quic_tls_enc_level 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 "CP";
|
||
|
case QUIC_HS_ST_CONFIRMED:
|
||
|
return "CF";
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Initialize a TLS cryptographic context for the Initial encryption level. */
|
||
|
static inline void 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();
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
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)
|
||
|
{
|
||
|
switch (state) {
|
||
|
case QUIC_HS_ST_SERVER_INITIAL:
|
||
|
case QUIC_HS_ST_CLIENT_INITIAL:
|
||
|
*level = QUIC_TLS_ENC_LEVEL_INITIAL;
|
||
|
*next_level = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
|
||
|
break;
|
||
|
case QUIC_HS_ST_SERVER_HANDSHAKE:
|
||
|
case QUIC_HS_ST_CLIENT_HANDSHAKE:
|
||
|
case QUIC_HS_ST_COMPLETE:
|
||
|
case QUIC_HS_ST_CONFIRMED:
|
||
|
*level = QUIC_TLS_ENC_LEVEL_HANDSHAKE;
|
||
|
*next_level = QUIC_TLS_ENC_LEVEL_APP;
|
||
|
break;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Flag the keys at <qel> encryption level as discarded. */
|
||
|
static inline void quic_tls_discard_keys(struct quic_enc_level *qel)
|
||
|
{
|
||
|
qel->tls_ctx.rx.flags |= QUIC_FL_TLS_SECRETS_DCD;
|
||
|
qel->tls_ctx.tx.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 connection *conn,
|
||
|
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, conn);
|
||
|
ctx = &conn->qc->els[QUIC_TLS_ENC_LEVEL_INITIAL].tls_ctx;
|
||
|
quic_initial_tls_ctx_init(ctx);
|
||
|
if (!quic_derive_initial_secret(ctx->rx.md,
|
||
|
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, sizeof rx_ctx->key,
|
||
|
rx_ctx->iv, sizeof rx_ctx->iv,
|
||
|
rx_ctx->hp_key, sizeof rx_ctx->hp_key,
|
||
|
rx_init_sec, sizeof rx_init_sec))
|
||
|
goto err;
|
||
|
|
||
|
rx_ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
|
||
|
if (!quic_tls_derive_keys(ctx->tx.aead, ctx->tx.hp, ctx->tx.md,
|
||
|
tx_ctx->key, sizeof tx_ctx->key,
|
||
|
tx_ctx->iv, sizeof tx_ctx->iv,
|
||
|
tx_ctx->hp_key, sizeof tx_ctx->hp_key,
|
||
|
tx_init_sec, sizeof tx_init_sec))
|
||
|
goto err;
|
||
|
|
||
|
tx_ctx->flags |= QUIC_FL_TLS_SECRETS_SET;
|
||
|
TRACE_LEAVE(QUIC_EV_CONN_ISEC, conn, rx_init_sec, tx_init_sec);
|
||
|
|
||
|
return 1;
|
||
|
|
||
|
err:
|
||
|
TRACE_DEVEL("leaving in error", QUIC_EV_CONN_ISEC, conn);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif /* USE_QUIC */
|
||
|
#endif /* _PROTO_QUIC_TLS_H */
|
||
|
|