Fix typo in CONTRIBUTING.md
[openssl.git] / ssl / statem / statem_clnt.c
index d07c45879c6966354156821261c634a6d3cbed56..6f73d5f698e88192b283c9e3ff5a518398c5a3d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
  * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
  * Copyright 2005 Nokia. All rights reserved.
  *
@@ -38,6 +38,11 @@ static int key_exchange_expected(SSL_CONNECTION *s);
 static int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
                                     WPACKET *pkt);
 
+static ossl_inline int received_server_cert(SSL_CONNECTION *sc)
+{
+    return sc->session->peer_rpk != NULL || sc->session->peer != NULL;
+}
+
 /*
  * Is a CertificateRequest message allowed at the moment or not?
  *
@@ -135,6 +140,13 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
                 st->hand_state = TLS_ST_CR_CERT;
                 return 1;
             }
+#ifndef OPENSSL_NO_COMP_ALG
+            if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+                    && s->ext.compress_certificate_sent) {
+                st->hand_state = TLS_ST_CR_COMP_CERT;
+                return 1;
+            }
+#endif
         }
         break;
 
@@ -143,9 +155,17 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
             st->hand_state = TLS_ST_CR_CERT;
             return 1;
         }
+#ifndef OPENSSL_NO_COMP_ALG
+        if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+                && s->ext.compress_certificate_sent) {
+            st->hand_state = TLS_ST_CR_COMP_CERT;
+            return 1;
+        }
+#endif
         break;
 
     case TLS_ST_CR_CERT:
+    case TLS_ST_CR_COMP_CERT:
         if (mt == SSL3_MT_CERTIFICATE_VERIFY) {
             st->hand_state = TLS_ST_CR_CERT_VRFY;
             return 1;
@@ -164,7 +184,7 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
             st->hand_state = TLS_ST_CR_SESSION_TICKET;
             return 1;
         }
-        if (mt == SSL3_MT_KEY_UPDATE) {
+        if (mt == SSL3_MT_KEY_UPDATE && !SSL_IS_QUIC_HANDSHAKE(s)) {
             st->hand_state = TLS_ST_CR_KEY_UPDATE;
             return 1;
         }
@@ -309,6 +329,7 @@ int ossl_statem_client_read_transition(SSL_CONNECTION *s, int mt)
         break;
 
     case TLS_ST_CR_CERT:
+    case TLS_ST_CR_COMP_CERT:
         /*
          * The CertificateStatus message is optional even if
          * |ext.status_expected| is set
@@ -403,6 +424,13 @@ int ossl_statem_client_read_transition(SSL_CONNECTION *s, int mt)
     return 0;
 }
 
+static int do_compressed_cert(SSL_CONNECTION *sc)
+{
+    /* If we negotiated RPK, we won't try to compress it */
+    return sc->ext.client_cert_type == TLSEXT_cert_type_x509
+        && sc->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none;
+}
+
 /*
  * ossl_statem_client13_write_transition() works out what handshake state to
  * move to next when the TLSv1.3 client is writing messages to be sent to the
@@ -425,7 +453,10 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
 
     case TLS_ST_CR_CERT_REQ:
         if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
-            st->hand_state = TLS_ST_CW_CERT;
+            if (do_compressed_cert(s))
+                st->hand_state = TLS_ST_CW_COMP_CERT;
+            else
+                st->hand_state = TLS_ST_CW_CERT;
             return WRITE_TRAN_CONTINUE;
         }
         /*
@@ -447,9 +478,14 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
         else if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
                  && s->hello_retry_request == SSL_HRR_NONE)
             st->hand_state = TLS_ST_CW_CHANGE;
+        else if (s->s3.tmp.cert_req == 0)
+            st->hand_state = TLS_ST_CW_FINISHED;
+        else if (do_compressed_cert(s))
+            st->hand_state = TLS_ST_CW_COMP_CERT;
         else
-            st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT
-                                                        : TLS_ST_CW_FINISHED;
+            st->hand_state = TLS_ST_CW_CERT;
+
+        s->ts_msg_read = ossl_time_now();
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_PENDING_EARLY_DATA_END:
@@ -461,10 +497,15 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
 
     case TLS_ST_CW_END_OF_EARLY_DATA:
     case TLS_ST_CW_CHANGE:
-        st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT
-                                                    : TLS_ST_CW_FINISHED;
+        if (s->s3.tmp.cert_req == 0)
+            st->hand_state = TLS_ST_CW_FINISHED;
+        else if (do_compressed_cert(s))
+            st->hand_state = TLS_ST_CW_COMP_CERT;
+        else
+            st->hand_state = TLS_ST_CW_CERT;
         return WRITE_TRAN_CONTINUE;
 
+    case TLS_ST_CW_COMP_CERT:
     case TLS_ST_CW_CERT:
         /* If a non-empty Certificate we also send CertificateVerify */
         st->hand_state = (s->s3.tmp.cert_req == 1) ? TLS_ST_CW_CERT_VRFY
@@ -545,6 +586,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL_CONNECTION *s)
          * No transition at the end of writing because we don't know what
          * we will be sent
          */
+        s->ts_msg_write = ossl_time_now();
         return WRITE_TRAN_FINISHED;
 
     case TLS_ST_CR_SRVR_HELLO:
@@ -561,6 +603,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL_CONNECTION *s)
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_EARLY_DATA:
+        s->ts_msg_write = ossl_time_now();
         return WRITE_TRAN_FINISHED;
 
     case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
@@ -568,6 +611,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL_CONNECTION *s)
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_CR_SRVR_DONE:
+        s->ts_msg_read = ossl_time_now();
         if (s->s3.tmp.cert_req)
             st->hand_state = TLS_ST_CW_CERT;
         else
@@ -682,6 +726,23 @@ WORK_STATE ossl_statem_client_pre_work(SSL_CONNECTION *s, WORK_STATE wst)
                 /* SSLfatal() already called */
                 return WORK_ERROR;
             }
+        } else if (s->ext.early_data == SSL_EARLY_DATA_REJECTED) {
+            /*
+             * This must be a second ClientHello after an HRR following an
+             * earlier rejected attempt to send early data. Since we were
+             * previously encrypting the early data we now need to reset the
+             * write record layer in order to write in plaintext again.
+             */
+            if (!ssl_set_new_record_layer(s,
+                                          TLS_ANY_VERSION,
+                                          OSSL_RECORD_DIRECTION_WRITE,
+                                          OSSL_RECORD_PROTECTION_LEVEL_NONE,
+                                          NULL, 0, NULL, 0, NULL, 0, NULL,  0,
+                                          NULL, 0, NID_undef, NULL, NULL,
+                                          NULL)) {
+                /* SSLfatal already called */
+                return WORK_ERROR;
+            }
         }
         break;
 
@@ -767,15 +828,6 @@ WORK_STATE ossl_statem_client_post_work(SSL_CONNECTION *s, WORK_STATE wst)
         }
         break;
 
-    case TLS_ST_CW_END_OF_EARLY_DATA:
-        /*
-         * We set the enc_write_ctx back to NULL because we may end up writing
-         * in cleartext again if we get a HelloRetryRequest from the server.
-         */
-        EVP_CIPHER_CTX_free(s->enc_write_ctx);
-        s->enc_write_ctx = NULL;
-        break;
-
     case TLS_ST_CW_KEY_EXCH:
         if (tls_client_key_exchange_post_work(s) == 0) {
             /* SSLfatal() already called */
@@ -819,20 +871,16 @@ WORK_STATE ossl_statem_client_post_work(SSL_CONNECTION *s, WORK_STATE wst)
             return WORK_ERROR;
         }
 
-        if (SSL_CONNECTION_IS_DTLS(s)) {
 #ifndef OPENSSL_NO_SCTP
-            if (s->hit) {
-                /*
-                 * Change to new shared key of SCTP-Auth, will be ignored if
-                 * no SCTP used.
-                 */
-                BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
-                         0, NULL);
-            }
-#endif
-
-            dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
+        if (SSL_CONNECTION_IS_DTLS(s) && s->hit) {
+            /*
+            * Change to new shared key of SCTP-Auth, will be ignored if
+            * no SCTP used.
+            */
+            BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY,
+                     0, NULL);
         }
+#endif
         break;
 
     case TLS_ST_CW_FINISHED:
@@ -924,6 +972,13 @@ int ossl_statem_client_construct_message(SSL_CONNECTION *s,
         *mt = SSL3_MT_CERTIFICATE;
         break;
 
+#ifndef OPENSSL_NO_COMP_ALG
+    case TLS_ST_CW_COMP_CERT:
+        *confunc = tls_construct_client_compressed_certificate;
+        *mt = SSL3_MT_COMPRESSED_CERTIFICATE;
+        break;
+#endif
+
     case TLS_ST_CW_KEY_EXCH:
         *confunc = tls_construct_client_key_exchange;
         *mt = SSL3_MT_CLIENT_KEY_EXCHANGE;
@@ -973,11 +1028,12 @@ size_t ossl_statem_client_max_message_size(SSL_CONNECTION *s)
     case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
         return HELLO_VERIFY_REQUEST_MAX_LENGTH;
 
+    case TLS_ST_CR_COMP_CERT:
     case TLS_ST_CR_CERT:
         return s->max_cert_list;
 
     case TLS_ST_CR_CERT_VRFY:
-        return SSL3_RT_MAX_PLAIN_LENGTH;
+        return CERTIFICATE_VERIFY_MAX_LENGTH;
 
     case TLS_ST_CR_CERT_STATUS:
         return SSL3_RT_MAX_PLAIN_LENGTH;
@@ -1039,6 +1095,11 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL_CONNECTION *s,
     case TLS_ST_CR_CERT:
         return tls_process_server_certificate(s, pkt);
 
+#ifndef OPENSSL_NO_COMP_ALG
+    case TLS_ST_CR_COMP_CERT:
+        return tls_process_server_compressed_certificate(s, pkt);
+#endif
+
     case TLS_ST_CR_CERT_VRFY:
         return tls_process_cert_verify(s, pkt);
 
@@ -1090,6 +1151,7 @@ WORK_STATE ossl_statem_client_post_process_message(SSL_CONNECTION *s,
         return WORK_ERROR;
 
     case TLS_ST_CR_CERT:
+    case TLS_ST_CR_COMP_CERT:
         return tls_post_process_server_certificate(s, wst);
 
     case TLS_ST_CR_CERT_VRFY:
@@ -1361,6 +1423,10 @@ static int set_client_ciphersuite(SSL_CONNECTION *s,
         if (SSL_CONNECTION_IS_TLS13(s)) {
             const EVP_MD *md = ssl_md(sctx, c->algorithm2);
 
+            if (!ossl_assert(s->session->cipher != NULL)) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
             /*
              * In TLSv1.3 it is valid for the server to select a different
              * ciphersuite as long as the hash is the same.
@@ -1716,12 +1782,29 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL_CONNECTION *s, PACKET *pkt)
      * In TLSv1.3 we have some post-processing to change cipher state, otherwise
      * we're done with this message
      */
-    if (SSL_CONNECTION_IS_TLS13(s)
-            && (!ssl->method->ssl3_enc->setup_key_block(s)
+    if (SSL_CONNECTION_IS_TLS13(s)) {
+        if (!ssl->method->ssl3_enc->setup_key_block(s)
                 || !ssl->method->ssl3_enc->change_cipher_state(s,
-                    SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_READ))) {
-        /* SSLfatal() already called */
-        goto err;
+                    SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_READ)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+        /*
+         * If we're not doing early-data and we're not going to send a dummy CCS
+         * (i.e. no middlebox compat mode) then we can change the write keys
+         * immediately. Otherwise we have to defer this until after all possible
+         * early data is written. We could just always defer until the last
+         * moment except QUIC needs it done at the same time as the read keys
+         * are changed. Since QUIC doesn't do TLS early data or need middlebox
+         * compat this doesn't cause a problem.
+         */
+        if (s->early_data_state == SSL_EARLY_DATA_NONE
+                && (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) == 0
+                && !ssl->method->ssl3_enc->change_cipher_state(s,
+                    SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
     }
 
     OPENSSL_free(extensions);
@@ -1737,11 +1820,21 @@ static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL_CONNECTION *s,
     RAW_EXTENSION *extensions = NULL;
 
     /*
-     * If we were sending early_data then the enc_write_ctx is now invalid and
-     * should not be used.
+     * If we were sending early_data then any alerts should not be sent using
+     * the old wrlmethod.
      */
-    EVP_CIPHER_CTX_free(s->enc_write_ctx);
-    s->enc_write_ctx = NULL;
+    if (s->early_data_state == SSL_EARLY_DATA_FINISHED_WRITING
+            && !ssl_set_new_record_layer(s,
+                                         TLS_ANY_VERSION,
+                                         OSSL_RECORD_DIRECTION_WRITE,
+                                         OSSL_RECORD_PROTECTION_LEVEL_NONE,
+                                         NULL, 0, NULL, 0, NULL, 0, NULL,  0,
+                                         NULL, 0, NID_undef, NULL, NULL, NULL)) {
+        /* SSLfatal already called */
+        goto err;
+    }
+    /* We are definitely going to be using TLSv1.3 */
+    s->rlayer.wrlmethod->set_protocol_version(s->rlayer.wrl, TLS1_3_VERSION);
 
     if (!tls_collect_extensions(s, extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                 &extensions, NULL, 1)
@@ -1790,6 +1883,81 @@ static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL_CONNECTION *s,
     return MSG_PROCESS_ERROR;
 }
 
+MSG_PROCESS_RETURN tls_process_server_rpk(SSL_CONNECTION *sc, PACKET *pkt)
+{
+    EVP_PKEY *peer_rpk;
+
+    if (!tls_process_rpk(sc, pkt, &peer_rpk)) {
+        /* SSLfatal() already called */
+        return MSG_PROCESS_ERROR;
+    }
+
+    if (peer_rpk == NULL) {
+        SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_CERTIFICATE);
+        return MSG_PROCESS_ERROR;
+    }
+
+    EVP_PKEY_free(sc->session->peer_rpk);
+    sc->session->peer_rpk = peer_rpk;
+
+    return MSG_PROCESS_CONTINUE_PROCESSING;
+}
+
+static WORK_STATE tls_post_process_server_rpk(SSL_CONNECTION *sc,
+                                              WORK_STATE wst)
+{
+    size_t certidx;
+    const SSL_CERT_LOOKUP *clu;
+
+    if (sc->session->peer_rpk == NULL) {
+        SSLfatal(sc, SSL_AD_ILLEGAL_PARAMETER,
+                 SSL_R_INVALID_RAW_PUBLIC_KEY);
+        return WORK_ERROR;
+    }
+
+    if (sc->rwstate == SSL_RETRY_VERIFY)
+        sc->rwstate = SSL_NOTHING;
+    if (ssl_verify_rpk(sc, sc->session->peer_rpk) > 0
+            && sc->rwstate == SSL_RETRY_VERIFY)
+        return WORK_MORE_A;
+
+    if ((clu = ssl_cert_lookup_by_pkey(sc->session->peer_rpk, &certidx,
+                                       SSL_CONNECTION_GET_CTX(sc))) == NULL) {
+        SSLfatal(sc, SSL_AD_ILLEGAL_PARAMETER, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+        return WORK_ERROR;
+    }
+
+    /*
+     * Check certificate type is consistent with ciphersuite. For TLS 1.3
+     * skip check since TLS 1.3 ciphersuites can be used with any certificate
+     * type.
+     */
+    if (!SSL_CONNECTION_IS_TLS13(sc)) {
+        if ((clu->amask & sc->s3.tmp.new_cipher->algorithm_auth) == 0) {
+            SSLfatal(sc, SSL_AD_ILLEGAL_PARAMETER, SSL_R_WRONG_RPK_TYPE);
+            return WORK_ERROR;
+        }
+    }
+
+    /* Ensure there is no peer/peer_chain */
+    X509_free(sc->session->peer);
+    sc->session->peer = NULL;
+    sk_X509_pop_free(sc->session->peer_chain, X509_free);
+    sc->session->peer_chain = NULL;
+    sc->session->verify_result = sc->verify_result;
+
+    /* Save the current hash state for when we receive the CertificateVerify */
+    if (SSL_CONNECTION_IS_TLS13(sc)
+            && !ssl_handshake_hash(sc, sc->cert_verify_hash,
+                                   sizeof(sc->cert_verify_hash),
+                                   &sc->cert_verify_hash_len)) {
+        /* SSLfatal() already called */
+        return WORK_ERROR;
+    }
+
+    return WORK_FINISHED_CONTINUE;
+}
+
 /* prepare server cert verification by setting s->session->peer_chain from pkt */
 MSG_PROCESS_RETURN tls_process_server_certificate(SSL_CONNECTION *s,
                                                   PACKET *pkt)
@@ -1801,6 +1969,14 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL_CONNECTION *s,
     unsigned int context = 0;
     SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
 
+    if (s->ext.server_cert_type == TLSEXT_cert_type_rpk)
+        return tls_process_server_rpk(s, pkt);
+    if (s->ext.server_cert_type != TLSEXT_cert_type_x509) {
+        SSLfatal(s, SSL_AD_UNSUPPORTED_CERTIFICATE,
+                 SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+        goto err;
+    }
+
     if ((s->session->peer_chain = sk_X509_new_null()) == NULL) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
         goto err;
@@ -1888,6 +2064,9 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
     size_t certidx;
     int i;
 
+    if (s->ext.server_cert_type == TLSEXT_cert_type_rpk)
+        return tls_post_process_server_rpk(s, wst);
+
     if (s->rwstate == SSL_RETRY_VERIFY)
         s->rwstate = SSL_NOTHING;
     i = ssl_verify_cert_chain(s, s->session->peer_chain);
@@ -1929,7 +2108,8 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
         return WORK_ERROR;
     }
 
-    if ((clu = ssl_cert_lookup_by_pkey(pkey, &certidx)) == NULL) {
+    if ((clu = ssl_cert_lookup_by_pkey(pkey, &certidx,
+                                      SSL_CONNECTION_GET_CTX(s))) == NULL) {
         SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
         return WORK_ERROR;
     }
@@ -1949,6 +2129,9 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
     X509_up_ref(x);
     s->session->peer = x;
     s->session->verify_result = s->verify_result;
+    /* Ensure there is no RPK */
+    EVP_PKEY_free(s->session->peer_rpk);
+    s->session->peer_rpk = NULL;
 
     /* Save the current hash state for when we receive the CertificateVerify */
     if (SSL_CONNECTION_IS_TLS13(s)
@@ -1961,6 +2144,21 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
     return WORK_FINISHED_CONTINUE;
 }
 
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt)
+{
+    MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+    PACKET tmppkt;
+    BUF_MEM *buf = BUF_MEM_new();
+
+    if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR)
+        ret = tls_process_server_certificate(sc, &tmppkt);
+
+    BUF_MEM_free(buf);
+    return ret;
+}
+#endif
+
 static int tls_process_ske_psk_preamble(SSL_CONNECTION *s, PACKET *pkt)
 {
 #ifndef OPENSSL_NO_PSK
@@ -2036,7 +2234,7 @@ static int tls_process_ske_srp(SSL_CONNECTION *s, PACKET *pkt, EVP_PKEY **pkey)
 
     /* We must check if there is a certificate */
     if (s->s3.tmp.new_cipher->algorithm_auth & (SSL_aRSA | SSL_aDSS))
-        *pkey = X509_get0_pubkey(s->session->peer);
+        *pkey = tls_get_peer_pkey(s);
 
     return 1;
 #else
@@ -2125,7 +2323,7 @@ static int tls_process_ske_dhe(SSL_CONNECTION *s, PACKET *pkt, EVP_PKEY **pkey)
      * public keys. We should have a less ad-hoc way of doing this
      */
     if (s->s3.tmp.new_cipher->algorithm_auth & (SSL_aRSA | SSL_aDSS))
-        *pkey = X509_get0_pubkey(s->session->peer);
+        *pkey = tls_get_peer_pkey(s);
     /* else anonymous DH, so no certificate or pkey. */
 
     ret = 1;
@@ -2190,9 +2388,9 @@ static int tls_process_ske_ecdhe(SSL_CONNECTION *s, PACKET *pkt, EVP_PKEY **pkey
      * and ECDSA.
      */
     if (s->s3.tmp.new_cipher->algorithm_auth & SSL_aECDSA)
-        *pkey = X509_get0_pubkey(s->session->peer);
+        *pkey = tls_get_peer_pkey(s);
     else if (s->s3.tmp.new_cipher->algorithm_auth & SSL_aRSA)
-        *pkey = X509_get0_pubkey(s->session->peer);
+        *pkey = tls_get_peer_pkey(s);
     /* else anonymous ECDH, so no certificate or pkey. */
 
     /* Cache the agreed upon group in the SSL_SESSION */
@@ -2276,7 +2474,8 @@ MSG_PROCESS_RETURN tls_process_key_exchange(SSL_CONNECTION *s, PACKET *pkt)
                 goto err;
             }
         } else if (!tls1_set_peer_legacy_sigalg(s, pkey)) {
-            SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+            SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                     SSL_R_LEGACY_SIGALG_DISALLOWED_OR_UNSUPPORTED);
             goto err;
         }
 
@@ -2359,11 +2558,15 @@ MSG_PROCESS_RETURN tls_process_key_exchange(SSL_CONNECTION *s, PACKET *pkt)
 MSG_PROCESS_RETURN tls_process_certificate_request(SSL_CONNECTION *s,
                                                    PACKET *pkt)
 {
-    size_t i;
-
     /* Clear certificate validity flags */
-    for (i = 0; i < SSL_PKEY_NUM; i++)
-        s->s3.tmp.valid_flags[i] = 0;
+    if (s->s3.tmp.valid_flags != NULL)
+        memset(s->s3.tmp.valid_flags, 0, s->ssl_pkey_num * sizeof(uint32_t));
+    else
+        s->s3.tmp.valid_flags = OPENSSL_zalloc(s->ssl_pkey_num * sizeof(uint32_t));
+
+    /* Give up for good if allocation didn't work */
+    if (s->s3.tmp.valid_flags == NULL)
+        return 0;
 
     if (SSL_CONNECTION_IS_TLS13(s)) {
         PACKET reqctx, extensions;
@@ -2865,7 +3068,7 @@ static int tls_construct_cke_rsa(SSL_CONNECTION *s, WPACKET *pkt)
     size_t pmslen = 0;
     SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
 
-    if (s->session->peer == NULL) {
+    if (!received_server_cert(s)) {
         /*
          * We should always have a server certificate with SSL_kRSA.
          */
@@ -2873,7 +3076,11 @@ static int tls_construct_cke_rsa(SSL_CONNECTION *s, WPACKET *pkt)
         return 0;
     }
 
-    pkey = X509_get0_pubkey(s->session->peer);
+    if ((pkey = tls_get_peer_pkey(s)) == NULL) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
     if (!EVP_PKEY_is_a(pkey, "RSA")) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return 0;
@@ -3048,7 +3255,7 @@ static int tls_construct_cke_gost(SSL_CONNECTION *s, WPACKET *pkt)
 #ifndef OPENSSL_NO_GOST
     /* GOST key exchange message creation */
     EVP_PKEY_CTX *pkey_ctx = NULL;
-    X509 *peer_cert;
+    EVP_PKEY *pkey = NULL;
     size_t msglen;
     unsigned int md_len;
     unsigned char shared_ukm[32], tmp[256];
@@ -3064,15 +3271,14 @@ static int tls_construct_cke_gost(SSL_CONNECTION *s, WPACKET *pkt)
     /*
      * Get server certificate PKEY and create ctx from it
      */
-    peer_cert = s->session->peer;
-    if (peer_cert == NULL) {
+    if ((pkey = tls_get_peer_pkey(s)) == NULL) {
         SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,
                  SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER);
         return 0;
     }
 
     pkey_ctx = EVP_PKEY_CTX_new_from_pkey(sctx->libctx,
-                                          X509_get0_pubkey(peer_cert),
+                                          pkey,
                                           sctx->propq);
     if (pkey_ctx == NULL) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
@@ -3167,7 +3373,7 @@ int ossl_gost18_cke_cipher_nid(const SSL_CONNECTION *s)
 
 int ossl_gost_ukm(const SSL_CONNECTION *s, unsigned char *dgst_buf)
 {
-    EVP_MD_CTX * hash = NULL;
+    EVP_MD_CTX *hash = NULL;
     unsigned int md_len;
     SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
     const EVP_MD *md = ssl_evp_md_fetch(sctx->libctx, NID_id_GostR3411_2012_256,
@@ -3199,7 +3405,7 @@ static int tls_construct_cke_gost18(SSL_CONNECTION *s, WPACKET *pkt)
     unsigned char rnd_dgst[32];
     unsigned char *encdata = NULL;
     EVP_PKEY_CTX *pkey_ctx = NULL;
-    X509 *peer_cert;
+    EVP_PKEY *pkey;
     unsigned char *pms = NULL;
     size_t pmslen = 0;
     size_t msglen;
@@ -3230,15 +3436,14 @@ static int tls_construct_cke_gost18(SSL_CONNECTION *s, WPACKET *pkt)
     }
 
      /* Get server certificate PKEY and create ctx from it */
-    peer_cert = s->session->peer;
-    if (peer_cert == NULL) {
+    if ((pkey = tls_get_peer_pkey(s)) == NULL) {
         SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,
                  SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER);
         goto err;
     }
 
     pkey_ctx = EVP_PKEY_CTX_new_from_pkey(sctx->libctx,
-                                          X509_get0_pubkey(peer_cert),
+                                          pkey,
                                           sctx->propq);
     if (pkey_ctx == NULL) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
@@ -3524,6 +3729,7 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
                 return WORK_FINISHED_CONTINUE;
             } else {
                 s->s3.tmp.cert_req = 2;
+                s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
                 if (!ssl3_digest_cached_records(s, 0)) {
                     /* SSLfatal() already called */
                     return WORK_ERROR;
@@ -3531,6 +3737,10 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
             }
         }
 
+        if (!SSL_CONNECTION_IS_TLS13(s)
+                || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+            s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
+
         if (s->post_handshake_auth == SSL_PHA_REQUESTED)
             return WORK_FINISHED_STOP;
         return WORK_FINISHED_CONTINUE;
@@ -3544,6 +3754,7 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
 CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
                                                  WPACKET *pkt)
 {
+    CERT_PKEY *cpk = NULL;
     SSL *ssl = SSL_CONNECTION_GET_SSL(s);
 
     if (SSL_CONNECTION_IS_TLS13(s)) {
@@ -3558,15 +3769,35 @@ CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
             return CON_FUNC_ERROR;
         }
     }
-    if (!ssl3_output_cert_chain(s, pkt,
-                                (s->s3.tmp.cert_req == 2) ? NULL
-                                                           : s->cert->key)) {
-        /* SSLfatal() already called */
+    if (s->s3.tmp.cert_req != 2)
+        cpk = s->cert->key;
+    switch (s->ext.client_cert_type) {
+    case TLSEXT_cert_type_rpk:
+        if (!tls_output_rpk(s, pkt, cpk)) {
+            /* SSLfatal() already called */
+            return CON_FUNC_ERROR;
+        }
+        break;
+    case TLSEXT_cert_type_x509:
+        if (!ssl3_output_cert_chain(s, pkt, cpk, 0)) {
+            /* SSLfatal() already called */
+            return CON_FUNC_ERROR;
+        }
+        break;
+    default:
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
         return CON_FUNC_ERROR;
     }
 
+    /*
+     * If we attempted to write early data or we're in middlebox compat mode
+     * then we deferred changing the handshake write keys to the last possible
+     * moment. We need to do it now.
+     */
     if (SSL_CONNECTION_IS_TLS13(s)
             && SSL_IS_FIRST_HANDSHAKE(s)
+            && (s->early_data_state != SSL_EARLY_DATA_NONE
+                || (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0)
             && (!ssl->method->ssl3_enc->change_cipher_state(s,
                     SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE))) {
         /*
@@ -3580,11 +3811,113 @@ CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
     return CON_FUNC_SUCCESS;
 }
 
+#ifndef OPENSSL_NO_COMP_ALG
+CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc,
+                                                            WPACKET *pkt)
+{
+    SSL *ssl = SSL_CONNECTION_GET_SSL(sc);
+    WPACKET tmppkt;
+    BUF_MEM *buf = NULL;
+    size_t length;
+    size_t max_length;
+    COMP_METHOD *method;
+    COMP_CTX *comp = NULL;
+    int comp_len;
+    int ret = 0;
+    int alg = sc->ext.compress_certificate_from_peer[0];
+
+    /* Note that sc->s3.tmp.cert_req == 2 is checked in write transition */
+
+    if ((buf = BUF_MEM_new()) == NULL || !WPACKET_init(&tmppkt, buf))
+        goto err;
+
+    /* Use the |tmppkt| for the to-be-compressed data */
+    if (sc->pha_context == NULL) {
+        /* no context available, add 0-length context */
+        if (!WPACKET_put_bytes_u8(&tmppkt, 0))
+            goto err;
+    } else if (!WPACKET_sub_memcpy_u8(&tmppkt, sc->pha_context, sc->pha_context_len))
+        goto err;
+
+    if (!ssl3_output_cert_chain(sc, &tmppkt, sc->cert->key, 0)) {
+        /* SSLfatal() already called */
+        goto out;
+    }
+
+    /* continue with the real |pkt| */
+    if (!WPACKET_put_bytes_u16(pkt, alg)
+            || !WPACKET_get_total_written(&tmppkt, &length)
+            || !WPACKET_put_bytes_u24(pkt, length))
+        goto err;
+
+    switch (alg) {
+    case TLSEXT_comp_cert_zlib:
+        method = COMP_zlib_oneshot();
+        break;
+    case TLSEXT_comp_cert_brotli:
+        method = COMP_brotli_oneshot();
+        break;
+    case TLSEXT_comp_cert_zstd:
+        method = COMP_zstd_oneshot();
+        break;
+    default:
+        goto err;
+    }
+    max_length = ossl_calculate_comp_expansion(alg, length);
+
+    if ((comp = COMP_CTX_new(method)) == NULL
+            || !WPACKET_start_sub_packet_u24(pkt)
+            || !WPACKET_reserve_bytes(pkt, max_length, NULL))
+        goto err;
+
+    comp_len = COMP_compress_block(comp, WPACKET_get_curr(pkt), max_length,
+                                   (unsigned char *)buf->data, length);
+    if (comp_len <= 0)
+        goto err;
+
+    if (!WPACKET_allocate_bytes(pkt, comp_len, NULL)
+            || !WPACKET_close(pkt))
+        goto err;
+
+    /*
+     * If we attempted to write early data or we're in middlebox compat mode
+     * then we deferred changing the handshake write keys to the last possible
+     * moment. We need to do it now.
+     */
+    if (SSL_IS_FIRST_HANDSHAKE(sc)
+            && (sc->early_data_state != SSL_EARLY_DATA_NONE
+                || (sc->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0)
+            && (!ssl->method->ssl3_enc->change_cipher_state(sc,
+                    SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE))) {
+        /*
+         * This is a fatal error, which leaves sc->enc_write_ctx in an
+         * inconsistent state and thus ssl3_send_alert may crash.
+         */
+        SSLfatal(sc, SSL_AD_NO_ALERT, SSL_R_CANNOT_CHANGE_CIPHER);
+        goto out;
+    }
+    ret = 1;
+    goto out;
+
+ err:
+    SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ out:
+    if (buf != NULL) {
+        /* If |buf| is NULL, then |tmppkt| could not have been initialized */
+        WPACKET_cleanup(&tmppkt);
+    }
+    BUF_MEM_free(buf);
+    COMP_CTX_free(comp);
+    return ret;
+}
+#endif
+
 int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s)
 {
     const SSL_CERT_LOOKUP *clu;
     size_t idx;
     long alg_k, alg_a;
+    EVP_PKEY *pkey;
 
     alg_k = s->s3.tmp.new_cipher->algorithm_mkey;
     alg_a = s->s3.tmp.new_cipher->algorithm_auth;
@@ -3594,7 +3927,8 @@ int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s)
         return 1;
 
     /* This is the passed certificate */
-    clu = ssl_cert_lookup_by_pkey(X509_get0_pubkey(s->session->peer), &idx);
+    pkey = tls_get_peer_pkey(s);
+    clu = ssl_cert_lookup_by_pkey(pkey, &idx, SSL_CONNECTION_GET_CTX(s));
 
     /* Check certificate is recognised and suitable for cipher */
     if (clu == NULL || (alg_a & clu->amask) == 0) {
@@ -3602,13 +3936,6 @@ int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s)
         return 0;
     }
 
-    if (clu->amask & SSL_aECDSA) {
-        if (ssl_check_srvr_ecc_cert_and_alg(s->session->peer, s))
-            return 1;
-        SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_BAD_ECC_CERT);
-        return 0;
-    }
-
     if (alg_k & (SSL_kRSA | SSL_kRSAPSK) && idx != SSL_PKEY_RSA) {
         SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE,
                  SSL_R_MISSING_RSA_ENCRYPTING_CERT);
@@ -3620,6 +3947,17 @@ int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s)
         return 0;
     }
 
+    /* Early out to skip the checks below */
+    if (s->session->peer_rpk != NULL)
+        return 1;
+
+    if (clu->amask & SSL_aECDSA) {
+        if (ssl_check_srvr_ecc_cert_and_alg(s->session->peer, s))
+            return 1;
+        SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_BAD_ECC_CERT);
+        return 0;
+    }
+
     return 1;
 }
 
@@ -3725,7 +4063,10 @@ int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
 {
     int i;
     size_t totlen = 0, len, maxlen, maxverok = 0;
-    int empty_reneg_info_scsv = !s->renegotiate;
+    int empty_reneg_info_scsv = !s->renegotiate
+                                && !SSL_CONNECTION_IS_DTLS(s)
+                                && ssl_security(s, SSL_SECOP_VERSION, 0, TLS1_VERSION, NULL)
+                                && s->min_proto_version <= TLS1_VERSION;
     SSL *ssl = SSL_CONNECTION_GET_SSL(s);
 
     /* Set disabled masks for this session */
@@ -3775,15 +4116,12 @@ int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
 
         /* Sanity check that the maximum version we offer has ciphers enabled */
         if (!maxverok) {
-            if (SSL_CONNECTION_IS_DTLS(s)) {
-                if (DTLS_VERSION_GE(c->max_dtls, s->s3.tmp.max_ver)
-                        && DTLS_VERSION_LE(c->min_dtls, s->s3.tmp.max_ver))
-                    maxverok = 1;
-            } else {
-                if (c->max_tls >= s->s3.tmp.max_ver
-                        && c->min_tls <= s->s3.tmp.max_ver)
-                    maxverok = 1;
-            }
+            int minproto = SSL_CONNECTION_IS_DTLS(s) ? c->min_dtls : c->min_tls;
+            int maxproto = SSL_CONNECTION_IS_DTLS(s) ? c->max_dtls : c->max_tls;
+
+            if (ssl_version_cmp(s, maxproto, s->s3.tmp.max_ver) >= 0
+                    && ssl_version_cmp(s, minproto, s->s3.tmp.max_ver) <= 0)
+                maxverok = 1;
         }
 
         totlen += len;
@@ -3802,7 +4140,7 @@ int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
 
     if (totlen != 0) {
         if (empty_reneg_info_scsv) {
-            static SSL_CIPHER scsv = {
+            static const SSL_CIPHER scsv = {
                 0, NULL, NULL, SSL3_CK_SCSV, 0, 0, 0, 0, 0, 0, 0, 0, 0
             };
             if (!ssl->method->put_cipher_by_char(&scsv, pkt, &len)) {
@@ -3811,7 +4149,7 @@ int ssl_cipher_list_to_bytes(SSL_CONNECTION *s, STACK_OF(SSL_CIPHER) *sk,
             }
         }
         if (s->mode & SSL_MODE_SEND_FALLBACK_SCSV) {
-            static SSL_CIPHER scsv = {
+            static const SSL_CIPHER scsv = {
                 0, NULL, NULL, SSL3_CK_FALLBACK_SCSV, 0, 0, 0, 0, 0, 0, 0, 0, 0
             };
             if (!ssl->method->put_cipher_by_char(&scsv, pkt, &len)) {