diff options
author | Todd Short <tshort@akamai.com> | 2021-01-27 14:23:33 -0500 |
---|---|---|
committer | Todd Short <todd.short@me.com> | 2023-03-28 13:49:54 -0400 |
commit | 3c95ef22df55cb2d9dc64ce1f3be6e5a8ee63206 (patch) | |
tree | 0f7fcff4ec4735c778595db4f4a85bce70715d8b /ssl/statem | |
parent | 5ab3f71a33cb0140fc29ae9244cd4f8331c2f3a5 (diff) | |
download | openssl-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.c | 60 | ||||
-rw-r--r-- | ssl/statem/extensions_clnt.c | 106 | ||||
-rw-r--r-- | ssl/statem/extensions_cust.c | 5 | ||||
-rw-r--r-- | ssl/statem/extensions_srvr.c | 163 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 184 | ||||
-rw-r--r-- | ssl/statem/statem_lib.c | 292 | ||||
-rw-r--r-- | ssl/statem/statem_local.h | 36 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 145 |
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; |