From 43054d3d734a8fa8a3d2da20c206a47d4060b7bd Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Mon, 11 Sep 2017 15:43:56 +0100 Subject: [PATCH] Add support for sending TLSv1.3 cookies This just adds the various extension functions. More changes will be required to actually use them. Reviewed-by: Ben Kaduk (Merged from https://github.com/openssl/openssl/pull/4435) --- crypto/err/openssl.txt | 2 + include/openssl/dtls1.h | 4 + include/openssl/ssl.h | 3 + include/openssl/ssl3.h | 2 + include/openssl/sslerr.h | 2 + ssl/ssl_err.c | 4 + ssl/ssl_lib.c | 4 + ssl/ssl_locl.h | 2 + ssl/statem/extensions.c | 4 +- ssl/statem/extensions_srvr.c | 375 +++++++++++++++++++++++++++++++++++ ssl/statem/statem_clnt.c | 2 +- ssl/statem/statem_lib.c | 36 +++- ssl/statem/statem_locl.h | 8 +- ssl/statem/statem_srvr.c | 2 +- 14 files changed, 437 insertions(+), 13 deletions(-) diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index e98bd95938..0ed1b09fe1 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1275,6 +1275,7 @@ SSL_F_TLS_CONSTRUCT_SERVER_HELLO:491:tls_construct_server_hello SSL_F_TLS_CONSTRUCT_SERVER_KEY_EXCHANGE:492:tls_construct_server_key_exchange SSL_F_TLS_CONSTRUCT_STOC_ALPN:451:tls_construct_stoc_alpn SSL_F_TLS_CONSTRUCT_STOC_CERTIFICATE:374:* +SSL_F_TLS_CONSTRUCT_STOC_COOKIE:613:tls_construct_stoc_cookie SSL_F_TLS_CONSTRUCT_STOC_CRYPTOPRO_BUG:452:tls_construct_stoc_cryptopro_bug SSL_F_TLS_CONSTRUCT_STOC_DONE:375:* SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA:531:tls_construct_stoc_early_data @@ -1307,6 +1308,7 @@ SSL_F_TLS_HANDLE_STATUS_REQUEST:563:tls_handle_status_request SSL_F_TLS_PARSE_CERTIFICATE_AUTHORITIES:566:tls_parse_certificate_authorities SSL_F_TLS_PARSE_CLIENTHELLO_TLSEXT:449:* SSL_F_TLS_PARSE_CTOS_ALPN:567:tls_parse_ctos_alpn +SSL_F_TLS_PARSE_CTOS_COOKIE:614:tls_parse_ctos_cookie SSL_F_TLS_PARSE_CTOS_EARLY_DATA:568:tls_parse_ctos_early_data SSL_F_TLS_PARSE_CTOS_EC_PT_FORMATS:569:tls_parse_ctos_ec_pt_formats SSL_F_TLS_PARSE_CTOS_EMS:570:tls_parse_ctos_ems diff --git a/include/openssl/dtls1.h b/include/openssl/dtls1.h index 86a8981d36..aee8cfdbe4 100644 --- a/include/openssl/dtls1.h +++ b/include/openssl/dtls1.h @@ -26,6 +26,10 @@ extern "C" { # define DTLS_ANY_VERSION 0x1FFFF /* lengths of messages */ +/* + * Actually the max cookie length in DTLS is 255. But we can't change this now + * due to compatibility concerns. + */ # define DTLS1_COOKIE_LENGTH 256 # define DTLS1_RT_HEADER_LENGTH 13 diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 05a07eb98d..7aa98dab67 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -546,6 +546,9 @@ typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx); # define SSL_CONF_TYPE_DIR 0x3 # define SSL_CONF_TYPE_NONE 0x4 +/* Length of a TLSv1.3 cookie */ +# define SSL_COOKIE_LENGTH 255 + /* * Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value, they * cannot be used to clear bits. diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index b781f61dab..6e8ef6b729 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -284,6 +284,8 @@ extern "C" { # define TLS1_FLAGS_ENCRYPT_THEN_MAC_WRITE 0x0400 +# define TLS1_FLAGS_STATELESS 0x0800 + # define SSL3_MT_HELLO_REQUEST 0 # define SSL3_MT_CLIENT_HELLO 1 # define SSL3_MT_SERVER_HELLO 2 diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index fdb17a533c..2431b492d1 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -322,6 +322,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_SERVER_KEY_EXCHANGE 492 # define SSL_F_TLS_CONSTRUCT_STOC_ALPN 451 # define SSL_F_TLS_CONSTRUCT_STOC_CERTIFICATE 374 +# define SSL_F_TLS_CONSTRUCT_STOC_COOKIE 613 # define SSL_F_TLS_CONSTRUCT_STOC_CRYPTOPRO_BUG 452 # define SSL_F_TLS_CONSTRUCT_STOC_DONE 375 # define SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA 531 @@ -351,6 +352,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_CERTIFICATE_AUTHORITIES 566 # define SSL_F_TLS_PARSE_CLIENTHELLO_TLSEXT 449 # define SSL_F_TLS_PARSE_CTOS_ALPN 567 +# define SSL_F_TLS_PARSE_CTOS_COOKIE 614 # define SSL_F_TLS_PARSE_CTOS_EARLY_DATA 568 # define SSL_F_TLS_PARSE_CTOS_EC_PT_FORMATS 569 # define SSL_F_TLS_PARSE_CTOS_EMS 570 diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 1e3eb2cc72..111579b3da 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -492,6 +492,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_ALPN, 0), "tls_construct_stoc_alpn"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_CERTIFICATE, 0), ""}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, 0), + "tls_construct_stoc_cookie"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_CRYPTOPRO_BUG, 0), "tls_construct_stoc_cryptopro_bug"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_DONE, 0), ""}, @@ -544,6 +546,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CLIENTHELLO_TLSEXT, 0), ""}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_ALPN, 0), "tls_parse_ctos_alpn"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_COOKIE, 0), + "tls_parse_ctos_cookie"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_EARLY_DATA, 0), "tls_parse_ctos_early_data"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_EC_PT_FORMATS, 0), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 8094e2fde8..1457fc68f6 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2939,6 +2939,10 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) sizeof(ret->ext.tick_aes_key)) <= 0)) ret->options |= SSL_OP_NO_TICKET; + if (RAND_bytes(ret->ext.cookie_hmac_key, + sizeof(ret->ext.cookie_hmac_key)) <= 0) + goto err; + #ifndef OPENSSL_NO_SRP if (!SSL_CTX_SRP_CTX_init(ret)) goto err; diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index de73f440a8..9247cf3530 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -972,6 +972,8 @@ struct ssl_ctx_st { SSL_CTX_npn_select_cb_func npn_select_cb; void *npn_select_cb_arg; # endif + + unsigned char cookie_hmac_key[SHA256_DIGEST_LENGTH]; } ext; # ifndef OPENSSL_NO_PSK diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index cc862f5c4e..6022c493dc 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -323,8 +323,8 @@ static const EXTENSION_DEFINITION ext_defs[] = { TLSEXT_TYPE_cookie, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY, - NULL, NULL, tls_parse_stoc_cookie, NULL, tls_construct_ctos_cookie, - NULL + NULL, tls_parse_ctos_cookie, tls_parse_stoc_cookie, + tls_construct_stoc_cookie, tls_construct_ctos_cookie, NULL }, { /* diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 30cbf9e6ae..16f1b85367 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -10,6 +10,30 @@ #include #include "../ssl_locl.h" #include "statem_locl.h" +#include "internal/cryptlib.h" + +#define COOKIE_STATE_FORMAT_VERSION 0 + +/* + * 2 bytes for packet length, 2 bytes for format version, 2 bytes for + * protocol version, 2 bytes for group id, 2 bytes for cipher id, 1 byte for + * key_share present flag, 2 bytes for the hashlen, EVP_MAX_MD_SIZE for + * transcript hash, 1 byte for app cookie length, app cookie length bytes, + * SHA256_DIGEST_LENGTH bytes for the HMAC of the whole thing. + */ +#define MAX_COOKIE_SIZE (2 + 2 + 2 + 2 + 2 + 1 + 2 + EVP_MAX_MD_SIZE + 1 \ + + SSL_COOKIE_LENGTH + SHA256_DIGEST_LENGTH) + +/* + * Message header + 2 bytes for protocol version + number of random bytes + + * + number of bytes in legacy session id + 2 bytes for ciphersuite + * + 1 byte for legacy compression + 2 bytes for extension block length + * + 6 bytes for key_share extension + 4 bytes for cookie extension header + * + the number of bytes in the cookie + */ +#define MAX_HRR_SIZE (SSL3_HM_HEADER_LENGTH + 2 + SSL3_RANDOM_SIZE \ + + SSL_MAX_SSL_SESSION_ID_LENGTH + 2 + 1 + 2 + 6 + 4 \ + + MAX_COOKIE_SIZE) /* * Parse the client's renegotiation binding and abort if it's not right @@ -594,6 +618,17 @@ int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 0; } + if (s->s3->group_id != 0 && PACKET_remaining(&key_share_list) == 0) { + /* + * If we set a group_id already, then we must have sent an HRR + * requesting a new key_share. If we haven't got one then that is an + * error + */ + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_CTOS_KEY_SHARE, + SSL_R_BAD_KEY_SHARE); + return 0; + } + while (PACKET_remaining(&key_share_list) > 0) { if (!PACKET_get_net_2(&key_share_list, &group_id) || !PACKET_get_length_prefixed_2(&key_share_list, &encoded_pt) @@ -610,6 +645,18 @@ int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, if (found) continue; + /* + * If we sent an HRR then the key_share sent back MUST be for the group + * we requested, and must be the only key_share sent. + */ + if (s->s3->group_id != 0 + && (group_id != s->s3->group_id + || PACKET_remaining(&key_share_list) != 0)) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_F_TLS_PARSE_CTOS_KEY_SHARE, SSL_R_BAD_KEY_SHARE); + return 0; + } + /* Check if this share is in supported_groups sent from client */ if (!check_in_list(s, group_id, clntgroups, clnt_num_groups, 0)) { SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, @@ -646,6 +693,211 @@ int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 1; } +int tls_parse_ctos_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx) +{ + unsigned int format, version, key_share; + EVP_MD_CTX *hctx; + EVP_PKEY *pkey; + PACKET cookie, raw, chhash, appcookie; + WPACKET hrrpkt; + const unsigned char *data, *mdin, *ciphdata; + unsigned char hmac[SHA256_DIGEST_LENGTH]; + unsigned char hrr[MAX_HRR_SIZE]; + size_t rawlen, hmaclen, hrrlen, ciphlen; + + /* Ignore any cookie if we're not set up to verify it */ + if (s->ctx->app_verify_cookie_cb == NULL + || (s->s3->flags & TLS1_FLAGS_STATELESS) == 0) + return 1; + + if (!PACKET_as_length_prefixed_2(pkt, &cookie)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + + raw = cookie; + data = PACKET_data(&raw); + rawlen = PACKET_remaining(&raw); + if (rawlen < SHA256_DIGEST_LENGTH + || !PACKET_forward(&raw, rawlen - SHA256_DIGEST_LENGTH)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + mdin = PACKET_data(&raw); + + /* Verify the HMAC of the cookie */ + hctx = EVP_MD_CTX_create(); + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, + s->session_ctx->ext.cookie_hmac_key, + sizeof(s->session_ctx->ext.cookie_hmac_key)); + if (hctx == NULL || pkey == NULL) { + EVP_MD_CTX_free(hctx); + EVP_PKEY_free(pkey); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_MALLOC_FAILURE); + return 0; + } + + hmaclen = sizeof(s->session_ctx->ext.cookie_hmac_key); + if (EVP_DigestSignInit(hctx, NULL, EVP_sha256(), NULL, pkey) <= 0 + || EVP_DigestSignUpdate(hctx, data, + rawlen - SHA256_DIGEST_LENGTH) <= 0 + || EVP_DigestSignFinal(hctx, hmac, &hmaclen) <= 0 + || hmaclen != SHA256_DIGEST_LENGTH) { + EVP_MD_CTX_free(hctx); + EVP_PKEY_free(pkey); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + + EVP_MD_CTX_free(hctx); + EVP_PKEY_free(pkey); + + if (CRYPTO_memcmp(hmac, mdin, SHA256_DIGEST_LENGTH) != 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_COOKIE_MISMATCH); + return 0; + } + + if (!PACKET_get_net_2(&cookie, &format)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + /* Check the cookie format is something we recognise. Ignore it if not */ + if (format != COOKIE_STATE_FORMAT_VERSION) + return 1; + + /* + * The rest of these checks really shouldn't fail since we have verified the + * HMAC above. + */ + + /* Check the version number is sane */ + if (!PACKET_get_net_2(&cookie, &version)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + if (version != TLS1_3_VERSION) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_BAD_PROTOCOL_VERSION_NUMBER); + return 0; + } + + if (!PACKET_get_net_2(&cookie, &s->s3->group_id)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + ciphdata = PACKET_data(&cookie); + if (!PACKET_forward(&cookie, 2)) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + s->s3->tmp.new_cipher = ssl_get_cipher_by_char(s, ciphdata, 0); + if (s->s3->tmp.new_cipher == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!PACKET_get_1(&cookie, &key_share) + || !PACKET_get_length_prefixed_2(&cookie, &chhash) + || !PACKET_get_length_prefixed_1(&cookie, &appcookie) + || PACKET_remaining(&cookie) != SHA256_DIGEST_LENGTH) { + SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_LENGTH_MISMATCH); + return 0; + } + + /* Verify the app cookie */ + if (s->ctx->app_verify_cookie_cb(s, PACKET_data(&appcookie), + PACKET_remaining(&appcookie)) == 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_CTOS_COOKIE, + SSL_R_COOKIE_MISMATCH); + return 0; + } + + /* + * Reconstruct the HRR that we would have sent in response to the original + * ClientHello so we can add it to the transcript hash. + * Note: This won't work with custom HRR extensions + */ + if (!WPACKET_init_static_len(&hrrpkt, hrr, sizeof(hrr), 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + if (!WPACKET_put_bytes_u8(&hrrpkt, SSL3_MT_SERVER_HELLO) + || !WPACKET_start_sub_packet_u24(&hrrpkt) + || !WPACKET_put_bytes_u16(&hrrpkt, TLS1_2_VERSION) + || !WPACKET_memcpy(&hrrpkt, hrrrandom, SSL3_RANDOM_SIZE) + || !WPACKET_sub_memcpy_u8(&hrrpkt, s->tmp_session_id, + s->tmp_session_id_len) + || !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, &hrrpkt, + &ciphlen) + || !WPACKET_put_bytes_u8(&hrrpkt, 0) + || !WPACKET_start_sub_packet_u16(&hrrpkt)) { + WPACKET_cleanup(&hrrpkt); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + if (!WPACKET_put_bytes_u16(&hrrpkt, TLSEXT_TYPE_supported_versions) + || !WPACKET_start_sub_packet_u16(&hrrpkt) + /* TODO(TLS1.3): Fix this before release */ + || !WPACKET_put_bytes_u16(&hrrpkt, TLS1_3_VERSION_DRAFT) + || !WPACKET_close(&hrrpkt)) { + WPACKET_cleanup(&hrrpkt); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + if (key_share) { + if (!WPACKET_put_bytes_u16(&hrrpkt, TLSEXT_TYPE_key_share) + || !WPACKET_start_sub_packet_u16(&hrrpkt) + || !WPACKET_put_bytes_u16(&hrrpkt, s->s3->group_id) + || !WPACKET_close(&hrrpkt)) { + WPACKET_cleanup(&hrrpkt); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + } + if (!WPACKET_put_bytes_u16(&hrrpkt, TLSEXT_TYPE_cookie) + || !WPACKET_start_sub_packet_u16(&hrrpkt) + || !WPACKET_sub_memcpy_u16(&hrrpkt, data, rawlen) + || !WPACKET_close(&hrrpkt) /* cookie extension */ + || !WPACKET_close(&hrrpkt) /* extension block */ + || !WPACKET_close(&hrrpkt) /* message */ + || !WPACKET_get_total_written(&hrrpkt, &hrrlen) + || !WPACKET_finish(&hrrpkt)) { + WPACKET_cleanup(&hrrpkt); + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_PARSE_CTOS_COOKIE, + ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Reconstruct the transcript hash */ + if (!create_synthetic_message_hash(s, PACKET_data(&chhash), + PACKET_remaining(&chhash), hrr, + hrrlen)) { + /* SSLfatal() already called */ + return 0; + } + + /* Act as if this ClientHello came after a HelloRetryRequest */ + s->hello_retry_request = 1; + + return 1; +} + #ifndef OPENSSL_NO_EC int tls_parse_ctos_supported_groups(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx) @@ -1313,6 +1565,129 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, return EXT_RETURN_SENT; } +EXT_RETURN tls_construct_stoc_cookie(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + unsigned char *hashval1, *hashval2, *appcookie1, *appcookie2, *cookie; + unsigned char *hmac, *hmac2; + size_t startlen, ciphlen, totcookielen, hashlen, hmaclen; + unsigned int appcookielen; + EVP_MD_CTX *hctx; + EVP_PKEY *pkey; + int ret = EXT_RETURN_FAIL; + + if (s->ctx->app_gen_cookie_cb == NULL + || (s->s3->flags & TLS1_FLAGS_STATELESS) == 0) + return EXT_RETURN_NOT_SENT; + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_cookie) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_get_total_written(pkt, &startlen) + || !WPACKET_reserve_bytes(pkt, MAX_COOKIE_SIZE, &cookie) + || !WPACKET_put_bytes_u16(pkt, COOKIE_STATE_FORMAT_VERSION) + || !WPACKET_put_bytes_u16(pkt, TLS1_3_VERSION) + || !WPACKET_put_bytes_u16(pkt, s->s3->group_id) + || !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, pkt, + &ciphlen) + /* Is there a key_share extension present in this HRR? */ + || !WPACKET_put_bytes_u8(pkt, s->s3->peer_tmp == NULL) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_reserve_bytes(pkt, EVP_MAX_MD_SIZE, &hashval1)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + /* + * Get the hash of the initial ClientHello. ssl_handshake_hash() operates + * on raw buffers, so we first reserve sufficient bytes (above) and then + * subsequently allocate them (below) + */ + if (!ssl3_digest_cached_records(s, 0) + || !ssl_handshake_hash(s, hashval1, EVP_MAX_MD_SIZE, &hashlen)) { + /* SSLfatal() already called */ + return EXT_RETURN_FAIL; + } + + if (!WPACKET_allocate_bytes(pkt, hashlen, &hashval2) + || !ossl_assert(hashval1 == hashval2) + || !WPACKET_close(pkt) + || !WPACKET_start_sub_packet_u8(pkt) + || !WPACKET_reserve_bytes(pkt, SSL_COOKIE_LENGTH, &appcookie1)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + /* Generate the application cookie */ + if (s->ctx->app_gen_cookie_cb(s, appcookie1, &appcookielen) == 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + SSL_R_COOKIE_GEN_CALLBACK_FAILURE); + return EXT_RETURN_FAIL; + } + + if (!WPACKET_allocate_bytes(pkt, appcookielen, &appcookie2) + || !ossl_assert(appcookie1 == appcookie2) + || !WPACKET_close(pkt) + || !WPACKET_get_total_written(pkt, &totcookielen) + || !WPACKET_reserve_bytes(pkt, SHA256_DIGEST_LENGTH, &hmac)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + hmaclen = SHA256_DIGEST_LENGTH; + + totcookielen -= startlen; + if (!ossl_assert(totcookielen <= MAX_COOKIE_SIZE - SHA256_DIGEST_LENGTH)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + /* HMAC the cookie */ + hctx = EVP_MD_CTX_create(); + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, + s->session_ctx->ext.cookie_hmac_key, + sizeof(s->session_ctx->ext.cookie_hmac_key)); + if (hctx == NULL || pkey == NULL) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (EVP_DigestSignInit(hctx, NULL, EVP_sha256(), NULL, pkey) <= 0 + || EVP_DigestSign(hctx, hmac, &hmaclen, cookie, + totcookielen) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + goto err; + } + + if (!ossl_assert(totcookielen + hmaclen <= MAX_COOKIE_SIZE)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + goto err; + } + + if (!WPACKET_allocate_bytes(pkt, hmaclen, &hmac2) + || !ossl_assert(hmac == hmac2) + || !ossl_assert(cookie == hmac - totcookielen) + || !WPACKET_close(pkt) + || !WPACKET_close(pkt)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_COOKIE, + ERR_R_INTERNAL_ERROR); + goto err; + } + + ret = EXT_RETURN_SENT; + + err: + EVP_MD_CTX_free(hctx); + EVP_PKEY_free(pkey); + return ret; +} + EXT_RETURN tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx) diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 40fedb225f..31291383fd 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -1739,7 +1739,7 @@ static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL *s, * Re-initialise the Transcript Hash. We're going to prepopulate it with * a synthetic message_hash in place of ClientHello1. */ - if (!create_synthetic_message_hash(s)) { + if (!create_synthetic_message_hash(s, NULL, 0, NULL, 0)) { /* SSLfatal() already called */ goto err; } diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index 02d75e79ac..38b86c9ef2 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -2033,19 +2033,25 @@ int check_in_list(SSL *s, uint16_t group_id, const uint16_t *groups, #endif /* Replace ClientHello1 in the transcript hash with a synthetic message */ -int create_synthetic_message_hash(SSL *s) +int create_synthetic_message_hash(SSL *s, const unsigned char *hashval, + size_t hashlen, const unsigned char *hrr, + size_t hrrlen) { - unsigned char hashval[EVP_MAX_MD_SIZE]; - size_t hashlen = 0; + unsigned char hashvaltmp[EVP_MAX_MD_SIZE]; unsigned char msghdr[SSL3_HM_HEADER_LENGTH]; memset(msghdr, 0, sizeof(msghdr)); - /* Get the hash of the initial ClientHello */ - if (!ssl3_digest_cached_records(s, 0) - || !ssl_handshake_hash(s, hashval, sizeof(hashval), &hashlen)) { - /* SSLfatal() already called */ - return 0; + if (hashval == NULL) { + hashval = hashvaltmp; + hashlen = 0; + /* Get the hash of the initial ClientHello */ + if (!ssl3_digest_cached_records(s, 0) + || !ssl_handshake_hash(s, hashvaltmp, sizeof(hashvaltmp), + &hashlen)) { + /* SSLfatal() already called */ + return 0; + } } /* Reinitialise the transcript hash */ @@ -2063,6 +2069,20 @@ int create_synthetic_message_hash(SSL *s) return 0; } + /* + * Now re-inject the HRR and current message if appropriate (we just deleted + * it when we reinitialised the transcript hash above). Only necessary after + * receiving a ClientHello2 with a cookie. + */ + if (hrr != NULL + && (!ssl3_finish_mac(s, hrr, hrrlen) + || !ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, + s->s3->tmp.message_size + + SSL3_HM_HEADER_LENGTH))) { + /* SSLfatal() already called */ + return 0; + } + return 1; } diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h index eae9a361d7..38b0ce8ba7 100644 --- a/ssl/statem/statem_locl.h +++ b/ssl/statem/statem_locl.h @@ -56,7 +56,9 @@ typedef int (*confunc_f) (SSL *s, WPACKET *pkt); int check_in_list(SSL *s, uint16_t group_id, const uint16_t *groups, size_t num_groups, int checkallow); -int create_synthetic_message_hash(SSL *s); +int create_synthetic_message_hash(SSL *s, const unsigned char *hashval, + size_t hashlen, const unsigned char *hrr, + size_t hrrlen); int parse_ca_names(SSL *s, PACKET *pkt); int construct_ca_names(SSL *s, WPACKET *pkt); size_t construct_key_exchange_tbs(SSL *s, unsigned char **ptbs, @@ -223,6 +225,8 @@ int tls_parse_ctos_etm(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +int tls_parse_ctos_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, + size_t chainidx); int tls_parse_ctos_ems(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_ctos_psk_kex_modes(SSL *s, PACKET *pkt, unsigned int context, @@ -279,6 +283,8 @@ EXT_RETURN tls_construct_stoc_supported_versions(SSL *s, WPACKET *pkt, EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +EXT_RETURN tls_construct_stoc_cookie(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); /* * Not in public headers as this is not an official extension. Only used when * SSL_OP_CRYPTOPRO_TLSEXT_BUG is set. diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 70e026d0e4..211a4ca14d 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -2303,7 +2303,7 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt) * Re-initialise the Transcript Hash. We're going to prepopulate it with * a synthetic message_hash in place of ClientHello1. */ - if (!create_synthetic_message_hash(s)) { + if (!create_synthetic_message_hash(s, NULL, 0, NULL, 0)) { /* SSLfatal() already called */ return 0; }