summaryrefslogtreecommitdiff
path: root/ssl/statem
diff options
context:
space:
mode:
authorTodd Short <tshort@akamai.com>2021-08-09 16:56:50 -0400
committerTodd Short <todd.short@me.com>2022-10-18 09:30:22 -0400
commitb67cb09f8ddf258cf326f3e7b20be095fb53457c (patch)
treeb31a978e8c71e972e84fd03b4de92491deff032a /ssl/statem
parent59d21298df9176b64b41cc8583c7024f7f5895d4 (diff)
downloadopenssl-new-b67cb09f8ddf258cf326f3e7b20be095fb53457c.tar.gz
Add support for compressed certificates (RFC8879)
* Compressed Certificate extension (server/client) * Server certificates (send/receive) * Client certificate (send/receive) Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18186)
Diffstat (limited to 'ssl/statem')
-rw-r--r--ssl/statem/extensions.c125
-rw-r--r--ssl/statem/extensions_cust.c1
-rw-r--r--ssl/statem/statem.c10
-rw-r--r--ssl/statem/statem.h1
-rw-r--r--ssl/statem/statem_clnt.c162
-rw-r--r--ssl/statem/statem_lib.c73
-rw-r--r--ssl/statem/statem_local.h23
-rw-r--r--ssl/statem/statem_srvr.c117
8 files changed, 503 insertions, 9 deletions
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 880189d998..2cfc9f2b7d 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -62,6 +62,13 @@ static int final_maxfragmentlen(SSL_CONNECTION *s, unsigned int context,
int sent);
static int init_post_handshake_auth(SSL_CONNECTION *s, unsigned int context);
static int final_psk(SSL_CONNECTION *s, unsigned int context, int sent);
+static int tls_init_compress_certificate(SSL_CONNECTION *sc, unsigned int context);
+static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+static int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
/* Structure to define a built-in extension */
typedef struct extensions_definition_st {
@@ -359,6 +366,15 @@ static const EXTENSION_DEFINITION ext_defs[] = {
NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL
},
{
+ TLSEXT_TYPE_compress_certificate,
+ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
+ | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
+ tls_init_compress_certificate,
+ tls_parse_compress_certificate, tls_parse_compress_certificate,
+ tls_construct_compress_certificate, tls_construct_compress_certificate,
+ NULL
+ },
+ {
TLSEXT_TYPE_early_data,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_3_NEW_SESSION_TICKET | SSL_EXT_TLS1_3_ONLY,
@@ -1746,3 +1762,112 @@ static int final_psk(SSL_CONNECTION *s, unsigned int context, int sent)
return 1;
}
+
+static int tls_init_compress_certificate(SSL_CONNECTION *sc, unsigned int context)
+{
+ memset(sc->ext.compress_certificate_from_peer, 0,
+ sizeof(sc->ext.compress_certificate_from_peer));
+ return 1;
+}
+
+/* The order these are put into the packet imply a preference order: [brotli, zlib, zstd] */
+static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ int i;
+
+ if (!ossl_comp_has_alg(0))
+ 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;
+
+ if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none)
+ return EXT_RETURN_NOT_SENT;
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_compress_certificate)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_start_sub_packet_u8(pkt))
+ goto err;
+
+ for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) {
+ if (!WPACKET_put_bytes_u16(pkt, sc->cert_comp_prefs[i]))
+ goto err;
+ }
+ if (!WPACKET_close(pkt) || !WPACKET_close(pkt))
+ goto err;
+
+ sc->ext.compress_certificate_sent = 1;
+ return EXT_RETURN_SENT;
+ err:
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+#else
+ return EXT_RETURN_NOT_SENT;
+#endif
+}
+
+#ifndef OPENSSL_NO_COMP_ALG
+static int tls_comp_in_pref(SSL_CONNECTION *sc, int alg)
+{
+ int i;
+
+ /* ossl_comp_has_alg() considers 0 as "any" */
+ if (alg == 0)
+ return 0;
+ /* Make sure algorithm is enabled */
+ if (!ossl_comp_has_alg(alg))
+ return 0;
+ /* If no preferences are set, it's ok */
+ if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none)
+ return 1;
+ /* Find the algorithm */
+ for (i = 0; i < TLSEXT_comp_cert_limit; i++)
+ if (sc->cert_comp_prefs[i] == alg)
+ return 1;
+ return 0;
+}
+#endif
+
+int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ PACKET supported_comp_algs;
+ unsigned int comp;
+ int already_set[TLSEXT_comp_cert_limit];
+ int j = 0;
+
+ /* If no algorithms are available, ignore the extension */
+ if (!ossl_comp_has_alg(0))
+ return 1;
+
+ /* Ignore the extension and don't send compressed certificates */
+ if ((sc->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ return 1;
+
+ if (!PACKET_as_length_prefixed_1(pkt, &supported_comp_algs)
+ || PACKET_remaining(&supported_comp_algs) == 0) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+
+ memset(already_set, 0, sizeof(already_set));
+ /*
+ * The preference array has real values, so take a look at each
+ * value coming in, and make sure it's in our preference list
+ * The array is 0 (i.e. "none") terminated
+ * The preference list only contains supported algorithms
+ */
+ while (PACKET_get_1(&supported_comp_algs, &comp)) {
+ if (tls_comp_in_pref(sc, comp) && !already_set[comp]) {
+ sc->ext.compress_certificate_from_peer[j++] = comp;
+ already_set[comp] = 1;
+ }
+ }
+#endif
+ return 1;
+}
diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c
index a2c563b86b..57713702f7 100644
--- a/ssl/statem/extensions_cust.c
+++ b/ssl/statem/extensions_cust.c
@@ -525,6 +525,7 @@ int SSL_extension_supported(unsigned int ext_type)
case TLSEXT_TYPE_certificate_authorities:
case TLSEXT_TYPE_psk:
case TLSEXT_TYPE_post_handshake_auth:
+ case TLSEXT_TYPE_compress_certificate:
return 1;
default:
return 0;
diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c
index 448d655a17..ba3681285a 100644
--- a/ssl/statem/statem.c
+++ b/ssl/statem/statem.c
@@ -130,6 +130,7 @@ void ossl_statem_clear(SSL_CONNECTION *s)
s->statem.hand_state = TLS_ST_BEFORE;
ossl_statem_set_in_init(s, 1);
s->statem.no_cert_verify = 0;
+ s->statem.ignore_fatal = 0;
}
/*
@@ -143,6 +144,15 @@ void ossl_statem_set_renegotiate(SSL_CONNECTION *s)
void ossl_statem_send_fatal(SSL_CONNECTION *s, int al)
{
+ /*
+ * Some public APIs may call internal functions that fatal error,
+ * which doesn't make sense outside the state machine. Those APIs
+ * that can handle a failure set this flag to avoid errors sending
+ * alerts. Example: getting a wire-formatted certificate for
+ * compression.
+ */
+ if (s->statem.ignore_fatal)
+ return;
/* We shouldn't call SSLfatal() twice. Once is enough */
if (s->statem.in_init && s->statem.state == MSG_FLOW_ERROR)
return;
diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h
index 2b73eba6f6..b60103e6e5 100644
--- a/ssl/statem/statem.h
+++ b/ssl/statem/statem.h
@@ -96,6 +96,7 @@ struct ossl_statem_st {
OSSL_HANDSHAKE_STATE request_state;
int in_init;
int read_state_first_init;
+ int ignore_fatal;
/* true when we are actually in SSL_accept() or SSL_connect() */
int in_handshake;
/*
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 516aaf3984..31f0ebe8e5 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -135,6 +135,13 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
st->hand_state = TLS_ST_CR_CERT;
return 1;
}
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_CR_COMP_CERT;
+ return 1;
+ }
+#endif
}
break;
@@ -143,9 +150,17 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
st->hand_state = TLS_ST_CR_CERT;
return 1;
}
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_CR_COMP_CERT;
+ return 1;
+ }
+#endif
break;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
if (mt == SSL3_MT_CERTIFICATE_VERIFY) {
st->hand_state = TLS_ST_CR_CERT_VRFY;
return 1;
@@ -309,6 +324,7 @@ int ossl_statem_client_read_transition(SSL_CONNECTION *s, int mt)
break;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
/*
* The CertificateStatus message is optional even if
* |ext.status_expected| is set
@@ -425,7 +441,10 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
case TLS_ST_CR_CERT_REQ:
if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
- st->hand_state = TLS_ST_CW_CERT;
+ if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_CW_COMP_CERT;
+ else
+ st->hand_state = TLS_ST_CW_CERT;
return WRITE_TRAN_CONTINUE;
}
/*
@@ -447,9 +466,12 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
else if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
&& s->hello_retry_request == SSL_HRR_NONE)
st->hand_state = TLS_ST_CW_CHANGE;
+ else if (s->s3.tmp.cert_req == 0)
+ st->hand_state = TLS_ST_CW_FINISHED;
+ else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_CW_COMP_CERT;
else
- st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT
- : TLS_ST_CW_FINISHED;
+ st->hand_state = TLS_ST_CW_CERT;
return WRITE_TRAN_CONTINUE;
case TLS_ST_PENDING_EARLY_DATA_END:
@@ -461,10 +483,15 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
case TLS_ST_CW_END_OF_EARLY_DATA:
case TLS_ST_CW_CHANGE:
- st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT
- : TLS_ST_CW_FINISHED;
+ if (s->s3.tmp.cert_req == 0)
+ st->hand_state = TLS_ST_CW_FINISHED;
+ else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_CW_COMP_CERT;
+ else
+ st->hand_state = TLS_ST_CW_CERT;
return WRITE_TRAN_CONTINUE;
+ case TLS_ST_CW_COMP_CERT:
case TLS_ST_CW_CERT:
/* If a non-empty Certificate we also send CertificateVerify */
st->hand_state = (s->s3.tmp.cert_req == 1) ? TLS_ST_CW_CERT_VRFY
@@ -931,6 +958,13 @@ int ossl_statem_client_construct_message(SSL_CONNECTION *s,
*mt = SSL3_MT_CERTIFICATE;
break;
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_CW_COMP_CERT:
+ *confunc = tls_construct_client_compressed_certificate;
+ *mt = SSL3_MT_COMPRESSED_CERTIFICATE;
+ break;
+#endif
+
case TLS_ST_CW_KEY_EXCH:
*confunc = tls_construct_client_key_exchange;
*mt = SSL3_MT_CLIENT_KEY_EXCHANGE;
@@ -980,6 +1014,7 @@ size_t ossl_statem_client_max_message_size(SSL_CONNECTION *s)
case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
return HELLO_VERIFY_REQUEST_MAX_LENGTH;
+ case TLS_ST_CR_COMP_CERT:
case TLS_ST_CR_CERT:
return s->max_cert_list;
@@ -1046,6 +1081,11 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL_CONNECTION *s,
case TLS_ST_CR_CERT:
return tls_process_server_certificate(s, pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_CR_COMP_CERT:
+ return tls_process_server_compressed_certificate(s, pkt);
+#endif
+
case TLS_ST_CR_CERT_VRFY:
return tls_process_cert_verify(s, pkt);
@@ -1097,6 +1137,7 @@ WORK_STATE ossl_statem_client_post_process_message(SSL_CONNECTION *s,
return WORK_ERROR;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
return tls_post_process_server_certificate(s, wst);
case TLS_ST_CR_CERT_VRFY:
@@ -1968,6 +2009,21 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
return WORK_FINISHED_CONTINUE;
}
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ PACKET tmppkt;
+ BUF_MEM *buf = BUF_MEM_new();
+
+ if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR)
+ ret = tls_process_server_certificate(sc, &tmppkt);
+
+ BUF_MEM_free(buf);
+ return ret;
+}
+#endif
+
static int tls_process_ske_psk_preamble(SSL_CONNECTION *s, PACKET *pkt)
{
#ifndef OPENSSL_NO_PSK
@@ -3531,6 +3587,7 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
return WORK_FINISHED_CONTINUE;
} else {
s->s3.tmp.cert_req = 2;
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
if (!ssl3_digest_cached_records(s, 0)) {
/* SSLfatal() already called */
return WORK_ERROR;
@@ -3538,6 +3595,10 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
}
}
+ if (!SSL_CONNECTION_IS_TLS13(s)
+ || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
+
if (s->post_handshake_auth == SSL_PHA_REQUESTED)
return WORK_FINISHED_STOP;
return WORK_FINISHED_CONTINUE;
@@ -3587,6 +3648,97 @@ CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
return CON_FUNC_SUCCESS;
}
+#ifndef OPENSSL_NO_COMP_ALG
+CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc,
+ WPACKET *pkt)
+{
+ SSL *ssl = SSL_CONNECTION_GET_SSL(sc);
+ WPACKET tmppkt;
+ BUF_MEM *buf = NULL;
+ size_t length;
+ size_t max_length;
+ COMP_METHOD *method;
+ COMP_CTX *comp = NULL;
+ int comp_len;
+ int ret = 0;
+ int alg = sc->ext.compress_certificate_from_peer[0];
+
+ /* Note that sc->s3.tmp.cert_req == 2 is checked in write transition */
+
+ if ((buf = BUF_MEM_new()) == NULL || !WPACKET_init(&tmppkt, buf))
+ goto err;
+
+ /* Use the |tmppkt| for the to-be-compressed data */
+ if (sc->pha_context == NULL) {
+ /* no context available, add 0-length context */
+ if (!WPACKET_put_bytes_u8(&tmppkt, 0))
+ goto err;
+ } else if (!WPACKET_sub_memcpy_u8(&tmppkt, sc->pha_context, sc->pha_context_len))
+ goto err;
+
+ if (!ssl3_output_cert_chain(sc, &tmppkt, sc->cert->key)) {
+ /* SSLfatal() already called */
+ goto out;
+ }
+
+ /* continue with the real |pkt| */
+ if (!WPACKET_put_bytes_u16(pkt, alg)
+ || !WPACKET_get_total_written(&tmppkt, &length)
+ || !WPACKET_put_bytes_u24(pkt, length))
+ goto err;
+
+ switch (alg) {
+ case TLSEXT_comp_cert_zlib:
+ method = COMP_zlib();
+ break;
+ case TLSEXT_comp_cert_brotli:
+ method = COMP_brotli_oneshot();
+ break;
+ case TLSEXT_comp_cert_zstd:
+ method = COMP_zstd_oneshot();
+ break;
+ default:
+ goto err;
+ }
+ max_length = ossl_calculate_comp_expansion(alg, length);
+
+ if (!WPACKET_start_sub_packet_u24(pkt)
+ || !WPACKET_reserve_bytes(pkt, max_length, NULL)
+ || (comp = COMP_CTX_new(method)) == NULL)
+ goto err;
+
+ comp_len = COMP_compress_block(comp, WPACKET_get_curr(pkt), max_length,
+ (unsigned char *)buf->data, length);
+ if (comp_len <= 0)
+ goto err;
+
+ if (!WPACKET_allocate_bytes(pkt, comp_len, NULL)
+ || !WPACKET_close(pkt))
+ goto err;
+
+ if (SSL_IS_FIRST_HANDSHAKE(sc)
+ && (!ssl->method->ssl3_enc->change_cipher_state(sc,
+ SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE))) {
+ /*
+ * This is a fatal error, which leaves sc->enc_write_ctx in an
+ * inconsistent state and thus ssl3_send_alert may crash.
+ */
+ SSLfatal(sc, SSL_AD_NO_ALERT, SSL_R_CANNOT_CHANGE_CIPHER);
+ goto out;
+ }
+ ret = 1;
+ goto out;
+
+ err:
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ out:
+ WPACKET_cleanup(&tmppkt);
+ BUF_MEM_free(buf);
+ COMP_CTX_free(comp);
+ return ret;
+}
+#endif
+
int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s)
{
const SSL_CERT_LOOKUP *clu;
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 8b34c11048..69e900f90e 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -2466,3 +2466,76 @@ int tls13_restore_handshake_digest_for_pha(SSL_CONNECTION *s)
}
return 1;
}
+
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt,
+ PACKET *tmppkt,
+ BUF_MEM *buf)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ int comp_alg;
+ COMP_METHOD *method = NULL;
+ COMP_CTX *comp = NULL;
+ size_t expected_length;
+ size_t comp_length;
+ int i;
+ int found = 0;
+
+ if (buf == NULL) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ if (!PACKET_get_net_2(pkt, (unsigned int*)&comp_alg)) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ /* If we have a prefs list, make sure the algorithm is in it */
+ if (sc->cert_comp_prefs[0] != TLSEXT_comp_cert_none) {
+ for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) {
+ if (sc->cert_comp_prefs[i] == comp_alg) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM);
+ goto err;
+ }
+ }
+ if (!ossl_comp_has_alg(comp_alg)) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM);
+ goto err;
+ }
+ switch (comp_alg) {
+ case TLSEXT_comp_cert_zlib:
+ method = COMP_zlib();
+ break;
+ case TLSEXT_comp_cert_brotli:
+ method = COMP_brotli_oneshot();
+ break;
+ case TLSEXT_comp_cert_zstd:
+ method = COMP_zstd_oneshot();
+ break;
+ default:
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM);
+ goto err;
+ }
+
+ if (!PACKET_get_net_3_len(pkt, &expected_length)
+ || !PACKET_get_net_3_len(pkt, &comp_length)
+ || PACKET_remaining(pkt) != comp_length
+ || !BUF_MEM_grow(buf, expected_length)
+ || !PACKET_buf_init(tmppkt, (unsigned char *)buf->data, expected_length)
+ || (comp = COMP_CTX_new(method)) == NULL
+ || COMP_expand_block(comp, (unsigned char *)buf->data, expected_length,
+ (unsigned char*)PACKET_data(pkt), comp_length) != (int)expected_length) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_DECOMPRESSION);
+ goto err;
+ }
+ ret = MSG_PROCESS_CONTINUE_PROCESSING;
+ err:
+ COMP_CTX_free(comp);
+ return ret;
+}
+#endif
diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h
index e5c6cfe535..75b7274c2e 100644
--- a/ssl/statem/statem_local.h
+++ b/ssl/statem/statem_local.h
@@ -129,6 +129,13 @@ __owur WORK_STATE tls_finish_handshake(SSL_CONNECTION *s, WORK_STATE wst,
int clearbufs, int stop);
__owur WORK_STATE dtls_wait_for_dry(SSL_CONNECTION *s);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt,
+ PACKET *tmppkt,
+ BUF_MEM *buf);
+#endif
+
/* some client-only functions */
__owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s,
WPACKET *pkt);
@@ -149,6 +156,10 @@ __owur WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s,
WORK_STATE wst);
__owur CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
WPACKET *pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc,
+ WPACKET *pkt);
+#endif
__owur int ssl_do_client_cert_cb(SSL_CONNECTION *s, X509 **px509,
EVP_PKEY **ppkey);
__owur CON_FUNC_RETURN tls_construct_client_key_exchange(SSL_CONNECTION *s,
@@ -163,6 +174,10 @@ __owur MSG_PROCESS_RETURN tls_process_server_certificate(SSL_CONNECTION *s,
PACKET *pkt);
__owur WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
WORK_STATE wst);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt);
+#endif
__owur int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s);
#ifndef OPENSSL_NO_NEXTPROTONEG
__owur CON_FUNC_RETURN tls_construct_next_proto(SSL_CONNECTION *s, WPACKET *pkt);
@@ -183,6 +198,10 @@ __owur CON_FUNC_RETURN dtls_construct_hello_verify_request(SSL_CONNECTION *s,
WPACKET *pkt);
__owur CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s,
WPACKET *pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur CON_FUNC_RETURN tls_construct_server_compressed_certificate(SSL_CONNECTION *sc,
+ WPACKET *pkt);
+#endif
__owur CON_FUNC_RETURN tls_construct_server_key_exchange(SSL_CONNECTION *s,
WPACKET *pkt);
__owur CON_FUNC_RETURN tls_construct_certificate_request(SSL_CONNECTION *s,
@@ -191,6 +210,10 @@ __owur CON_FUNC_RETURN tls_construct_server_done(SSL_CONNECTION *s,
WPACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
PACKET *pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur MSG_PROCESS_RETURN tls_process_client_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt);
+#endif
__owur MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL_CONNECTION *s,
PACKET *pkt);
__owur WORK_STATE tls_post_process_client_key_exchange(SSL_CONNECTION *s,
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index ab1574419e..0d68e4b1cf 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -26,6 +26,7 @@
#include <openssl/trace.h>
#include <openssl/core_names.h>
#include <openssl/asn1t.h>
+#include <openssl/comp.h>
#define TICKET_NONCE_SIZE 8
@@ -91,6 +92,13 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
st->hand_state = TLS_ST_SR_CERT;
return 1;
}
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_SR_COMP_CERT;
+ return 1;
+ }
+#endif
} else {
if (mt == SSL3_MT_FINISHED) {
st->hand_state = TLS_ST_SR_FINISHED;
@@ -99,6 +107,7 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
}
break;
+ case TLS_ST_SR_COMP_CERT:
case TLS_ST_SR_CERT:
if (s->session->peer == NULL) {
if (mt == SSL3_MT_FINISHED) {
@@ -128,10 +137,18 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
if (s->early_data_state == SSL_EARLY_DATA_READING)
break;
- if (mt == SSL3_MT_CERTIFICATE
- && s->post_handshake_auth == SSL_PHA_REQUESTED) {
- st->hand_state = TLS_ST_SR_CERT;
- return 1;
+ if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+ if (mt == SSL3_MT_CERTIFICATE) {
+ st->hand_state = TLS_ST_SR_CERT;
+ return 1;
+ }
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_SR_COMP_CERT;
+ return 1;
+ }
+#endif
}
if (mt == SSL3_MT_KEY_UPDATE) {
@@ -357,6 +374,27 @@ static int send_server_key_exchange(SSL_CONNECTION *s)
}
/*
+ * Used to determine if we shoud send a CompressedCertificate message
+ *
+ * Returns the algorithm to use, TLSEXT_comp_cert_none means no compression
+ */
+static int get_compressed_certificate_alg(SSL_CONNECTION *sc)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ int *alg = sc->ext.compress_certificate_from_peer;
+
+ if (sc->s3.tmp.cert == NULL)
+ return TLSEXT_comp_cert_none;
+
+ for (; *alg != TLSEXT_comp_cert_none; alg++) {
+ if (sc->s3.tmp.cert->comp_cert[*alg] != NULL)
+ return *alg;
+ }
+#endif
+ return TLSEXT_comp_cert_none;
+}
+
+/*
* Should we send a CertificateRequest message?
*
* Valid return values are:
@@ -468,6 +506,8 @@ 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)
+ st->hand_state = TLS_ST_SW_COMP_CERT;
else
st->hand_state = TLS_ST_SW_CERT;
@@ -477,11 +517,14 @@ 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) {
+ st->hand_state = TLS_ST_SW_COMP_CERT;
} else {
st->hand_state = TLS_ST_SW_CERT;
}
return WRITE_TRAN_CONTINUE;
+ case TLS_ST_SW_COMP_CERT:
case TLS_ST_SW_CERT:
st->hand_state = TLS_ST_SW_CERT_VRFY;
return WRITE_TRAN_CONTINUE;
@@ -975,6 +1018,18 @@ WORK_STATE ossl_statem_server_post_work(SSL_CONNECTION *s, WORK_STATE wst)
if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
if (statem_flush(s) != 1)
return WORK_MORE_A;
+ } else {
+ if (!SSL_CONNECTION_IS_TLS13(s)
+ || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
+ }
+ break;
+
+ case TLS_ST_SW_ENCRYPTED_EXTENSIONS:
+ if (!s->hit && !send_certificate_request(s)) {
+ if (!SSL_CONNECTION_IS_TLS13(s)
+ || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
}
break;
@@ -1059,6 +1114,13 @@ int ossl_statem_server_construct_message(SSL_CONNECTION *s,
*mt = SSL3_MT_CERTIFICATE;
break;
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_SW_COMP_CERT:
+ *confunc = tls_construct_server_compressed_certificate;
+ *mt = SSL3_MT_COMPRESSED_CERTIFICATE;
+ break;
+#endif
+
case TLS_ST_SW_CERT_VRFY:
*confunc = tls_construct_cert_verify;
*mt = SSL3_MT_CERTIFICATE_VERIFY;
@@ -1153,6 +1215,7 @@ size_t ossl_statem_server_max_message_size(SSL_CONNECTION *s)
case TLS_ST_SR_END_OF_EARLY_DATA:
return END_OF_EARLY_DATA_MAX_LENGTH;
+ case TLS_ST_SR_COMP_CERT:
case TLS_ST_SR_CERT:
return s->max_cert_list;
@@ -1201,6 +1264,11 @@ MSG_PROCESS_RETURN ossl_statem_server_process_message(SSL_CONNECTION *s,
case TLS_ST_SR_CERT:
return tls_process_client_certificate(s, pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_SR_COMP_CERT:
+ return tls_process_client_compressed_certificate(s, pkt);
+#endif
+
case TLS_ST_SR_KEY_EXCH:
return tls_process_client_key_exchange(s, pkt);
@@ -3633,6 +3701,21 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
return ret;
}
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls_process_client_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ PACKET tmppkt;
+ BUF_MEM *buf = BUF_MEM_new();
+
+ if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR)
+ ret = tls_process_client_certificate(sc, &tmppkt);
+
+ BUF_MEM_free(buf);
+ return ret;
+}
+#endif
+
CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt)
{
CERT_PKEY *cpk = s->s3.tmp.cert;
@@ -3658,6 +3741,32 @@ CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt
return CON_FUNC_SUCCESS;
}
+#ifndef OPENSSL_NO_COMP_ALG
+CON_FUNC_RETURN tls_construct_server_compressed_certificate(SSL_CONNECTION *sc, WPACKET *pkt)
+{
+ int alg = get_compressed_certificate_alg(sc);
+ OSSL_COMP_CERT *cc = sc->s3.tmp.cert->comp_cert[alg];
+
+ if (!ossl_assert(cc != NULL)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ /*
+ * Server can't compress on-demand
+ * Use pre-compressed certificate
+ */
+ if (!WPACKET_put_bytes_u16(pkt, alg)
+ || !WPACKET_put_bytes_u24(pkt, cc->orig_len)
+ || !WPACKET_start_sub_packet_u24(pkt)
+ || !WPACKET_memcpy(pkt, cc->data, cc->len)
+ || !WPACKET_close(pkt))
+ return 0;
+
+ sc->s3.tmp.cert->cert_comp_used++;
+ return 1;
+}
+#endif
+
static int create_ticket_prequel(SSL_CONNECTION *s, WPACKET *pkt,
uint32_t age_add, unsigned char *tick_nonce)
{