aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crypto/err/openssl.txt2
-rw-r--r--include/openssl/dtls1.h4
-rw-r--r--include/openssl/ssl.h3
-rw-r--r--include/openssl/ssl3.h2
-rw-r--r--include/openssl/sslerr.h2
-rw-r--r--ssl/ssl_err.c4
-rw-r--r--ssl/ssl_lib.c4
-rw-r--r--ssl/ssl_locl.h2
-rw-r--r--ssl/statem/extensions.c4
-rw-r--r--ssl/statem/extensions_srvr.c375
-rw-r--r--ssl/statem/statem_clnt.c2
-rw-r--r--ssl/statem/statem_lib.c36
-rw-r--r--ssl/statem/statem_locl.h8
-rw-r--r--ssl/statem/statem_srvr.c2
14 files changed, 437 insertions, 13 deletions
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index e98bd95..0ed1b09 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 86a8981..aee8cfd 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 05a07eb..7aa98da 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 b781f61..6e8ef6b 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 fdb17a5..2431b49 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 1e3eb2c..111579b 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 8094e2f..1457fc6 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 de73f44..9247cf3 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 cc862f5..6022c49 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 30cbf9e..16f1b85 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -10,6 +10,30 @@
#include <openssl/ocsp.h>
#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 40fedb2..3129138 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 02d75e7..38b86c9 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 eae9a36..38b0ce8 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 70e026d..211a4ca 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;
}