diff options
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/build.info | 1 | ||||
-rw-r--r-- | ssl/ssl_cert.c | 35 | ||||
-rw-r--r-- | ssl/ssl_cert_comp.c | 479 | ||||
-rw-r--r-- | ssl/ssl_conf.c | 12 | ||||
-rw-r--r-- | ssl/ssl_err.c | 2 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 25 | ||||
-rw-r--r-- | ssl/ssl_local.h | 41 | ||||
-rw-r--r-- | ssl/ssl_stat.c | 16 | ||||
-rw-r--r-- | ssl/statem/extensions.c | 125 | ||||
-rw-r--r-- | ssl/statem/extensions_cust.c | 1 | ||||
-rw-r--r-- | ssl/statem/statem.c | 10 | ||||
-rw-r--r-- | ssl/statem/statem.h | 1 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 162 | ||||
-rw-r--r-- | ssl/statem/statem_lib.c | 73 | ||||
-rw-r--r-- | ssl/statem/statem_local.h | 23 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 117 | ||||
-rw-r--r-- | ssl/t1_trce.c | 89 |
17 files changed, 1194 insertions, 18 deletions
diff --git a/ssl/build.info b/ssl/build.info index 81631c7497..00e9e8fd7d 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -19,6 +19,7 @@ SOURCE[../libssl]=\ ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c ssl_mcnf.c \ bio_ssl.c ssl_err.c ssl_err_legacy.c tls_srp.c t1_trce.c ssl_utst.c \ statem/statem.c \ + ssl_cert_comp.c \ tls_depr.c # For shared builds we need to include the libcrypto packet.c and quic_vlint.c diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c index 8ec36b9c19..d2bc61476d 100644 --- a/ssl/ssl_cert.c +++ b/ssl/ssl_cert.c @@ -74,6 +74,9 @@ CERT *ssl_cert_dup(CERT *cert) { CERT *ret = OPENSSL_zalloc(sizeof(*ret)); int i; +#ifndef OPENSSL_NO_COMP_ALG + int j; +#endif if (ret == NULL) return NULL; @@ -98,6 +101,7 @@ CERT *ssl_cert_dup(CERT *cert) for (i = 0; i < SSL_PKEY_NUM; i++) { CERT_PKEY *cpk = cert->pkeys + i; CERT_PKEY *rpk = ret->pkeys + i; + if (cpk->x509 != NULL) { rpk->x509 = cpk->x509; X509_up_ref(rpk->x509); @@ -115,16 +119,22 @@ CERT *ssl_cert_dup(CERT *cert) goto err; } } - if (cert->pkeys[i].serverinfo != NULL) { + if (cpk->serverinfo != NULL) { /* Just copy everything. */ - ret->pkeys[i].serverinfo = - OPENSSL_malloc(cert->pkeys[i].serverinfo_length); - if (ret->pkeys[i].serverinfo == NULL) + rpk->serverinfo = OPENSSL_memdup(cpk->serverinfo, cpk->serverinfo_length); + if (rpk->serverinfo == NULL) goto err; - ret->pkeys[i].serverinfo_length = cert->pkeys[i].serverinfo_length; - memcpy(ret->pkeys[i].serverinfo, - cert->pkeys[i].serverinfo, cert->pkeys[i].serverinfo_length); + rpk->serverinfo_length = cpk->serverinfo_length; + } +#ifndef OPENSSL_NO_COMP_ALG + for (j = TLSEXT_comp_cert_none; j < TLSEXT_comp_cert_limit; j++) { + if (cpk->comp_cert[j] != NULL) { + if (!OSSL_COMP_CERT_up_ref(cpk->comp_cert[j])) + goto err; + rpk->comp_cert[j] = cpk->comp_cert[j]; + } } +#endif } /* Configured sigalgs copied across */ @@ -198,6 +208,10 @@ CERT *ssl_cert_dup(CERT *cert) void ssl_cert_clear_certs(CERT *c) { int i; +#ifndef OPENSSL_NO_COMP_ALG + int j; +#endif + if (c == NULL) return; for (i = 0; i < SSL_PKEY_NUM; i++) { @@ -211,6 +225,13 @@ void ssl_cert_clear_certs(CERT *c) OPENSSL_free(cpk->serverinfo); cpk->serverinfo = NULL; cpk->serverinfo_length = 0; +#ifndef OPENSSL_NO_COMP_ALG + for (j = 0; j < TLSEXT_comp_cert_limit; j++) { + OSSL_COMP_CERT_free(cpk->comp_cert[j]); + cpk->comp_cert[j] = NULL; + cpk->cert_comp_used = 0; + } +#endif } } diff --git a/ssl/ssl_cert_comp.c b/ssl/ssl_cert_comp.c new file mode 100644 index 0000000000..a86282279c --- /dev/null +++ b/ssl/ssl_cert_comp.c @@ -0,0 +1,479 @@ +/* + * Copyright 2022 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdio.h> +#include "ssl_local.h" +#include "internal/e_os.h" +#include "internal/refcount.h" + +size_t ossl_calculate_comp_expansion(int alg, size_t length) +{ + size_t ret; + /* + * Uncompressibility expansion: + * ZLIB: N + 11 + 5 * (N >> 14) + * Brotli: per RFC7932: N + 5 + 3 * (N >> 16) + * ZSTD: N + 4 + 14 + 3 * (N >> 17) + 4 + */ + + switch (alg) { + case TLSEXT_comp_cert_zlib: + ret = length + 11 + 5 * (length >> 14); + break; + case TLSEXT_comp_cert_brotli: + ret = length + 5 + 3 * (length >> 16); + break; + case TLSEXT_comp_cert_zstd: + ret = length + 22 + 3 * (length >> 17); + break; + default: + return 0; + } + /* Check for overflow */ + if (ret < length) + return 0; + return ret; +} + +int ossl_comp_has_alg(int a) +{ +#ifndef OPENSSL_NO_COMP_ALG + /* 0 means "any" algorithm */ + if ((a == 0 || a == TLSEXT_comp_cert_brotli) && BIO_f_brotli() != NULL) + return 1; + if ((a == 0 || a == TLSEXT_comp_cert_zstd) && BIO_f_zstd() != NULL) + return 1; + if ((a == 0 || a == TLSEXT_comp_cert_zlib) && BIO_f_zlib() != NULL) + return 1; +#endif + return 0; +} + +/* New operation Helper routine */ +#ifndef OPENSSL_NO_COMP_ALG +static OSSL_COMP_CERT *OSSL_COMP_CERT_new(unsigned char *data, size_t len, size_t orig_len, int alg) +{ + OSSL_COMP_CERT *ret = NULL; + + if (!ossl_comp_has_alg(alg) + || data == NULL + || (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL + || (ret->lock = CRYPTO_THREAD_lock_new()) == NULL) + goto err; + + ret->references = 1; + ret->data = data; + ret->len = len; + ret->orig_len = orig_len; + ret->alg = alg; + return ret; + err: + ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE); + OPENSSL_free(data); + OPENSSL_free(ret); + return NULL; +} + +__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_compressed_data(unsigned char *data, size_t len, + size_t orig_len, int alg) +{ + return OSSL_COMP_CERT_new(OPENSSL_memdup(data, len), len, orig_len, alg); +} + +__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_uncompressed_data(unsigned char *data, size_t len, + int alg) +{ + OSSL_COMP_CERT *ret = NULL; + size_t max_length; + int comp_length; + COMP_METHOD *method; + unsigned char *comp_data = NULL; + COMP_CTX *comp_ctx = NULL; + + switch (alg) { + case TLSEXT_comp_cert_brotli: + method = COMP_brotli_oneshot(); + break; + case TLSEXT_comp_cert_zlib: + method = COMP_zlib(); + break; + case TLSEXT_comp_cert_zstd: + method = COMP_zstd_oneshot(); + break; + default: + goto err; + } + + if ((max_length = ossl_calculate_comp_expansion(alg, len)) == 0 + || method == NULL + || (comp_ctx = COMP_CTX_new(method)) == NULL + || (comp_data = OPENSSL_zalloc(max_length)) == NULL) + goto err; + + comp_length = COMP_compress_block(comp_ctx, comp_data, max_length, data, len); + if (comp_length <= 0) + goto err; + + ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg); + comp_data = NULL; + + err: + OPENSSL_free(comp_data); + COMP_CTX_free(comp_ctx); + return ret; +} + +void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc) +{ + int i; + + if (cc == NULL) + return; + + CRYPTO_DOWN_REF(&cc->references, &i, cc->lock); + REF_PRINT_COUNT("OSSL_COMP_CERT", cc); + if (i > 0) + return; + REF_ASSERT_ISNT(i < 0); + + OPENSSL_free(cc->data); + CRYPTO_THREAD_lock_free(cc->lock); + OPENSSL_free(cc); +} +int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc) +{ + int i; + + if (CRYPTO_UP_REF(&cc->references, &i, cc->lock) <= 0) + return 0; + + REF_PRINT_COUNT("OSSL_COMP_CERT", cc); + REF_ASSERT_ISNT(i < 2); + return ((i > 1) ? 1 : 0); +} + +static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len) +{ + size_t j = 0; + size_t i; + int found = 0; + int already_set[TLSEXT_comp_cert_limit]; + int tmp_prefs[TLSEXT_comp_cert_limit]; + + /* Note that |len| is the number of |algs| elements */ + /* clear all algorithms */ + if (len == 0 || algs == NULL) { + memset(prefs, 0, sizeof(tmp_prefs)); + return 1; + } + + /* This will 0-terminate the array */ + memset(tmp_prefs, 0, sizeof(tmp_prefs)); + memset(already_set, 0, sizeof(already_set)); + /* Include only those algorithms we support, ignoring duplicates and unknowns */ + for (i = 0; i < len; i++) { + if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) { + /* Check for duplicate */ + if (already_set[algs[i]]) + return 0; + tmp_prefs[j++] = algs[i]; + already_set[algs[i]] = 1; + found = 1; + } + } + if (found) + memcpy(prefs, tmp_prefs, sizeof(tmp_prefs)); + return found; +} + +static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data) +{ + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + WPACKET tmppkt; + BUF_MEM buf = { 0 }; + size_t ret = 0; + const SSL_METHOD *method = NULL; + + if (sc == NULL + || cpk == NULL + || !sc->server + || !SSL_in_before(ssl)) + return 0; + + /* Use the |tmppkt| for the to-be-compressed data */ + if (!WPACKET_init(&tmppkt, &buf)) + goto out; + + /* no context present, add 0-length context */ + if (!WPACKET_put_bytes_u8(&tmppkt, 0)) + goto out; + + /* + * ssl3_output_cert_chain() may generate an SSLfata() error, + * for this case, we want to ignore it + */ + sc->statem.ignore_fatal = 1; + ERR_set_mark(); + /* Must get the certificate as TLSv1.3, restore before returning */ + method = SSL_get_ssl_method(ssl); + if (!SSL_set_ssl_method(ssl, tlsv1_3_server_method())) + goto out; + + if (!ssl3_output_cert_chain(sc, &tmppkt, cpk)) + goto out; + WPACKET_get_total_written(&tmppkt, &ret); + + out: + /* Don't leave errors in the queue */ + ERR_pop_to_mark(); + sc->statem.ignore_fatal = 0; + if (method != NULL && !SSL_set_ssl_method(ssl, method)) + ret = 0; + WPACKET_cleanup(&tmppkt); + if (ret != 0 && data != NULL) + *data = (unsigned char *)buf.data; + else + OPENSSL_free(buf.data); + return ret; +} + +static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg) +{ + unsigned char *cert_data = NULL; + OSSL_COMP_CERT *comp_cert = NULL; + size_t length; + + if (cpk == NULL + || alg == TLSEXT_comp_cert_none + || !ossl_comp_has_alg(alg)) + return 0; + + if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0) + return 0; + comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg); + OPENSSL_free(cert_data); + if (comp_cert == NULL) + return 0; + + OSSL_COMP_CERT_free(cpk->comp_cert[alg]); + cpk->comp_cert[alg] = comp_cert; + return 1; +} + +/* alg_in can be 0, meaning any/all algorithms */ +static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in) +{ + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + int i; + int j; + int alg; + int count = 0; + + if (sc == NULL + || cpks == NULL + || !ossl_comp_has_alg(alg_in)) + return 0; + + /* Look through the preferences to see what we have */ + for (i = 0; i < TLSEXT_comp_cert_limit; i++) { + /* + * alg = 0 means compress for everything, but only for algorithms enabled + * alg != 0 means compress for that algorithm if enabled + */ + alg = sc->cert_comp_prefs[i]; + if ((alg_in == 0 && alg != TLSEXT_comp_cert_none) + || (alg_in != 0 && alg == alg_in)) { + + for (j = 0; j < SSL_PKEY_NUM; j++) { + /* No cert, move on */ + if (cpks[j].x509 == NULL) + continue; + + if (!ssl_compress_one_cert(ssl, &cpks[j], alg)) + return 0; + + /* if the cert expanded, set the value in the CERT_PKEY to NULL */ + if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) { + OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]); + cpks[j].comp_cert[alg] = NULL; + } else { + count++; + } + } + } + } + return (count > 0); +} + +static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data, + size_t *orig_len) +{ + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + size_t cert_len = 0; + size_t comp_len = 0; + unsigned char *cert_data = NULL; + OSSL_COMP_CERT *comp_cert = NULL; + + if (sc == NULL + || cpk == NULL + || data == NULL + || orig_len == NULL + || !sc->server + || !SSL_in_before(ssl) + || !ossl_comp_has_alg(alg)) + return 0; + + if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0) + goto err; + + comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg); + OPENSSL_free(cert_data); + if (comp_cert == NULL) + goto err; + + comp_len = comp_cert->len; + *orig_len = comp_cert->orig_len; + *data = comp_cert->data; + comp_cert->data = NULL; + err: + OSSL_COMP_CERT_free(comp_cert); + return comp_len; +} + +static int ossl_set1_compressed_cert(CERT *cert, int algorithm, + unsigned char *comp_data, size_t comp_length, + size_t orig_length) +{ + OSSL_COMP_CERT *comp_cert; + + /* No explicit cert set */ + if (cert == NULL || cert->key == NULL) + return 0; + + comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length, + orig_length, algorithm); + if (comp_cert == NULL) + return 0; + + OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]); + cert->key->comp_cert[algorithm] = comp_cert; + + return 1; +} +#endif + +/*- + * Public API + */ +int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len) +{ +#ifndef OPENSSL_NO_COMP_ALG + return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len); +#else + return 0; +#endif +} + +int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len) +{ +#ifndef OPENSSL_NO_COMP_ALG + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + + if (sc == NULL) + return 0; + return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len); +#else + return 0; +#endif +} + +int SSL_compress_certs(SSL *ssl, int alg) +{ +#ifndef OPENSSL_NO_COMP_ALG + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + + if (sc == NULL || sc->cert == NULL) + return 0; + + return ssl_compress_certs(ssl, sc->cert->pkeys, alg); +#endif + return 0; +} + +int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg) +{ + int ret = 0; +#ifndef OPENSSL_NO_COMP_ALG + SSL *new = SSL_new(ctx); + + if (new == NULL) + return 0; + + ret = ssl_compress_certs(new, ctx->cert->pkeys, alg); + SSL_free(new); +#endif + return ret; +} + +size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len) +{ +#ifndef OPENSSL_NO_COMP_ALG + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + CERT_PKEY *cpk = NULL; + + if (sc->cert != NULL) + cpk = sc->cert->key; + else + cpk = ssl->ctx->cert->key; + + return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len); +#else + return 0; +#endif +} + +size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len) +{ +#ifndef OPENSSL_NO_COMP_ALG + size_t ret; + SSL *new = SSL_new(ctx); + + ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len); + SSL_free(new); + return ret; +#else + return 0; +#endif +} + +int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data, + size_t comp_length, size_t orig_length) +{ +#ifndef OPENSSL_NO_COMP_ALG + return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length); +#else + return 0; +#endif +} + +int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data, + size_t comp_length, size_t orig_length) +{ +#ifndef OPENSSL_NO_COMP_ALG + SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl); + + /* Cannot set a pre-compressed certificate on a client */ + if (sc == NULL || !sc->server) + return 0; + + return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length); +#else + return 0; +#endif +} diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c index e1a4bda9ed..bebfc501a9 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -397,7 +397,9 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value) SSL_FLAG_TBL_INV("ExtendedMasterSecret", SSL_OP_NO_EXTENDED_MASTER_SECRET), SSL_FLAG_TBL_INV("CANames", SSL_OP_DISABLE_TLSEXT_CA_NAMES), SSL_FLAG_TBL("KTLS", SSL_OP_ENABLE_KTLS), - SSL_FLAG_TBL_CERT("StrictCertCheck", SSL_CERT_FLAG_TLS_STRICT) + SSL_FLAG_TBL_CERT("StrictCertCheck", SSL_CERT_FLAG_TLS_STRICT), + SSL_FLAG_TBL_INV("TxCertificateCompression", SSL_OP_NO_TX_CERTIFICATE_COMPRESSION), + SSL_FLAG_TBL_INV("RxCertificateCompression", SSL_OP_NO_RX_CERTIFICATE_COMPRESSION), }; if (value == NULL) return -3; @@ -707,6 +709,10 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = { SSL_CONF_CMD_SWITCH("bugs", 0), SSL_CONF_CMD_SWITCH("no_comp", 0), SSL_CONF_CMD_SWITCH("comp", 0), + SSL_CONF_CMD_SWITCH("no_tx_cert_comp", 0), + SSL_CONF_CMD_SWITCH("tx_cert_comp", 0), + SSL_CONF_CMD_SWITCH("no_rx_cert_comp", 0), + SSL_CONF_CMD_SWITCH("rx_cert_comp", 0), SSL_CONF_CMD_SWITCH("ecdh_single", SSL_CONF_FLAG_SERVER), SSL_CONF_CMD_SWITCH("no_ticket", 0), SSL_CONF_CMD_SWITCH("serverpref", SSL_CONF_FLAG_SERVER), @@ -787,6 +793,10 @@ static const ssl_switch_tbl ssl_cmd_switches[] = { {SSL_OP_ALL, 0}, /* bugs */ {SSL_OP_NO_COMPRESSION, 0}, /* no_comp */ {SSL_OP_NO_COMPRESSION, SSL_TFLAG_INV}, /* comp */ + {SSL_OP_NO_TX_CERTIFICATE_COMPRESSION, 0}, /* no_tx_cert_comp */ + {SSL_OP_NO_TX_CERTIFICATE_COMPRESSION, SSL_TFLAG_INV}, /* tx_cert_comp */ + {SSL_OP_NO_RX_CERTIFICATE_COMPRESSION, 0}, /* no_rx_cert_comp */ + {SSL_OP_NO_RX_CERTIFICATE_COMPRESSION, SSL_TFLAG_INV}, /* rx_cert_comp */ {SSL_OP_SINGLE_ECDH_USE, 0}, /* ecdh_single */ {SSL_OP_NO_TICKET, 0}, /* no_ticket */ {SSL_OP_CIPHER_SERVER_PREFERENCE, 0}, /* serverpref */ diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index fb825eb371..1a4f441a9f 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -26,6 +26,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHANGE_CIPHER_SPEC), "bad change cipher spec"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CIPHER), "bad cipher"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_COMPRESSION_ALGORITHM), + "bad compression algorithm"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA), "bad data"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK), "bad data returned by callback"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index fb43b9b369..186e60f34c 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -615,6 +615,9 @@ int ossl_ssl_connection_reset(SSL *s) sc->first_packet = 0; sc->key_update = SSL_KEY_UPDATE_NONE; + memset(sc->ext.compress_certificate_from_peer, 0, + sizeof(sc->ext.compress_certificate_from_peer)); + sc->ext.compress_certificate_sent = 0; EVP_MD_CTX_free(sc->pha_dgst); sc->pha_dgst = NULL; @@ -890,6 +893,10 @@ SSL *ossl_ssl_connection_new(SSL_CTX *ctx) s->job = NULL; +#ifndef OPENSSL_NO_COMP_ALG + memcpy(s->cert_comp_prefs, ctx->cert_comp_prefs, sizeof(s->cert_comp_prefs)); +#endif + #ifndef OPENSSL_NO_CT if (!SSL_set_ct_validation_callback(ssl, ctx->ct_validation_callback, ctx->ct_validation_callback_arg)) @@ -3658,6 +3665,9 @@ SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq, const SSL_METHOD *meth) { SSL_CTX *ret = NULL; +#ifndef OPENSSL_NO_COMP_ALG + int i; +#endif if (meth == NULL) { ERR_raise(ERR_LIB_SSL, SSL_R_NULL_SSL_METHOD_PASSED); @@ -3832,6 +3842,21 @@ SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq, } # endif #endif + +#ifndef OPENSSL_NO_COMP_ALG + /* + * Set the default order: brotli, zlib, zstd + * Including only those enabled algorithms + */ + memset(ret->cert_comp_prefs, 0, sizeof(ret->cert_comp_prefs)); + i = 0; + if (ossl_comp_has_alg(TLSEXT_comp_cert_brotli)) + ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_brotli; + if (ossl_comp_has_alg(TLSEXT_comp_cert_zlib)) + ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_zlib; + if (ossl_comp_has_alg(TLSEXT_comp_cert_zstd)) + ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_zstd; +#endif /* * Disable compression by default to prevent CRIME. Applications can * re-enable compression by configuring diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 75614c9fc9..2580064ebd 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -19,8 +19,8 @@ # include "internal/common.h" /* for HAS_PREFIX */ # include <openssl/buffer.h> -# include <openssl/comp.h> # include <openssl/bio.h> +# include <openssl/comp.h> # include <openssl/dsa.h> # include <openssl/err.h> # include <openssl/ssl.h> @@ -771,6 +771,7 @@ typedef enum tlsext_index_en { TLSEXT_IDX_key_share, TLSEXT_IDX_cookie, TLSEXT_IDX_cryptopro_bug, + TLSEXT_IDX_compress_certificate, TLSEXT_IDX_early_data, TLSEXT_IDX_certificate_authorities, TLSEXT_IDX_padding, @@ -1212,6 +1213,11 @@ struct ssl_ctx_st { uint32_t disabled_mac_mask; uint32_t disabled_mkey_mask; uint32_t disabled_auth_mask; + +#ifndef OPENSSL_NO_COMP_ALG + /* certificate compression preferences */ + int cert_comp_prefs[TLSEXT_comp_cert_limit]; +#endif }; typedef struct cert_pkey_st CERT_PKEY; @@ -1699,6 +1705,11 @@ struct ssl_connection_st { * selected. */ int tick_identity; + + /* This is the list of algorithms the peer supports that we also support */ + int compress_certificate_from_peer[TLSEXT_comp_cert_limit]; + /* indicate that we sent the extension, so we'll accept it */ + int compress_certificate_sent; } ext; /* @@ -1814,6 +1825,11 @@ struct ssl_connection_st { */ const struct sigalg_lookup_st **shared_sigalgs; size_t shared_sigalgslen; + +#ifndef OPENSSL_NO_COMP_ALG + /* certificate compression preferences */ + int cert_comp_prefs[TLSEXT_comp_cert_limit]; +#endif }; # define SSL_CONNECTION_FROM_SSL_ONLY_int(ssl, c) \ @@ -1986,6 +2002,21 @@ typedef struct dtls1_state_st { # define EXPLICIT_CHAR2_CURVE_TYPE 2 # define NAMED_CURVE_TYPE 3 +# ifndef OPENSSL_NO_COMP_ALG +struct ossl_comp_cert_st { + unsigned char *data; + size_t len; + size_t orig_len; + CRYPTO_REF_COUNT references; + CRYPTO_RWLOCK *lock; + int alg; +}; +typedef struct ossl_comp_cert_st OSSL_COMP_CERT; + +void OSSL_COMP_CERT_free(OSSL_COMP_CERT *c); +int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *c); +# endif + struct cert_pkey_st { X509 *x509; EVP_PKEY *privatekey; @@ -2000,6 +2031,11 @@ struct cert_pkey_st { */ unsigned char *serverinfo; size_t serverinfo_length; +# ifndef OPENSSL_NO_COMP_ALG + /* Compressed certificate data - index 0 is unused */ + OSSL_COMP_CERT *comp_cert[TLSEXT_comp_cert_limit]; + int cert_comp_used; +# endif }; /* Retrieve Suite B flags */ # define tls1_suiteb(s) (s->cert->cert_flags & SSL_CERT_FLAG_SUITEB_128_LOS) @@ -2956,4 +2992,7 @@ static ossl_unused ossl_inline void ssl_tsan_counter(const SSL_CTX *ctx, } } +int ossl_comp_has_alg(int a); +size_t ossl_calculate_comp_expansion(int alg, size_t length); + #endif diff --git a/ssl/ssl_stat.c b/ssl/ssl_stat.c index 8854abcbd1..8b93ccd4ac 100644 --- a/ssl/ssl_stat.c +++ b/ssl/ssl_stat.c @@ -37,6 +37,8 @@ const char *SSL_state_string_long(const SSL *s) return "SSLv3/TLS read server hello"; case TLS_ST_CR_CERT: return "SSLv3/TLS read server certificate"; + case TLS_ST_CR_COMP_CERT: + return "TLSv1.3 read server compressed certificate"; case TLS_ST_CR_KEY_EXCH: return "SSLv3/TLS read server key exchange"; case TLS_ST_CR_CERT_REQ: @@ -47,6 +49,8 @@ const char *SSL_state_string_long(const SSL *s) return "SSLv3/TLS read server done"; case TLS_ST_CW_CERT: return "SSLv3/TLS write client certificate"; + case TLS_ST_CW_COMP_CERT: + return "TLSv1.3 write client compressed certificate"; case TLS_ST_CW_KEY_EXCH: return "SSLv3/TLS write client key exchange"; case TLS_ST_CW_CERT_VRFY: @@ -71,6 +75,8 @@ const char *SSL_state_string_long(const SSL *s) return "SSLv3/TLS write server hello"; case TLS_ST_SW_CERT: return "SSLv3/TLS write certificate"; + case TLS_ST_SW_COMP_CERT: + return "TLSv1.3 write server compressed certificate"; case TLS_ST_SW_KEY_EXCH: return "SSLv3/TLS write key exchange"; case TLS_ST_SW_CERT_REQ: @@ -81,6 +87,8 @@ const char *SSL_state_string_long(const SSL *s) return "SSLv3/TLS write server done"; case TLS_ST_SR_CERT: return "SSLv3/TLS read client certificate"; + case TLS_ST_SR_COMP_CERT: + return "TLSv1.3 read client compressed certificate"; case TLS_ST_SR_KEY_EXCH: return "SSLv3/TLS read client key exchange"; case TLS_ST_SR_CERT_VRFY: @@ -150,6 +158,8 @@ const char *SSL_state_string(const SSL *s) return "TRSH"; case TLS_ST_CR_CERT: return "TRSC"; + case TLS_ST_CR_COMP_CERT: + return "TRSCC"; case TLS_ST_CR_KEY_EXCH: return "TRSKE"; case TLS_ST_CR_CERT_REQ: @@ -158,6 +168,8 @@ const char *SSL_state_string(const SSL *s) return "TRSD"; case TLS_ST_CW_CERT: return "TWCC"; + case TLS_ST_CW_COMP_CERT: + return "TWCCC"; case TLS_ST_CW_KEY_EXCH: return "TWCKE"; case TLS_ST_CW_CERT_VRFY: @@ -182,6 +194,8 @@ const char *SSL_state_string(const SSL *s) return "TWSH"; case TLS_ST_SW_CERT: return "TWSC"; + case TLS_ST_SW_COMP_CERT: + return "TWSCC"; case TLS_ST_SW_KEY_EXCH: return "TWSKE"; case TLS_ST_SW_CERT_REQ: @@ -190,6 +204,8 @@ const char *SSL_state_string(const SSL *s) return "TWSD"; case TLS_ST_SR_CERT: return "TRCC"; + case TLS_ST_SR_COMP_CERT: + return "TRCCC"; case TLS_ST_SR_KEY_EXCH: return "TRCKE"; case TLS_ST_SR_CERT_VRFY: 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; |