/* * include/proto/quic_tls.h * This file provides definitions for QUIC-TLS. * * Copyright 2019 HAProxy Technologies, Frédéric Lécaille * * 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 #include #include #include #include #include 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 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 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 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 encryption level from 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, and its 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 * QUIC packet type. * Returns -1 if there is no TLS encryption level for * 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 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 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 as QUIC TLS context which is the * cryptographic context for the first encryption level (Initial) from * connection ID with as length (in bytes) for a server or not * depending on 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 */