summaryrefslogtreecommitdiff
path: root/ssl/statem
diff options
context:
space:
mode:
authorTodd Short <tshort@akamai.com>2021-01-27 14:23:33 -0500
committerTodd Short <todd.short@me.com>2023-03-28 13:49:54 -0400
commit3c95ef22df55cb2d9dc64ce1f3be6e5a8ee63206 (patch)
tree0f7fcff4ec4735c778595db4f4a85bce70715d8b /ssl/statem
parent5ab3f71a33cb0140fc29ae9244cd4f8331c2f3a5 (diff)
downloadopenssl-new-3c95ef22df55cb2d9dc64ce1f3be6e5a8ee63206.tar.gz
RFC7250 (RPK) support
Add support for the RFC7250 certificate-type extensions. Alows the use of only private keys for connection (i.e. certs not needed). Add APIs Add unit tests Add documentation Add s_client/s_server support Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Viktor Dukhovni <viktor@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18185)
Diffstat (limited to 'ssl/statem')
-rw-r--r--ssl/statem/extensions.c60
-rw-r--r--ssl/statem/extensions_clnt.c106
-rw-r--r--ssl/statem/extensions_cust.c5
-rw-r--r--ssl/statem/extensions_srvr.c163
-rw-r--r--ssl/statem/statem_clnt.c184
-rw-r--r--ssl/statem/statem_lib.c292
-rw-r--r--ssl/statem/statem_local.h36
-rw-r--r--ssl/statem/statem_srvr.c145
8 files changed, 940 insertions, 51 deletions
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index d164719476..3bb7c4af26 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -34,6 +34,8 @@ static int init_alpn(SSL_CONNECTION *s, unsigned int context);
static int final_alpn(SSL_CONNECTION *s, unsigned int context, int sent);
static int init_sig_algs_cert(SSL_CONNECTION *s, unsigned int context);
static int init_sig_algs(SSL_CONNECTION *s, unsigned int context);
+static int init_server_cert_type(SSL_CONNECTION *sc, unsigned int context);
+static int init_client_cert_type(SSL_CONNECTION *sc, unsigned int context);
static int init_certificate_authorities(SSL_CONNECTION *s,
unsigned int context);
static EXT_RETURN tls_construct_certificate_authorities(SSL_CONNECTION *s,
@@ -310,6 +312,24 @@ static const EXTENSION_DEFINITION ext_defs[] = {
NULL,
},
{
+ TLSEXT_TYPE_client_cert_type,
+ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
+ | SSL_EXT_TLS1_2_SERVER_HELLO,
+ init_client_cert_type,
+ tls_parse_ctos_client_cert_type, tls_parse_stoc_client_cert_type,
+ tls_construct_stoc_client_cert_type, tls_construct_ctos_client_cert_type,
+ NULL
+ },
+ {
+ TLSEXT_TYPE_server_cert_type,
+ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
+ | SSL_EXT_TLS1_2_SERVER_HELLO,
+ init_server_cert_type,
+ tls_parse_ctos_server_cert_type, tls_parse_stoc_server_cert_type,
+ tls_construct_stoc_server_cert_type, tls_construct_ctos_server_cert_type,
+ NULL
+ },
+ {
TLSEXT_TYPE_signature_algorithms,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
init_sig_algs, tls_parse_ctos_sig_algs,
@@ -1779,6 +1799,18 @@ static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET
if (!ossl_comp_has_alg(0))
return EXT_RETURN_NOT_SENT;
+ /* Server: Don't attempt to compress a non-X509 (i.e. an RPK) */
+ if (sc->server && sc->ext.server_cert_type != TLSEXT_cert_type_x509) {
+ sc->cert_comp_prefs[0] = TLSEXT_comp_cert_none;
+ return EXT_RETURN_NOT_SENT;
+ }
+
+ /* Client: If we sent a client cert-type extension, don't indicate compression */
+ if (!sc->server && sc->ext.client_cert_type_ctos) {
+ sc->cert_comp_prefs[0] = TLSEXT_comp_cert_none;
+ return EXT_RETURN_NOT_SENT;
+ }
+
/* Do not indicate we support receiving compressed certificates */
if ((sc->options & SSL_OP_NO_RX_CERTIFICATE_COMPRESSION) != 0)
return EXT_RETURN_NOT_SENT;
@@ -1843,6 +1875,12 @@ int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, unsigned int
if (!ossl_comp_has_alg(0))
return 1;
+ /* Don't attempt to compress a non-X509 (i.e. an RPK) */
+ if (sc->server && sc->ext.server_cert_type != TLSEXT_cert_type_x509)
+ return 1;
+ if (!sc->server && sc->ext.client_cert_type != TLSEXT_cert_type_x509)
+ return 1;
+
/* Ignore the extension and don't send compressed certificates */
if ((sc->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
return 1;
@@ -1869,3 +1907,23 @@ int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, unsigned int
#endif
return 1;
}
+
+static int init_server_cert_type(SSL_CONNECTION *sc, unsigned int context)
+{
+ /* Only reset when parsing client hello */
+ if (sc->server) {
+ sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ sc->ext.server_cert_type = TLSEXT_cert_type_x509;
+ }
+ return 1;
+}
+
+static int init_client_cert_type(SSL_CONNECTION *sc, unsigned int context)
+{
+ /* Only reset when parsing client hello */
+ if (sc->server) {
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ sc->ext.client_cert_type = TLSEXT_cert_type_x509;
+ }
+ return 1;
+}
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index de71363fc1..d32dcfbd06 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -2015,3 +2015,107 @@ int tls_parse_stoc_psk(SSL_CONNECTION *s, PACKET *pkt,
return 1;
}
+
+EXT_RETURN tls_construct_ctos_client_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ if (sc->client_cert_type == NULL)
+ return EXT_RETURN_NOT_SENT;
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_client_cert_type)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_sub_memcpy_u8(pkt, sc->client_cert_type, sc->client_cert_type_len)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_GOOD;
+ return EXT_RETURN_SENT;
+}
+
+int tls_parse_stoc_client_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ unsigned int type;
+
+ if (PACKET_remaining(pkt) != 1) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ if (!PACKET_get_1(pkt, &type)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* We did not send/ask for this */
+ if (!ossl_assert(sc->ext.client_cert_type_ctos == OSSL_CERT_TYPE_CTOS_GOOD)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* We don't have this enabled */
+ if (sc->client_cert_type == NULL) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* Given back a value we didn't configure */
+ if (memchr(sc->client_cert_type, type, sc->client_cert_type_len) == NULL) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_VALUE);
+ return 0;
+ }
+ sc->ext.client_cert_type = type;
+ return 1;
+}
+
+EXT_RETURN tls_construct_ctos_server_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ if (sc->server_cert_type == NULL)
+ return EXT_RETURN_NOT_SENT;
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_cert_type)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_sub_memcpy_u8(pkt, sc->server_cert_type, sc->server_cert_type_len)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_GOOD;
+ return EXT_RETURN_SENT;
+}
+
+int tls_parse_stoc_server_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ unsigned int type;
+
+ if (PACKET_remaining(pkt) != 1) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ if (!PACKET_get_1(pkt, &type)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* We did not send/ask for this */
+ if (!ossl_assert(sc->ext.server_cert_type_ctos == OSSL_CERT_TYPE_CTOS_GOOD)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* We don't have this enabled */
+ if (sc->server_cert_type == NULL) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* Given back a value we didn't configure */
+ if (memchr(sc->server_cert_type, type, sc->server_cert_type_len) == NULL) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_VALUE);
+ return 0;
+ }
+ sc->ext.server_cert_type = type;
+ return 1;
+}
diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c
index 83470b1bf3..81f20b5f6b 100644
--- a/ssl/statem/extensions_cust.c
+++ b/ssl/statem/extensions_cust.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2014-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -193,6 +193,7 @@ int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x,
| SSL_EXT_TLS1_3_SERVER_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_3_CERTIFICATE
+ | SSL_EXT_TLS1_3_RAW_PUBLIC_KEY
| SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST)) != 0) {
/* Only send extensions present in ClientHello/CertificateRequest */
if (!(meth->ext_flags & SSL_EXT_FLAG_RECEIVED))
@@ -534,6 +535,8 @@ int SSL_extension_supported(unsigned int ext_type)
case TLSEXT_TYPE_psk:
case TLSEXT_TYPE_post_handshake_auth:
case TLSEXT_TYPE_compress_certificate:
+ case TLSEXT_TYPE_client_cert_type:
+ case TLSEXT_TYPE_server_cert_type:
return 1;
default:
return 0;
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index ff1f2a77e0..621df5e2f5 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -1948,3 +1948,164 @@ EXT_RETURN tls_construct_stoc_psk(SSL_CONNECTION *s, WPACKET *pkt,
return EXT_RETURN_SENT;
}
+
+EXT_RETURN tls_construct_stoc_client_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ if (sc->ext.client_cert_type_ctos == OSSL_CERT_TYPE_CTOS_ERROR
+ && (send_certificate_request(sc)
+ || sc->post_handshake_auth == SSL_PHA_EXT_RECEIVED)) {
+ /* Did not receive an acceptable cert type - and doing client auth */
+ SSLfatal(sc, SSL_AD_UNSUPPORTED_CERTIFICATE, SSL_R_BAD_EXTENSION);
+ return EXT_RETURN_FAIL;
+ }
+
+ if (sc->ext.client_cert_type == TLSEXT_cert_type_x509) {
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ return EXT_RETURN_NOT_SENT;
+ }
+
+ /*
+ * Note: only supposed to send this if we are going to do a cert request,
+ * but TLSv1.3 could do a PHA request if the client supports it
+ */
+ if ((!send_certificate_request(sc) && sc->post_handshake_auth != SSL_PHA_EXT_RECEIVED)
+ || sc->ext.client_cert_type_ctos != OSSL_CERT_TYPE_CTOS_GOOD
+ || sc->client_cert_type == NULL) {
+ /* if we don't send it, reset to TLSEXT_cert_type_x509 */
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ sc->ext.client_cert_type = TLSEXT_cert_type_x509;
+ return EXT_RETURN_NOT_SENT;
+ }
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_client_cert_type)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_put_bytes_u8(pkt, sc->ext.client_cert_type)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ return EXT_RETURN_SENT;
+}
+
+/* One of |pref|, |other| is configured and the values are sanitized */
+static int reconcile_cert_type(const unsigned char *pref, size_t pref_len,
+ const unsigned char *other, size_t other_len,
+ uint8_t *chosen_cert_type)
+{
+ size_t i;
+
+ for (i = 0; i < pref_len; i++) {
+ if (memchr(other, pref[i], other_len) != NULL) {
+ *chosen_cert_type = pref[i];
+ return OSSL_CERT_TYPE_CTOS_GOOD;
+ }
+ }
+ return OSSL_CERT_TYPE_CTOS_ERROR;
+}
+
+int tls_parse_ctos_client_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ PACKET supported_cert_types;
+ const unsigned char *data;
+ size_t len;
+
+ /* Ignore the extension */
+ if (sc->client_cert_type == NULL) {
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ sc->ext.client_cert_type = TLSEXT_cert_type_x509;
+ return 1;
+ }
+
+ if (!PACKET_as_length_prefixed_1(pkt, &supported_cert_types)) {
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_ERROR;
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ if ((len = PACKET_remaining(&supported_cert_types)) == 0) {
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_ERROR;
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ if (!PACKET_get_bytes(&supported_cert_types, &data, len)) {
+ sc->ext.client_cert_type_ctos = OSSL_CERT_TYPE_CTOS_ERROR;
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* client_cert_type: client (peer) has priority */
+ sc->ext.client_cert_type_ctos = reconcile_cert_type(data, len,
+ sc->client_cert_type, sc->client_cert_type_len,
+ &sc->ext.client_cert_type);
+
+ /* Ignore the error until sending - so we can check cert auth*/
+ return 1;
+}
+
+EXT_RETURN tls_construct_stoc_server_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ if (sc->ext.server_cert_type == TLSEXT_cert_type_x509) {
+ sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ return EXT_RETURN_NOT_SENT;
+ }
+ if (sc->ext.server_cert_type_ctos != OSSL_CERT_TYPE_CTOS_GOOD
+ || sc->server_cert_type == NULL) {
+ /* if we don't send it, reset to TLSEXT_cert_type_x509 */
+ sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ sc->ext.server_cert_type = TLSEXT_cert_type_x509;
+ return EXT_RETURN_NOT_SENT;
+ }
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_server_cert_type)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_put_bytes_u8(pkt, sc->ext.server_cert_type)
+ || !WPACKET_close(pkt)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+ }
+ return EXT_RETURN_SENT;
+}
+
+int tls_parse_ctos_server_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+ PACKET supported_cert_types;
+ const unsigned char *data;
+ size_t len;
+
+ /* Ignore the extension */
+ if (sc->server_cert_type == NULL) {
+ sc->ext.server_cert_type_ctos = OSSL_CERT_TYPE_CTOS_NONE;
+ sc->ext.server_cert_type = TLSEXT_cert_type_x509;
+ return 1;
+ }
+
+ if (!PACKET_as_length_prefixed_1(pkt, &supported_cert_types)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+
+ if ((len = PACKET_remaining(&supported_cert_types)) == 0) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ if (!PACKET_get_bytes(&supported_cert_types, &data, len)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+ /* server_cert_type: server (this) has priority */
+ sc->ext.server_cert_type_ctos = reconcile_cert_type(sc->server_cert_type, sc->server_cert_type_len,
+ data, len,
+ &sc->ext.server_cert_type);
+ if (sc->ext.server_cert_type_ctos == OSSL_CERT_TYPE_CTOS_GOOD)
+ return 1;
+
+ /* Did not receive an acceptable cert type */
+ SSLfatal(sc, SSL_AD_UNSUPPORTED_CERTIFICATE, SSL_R_BAD_EXTENSION);
+ return 0;
+}
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 8d90520d14..1e33319413 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2023 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?
*
@@ -419,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
@@ -441,7 +453,7 @@ 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) {
- if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ if (do_compressed_cert(s))
st->hand_state = TLS_ST_CW_COMP_CERT;
else
st->hand_state = TLS_ST_CW_CERT;
@@ -468,7 +480,7 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
st->hand_state = TLS_ST_CW_CHANGE;
else if (s->s3.tmp.cert_req == 0)
st->hand_state = TLS_ST_CW_FINISHED;
- else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ else if (do_compressed_cert(s))
st->hand_state = TLS_ST_CW_COMP_CERT;
else
st->hand_state = TLS_ST_CW_CERT;
@@ -485,7 +497,7 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
case TLS_ST_CW_CHANGE:
if (s->s3.tmp.cert_req == 0)
st->hand_state = TLS_ST_CW_FINISHED;
- else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ else if (do_compressed_cert(s))
st->hand_state = TLS_ST_CW_COMP_CERT;
else
st->hand_state = TLS_ST_CW_CERT;
@@ -1849,6 +1861,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)
@@ -1860,6 +1947,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;
@@ -1947,6 +2042,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);
@@ -2009,6 +2107,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)
@@ -2111,7 +2212,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
@@ -2200,7 +2301,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;
@@ -2265,9 +2366,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 */
@@ -2945,7 +3046,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.
*/
@@ -2953,7 +3054,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;
@@ -3128,7 +3233,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];
@@ -3144,15 +3249,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);
@@ -3279,7 +3383,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;
@@ -3310,15 +3414,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);
@@ -3629,6 +3732,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)) {
@@ -3643,10 +3747,23 @@ 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, 0)) {
- /* 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;
}
@@ -3764,6 +3881,7 @@ 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;
@@ -3773,8 +3891,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,
- SSL_CONNECTION_GET_CTX(s));
+ 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) {
@@ -3782,13 +3900,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);
@@ -3800,6 +3911,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;
}
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 1bc01e1d25..4b498cd76f 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
@@ -20,6 +20,7 @@
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/trace.h>
+#include <openssl/encoder.h>
/*
* Map error codes to TLS/SSL alart types.
@@ -447,7 +448,6 @@ MSG_PROCESS_RETURN tls_process_cert_verify(SSL_CONNECTION *s, PACKET *pkt)
MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
int j;
unsigned int len;
- X509 *peer;
const EVP_MD *md = NULL;
size_t hdatalen = 0;
void *hdata;
@@ -461,8 +461,7 @@ MSG_PROCESS_RETURN tls_process_cert_verify(SSL_CONNECTION *s, PACKET *pkt)
goto err;
}
- peer = s->session->peer;
- pkey = X509_get0_pubkey(peer);
+ pkey = tls_get_peer_pkey(s);
if (pkey == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
@@ -1092,6 +1091,291 @@ static int ssl_add_cert_chain(SSL_CONNECTION *s, WPACKET *pkt, CERT_PKEY *cpk, i
return 1;
}
+EVP_PKEY* tls_get_peer_pkey(const SSL_CONNECTION *sc)
+{
+ if (sc->session->peer_rpk != NULL)
+ return sc->session->peer_rpk;
+ if (sc->session->peer != NULL)
+ return X509_get0_pubkey(sc->session->peer);
+ return NULL;
+}
+
+int tls_process_rpk(SSL_CONNECTION *sc, PACKET *pkt, EVP_PKEY **peer_rpk)
+{
+ EVP_PKEY *pkey = NULL;
+ int ret = 0;
+ RAW_EXTENSION *rawexts = NULL;
+ PACKET extensions;
+ PACKET context;
+ unsigned long cert_len = 0, spki_len = 0;
+ const unsigned char *spki, *spkistart;
+ SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(sc);
+
+ /*-
+ * ----------------------------
+ * TLS 1.3 Certificate message:
+ * ----------------------------
+ * https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2
+ *
+ * enum {
+ * X509(0),
+ * RawPublicKey(2),
+ * (255)
+ * } CertificateType;
+ *
+ * struct {
+ * select (certificate_type) {
+ * case RawPublicKey:
+ * // From RFC 7250 ASN.1_subjectPublicKeyInfo
+ * opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;
+ *
+ * case X509:
+ * opaque cert_data<1..2^24-1>;
+ * };
+ * Extension extensions<0..2^16-1>;
+ * } CertificateEntry;
+ *
+ * struct {
+ * opaque certificate_request_context<0..2^8-1>;
+ * CertificateEntry certificate_list<0..2^24-1>;
+ * } Certificate;
+ *
+ * The client MUST send a Certificate message if and only if the server
+ * has requested client authentication via a CertificateRequest message
+ * (Section 4.3.2). If the server requests client authentication but no
+ * suitable certificate is available, the client MUST send a Certificate
+ * message containing no certificates (i.e., with the "certificate_list"
+ * field having length 0).
+ *
+ * ----------------------------
+ * TLS 1.2 Certificate message:
+ * ----------------------------
+ * https://datatracker.ietf.org/doc/html/rfc7250#section-3
+ *
+ * opaque ASN.1Cert<1..2^24-1>;
+ *
+ * struct {
+ * select(certificate_type){
+ *
+ * // certificate type defined in this document.
+ * case RawPublicKey:
+ * opaque ASN.1_subjectPublicKeyInfo<1..2^24-1>;
+ *
+ * // X.509 certificate defined in RFC 5246
+ * case X.509:
+ * ASN.1Cert certificate_list<0..2^24-1>;
+ *
+ * // Additional certificate type based on
+ * // "TLS Certificate Types" subregistry
+ * };
+ * } Certificate;
+ *
+ * -------------
+ * Consequently:
+ * -------------
+ * After the (TLS 1.3 only) context octet string (1 byte length + data) the
+ * Certificate message has a 3-byte length that is zero in the client to
+ * server message when the client has no RPK to send. In that case, there
+ * are no (TLS 1.3 only) per-certificate extensions either, because the
+ * [CertificateEntry] list is empty.
+ *
+ * In the server to client direction, or when the client had an RPK to send,
+ * the TLS 1.3 message just prepends the length of the RPK+extensions,
+ * while TLS <= 1.2 sends just the RPK (octet-string).
+ *
+ * The context must be zero-length in the server to client direction, and
+ * must match the value recorded in the certificate request in the client
+ * to server direction.
+ */
+ if (SSL_CONNECTION_IS_TLS13(sc)) {
+ if (!PACKET_get_length_prefixed_1(pkt, &context)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT);
+ goto err;
+ }
+ if (sc->server) {
+ if (sc->pha_context == NULL) {
+ if (PACKET_remaining(&context) != 0) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT);
+ goto err;
+ }
+ } else {
+ if (!PACKET_equal(&context, sc->pha_context, sc->pha_context_len)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT);
+ goto err;
+ }
+ }
+ } else {
+ if (PACKET_remaining(&context) != 0) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_INVALID_CONTEXT);
+ goto err;
+ }
+ }
+ }
+
+ if (!PACKET_get_net_3(pkt, &cert_len)
+ || PACKET_remaining(pkt) != cert_len) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
+ goto err;
+ }
+
+ /*
+ * The list length may be zero when there is no RPK. In the case of TLS
+ * 1.2 this is actually the RPK length, which cannot be zero as specified,
+ * but that breaks the ability of the client to decline client auth. We
+ * overload the 0 RPK length to mean "no RPK". This interpretation is
+ * also used some other (reference?) implementations, but is not supported
+ * by the verbatim RFC7250 text.
+ */
+ if (cert_len == 0)
+ return 1;
+
+ if (SSL_CONNECTION_IS_TLS13(sc)) {
+ /*
+ * With TLS 1.3, a non-empty explicit-length RPK octet-string followed
+ * by a possibly empty extension block.
+ */
+ if (!PACKET_get_net_3(pkt, &spki_len)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
+ goto err;
+ }
+ if (spki_len == 0) {
+ /* empty RPK */
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_EMPTY_RAW_PUBLIC_KEY);
+ goto err;
+ }
+ } else {
+ spki_len = cert_len;
+ }
+
+ if (!PACKET_get_bytes(pkt, &spki, spki_len)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
+ goto err;
+ }
+ spkistart = spki;
+ if ((pkey = d2i_PUBKEY_ex(NULL, &spki, spki_len, sctx->libctx, sctx->propq)) == NULL
+ || spki != (spkistart + spki_len)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
+ goto err;
+ }
+ if (EVP_PKEY_missing_parameters(pkey)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR,
+ SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS);
+ goto err;
+ }
+
+ /* Process the Extensions block */
+ if (SSL_CONNECTION_IS_TLS13(sc)) {
+ if (PACKET_remaining(pkt) != (cert_len - 3 - spki_len)) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_LENGTH);
+ goto err;
+ }
+ if (!PACKET_as_length_prefixed_2(pkt, &extensions)
+ || PACKET_remaining(pkt) != 0) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_MISMATCH);
+ goto err;
+ }
+ if (!tls_collect_extensions(sc, &extensions, SSL_EXT_TLS1_3_RAW_PUBLIC_KEY,
+ &rawexts, NULL, 1)) {
+ /* SSLfatal already called */
+ goto err;
+ }
+ /* chain index is always zero and fin always 1 for RPK */
+ if (!tls_parse_all_extensions(sc, SSL_EXT_TLS1_3_RAW_PUBLIC_KEY,
+ rawexts, NULL, 0, 1)) {
+ /* SSLfatal already called */
+ goto err;
+ }
+ }
+ ret = 1;
+ if (peer_rpk != NULL) {
+ *peer_rpk = pkey;
+ pkey = NULL;
+ }
+
+ err:
+ OPENSSL_free(rawexts);
+ EVP_PKEY_free(pkey);
+ return ret;
+}
+
+unsigned long tls_output_rpk(SSL_CONNECTION *sc, WPACKET *pkt, CERT_PKEY *cpk)
+{
+ int pdata_len = 0;
+ unsigned char *pdata = NULL;
+ X509_PUBKEY *xpk = NULL;
+ unsigned long ret = 0;
+ X509 *x509 = NULL;
+
+ if (cpk != NULL && cpk->x509 != NULL) {
+ x509 = cpk->x509;
+ /* Get the RPK from the certificate */
+ xpk = X509_get_X509_PUBKEY(cpk->x509);
+ if (xpk == NULL) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ pdata_len = i2d_X509_PUBKEY(xpk, &pdata);
+ } else if (cpk != NULL && cpk->privatekey != NULL) {
+ /* Get the RPK from the private key */
+ pdata_len = i2d_PUBKEY(cpk->privatekey, &pdata);
+ } else {
+ /* The server RPK is not optional */
+ if (sc->server) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ /* The client can send a zero length certificate list */
+ if (!WPACKET_sub_memcpy_u24(pkt, pdata, pdata_len)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ return 1;
+ }
+
+ if (pdata_len <= 0) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ /*
+ * TLSv1.2 is _just_ the raw public key
+ * TLSv1.3 includes extensions, so there's a length wrapper
+ */
+ if (SSL_CONNECTION_IS_TLS13(sc)) {
+ if (!WPACKET_start_sub_packet_u24(pkt)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ if (!WPACKET_sub_memcpy_u24(pkt, pdata, pdata_len)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (SSL_CONNECTION_IS_TLS13(sc)) {
+ /*
+ * Only send extensions relevent to raw public keys. Until such
+ * extensions are defined, this will be an empty set of extensions.
+ * |x509| may be NULL, which raw public-key extensions need to handle.
+ */
+ if (!tls_construct_extensions(sc, pkt, SSL_EXT_TLS1_3_RAW_PUBLIC_KEY,
+ x509, 0)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+ if (!WPACKET_close(pkt)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ ret = 1;
+ err:
+ OPENSSL_free(pdata);
+ return ret;
+}
+
unsigned long ssl3_output_cert_chain(SSL_CONNECTION *s, WPACKET *pkt,
CERT_PKEY *cpk, int for_comp)
{
diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h
index 30b7d5b0a5..04114b1e27 100644
--- a/ssl/statem/statem_local.h
+++ b/ssl/statem/statem_local.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2015-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
@@ -173,6 +173,13 @@ __owur CON_FUNC_RETURN tls_construct_cert_status(SSL_CONNECTION *s,
WPACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_key_exchange(SSL_CONNECTION *s,
PACKET *pkt);
+__owur MSG_PROCESS_RETURN tls_process_server_rpk(SSL_CONNECTION *sc,
+ PACKET *pkt);
+__owur MSG_PROCESS_RETURN tls_process_client_rpk(SSL_CONNECTION *sc,
+ PACKET *pkt);
+__owur unsigned long tls_output_rpk(SSL_CONNECTION *sc, WPACKET *pkt,
+ CERT_PKEY *cpk);
+__owur int tls_process_rpk(SSL_CONNECTION *s, PACKET *pkt, EVP_PKEY **peer_rpk);
__owur MSG_PROCESS_RETURN tls_process_server_certificate(SSL_CONNECTION *s,
PACKET *pkt);
__owur WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
@@ -536,3 +543,30 @@ int tls_handle_alpn(SSL_CONNECTION *s);
int tls13_save_handshake_digest_for_pha(SSL_CONNECTION *s);
int tls13_restore_handshake_digest_for_pha(SSL_CONNECTION *s);
+
+__owur EVP_PKEY* tls_get_peer_pkey(const SSL_CONNECTION *sc);
+/* RFC7250 */
+EXT_RETURN tls_construct_ctos_client_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+EXT_RETURN tls_construct_stoc_client_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+int tls_parse_ctos_client_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+int tls_parse_stoc_client_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+EXT_RETURN tls_construct_ctos_server_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+EXT_RETURN tls_construct_stoc_server_cert_type(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+int tls_parse_ctos_server_cert_type(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+int tls_parse_stoc_server_cert_type(SSL_CONNECTION *s, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 3137f548ef..ddc74883f5 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved
* Copyright 2005 Nokia. All rights reserved.
*
@@ -47,6 +47,11 @@ IMPLEMENT_ASN1_FUNCTIONS(GOST_KX_MESSAGE)
static CON_FUNC_RETURN tls_construct_encrypted_extensions(SSL_CONNECTION *s,
WPACKET *pkt);
+static ossl_inline int received_client_cert(const SSL_CONNECTION *sc)
+{
+ return sc->session->peer_rpk != NULL || sc->session->peer != NULL;
+}
+
/*
* ossl_statem_server13_read_transition() encapsulates the logic for the allowed
* handshake state transitions when a TLSv1.3 server is reading messages from
@@ -109,7 +114,7 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
case TLS_ST_SR_COMP_CERT:
case TLS_ST_SR_CERT:
- if (s->session->peer == NULL) {
+ if (!received_client_cert(s)) {
if (mt == SSL3_MT_FINISHED) {
st->hand_state = TLS_ST_SR_FINISHED;
return 1;
@@ -250,7 +255,7 @@ int ossl_statem_server_read_transition(SSL_CONNECTION *s, int mt)
* the case of static DH). In that case |st->no_cert_verify| should be
* set.
*/
- if (s->session->peer == NULL || st->no_cert_verify) {
+ if (!received_client_cert(s) || st->no_cert_verify) {
if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
/*
* For the ECDH ciphersuites when the client sends its ECDH
@@ -444,6 +449,13 @@ int send_certificate_request(SSL_CONNECTION *s)
return 0;
}
+static int do_compressed_cert(SSL_CONNECTION *sc)
+{
+ /* If we negotiated RPK, we won't attempt to compress it */
+ return sc->ext.server_cert_type == TLSEXT_cert_type_x509
+ && get_compressed_certificate_alg(sc) != TLSEXT_comp_cert_none;
+}
+
/*
* ossl_statem_server13_write_transition() works out what handshake state to
* move to next when a TLSv1.3 server is writing messages to be sent to the
@@ -506,7 +518,7 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
st->hand_state = TLS_ST_SW_FINISHED;
else if (send_certificate_request(s))
st->hand_state = TLS_ST_SW_CERT_REQ;
- else if (get_compressed_certificate_alg(s) != TLSEXT_comp_cert_none)
+ else if (do_compressed_cert(s))
st->hand_state = TLS_ST_SW_COMP_CERT;
else
st->hand_state = TLS_ST_SW_CERT;
@@ -517,7 +529,7 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
s->post_handshake_auth = SSL_PHA_REQUESTED;
st->hand_state = TLS_ST_OK;
- } else if (get_compressed_certificate_alg(s) != TLSEXT_comp_cert_none) {
+ } else if (do_compressed_cert(s)) {
st->hand_state = TLS_ST_SW_COMP_CERT;
} else {
st->hand_state = TLS_ST_SW_CERT;
@@ -3243,7 +3255,7 @@ static int tls_process_cke_gost(SSL_CONNECTION *s, PACKET *pkt)
* EVP_PKEY_derive_set_peer, because it is completely valid to use a
* client certificate for authorization only.
*/
- client_pub_pkey = X509_get0_pubkey(s->session->peer);
+ client_pub_pkey = tls_get_peer_pkey(s);
if (client_pub_pkey) {
if (EVP_PKEY_derive_set_peer(pkey_ctx, client_pub_pkey) <= 0)
ERR_clear_error();
@@ -3485,7 +3497,7 @@ WORK_STATE tls_post_process_client_key_exchange(SSL_CONNECTION *s,
}
#endif
- if (s->statem.no_cert_verify || !s->session->peer) {
+ if (s->statem.no_cert_verify || !received_client_cert(s)) {
/*
* No certificate verify or no peer certificate so we no longer need
* the handshake_buffer
@@ -3513,6 +3525,91 @@ WORK_STATE tls_post_process_client_key_exchange(SSL_CONNECTION *s,
return WORK_FINISHED_CONTINUE;
}
+MSG_PROCESS_RETURN tls_process_client_rpk(SSL_CONNECTION *sc, PACKET *pkt)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ SSL_SESSION *new_sess = NULL;
+ EVP_PKEY *peer_rpk = NULL;
+
+ if (!tls_process_rpk(sc, pkt, &peer_rpk)) {
+ /* SSLfatal already called */
+ goto err;
+ }
+
+ if (peer_rpk == NULL) {
+ if ((sc->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ && (sc->verify_mode & SSL_VERIFY_PEER)) {
+ SSLfatal(sc, SSL_AD_CERTIFICATE_REQUIRED,
+ SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
+ goto err;
+ }
+ } else {
+ if (ssl_verify_rpk(sc, peer_rpk) <= 0) {
+ SSLfatal(sc, ssl_x509err2alert(sc->verify_result),
+ SSL_R_CERTIFICATE_VERIFY_FAILED);
+ goto err;
+ }
+ }
+
+ /*
+ * Sessions must be immutable once they go into the session cache. Otherwise
+ * we can get multi-thread problems. Therefore we don't "update" sessions,
+ * we replace them with a duplicate. Here, we need to do this every time
+ * a new RPK (or certificate) is received via post-handshake authentication,
+ * as the session may have already gone into the session cache.
+ */
+
+ if (sc->post_handshake_auth == SSL_PHA_REQUESTED) {
+ if ((new_sess = ssl_session_dup(sc->session, 0)) == NULL) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ SSL_SESSION_free(sc->session);
+ sc->session = new_sess;
+ }
+
+ /* 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;
+ /* Save RPK */
+ EVP_PKEY_free(sc->session->peer_rpk);
+ sc->session->peer_rpk = peer_rpk;
+ peer_rpk = NULL;
+
+ sc->session->verify_result = sc->verify_result;
+
+ /*
+ * Freeze the handshake buffer. For <TLS1.3 we do this after the CKE
+ * message
+ */
+ if (SSL_CONNECTION_IS_TLS13(sc)) {
+ if (!ssl3_digest_cached_records(sc, 1)) {
+ /* SSLfatal() already called */
+ goto err;
+ }
+
+ /* Save the current hash state for when we receive the CertificateVerify */
+ if (!ssl_handshake_hash(sc, sc->cert_verify_hash,
+ sizeof(sc->cert_verify_hash),
+ &sc->cert_verify_hash_len)) {
+ /* SSLfatal() already called */;
+ goto err;
+ }
+
+ /* resend session tickets */
+ sc->sent_tickets = 0;
+ }
+
+ ret = MSG_PROCESS_CONTINUE_READING;
+
+ err:
+ EVP_PKEY_free(peer_rpk);
+ return ret;
+}
+
MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
PACKET *pkt)
{
@@ -3534,6 +3631,15 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
if (s->rlayer.rrlmethod->set_plain_alerts != NULL)
s->rlayer.rrlmethod->set_plain_alerts(s->rlayer.rrl, 0);
+ if (s->ext.client_cert_type == TLSEXT_cert_type_rpk)
+ return tls_process_client_rpk(s, pkt);
+
+ if (s->ext.client_cert_type != TLSEXT_cert_type_x509) {
+ SSLfatal(s, SSL_AD_UNSUPPORTED_CERTIFICATE,
+ SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+ goto err;
+ }
+
if ((sk = sk_X509_new_null()) == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
goto err;
@@ -3665,6 +3771,9 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
OSSL_STACK_OF_X509_free(s->session->peer_chain);
s->session->peer_chain = sk;
sk = NULL;
+ /* Ensure there is no RPK */
+ EVP_PKEY_free(s->session->peer_rpk);
+ s->session->peer_rpk = NULL;
/*
* Freeze the handshake buffer. For <TLS1.3 we do this after the CKE
@@ -3733,9 +3842,22 @@ CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return CON_FUNC_ERROR;
}
- if (!ssl3_output_cert_chain(s, pkt, cpk, 0)) {
- /* SSLfatal() already called */
- return CON_FUNC_ERROR;
+ switch (s->ext.server_cert_type) {
+ case TLSEXT_cert_type_rpk:
+ if (!tls_output_rpk(s, pkt, cpk)) {
+ /* SSLfatal() already called */
+ return 0;
+ }
+ break;
+ case TLSEXT_cert_type_x509:
+ if (!ssl3_output_cert_chain(s, pkt, cpk, 0)) {
+ /* SSLfatal() already called */
+ return 0;
+ }
+ break;
+ default:
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
}
return CON_FUNC_SUCCESS;
@@ -3869,7 +3991,8 @@ static CON_FUNC_RETURN construct_stateless_ticket(SSL_CONNECTION *s,
* create a fresh copy (not shared with other threads) to clean up
*/
const_p = senc;
- sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
+ sess = d2i_SSL_SESSION_ex(NULL, &const_p, slen_full, sctx->libctx,
+ sctx->propq);
if (sess == NULL) {
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;