Add support for sending TLSv1.3 cookies
authorMatt Caswell <matt@openssl.org>
Mon, 11 Sep 2017 14:43:56 +0000 (15:43 +0100)
committerMatt Caswell <matt@openssl.org>
Wed, 24 Jan 2018 18:02:35 +0000 (18:02 +0000)
This just adds the various extension functions. More changes will be
required to actually use them.

Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from https://github.com/openssl/openssl/pull/4435)

14 files changed:
crypto/err/openssl.txt
include/openssl/dtls1.h
include/openssl/ssl.h
include/openssl/ssl3.h
include/openssl/sslerr.h
ssl/ssl_err.c
ssl/ssl_lib.c
ssl/ssl_locl.h
ssl/statem/extensions.c
ssl/statem/extensions_srvr.c
ssl/statem/statem_clnt.c
ssl/statem/statem_lib.c
ssl/statem/statem_locl.h
ssl/statem/statem_srvr.c

index e98bd95..0ed1b09 100644 (file)
@@ -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
index 86a8981..aee8cfd 100644 (file)
@@ -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
index 05a07eb..7aa98da 100644 (file)
@@ -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.
index b781f61..6e8ef6b 100644 (file)
@@ -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
index fdb17a5..2431b49 100644 (file)
@@ -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
index 1e3eb2c..111579b 100644 (file)
@@ -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),
index 8094e2f..1457fc6 100644 (file)
@@ -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;
index de73f44..9247cf3 100644 (file)
@@ -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
index cc862f5..6022c49 100644 (file)
@@ -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
     },
     {
         /*
index 30cbf9e..16f1b85 100644 (file)
 #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)
index 40fedb2..3129138 100644 (file)
@@ -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;
     }
index 02d75e7..38b86c9 100644 (file)
@@ -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;
 }
 
index eae9a36..38b0ce8 100644 (file)
@@ -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.
index 70e026d..211a4ca 100644 (file)
@@ -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;
         }