Path: blob/main/crypto/openssl/ssl/ssl_cert_comp.c
48150 views
/*1* Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include <stdio.h>10#include "ssl_local.h"11#include "internal/e_os.h"12#include "internal/refcount.h"13#include "internal/ssl_unwrap.h"1415size_t ossl_calculate_comp_expansion(int alg, size_t length)16{17size_t ret;18/*19* Uncompressibility expansion:20* ZLIB: N + 11 + 5 * (N >> 14)21* Brotli: per RFC7932: N + 5 + 3 * (N >> 16)22* ZSTD: N + 4 + 14 + 3 * (N >> 17) + 423*/2425switch (alg) {26case TLSEXT_comp_cert_zlib:27ret = length + 11 + 5 * (length >> 14);28break;29case TLSEXT_comp_cert_brotli:30ret = length + 5 + 3 * (length >> 16);31break;32case TLSEXT_comp_cert_zstd:33ret = length + 22 + 3 * (length >> 17);34break;35default:36return 0;37}38/* Check for overflow */39if (ret < length)40return 0;41return ret;42}4344int ossl_comp_has_alg(int a)45{46#ifndef OPENSSL_NO_COMP_ALG47/* 0 means "any" algorithm */48if ((a == 0 || a == TLSEXT_comp_cert_brotli) && BIO_f_brotli() != NULL)49return 1;50if ((a == 0 || a == TLSEXT_comp_cert_zstd) && BIO_f_zstd() != NULL)51return 1;52if ((a == 0 || a == TLSEXT_comp_cert_zlib) && BIO_f_zlib() != NULL)53return 1;54#endif55return 0;56}5758/* New operation Helper routine */59#ifndef OPENSSL_NO_COMP_ALG60static OSSL_COMP_CERT *OSSL_COMP_CERT_new(unsigned char *data, size_t len, size_t orig_len, int alg)61{62OSSL_COMP_CERT *ret = NULL;6364if (!ossl_comp_has_alg(alg)65|| data == NULL66|| (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL67|| !CRYPTO_NEW_REF(&ret->references, 1))68goto err;6970ret->data = data;71ret->len = len;72ret->orig_len = orig_len;73ret->alg = alg;74return ret;75err:76ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE);77OPENSSL_free(data);78OPENSSL_free(ret);79return NULL;80}8182__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_compressed_data(unsigned char *data, size_t len,83size_t orig_len, int alg)84{85return OSSL_COMP_CERT_new(OPENSSL_memdup(data, len), len, orig_len, alg);86}8788__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_uncompressed_data(unsigned char *data, size_t len,89int alg)90{91OSSL_COMP_CERT *ret = NULL;92size_t max_length;93int comp_length;94COMP_METHOD *method;95unsigned char *comp_data = NULL;96COMP_CTX *comp_ctx = NULL;9798switch (alg) {99case TLSEXT_comp_cert_brotli:100method = COMP_brotli_oneshot();101break;102case TLSEXT_comp_cert_zlib:103method = COMP_zlib_oneshot();104break;105case TLSEXT_comp_cert_zstd:106method = COMP_zstd_oneshot();107break;108default:109goto err;110}111112if ((max_length = ossl_calculate_comp_expansion(alg, len)) == 0113|| method == NULL114|| (comp_ctx = COMP_CTX_new(method)) == NULL115|| (comp_data = OPENSSL_zalloc(max_length)) == NULL)116goto err;117118comp_length = COMP_compress_block(comp_ctx, comp_data, max_length, data, len);119if (comp_length <= 0)120goto err;121122ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg);123comp_data = NULL;124125err:126OPENSSL_free(comp_data);127COMP_CTX_free(comp_ctx);128return ret;129}130131void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc)132{133int i;134135if (cc == NULL)136return;137138CRYPTO_DOWN_REF(&cc->references, &i);139REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc);140if (i > 0)141return;142REF_ASSERT_ISNT(i < 0);143144OPENSSL_free(cc->data);145CRYPTO_FREE_REF(&cc->references);146OPENSSL_free(cc);147}148int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc)149{150int i;151152if (CRYPTO_UP_REF(&cc->references, &i) <= 0)153return 0;154155REF_PRINT_COUNT("OSSL_COMP_CERT", i, cc);156REF_ASSERT_ISNT(i < 2);157return ((i > 1) ? 1 : 0);158}159160static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len)161{162size_t j = 0;163size_t i;164int found = 0;165int already_set[TLSEXT_comp_cert_limit];166int tmp_prefs[TLSEXT_comp_cert_limit];167168/* Note that |len| is the number of |algs| elements */169/* clear all algorithms */170if (len == 0 || algs == NULL) {171memset(prefs, 0, sizeof(tmp_prefs));172return 1;173}174175/* This will 0-terminate the array */176memset(tmp_prefs, 0, sizeof(tmp_prefs));177memset(already_set, 0, sizeof(already_set));178/* Include only those algorithms we support, ignoring duplicates and unknowns */179for (i = 0; i < len; i++) {180if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) {181/* Check for duplicate */182if (already_set[algs[i]])183return 0;184tmp_prefs[j++] = algs[i];185already_set[algs[i]] = 1;186found = 1;187}188}189if (found)190memcpy(prefs, tmp_prefs, sizeof(tmp_prefs));191return found;192}193194static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data)195{196SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);197WPACKET tmppkt;198BUF_MEM buf = { 0 };199size_t ret = 0;200201if (sc == NULL202|| cpk == NULL203|| !sc->server204|| !SSL_in_before(ssl))205return 0;206207/* Use the |tmppkt| for the to-be-compressed data */208if (!WPACKET_init(&tmppkt, &buf))209goto out;210211/* no context present, add 0-length context */212if (!WPACKET_put_bytes_u8(&tmppkt, 0))213goto out;214215/*216* ssl3_output_cert_chain() may generate an SSLfatal() error,217* for this case, we want to ignore it, argument for_comp = 1218*/219if (!ssl3_output_cert_chain(sc, &tmppkt, cpk, 1))220goto out;221WPACKET_get_total_written(&tmppkt, &ret);222223out:224WPACKET_cleanup(&tmppkt);225if (ret != 0 && data != NULL)226*data = (unsigned char *)buf.data;227else228OPENSSL_free(buf.data);229return ret;230}231232static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg)233{234unsigned char *cert_data = NULL;235OSSL_COMP_CERT *comp_cert = NULL;236size_t length;237238if (cpk == NULL239|| alg == TLSEXT_comp_cert_none240|| !ossl_comp_has_alg(alg))241return 0;242243if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)244return 0;245comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg);246OPENSSL_free(cert_data);247if (comp_cert == NULL)248return 0;249250OSSL_COMP_CERT_free(cpk->comp_cert[alg]);251cpk->comp_cert[alg] = comp_cert;252return 1;253}254255/* alg_in can be 0, meaning any/all algorithms */256static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in)257{258SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);259int i;260int j;261int alg;262int count = 0;263264if (sc == NULL265|| cpks == NULL266|| !ossl_comp_has_alg(alg_in))267return 0;268269/* Look through the preferences to see what we have */270for (i = 0; i < TLSEXT_comp_cert_limit; i++) {271/*272* alg = 0 means compress for everything, but only for algorithms enabled273* alg != 0 means compress for that algorithm if enabled274*/275alg = sc->cert_comp_prefs[i];276if ((alg_in == 0 && alg != TLSEXT_comp_cert_none)277|| (alg_in != 0 && alg == alg_in)) {278279for (j = 0; j < SSL_PKEY_NUM; j++) {280/* No cert, move on */281if (cpks[j].x509 == NULL)282continue;283284if (!ssl_compress_one_cert(ssl, &cpks[j], alg))285return 0;286287/* if the cert expanded, set the value in the CERT_PKEY to NULL */288if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) {289OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]);290cpks[j].comp_cert[alg] = NULL;291} else {292count++;293}294}295}296}297return (count > 0);298}299300static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data,301size_t *orig_len)302{303SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);304size_t cert_len = 0;305size_t comp_len = 0;306unsigned char *cert_data = NULL;307OSSL_COMP_CERT *comp_cert = NULL;308309if (sc == NULL310|| cpk == NULL311|| data == NULL312|| orig_len == NULL313|| !sc->server314|| !SSL_in_before(ssl)315|| !ossl_comp_has_alg(alg))316return 0;317318if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)319goto err;320321comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg);322OPENSSL_free(cert_data);323if (comp_cert == NULL)324goto err;325326comp_len = comp_cert->len;327*orig_len = comp_cert->orig_len;328*data = comp_cert->data;329comp_cert->data = NULL;330err:331OSSL_COMP_CERT_free(comp_cert);332return comp_len;333}334335static int ossl_set1_compressed_cert(CERT *cert, int algorithm,336unsigned char *comp_data, size_t comp_length,337size_t orig_length)338{339OSSL_COMP_CERT *comp_cert;340341/* No explicit cert set */342if (cert == NULL || cert->key == NULL)343return 0;344345comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length,346orig_length, algorithm);347if (comp_cert == NULL)348return 0;349350OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]);351cert->key->comp_cert[algorithm] = comp_cert;352353return 1;354}355#endif356357/*-358* Public API359*/360int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len)361{362#ifndef OPENSSL_NO_COMP_ALG363return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len);364#else365return 0;366#endif367}368369int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len)370{371#ifndef OPENSSL_NO_COMP_ALG372SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);373374if (sc == NULL)375return 0;376return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len);377#else378return 0;379#endif380}381382int SSL_compress_certs(SSL *ssl, int alg)383{384#ifndef OPENSSL_NO_COMP_ALG385SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);386387if (sc == NULL || sc->cert == NULL)388return 0;389390return ssl_compress_certs(ssl, sc->cert->pkeys, alg);391#endif392return 0;393}394395int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg)396{397int ret = 0;398#ifndef OPENSSL_NO_COMP_ALG399SSL *new = SSL_new(ctx);400401if (new == NULL)402return 0;403404ret = ssl_compress_certs(new, ctx->cert->pkeys, alg);405SSL_free(new);406#endif407return ret;408}409410size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len)411{412#ifndef OPENSSL_NO_COMP_ALG413SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);414CERT_PKEY *cpk = NULL;415416if (sc == NULL)417return 0;418419if (sc->cert != NULL)420cpk = sc->cert->key;421else422cpk = ssl->ctx->cert->key;423424return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len);425#else426return 0;427#endif428}429430size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len)431{432#ifndef OPENSSL_NO_COMP_ALG433size_t ret;434SSL *new = SSL_new(ctx);435436ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len);437SSL_free(new);438return ret;439#else440return 0;441#endif442}443444int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data,445size_t comp_length, size_t orig_length)446{447#ifndef OPENSSL_NO_COMP_ALG448return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length);449#else450return 0;451#endif452}453454int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data,455size_t comp_length, size_t orig_length)456{457#ifndef OPENSSL_NO_COMP_ALG458SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);459460/* Cannot set a pre-compressed certificate on a client */461if (sc == NULL || !sc->server)462return 0;463464return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length);465#else466return 0;467#endif468}469470471