]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add support for compressed certificates (RFC8879)
authorTodd Short <tshort@akamai.com>
Mon, 9 Aug 2021 20:56:50 +0000 (16:56 -0400)
committerTodd Short <todd.short@me.com>
Tue, 18 Oct 2022 13:30:22 +0000 (09:30 -0400)
* 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)

57 files changed:
CHANGES.md
NEWS.md
apps/include/opt.h
apps/lib/s_cb.c
apps/s_server.c
crypto/comp/c_brotli.c
crypto/comp/c_zstd.c
crypto/comp/comp_err.c
crypto/err/openssl.txt
doc/build.info
doc/man1/openssl-s_client.pod.in
doc/man1/openssl-s_server.pod.in
doc/man3/COMP_CTX_new.pod
doc/man3/SSL_CONF_cmd.pod
doc/man3/SSL_CTX_set1_cert_comp_preference.pod [new file with mode: 0644]
doc/man3/SSL_CTX_set_options.pod
include/openssl/comperr.h
include/openssl/configuration.h.in
include/openssl/ssl.h.in
include/openssl/ssl3.h
include/openssl/sslerr.h
include/openssl/tls1.h
ssl/build.info
ssl/ssl_cert.c
ssl/ssl_cert_comp.c [new file with mode: 0644]
ssl/ssl_conf.c
ssl/ssl_err.c
ssl/ssl_lib.c
ssl/ssl_local.h
ssl/ssl_stat.c
ssl/statem/extensions.c
ssl/statem/extensions_cust.c
ssl/statem/statem.c
ssl/statem/statem.h
ssl/statem/statem_clnt.c
ssl/statem/statem_lib.c
ssl/statem/statem_local.h
ssl/statem/statem_srvr.c
ssl/t1_trce.c
test/bio_comp_test.c
test/build.info
test/cert_comp_test.c [new file with mode: 0644]
test/ext_internal_test.c
test/helpers/handshake.c
test/helpers/ssl_test_ctx.c
test/helpers/ssl_test_ctx.h
test/recipes/70-test_tls13certcomp.t [new file with mode: 0644]
test/recipes/70-test_tls13kexmodes.t
test/recipes/70-test_tls13messages.t
test/recipes/80-test_ssl_new.t
test/recipes/90-test_cert_comp.t [new file with mode: 0644]
test/ssl-tests/32-compressed-certificate.cnf [new file with mode: 0644]
test/ssl-tests/32-compressed-certificate.cnf.in [new file with mode: 0644]
test/sslapitest.c
util/libssl.num
util/perl/TLSProxy/Message.pm
util/perl/checkhandshake.pm

index fac9d80cc02d8bc9420dd128a2f380e844165b72..2fc80041a31548ac3b5b2858773937bc182ef203 100644 (file)
@@ -24,6 +24,11 @@ OpenSSL 3.2
 
 ### Changes between 3.0 and 3.2 [xx XXX xxxx]
 
+ * Add support for certificate compression (RFC8879), including
+   library support for Brotli and Zstandard compression.
+
+   *Todd Short*
+
  * Add the ability to add custom attributes to PKCS12 files. Add a new API
    PKCS12_create_ex2, identical to the existing PKCS12_create_ex but allows
    for a user specified callback and optional argument.
diff --git a/NEWS.md b/NEWS.md
index 3df3bbc007acbb39f2aeadd54ddc7bd2657ef24d..b934aaa0a8a8e799b228696a735f331f84849e4e 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -21,6 +21,8 @@ OpenSSL 3.2
 
 ### Major changes between OpenSSL 3.0 and OpenSSL 3.2 [under development]
 
+  * Added support for certificate compression (RFC8879), including
+    library support for Brotli and Zstandard compression.
   * Subject or issuer names in X.509 objects are now displayed as UTF-8 strings
     by default.
   * TCP Fast Open (RFC7413) support is available on Linux, macOS, and FreeBSD
index 849e10c09e8c35e87e8c3b9127aad4a978e6104f..787dac5468d69e0fbf7be2bdb061794dddc3b55c 100644 (file)
         OPT_S_RECORD_PADDING, OPT_S_DEBUGBROKE, OPT_S_COMP, \
         OPT_S_MINPROTO, OPT_S_MAXPROTO, \
         OPT_S_NO_RENEGOTIATION, OPT_S_NO_MIDDLEBOX, OPT_S_NO_ETM, \
-        OPT_S_NO_EMS, OPT_S__LAST
+        OPT_S_NO_EMS, \
+        OPT_S_NO_TX_CERT_COMP, \
+        OPT_S_NO_RX_CERT_COMP, \
+        OPT_S__LAST
 
 # define OPT_S_OPTIONS \
         OPT_SECTION("TLS/SSL"), \
         {"bugs", OPT_S_BUGS, '-', "Turn on SSL bug compatibility"}, \
         {"no_comp", OPT_S_NO_COMP, '-', "Disable SSL/TLS compression (default)" }, \
         {"comp", OPT_S_COMP, '-', "Use SSL/TLS-level compression" }, \
+        {"no_tx_cert_comp", OPT_S_NO_TX_CERT_COMP, '-', "Disable sending TLSv1.3 compressed certificates" }, \
+        {"no_rx_cert_comp", OPT_S_NO_RX_CERT_COMP, '-', "Disable receiving TLSv1.3 compressed certificates" }, \
         {"no_ticket", OPT_S_NOTICKET, '-', \
             "Disable use of TLS session tickets"}, \
         {"serverpref", OPT_S_SERVERPREF, '-', "Use server's cipher preferences"}, \
         case OPT_S_BUGS: \
         case OPT_S_NO_COMP: \
         case OPT_S_COMP: \
+        case OPT_S_NO_TX_CERT_COMP: \
+        case OPT_S_NO_RX_CERT_COMP: \
         case OPT_S_NOTICKET: \
         case OPT_S_SERVERPREF: \
         case OPT_S_LEGACYRENEG: \
index 8f20113cd358cab5e15d29d17e9f66e188cb90ea..8325e7371f47ebdd3a16a9f274de8cacde35b850 100644 (file)
@@ -559,6 +559,7 @@ static STRINT_PAIR handshakes[] = {
     {", CertificateStatus", SSL3_MT_CERTIFICATE_STATUS},
     {", SupplementalData", SSL3_MT_SUPPLEMENTAL_DATA},
     {", KeyUpdate", SSL3_MT_KEY_UPDATE},
+    {", CompressedCertificate", SSL3_MT_COMPRESSED_CERTIFICATE},
 #ifndef OPENSSL_NO_NEXTPROTONEG
     {", NextProto", SSL3_MT_NEXT_PROTO},
 #endif
@@ -685,6 +686,7 @@ static STRINT_PAIR tlsext_types[] = {
 #ifdef TLSEXT_TYPE_extended_master_secret
     {"extended master secret", TLSEXT_TYPE_extended_master_secret},
 #endif
+    {"compress certificate", TLSEXT_TYPE_compress_certificate},
     {"key share", TLSEXT_TYPE_key_share},
     {"supported versions", TLSEXT_TYPE_supported_versions},
     {"psk", TLSEXT_TYPE_psk},
index 8a573d44146cbe82a76894287c40a6c1d11e5dee..f519505ade0566a21faaeb9a7a2baf3263ee1b30 100644 (file)
@@ -716,7 +716,7 @@ typedef enum OPTION_choice {
     OPT_KEYLOG_FILE, OPT_MAX_EARLY, OPT_RECV_MAX_EARLY, OPT_EARLY_DATA,
     OPT_S_NUM_TICKETS, OPT_ANTI_REPLAY, OPT_NO_ANTI_REPLAY, OPT_SCTP_LABEL_BUG,
     OPT_HTTP_SERVER_BINMODE, OPT_NOCANAMES, OPT_IGNORE_UNEXPECTED_EOF, OPT_KTLS,
-    OPT_TFO,
+    OPT_TFO, OPT_CERT_COMP,
     OPT_R_ENUM,
     OPT_S_ENUM,
     OPT_V_ENUM,
@@ -843,6 +843,9 @@ const OPTIONS s_server_options[] = {
      "No verify output except verify errors"},
     {"ign_eof", OPT_IGN_EOF, '-', "Ignore input EOF (default when -quiet)"},
     {"no_ign_eof", OPT_NO_IGN_EOF, '-', "Do not ignore input EOF"},
+#ifndef OPENSSL_NO_COMP_ALG
+    {"cert_comp", OPT_CERT_COMP, '-', "Pre-compress server certificates"},
+#endif
 
 #ifndef OPENSSL_NO_OCSP
     OPT_SECTION("OCSP"),
@@ -1061,6 +1064,7 @@ int s_server_main(int argc, char *argv[])
     int enable_ktls = 0;
 #endif
     int tfo = 0;
+    int cert_comp = 0;
 
     /* Init of few remaining global variables */
     local_argc = argc;
@@ -1658,6 +1662,9 @@ int s_server_main(int argc, char *argv[])
         case OPT_TFO:
             tfo = 1;
             break;
+        case OPT_CERT_COMP:
+            cert_comp = 1;
+            break;
         }
     }
 
@@ -2243,6 +2250,14 @@ int s_server_main(int argc, char *argv[])
     if (recv_max_early_data >= 0)
         SSL_CTX_set_recv_max_early_data(ctx, recv_max_early_data);
 
+    if (cert_comp) {
+        BIO_printf(bio_s_out, "Compressing certificates\n");
+        if (!SSL_CTX_compress_certs(ctx, 0))
+            BIO_printf(bio_s_out, "Error compressing certs on ctx\n");
+        if (ctx2 != NULL && !SSL_CTX_compress_certs(ctx2, 0))
+            BIO_printf(bio_s_out, "Error compressing certs on ctx2\n");
+    }
+
     if (rev)
         server_cb = rev_body;
     else if (www)
index ace6f221b8169e35db3326e117a139d5aab4dd25..377ea2b8d0c3549f7276dad1156cd09397edc368 100644 (file)
@@ -419,10 +419,10 @@ static const BIO_METHOD bio_meth_brotli = {
 const BIO_METHOD *BIO_f_brotli(void)
 {
 #ifndef OPENSSL_NO_BROTLI
-    return &bio_meth_brotli;
-#else
-    return NULL;
+    if (RUN_ONCE(&brotli_once, ossl_comp_brotli_init))
+        return &bio_meth_brotli;
 #endif
+    return NULL;
 }
 
 #ifndef OPENSSL_NO_BROTLI
index 99d326219e2e0408205c8cb297649a6b9dca843b..15b826c589075185b672c1dd0956eb4f19ef5508 100644 (file)
@@ -481,10 +481,10 @@ static const BIO_METHOD bio_meth_zstd = {
 const BIO_METHOD *BIO_f_zstd(void)
 {
 #ifndef OPENSSL_NO_ZSTD
-    return &bio_meth_zstd;
-#else
-    return NULL;
+    if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init))
+        return &bio_meth_zstd;
 #endif
+    return NULL;
 }
 
 #ifndef OPENSSL_NO_ZSTD
index 10a9e66860f08bd406e5f5ac6ec412ae8d829668..2345da693ec6af27c1058c6d2eeb7ae3f297808b 100644 (file)
 static const ERR_STRING_DATA COMP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_DECODE_ERROR),
     "brotli decode error"},
-    {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_DEFLATE_ERROR),
-    "brotli deflate error"},
     {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_ENCODE_ERROR),
     "brotli encode error"},
-    {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_INFLATE_ERROR),
-    "brotli inflate error"},
     {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_NOT_SUPPORTED),
     "brotli not supported"},
     {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_ZLIB_DEFLATE_ERROR),
index ef02a097e4c5eb585f7f747175fcebe3f5e10baf..653b775330e05512997a1ffbf1b647375b55b61b 100644 (file)
@@ -1271,6 +1271,7 @@ SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE:158:\
        at least (D)TLS 1.2 needed in Suite B mode
 SSL_R_BAD_CHANGE_CIPHER_SPEC:103:bad change cipher spec
 SSL_R_BAD_CIPHER:186:bad cipher
+SSL_R_BAD_COMPRESSION_ALGORITHM:326:bad compression algorithm
 SSL_R_BAD_DATA:390:bad data
 SSL_R_BAD_DATA_RETURNED_BY_CALLBACK:106:bad data returned by callback
 SSL_R_BAD_DECOMPRESSION:107:bad decompression
index 8884b0dd5f9142b6e0e10c83a147bdf98adbacbd..71c7f3aa4068ca06f8a6a2ec05baa42d7c7d5158 100644 (file)
@@ -2167,6 +2167,10 @@ DEPEND[html/man3/SSL_CTX_set0_CA_list.html]=man3/SSL_CTX_set0_CA_list.pod
 GENERATE[html/man3/SSL_CTX_set0_CA_list.html]=man3/SSL_CTX_set0_CA_list.pod
 DEPEND[man/man3/SSL_CTX_set0_CA_list.3]=man3/SSL_CTX_set0_CA_list.pod
 GENERATE[man/man3/SSL_CTX_set0_CA_list.3]=man3/SSL_CTX_set0_CA_list.pod
+DEPEND[html/man3/SSL_CTX_set1_cert_comp_preference.html]=man3/SSL_CTX_set1_cert_comp_preference.pod
+GENERATE[html/man3/SSL_CTX_set1_cert_comp_preference.html]=man3/SSL_CTX_set1_cert_comp_preference.pod
+DEPEND[man/man3/SSL_CTX_set1_cert_comp_preference.3]=man3/SSL_CTX_set1_cert_comp_preference.pod
+GENERATE[man/man3/SSL_CTX_set1_cert_comp_preference.3]=man3/SSL_CTX_set1_cert_comp_preference.pod
 DEPEND[html/man3/SSL_CTX_set1_curves.html]=man3/SSL_CTX_set1_curves.pod
 GENERATE[html/man3/SSL_CTX_set1_curves.html]=man3/SSL_CTX_set1_curves.pod
 DEPEND[man/man3/SSL_CTX_set1_curves.3]=man3/SSL_CTX_set1_curves.pod
@@ -3313,6 +3317,7 @@ html/man3/SSL_CTX_sess_set_cache_size.html \
 html/man3/SSL_CTX_sess_set_get_cb.html \
 html/man3/SSL_CTX_sessions.html \
 html/man3/SSL_CTX_set0_CA_list.html \
+html/man3/SSL_CTX_set1_cert_comp_preference.html \
 html/man3/SSL_CTX_set1_curves.html \
 html/man3/SSL_CTX_set1_sigalgs.html \
 html/man3/SSL_CTX_set1_verify_cert_store.html \
@@ -3918,6 +3923,7 @@ man/man3/SSL_CTX_sess_set_cache_size.3 \
 man/man3/SSL_CTX_sess_set_get_cb.3 \
 man/man3/SSL_CTX_sessions.3 \
 man/man3/SSL_CTX_set0_CA_list.3 \
+man/man3/SSL_CTX_set1_cert_comp_preference.3 \
 man/man3/SSL_CTX_set1_curves.3 \
 man/man3/SSL_CTX_set1_sigalgs.3 \
 man/man3/SSL_CTX_set1_verify_cert_store.3 \
index 5f6f74c51de4165c0d76cd9e311ff2679b9ec8fb..86b9aff91e724c9140e89c29797d2413a938b763 100644 (file)
@@ -83,6 +83,8 @@ B<openssl> B<s_client>
 [B<-read_buf>]
 [B<-ignore_unexpected_eof>]
 [B<-bugs>]
+[B<-no_tx_cert_comp>]
+[B<-no_rx_cert_comp>]
 [B<-comp>]
 [B<-no_comp>]
 [B<-brief>]
@@ -601,6 +603,14 @@ For more information on shutting down a connection, see L<SSL_shutdown(3)>.
 There are several known bugs in SSL and TLS implementations. Adding this
 option enables various workarounds.
 
+=item B<-no_tx_cert_comp>
+
+Disables support for sending TLSv1.3 compressed certificates.
+
+=item B<-no_rx_cert_comp>
+
+Disables support for receiving TLSv1.3 compressed certificate.
+
 =item B<-comp>
 
 Enables support for SSL/TLS compression.
@@ -930,7 +940,9 @@ The B<-certform> option has become obsolete in OpenSSL 3.0.0 and has no effect.
 
 The B<-engine> option was deprecated in OpenSSL 3.0.
 
-The -tfo option was added in OpenSSL 3.2.
+
+The B<-tfo>, B<-no_tx_cert_comp>, and B<-no_rx_cert_comp> options were added
+in OpenSSL 3.2.
 
 =head1 COPYRIGHT
 
index 8fa041c2fe933ab2f9f40f7e8ff4248646771320..94f3b4b46ce6da9d98a0b539b76e68fb7462742c 100644 (file)
@@ -92,6 +92,8 @@ B<openssl> B<s_server>
 [B<-naccept> I<+int>]
 [B<-read_buf> I<+int>]
 [B<-bugs>]
+[B<-no_tx_cert_comp>]
+[B<-no_rx_cert_comp>]
 [B<-no_comp>]
 [B<-comp>]
 [B<-no_ticket>]
@@ -139,6 +141,7 @@ B<openssl> B<s_server>
 [B<-no_anti_replay>]
 [B<-num_tickets>]
 [B<-tfo>]
+[B<-cert_comp>]
 {- $OpenSSL::safe::opt_name_synopsis -}
 {- $OpenSSL::safe::opt_version_synopsis -}
 {- $OpenSSL::safe::opt_v_synopsis -}
@@ -604,6 +607,14 @@ further information).
 There are several known bugs in SSL and TLS implementations. Adding this
 option enables various workarounds.
 
+=item B<-no_tx_cert_comp>
+
+Disables support for sending TLSv1.3 compressed certificates.
+
+=item B<-no_rx_cert_comp>
+
+Disables support for receiving TLSv1.3 compressed certificates.
+
 =item B<-no_comp>
 
 Disable negotiation of TLS compression.
@@ -820,6 +831,9 @@ data that was sent will be rejected.
 
 Enable acceptance of TCP Fast Open (RFC7413) connections.
 
+=item B<-cert_comp>
+
+Pre-compresses certificates (RFC8879) that will be sent during the handshake.
 
 {- $OpenSSL::safe::opt_name_item -}
 
@@ -947,7 +961,8 @@ The
 The B<-srpvfile>, B<-srpuserseed>, and B<-engine>
 option were deprecated in OpenSSL 3.0.
 
-The -tfo option was added in OpenSSL 3.2.
+The B<-tfo>, B<-no_tx_cert_comp>, and B<-no_rx_cert_comp> options were added
+in OpenSSL 3.2.
 
 =head1 COPYRIGHT
 
index 1fe1d691df92377d9feb8621968ef8179e269180..7e1c8c4a83ff92bafe7ae7562c7fd411478fc57d 100644 (file)
@@ -98,7 +98,7 @@ COMP_zstd_oneshot() returns a B<COMP_METHOD> for one-shot Zstandard compression.
 BIO_f_zlib(), BIO_f_brotli() BIO_f_zstd() each return a B<BIO_METHOD> that may be used to
 create a B<BIO> via B<BIO_new(3)> to read and write compressed files or streams.
 The functions are only available if the corresponding algorithm is compiled into
-the OpenSSL library.
+the OpenSSL library. NULL may be returned if the algorithm fails to load dynamically.
 
 =head1 NOTES
 
@@ -123,11 +123,12 @@ L<SSL_set_options(3)> functions.
 
 Compression is also used to support certificate compression as described
 in RFC8879 L<https://datatracker.ietf.org/doc/html/rfc8879>.
-It may be disabled via the SSL_OP_NO_CERTIFICATE_COMPRESSION option of
-the L<SSL_CTX_set_options(3)> or L<SSL_set_options(3)> functions.
+It may be disabled via the SSL_OP_NO_TX_CERTIFICATE_COMPRESSION and
+SSL_OP_NO_RX_CERTIFICATE_COMPRESSION options of the
+L<SSL_CTX_set_options(3)> or L<SSL_set_options(3)> functions.
 
 COMP_zlib(), COMP_brotli() and COMP_zstd() are stream-based compression methods.
-Internal state (including compression dictionary) is maintained between calls. 
+Internal state (including compression dictionary) is maintained between calls.
 If an error is returned, the stream is corrupted, and should be closed.
 
 COMP_brotli_oneshot() and COMP_zstd_oneshot() are not stream-based. These
@@ -152,7 +153,8 @@ bytes stored in the output buffer I<out>. This may be 0. On failure,
 COMP_get_name() returns a B<const char *> that must not be freed
 on success, or NULL on failure.
 
-BIO_f_zlib(), BIO_f_brotli() and BIO_f_zstd() return a B<BIO_METHOD>.
+BIO_f_zlib(), BIO_f_brotli() and BIO_f_zstd() return NULL on error, and
+a B<BIO_METHOD> on success.
 
 =head1 SEE ALSO
 
index 400bd223c6de728c73950df5864466d7edad4ac8..c20df37e3b203597bdba49b9de091a0ffb1e6e59 100644 (file)
@@ -162,6 +162,24 @@ This is a synonym for the B<-groups> command.
 This sets the temporary curve used for ephemeral ECDH modes. Only used
 by servers.
 
+=item B<-tx_cert_comp>
+
+Enables support for sending TLSv1.3 compressed certificates.
+
+=item B<-no_tx_cert_comp>
+
+Disables support for sending TLSv1.3 compressed certificates.
+
+=item B<-rx_cert_comp>
+
+Enables support for receiving TLSv1.3 compressed certificates.
+
+=item B<-no_rx_cert_comp>
+
+Disables support for receiving TLSv1.3 compressed certificates.
+
+=item B<-comp>
+
 The B<groups> argument is a curve name or the special value B<auto> which
 picks an appropriate curve based on client and server preferences. The
 curve can be either the B<NIST> name (e.g. B<P-256>) or an OpenSSL OID name
@@ -535,6 +553,14 @@ B<SSL_OP_ENABLE_KTLS>.
 B<StrictCertCheck>: Enable strict certificate checking. Equivalent to
 setting B<SSL_CERT_FLAG_TLS_STRICT> with SSL_CTX_set_cert_flags().
 
+B<TxCertificateCompression>: support sending compressed certificates, enabled by
+default. Inverse of B<SSL_OP_NO_TX_CERTIFICATE_COMPRESSION>: that is,
+B<-TxCertificateCompression> is the same as setting B<SSL_OP_NO_TX_CERTIFICATE_COMPRESSION>.
+
+B<RxCertificateCompression>: support receiving compressed certificates, enabled by
+default. Inverse of B<SSL_OP_NO_RX_CERTIFICATE_COMPRESSION>: that is,
+B<-RxCertificateCompression> is the same as setting B<SSL_OP_NO_RX_CERTIFICATE_COMPRESSION>.
+
 =item B<VerifyMode>
 
 The B<value> argument is a comma separated list of flags to set.
@@ -736,6 +762,9 @@ B<AllowNoDHEKEX> and B<PrioritizeChaCha> were added in OpenSSL 1.1.1.
 The B<UnsafeLegacyServerConnect> option is no longer set by default from
 OpenSSL 3.0.
 
+The B<TxCertificateCompression> and B<RxCertificateCompression> options were
+added in OpenSSL 3.2.
+
 =head1 COPYRIGHT
 
 Copyright 2012-2022 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man3/SSL_CTX_set1_cert_comp_preference.pod b/doc/man3/SSL_CTX_set1_cert_comp_preference.pod
new file mode 100644 (file)
index 0000000..2fe3587
--- /dev/null
@@ -0,0 +1,160 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_set1_cert_comp_preference,
+SSL_set1_cert_comp_preference,
+SSL_CTX_compress_certs,
+SSL_compress_certs,
+SSL_CTX_get1_compressed_cert,
+SSL_get1_compressed_cert,
+SSL_CTX_set1_compressed_cert,
+SSL_set1_compressed_cert - Certificate compression functions
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len);
+ int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len);
+
+ int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg);
+ int SSL_compress_certs(SSL *ssl, int alg);
+
+ size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data,
+                                     size_t *orig_len);
+ size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data,
+                                 size_t *orig_len);
+
+ int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int alg,
+                                  unsigned char *comp_data,
+                                  size_t comp_length, size_t orig_length);
+ int SSL_set1_compressed_cert(SSL *ssl, int alg, unsigned char *comp_data,
+                              size_t comp_length, size_t orig_length);
+
+
+=head1 DESCRIPTION
+
+These functions control the certificate compression feature. Certificate
+compression is only available for TLSv1.3 as defined in RFC8879.
+
+SSL_CTX_set1_cert_comp_preference() and SSL_set1_cert_comp_preference() are used
+to specify the preferred compression algorithms. The B<algs> argument is an array
+of algorithms, and B<length> is number of elements in the B<algs> array. Only
+those algorithms enabled in the library will be accepted in B<algs>, unknown
+algorithms in B<algs> are ignored. On an error, the preference order is left
+unmodified.
+
+The following compression algorithms (B<alg> arguments) may be used:
+
+=over 4
+
+=item * TLSEXT_comp_cert_brotli
+
+=item * TLSEXT_comp_cert_zlib
+
+=item * TLSEXT_comp_cert_zstd
+
+=back
+
+The above is also the default preference order. If a preference order is not
+specified, then the default preference order is sent to the peer and the
+received peer's preference order will be used when compressing a certificate.
+Otherwise, the configured preference order is sent to the peer and is used
+to filter the peer's preference order.
+
+SSL_CTX_compress_certs() and SSL_compress_certs() are used to pre-compress all
+the configured certificates on an SSL_CTX/SSL object with algorithm B<alg>. If
+B<alg> is 0, then the certificates are compressed with the algorithms specified
+in the preference list. Calling these functions on a client SSL_CTX/SSL object
+will result in an error, as only server certificates may be pre-compressed.
+
+SSL_CTX_get1_compressed_cert() and SSL_get1_compressed_cert() are used to get
+the pre-compressed certificate most recently set that may be stored for later
+use. Calling these functions on a client SSL_CTX/SSL object will result in an
+error, as only server certificates may be pre-compressed. The B<data> and
+B<orig_len> arguments are required.
+
+The compressed certificate data may be passed to SSL_CTX_set1_compressed_cert()
+or SSL_set1_compressed_cert() to provide a pre-compressed version of the
+most recently set certificate. This pre-compressed certificate can only be used
+by a server.
+
+=head1 NOTES
+
+Each side of the connection sends their compression algorithm preference list
+to their peer indicating compressed certificate support. The received preference
+list is filtered by the configured preference list (i.e. the intersection is
+saved). As the default list includes all the enabled algorithms, not specifying
+a preference will allow any enabled algorithm by the peer. The filtered peer's
+preference order is used to determine what algorithm to use when sending a
+compressed certificate.
+
+Only server certificates may be pre-compressed. Calling any of these functions
+(except SSL_CTX_set1_cert_comp_preference()/SSL_set1_cert_comp_preference())
+on a client SSL_CTX/SSL object will return an error. Client certificates are
+compressed on-demand as unique context data from the server is compressed along
+with the certificate.
+
+For SSL_CTX_set1_cert_comp_preference() and SSL_set1_cert_comp_preference()
+the B<len> argument is the size of the B<algs> argument in bytes.
+
+The compressed certificate returned by SSL_CTX_get1_compressed_cert() and
+SSL_get1_compressed_cert() is the last certificate set on the SSL_CTX/SSL object.
+The certificate is copied by the function and the caller must free B<*data> via
+OPENSSL_free().
+
+The compressed certificate data set by SSL_CTX_set1_compressed_cert() and
+SSL_set1_compressed_cert() is copied into the SSL_CTX/SSL object.
+
+SSL_CTX_compress_certs() and SSL_compress_certs() return an error under the
+following conditions:
+
+=over 4
+
+=item * If no certificates have been configured.
+
+=item * If the specified algorithm B<alg> is not enabled.
+
+=item * If B<alg> is 0 and no compression algorithms are enabled.
+
+=back
+
+Sending compressed certificates may be disabled on a connection via the
+SSL_OP_NO_TX_CERTIFICATE_COMPRESSION option. Receiving compressed certificates
+may be disabled on a connection via the SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
+option.
+
+=head1 RETURN VALUES
+
+SSL_CTX_set1_cert_comp_preference(),
+SSL_set1_cert_comp_preference(),
+SSL_CTX_compress_certs(),
+SSL_compress_certs(),
+SSL_CTX_set1_compressed_cert(), and
+SSL_set1_compressed_cert()
+return 1 for success and 0 on error.
+
+SSL_CTX_get1_compressed_cert() and
+SSL_get1_compressed_cert()
+return the length of the allocated memory on success and 0 on error.
+
+=head1 SEE ALSO
+
+L<SSL_CTX_set_options(3)>,
+L<SSL_CTX_use_certificate(3)>
+
+=head1 HISTORY
+
+These functions were added in OpenSSL 3.2.
+
+=head1 COPYRIGHT
+
+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
+L<https://www.openssl.org/source/license.html>.
+
+=cut
index 08522522cd0bb88d870c3be63f2b9c8ce62aa22f..1da057adb83721025d43662fec8f7a67b27d98fc 100644 (file)
@@ -214,6 +214,22 @@ functionality is not required. Those applications can turn this feature off by
 setting this option. This is a server-side opton only. It is ignored by
 clients.
 
+=item SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
+
+Normally clients and servers will transparently attempt to negotiate the
+RFC8879 certificate compression option on TLSv1.3 connections.
+
+If this option is set, the certificate compression extension is ignored
+upon receipt and compressed certificates will not be sent to the peer.
+
+=item SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
+
+Normally clients and servers will transparently attempt to negotiate the
+RFC8879 certificate compression option on TLSv1.3 connections.
+
+If this option is set, the certificate compression extension will not be sent
+and compressed certificates will not be accepted from the peer.
+
 =item SSL_OP_NO_COMPRESSION
 
 Do not use compression even if it is supported. This option is set by default.
index e075506e68f40f6d9df58be373fe5f276a619c13..1948d37f1a0096ab96292417db45951eca40d307 100644 (file)
  * COMP reason codes.
  */
 #  define COMP_R_BROTLI_DECODE_ERROR                       102
-#  define COMP_R_BROTLI_DEFLATE_ERROR                      103
-#  define COMP_R_BROTLI_ENCODE_ERROR                       106
-#  define COMP_R_BROTLI_INFLATE_ERROR                      104
-#  define COMP_R_BROTLI_NOT_SUPPORTED                      105
+#  define COMP_R_BROTLI_ENCODE_ERROR                       103
+#  define COMP_R_BROTLI_NOT_SUPPORTED                      104
 #  define COMP_R_ZLIB_DEFLATE_ERROR                        99
 #  define COMP_R_ZLIB_INFLATE_ERROR                        100
 #  define COMP_R_ZLIB_NOT_SUPPORTED                        101
-#  define COMP_R_ZSTD_COMPRESS_ERROR                       107
-#  define COMP_R_ZSTD_DECODE_ERROR                         108
-#  define COMP_R_ZSTD_DECOMPRESS_ERROR                     109
-#  define COMP_R_ZSTD_NOT_SUPPORTED                        110
+#  define COMP_R_ZSTD_COMPRESS_ERROR                       105
+#  define COMP_R_ZSTD_DECODE_ERROR                         106
+#  define COMP_R_ZSTD_DECOMPRESS_ERROR                     107
+#  define COMP_R_ZSTD_NOT_SUPPORTED                        108
 
 # endif
 #endif
index b84dc1dfe3c9fcb4cef213bda86cd6951b528eca..86077d0a6f5d6b6b06bb32b2d1d96a2958227b62 100644 (file)
@@ -62,6 +62,12 @@ extern "C" {
 
 # define RC4_INT {- $config{rc4_int} -}
 
+# if defined(OPENSSL_NO_COMP) || (defined(OPENSSL_NO_BROTLI) && defined(OPENSSL_NO_ZSTD) && defined(OPENSSL_NO_ZLIB))
+#  define OPENSSL_NO_COMP_ALG
+# else
+#  undef  OPENSSL_NO_COMP_ALG
+# endif
+
 # ifdef  __cplusplus
 }
 # endif
index d2cad44b9174e194ea34a8259cad73ff1b86ad41..4ae96cd1ee1dc090bd429111c1e613459d75293a 100644 (file)
@@ -410,6 +410,15 @@ typedef int (*SSL_async_callback_fn)(SSL *s, void *arg);
      * interoperability with CryptoPro CSP 3.x
      */
 # define SSL_OP_CRYPTOPRO_TLSEXT_BUG                     SSL_OP_BIT(31)
+/*
+ * Disable RFC8879 certificate compression
+ * SSL_OP_NO_TX_CERTIFICATE_COMPRESSION: don't send compressed certificates,
+ *     and ignore the extension when received.
+ * SSL_OP_NO_RX_CERTIFICATE_COMPRESSION: don't send the extension, and
+ *     subsequently indicating that receiving is not supported
+ */
+# define SSL_OP_NO_TX_CERTIFICATE_COMPRESSION            SSL_OP_BIT(32)
+# define SSL_OP_NO_RX_CERTIFICATE_COMPRESSION            SSL_OP_BIT(33)
 
 /*
  * Option "collections."
@@ -998,6 +1007,7 @@ typedef enum {
     DTLS_ST_CR_HELLO_VERIFY_REQUEST,
     TLS_ST_CR_SRVR_HELLO,
     TLS_ST_CR_CERT,
+    TLS_ST_CR_COMP_CERT,
     TLS_ST_CR_CERT_STATUS,
     TLS_ST_CR_KEY_EXCH,
     TLS_ST_CR_CERT_REQ,
@@ -1007,6 +1017,7 @@ typedef enum {
     TLS_ST_CR_FINISHED,
     TLS_ST_CW_CLNT_HELLO,
     TLS_ST_CW_CERT,
+    TLS_ST_CW_COMP_CERT,
     TLS_ST_CW_KEY_EXCH,
     TLS_ST_CW_CERT_VRFY,
     TLS_ST_CW_CHANGE,
@@ -1017,10 +1028,12 @@ typedef enum {
     DTLS_ST_SW_HELLO_VERIFY_REQUEST,
     TLS_ST_SW_SRVR_HELLO,
     TLS_ST_SW_CERT,
+    TLS_ST_SW_COMP_CERT,
     TLS_ST_SW_KEY_EXCH,
     TLS_ST_SW_CERT_REQ,
     TLS_ST_SW_SRVR_DONE,
     TLS_ST_SR_CERT,
+    TLS_ST_SR_COMP_CERT,
     TLS_ST_SR_KEY_EXCH,
     TLS_ST_SR_CERT_VRFY,
     TLS_ST_SR_NEXT_PROTO,
@@ -2530,6 +2543,22 @@ void SSL_set_allow_early_data_cb(SSL *s,
 const char *OSSL_default_cipher_list(void);
 const char *OSSL_default_ciphersuites(void);
 
+/* RFC8879 Certificate compression APIs */
+
+int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg);
+int SSL_compress_certs(SSL *ssl, int alg);
+
+int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len);
+int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len);
+
+int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data,
+                                size_t comp_length, size_t orig_length);
+int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data,
+                            size_t comp_length, size_t orig_length);
+size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len);
+size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len);
+
+
 # ifdef  __cplusplus
 }
 # endif
index 49bd51f24e32e3af546b32962ed9ac82d7556703..6c172f45f964c40160fdba24adbe504fcef4cf9e 100644 (file)
@@ -317,6 +317,7 @@ extern "C" {
 # define SSL3_MT_CERTIFICATE_STATUS              22
 # define SSL3_MT_SUPPLEMENTAL_DATA               23
 # define SSL3_MT_KEY_UPDATE                      24
+# define SSL3_MT_COMPRESSED_CERTIFICATE          25
 # ifndef OPENSSL_NO_NEXTPROTONEG
 #  define SSL3_MT_NEXT_PROTO                     67
 # endif
index 8248b641d70155c42c35fd430d4b9657a8c16d99..98bddbb857f0269d51d742ed459d9cff44b1f33b 100644 (file)
@@ -27,6 +27,7 @@
 # define SSL_R_AT_LEAST_TLS_1_2_NEEDED_IN_SUITEB_MODE     158
 # define SSL_R_BAD_CHANGE_CIPHER_SPEC                     103
 # define SSL_R_BAD_CIPHER                                 186
+# define SSL_R_BAD_COMPRESSION_ALGORITHM                  326
 # define SSL_R_BAD_DATA                                   390
 # define SSL_R_BAD_DATA_RETURNED_BY_CALLBACK              106
 # define SSL_R_BAD_DECOMPRESSION                          107
index c51629169579f4d694e007bdab9ffd9278f08359..0f1b854d42ae8bcbc5b4bf14c0b529462ee68a12 100644 (file)
@@ -134,6 +134,9 @@ extern "C" {
 /* ExtensionType value from RFC7627 */
 # define TLSEXT_TYPE_extended_master_secret      23
 
+/* ExtensionType value from RFC8879 */
+# define TLSEXT_TYPE_compress_certificate        27
+
 /* ExtensionType value from RFC4507 */
 # define TLSEXT_TYPE_session_ticket              35
 
@@ -195,6 +198,15 @@ extern "C" {
 
 # define TLSEXT_hash_num                                 10
 
+/* Possible compression values from RFC8879 */
+/* Not defined in RFC8879, but used internally for no-compression */
+# define TLSEXT_comp_cert_none                            0
+# define TLSEXT_comp_cert_zlib                            1
+# define TLSEXT_comp_cert_brotli                          2
+# define TLSEXT_comp_cert_zstd                            3
+/* one more than the number of defined values - used as size of 0-terminated array */
+# define TLSEXT_comp_cert_limit                           4
+
 /* Flag set for unrecognised algorithms */
 # define TLSEXT_nid_unknown                              0x1000000
 
index 81631c7497bdd69f60ecb982d4a7202cc09c148d..00e9e8fd7d4908e907fcf5b6a7f6e6d2697f4c65 100644 (file)
@@ -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
index 8ec36b9c19ee4bf233a4860741ac237de26ca4aa..d2bc61476d37f9c50a64261ea76c553521c2cff4 100644 (file)
@@ -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 (file)
index 0000000..a862822
--- /dev/null
@@ -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
+}
index e1a4bda9ed71b9d1a97310917757cd070a4071c3..bebfc501a91412a52a94e94ac7a4ff301efc9776 100644 (file)
@@ -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 */
index fb825eb37161b13f01bdb340e63e8ea44447caaa..1a4f441a9f09f8f7d2e01cd0665cbd97c9be83f9 100644 (file)
@@ -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"},
index fb43b9b3690aa5c73addd4d01719c8db09288d7b..186e60f34cbf3a87bebc78ad169d28c4ddd51508 100644 (file)
@@ -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);
@@ -3831,6 +3841,21 @@ SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq,
             ERR_clear_error();
     }
 # 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
index 75614c9fc9cac69ff249bd9fad1176fca6a419a7..2580064ebdd5ecaa94ffecd025440356cd3637ef 100644 (file)
@@ -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
index 8854abcbd1c025a9634819e328a2574a8fd0e1d7..8b93ccd4ac25416d89a78e82d2c9b785b02c8f6d 100644 (file)
@@ -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:
index 880189d9989fef8c6f6b68ceab94baff9001dd65..2cfc9f2b7ded651d68d8a0e08f9f37410821df42 100644 (file)
@@ -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 {
@@ -358,6 +365,15 @@ static const EXTENSION_DEFINITION ext_defs[] = {
         | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         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
@@ -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;
+}
index a2c563b86b7e285daa2814abb15afc42606a3d0e..57713702f7f487d10fc039fdcabf13a593d087d6 100644 (file)
@@ -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;
index 448d655a17cf772b67b990e986d7d62a2d68daeb..ba3681285a3527aecff800df9546f614d5e7148e 100644 (file)
@@ -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;
index 2b73eba6f68f598065765539a54746d4e22a01c1..b60103e6e511f948542230dcb294bdb4c19633c1 100644 (file)
@@ -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;
     /*
index 516aaf3984caf8947fcfaeac8a331d51f15baf41..31f0ebe8e532721bf651f64fb540d0cfd490de9d 100644 (file)
@@ -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;
index 8b34c11048e02330d01d8bb639a9990a46648934..69e900f90e1fbe90f48d45421a9988bb815a1f5b 100644 (file)
@@ -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
index e5c6cfe5352ead917f18bcd8f35dbe19f54723dc..75b7274c2ed3e623a26ff537402914c3d80fe362 100644 (file)
@@ -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,
index ab1574419e033184955f2b6a58af513d3b795def..0d68e4b1cf90961413fd39f4e8ce81bc31b97714 100644 (file)
@@ -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) {
@@ -356,6 +373,27 @@ static int send_server_key_exchange(SSL_CONNECTION *s)
     return 0;
 }
 
+/*
+ * 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?
  *
@@ -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)
 {
index 802958c38370a1cdd0387be05c27f61c071bb498..1a032b3137e92ae6be27456d6b4fdcd5a5562a8d 100644 (file)
@@ -97,6 +97,7 @@ static const ssl_trace_tbl ssl_handshake_tbl[] = {
     {SSL3_MT_CERTIFICATE_STATUS, "CertificateStatus"},
     {SSL3_MT_SUPPLEMENTAL_DATA, "SupplementalData"},
     {SSL3_MT_KEY_UPDATE, "KeyUpdate"},
+    {SSL3_MT_COMPRESSED_CERTIFICATE, "CompressedCertificate"},
 # ifndef OPENSSL_NO_NEXTPROTONEG
     {SSL3_MT_NEXT_PROTO, "NextProto"},
 # endif
@@ -478,6 +479,7 @@ static const ssl_trace_tbl ssl_exts_tbl[] = {
     {TLSEXT_TYPE_padding, "padding"},
     {TLSEXT_TYPE_encrypt_then_mac, "encrypt_then_mac"},
     {TLSEXT_TYPE_extended_master_secret, "extended_master_secret"},
+    {TLSEXT_TYPE_compress_certificate, "compress_certificate"},
     {TLSEXT_TYPE_session_ticket, "session_ticket"},
     {TLSEXT_TYPE_psk, "psk"},
     {TLSEXT_TYPE_early_data, "early_data"},
@@ -617,6 +619,13 @@ static const ssl_trace_tbl ssl_key_update_tbl[] = {
     {SSL_KEY_UPDATE_REQUESTED, "update_requested"}
 };
 
+static const ssl_trace_tbl ssl_comp_cert_tbl[] = {
+    {TLSEXT_comp_cert_none, "none"},
+    {TLSEXT_comp_cert_zlib, "zlib"},
+    {TLSEXT_comp_cert_brotli, "brotli"},
+    {TLSEXT_comp_cert_zstd, "zstd"}
+};
+
 static void ssl_print_hex(BIO *bio, int indent, const char *name,
                           const unsigned char *msg, size_t msglen)
 {
@@ -721,6 +730,14 @@ static int ssl_print_extension(BIO *bio, int indent, int server,
     BIO_printf(bio, "extension_type=%s(%d), length=%d\n",
                ssl_trace_str(extype, ssl_exts_tbl), extype, (int)extlen);
     switch (extype) {
+    case TLSEXT_TYPE_compress_certificate:
+        if (extlen < 1)
+            return 0;
+        xlen = ext[0];
+        if (extlen != xlen + 1)
+            return 0;
+        return ssl_trace_list(bio, indent + 2, ext + 1, xlen, 2, ssl_comp_cert_tbl);
+
     case TLSEXT_TYPE_max_fragment_length:
         if (extlen < 1)
             return 0;
@@ -1287,6 +1304,73 @@ static int ssl_print_certificates(BIO *bio, const SSL_CONNECTION *sc, int server
     return 1;
 }
 
+static int ssl_print_compressed_certificates(BIO *bio, const SSL_CONNECTION *sc,
+                                             int server, int indent,
+                                             const unsigned char *msg,
+                                             size_t msglen)
+{
+    size_t uclen;
+    size_t clen;
+    unsigned int alg;
+    int ret = 1;
+#ifndef OPENSSL_NO_COMP_ALG
+    COMP_METHOD *method;
+    COMP_CTX *comp = NULL;
+    unsigned char* ucdata = NULL;
+#endif
+
+    if (msglen < 8)
+        return 0;
+
+    alg = (msg[0] << 8) | msg[1];
+    uclen = (msg[2] << 16) | (msg[3] << 8) | msg[4];
+    clen = (msg[5] << 16) | (msg[6] << 8) | msg[7];
+    if (msglen != clen + 8)
+        return 0;
+
+    msg += 8;
+    BIO_indent(bio, indent, 80);
+    BIO_printf(bio, "Compression type=%s (0x%04x)\n", ssl_trace_str(alg, ssl_comp_cert_tbl), alg);
+    BIO_indent(bio, indent, 80);
+    BIO_printf(bio, "Uncompressed length=%d\n", (int)uclen);
+    BIO_indent(bio, indent, 80);
+    BIO_printf(bio, "Compressed length=%d, Ratio=%f:1\n", (int)clen, (float)uclen / (float)clen);
+
+    BIO_dump_indent(bio, (const char *)msg, clen, indent);
+
+#ifndef OPENSSL_NO_COMP_ALG
+    if (!ossl_comp_has_alg(alg))
+        return 0;
+
+    if ((ucdata = OPENSSL_malloc(uclen)) == NULL)
+        return 0;
+
+    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;
+    }
+
+    if ((comp = COMP_CTX_new(method)) == NULL
+            || COMP_expand_block(comp, ucdata, uclen, (unsigned char*)msg, clen) != (int)uclen)
+        goto err;
+
+    ret = ssl_print_certificates(bio, sc, server, indent, ucdata, uclen);
+ err:
+    COMP_CTX_free(comp);
+    OPENSSL_free(ucdata);
+#endif
+    return ret;
+}
+
 static int ssl_print_cert_request(BIO *bio, int indent, const SSL_CONNECTION *sc,
                                   const unsigned char *msg, size_t msglen)
 {
@@ -1486,6 +1570,11 @@ static int ssl_print_handshake(BIO *bio, const SSL_CONNECTION *sc, int server,
             return 0;
         break;
 
+    case SSL3_MT_COMPRESSED_CERTIFICATE:
+        if (!ssl_print_compressed_certificates(bio, sc, server, indent + 2, msg, msglen))
+            return 0;
+        break;
+
     case SSL3_MT_CERTIFICATE_VERIFY:
         if (!ssl_print_signature(bio, indent + 2, sc, &msg, &msglen))
             return 0;
index ab56e981f51e6a753eba588f7ed6e1a8e687c125..5dd13abd36c020f9ebaf4aa86f889638c3e37bf9 100644 (file)
@@ -44,6 +44,8 @@ static int do_bio_comp_test(const BIO_METHOD *meth, size_t size)
     int ret = 0;
 
     /* Compress */
+    if (!TEST_ptr(meth))
+        goto err;
     if (!TEST_ptr(bcomp = BIO_new(meth)))
         goto err;
     if (!TEST_ptr(bmem = BIO_new(BIO_s_mem())))
index 69941312758eb01372be804f9bc5ce9813dbc0a8..4a6fc835e1592c61609c3d020e5c8621f1f00d98 100644 (file)
@@ -73,6 +73,10 @@ IF[{- !$disabled{tests} -}]
     PROGRAMS{noinst}=priority_queue_test event_queue_test
   ENDIF
 
+  IF[{- !$disabled{comp} && (!$disabled{brotli} || !$disabled{zstd} || !$disabled{zlib}) -}]
+    PROGRAMS{noinst}=cert_comp_test
+  ENDIF
+
   SOURCE[confdump]=confdump.c
   INCLUDE[confdump]=../include ../apps/include
   DEPEND[confdump]=../libcrypto
@@ -1022,6 +1026,10 @@ ENDIF
   INCLUDE[quic_ackm_test]=../include ../apps/include
   DEPEND[quic_ackm_test]=../libcrypto.a ../libssl.a libtestutil.a
 
+  SOURCE[cert_comp_test]=cert_comp_test.c helpers/ssltestlib.c
+  INCLUDE[cert_comp_test]=../include ../apps/include ..
+  DEPEND[cert_comp_test]=../libcrypto ../libssl libtestutil.a
+
 {-
    use File::Spec::Functions;
    use File::Basename;
diff --git a/test/cert_comp_test.c b/test/cert_comp_test.c
new file mode 100644 (file)
index 0000000..66320bd
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2016-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
+ */
+
+/*
+ * We need access to the deprecated low level HMAC APIs for legacy purposes
+ * when the deprecated calls are not hidden
+ */
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+# define OPENSSL_SUPPRESS_DEPRECATED
+#endif
+
+#include <openssl/ssl.h>
+#include "internal/nelem.h"
+#include "helpers/ssltestlib.h"
+#include "testutil.h"
+#include "../ssl/ssl_local.h"
+
+#undef OSSL_NO_USABLE_TLS1_3
+#if defined(OPENSSL_NO_TLS1_3) \
+    || (defined(OPENSSL_NO_EC) && defined(OPENSSL_NO_DH))
+/*
+ * If we don't have ec or dh then there are no built-in groups that are usable
+ * with TLSv1.3
+ */
+# define OSSL_NO_USABLE_TLS1_3
+#endif
+
+#if !defined(OSSL_NO_USEABLE_TLS1_3)
+
+static char *certsdir = NULL;
+static char *cert = NULL;
+static char *privkey = NULL;
+
+static int client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+    X509 *xcert;
+    EVP_PKEY *privpkey;
+    BIO *in = NULL;
+    BIO *priv_in = NULL;
+
+    /* Check that SSL_get0_peer_certificate() returns something sensible */
+    if (!TEST_ptr(SSL_get0_peer_certificate(ssl)))
+        return 0;
+
+    in = BIO_new_file(cert, "r");
+    if (!TEST_ptr(in))
+        return 0;
+
+    if (!TEST_ptr(xcert = X509_new_ex(NULL, NULL))
+            || !TEST_ptr(PEM_read_bio_X509(in, &xcert, NULL, NULL))
+            || !TEST_ptr(priv_in = BIO_new_file(privkey, "r"))
+            || !TEST_ptr(privpkey = PEM_read_bio_PrivateKey_ex(priv_in, NULL,
+                                                               NULL, NULL,
+                                                               NULL, NULL)))
+        goto err;
+
+    *x509 = xcert;
+    *pkey = privpkey;
+
+    BIO_free(in);
+    BIO_free(priv_in);
+    return 1;
+err:
+    X509_free(xcert);
+    BIO_free(in);
+    BIO_free(priv_in);
+    return 0;
+}
+
+static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+    return 1;
+}
+
+/*
+ * Test 0 = app pre-compresses certificate in SSL
+ * Test 1 = app pre-compresses certificate in SSL_CTX
+ * Test 2 = app pre-compresses certificate in SSL_CTX, client authentication
+ * Test 3 = app pre-compresses certificate in SSL_CTX, but it's unused due to prefs
+ */
+/* Compression helper */
+static int ssl_comp_cert(SSL *ssl, int alg)
+{
+    unsigned char *comp_data = NULL;
+    size_t comp_len = 0;
+    size_t orig_len = 0;
+    int retval = 0;
+
+    if (!TEST_size_t_gt(comp_len = SSL_get1_compressed_cert(ssl, alg, &comp_data, &orig_len), 0))
+        goto err;
+
+    if (!TEST_true(SSL_set1_compressed_cert(ssl, alg, comp_data, comp_len, orig_len)))
+        goto err;
+    retval = alg;
+
+ err:
+    OPENSSL_free(comp_data);
+    return retval;
+}
+
+static void cert_comp_info_cb(const SSL *s, int where, int ret)
+{
+    int *seen = (int*)SSL_get_app_data(s);
+
+    if (SSL_is_server(s)) {
+        /* TLS_ST_SR_COMP_CERT */
+        if (!strcmp(SSL_state_string(s), "TRCCC") && seen != NULL)
+            *seen = 1;
+    } else {
+        /* TLS_ST_CR_COMP_CERT */
+        if (!strcmp(SSL_state_string(s), "TRSCC") && seen != NULL)
+            *seen = 1;
+    }
+}
+
+static int test_ssl_cert_comp(int test)
+{
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+    int expected_client = TLSEXT_comp_cert_none;
+    int expected_server = TLSEXT_comp_cert_none;
+    int client_seen = 0;
+    int server_seen = 0;
+    /* reverse default order */
+    int server_pref[] = { TLSEXT_comp_cert_zstd, TLSEXT_comp_cert_zlib, TLSEXT_comp_cert_brotli };
+    /* default order */
+    int client_pref[] = { TLSEXT_comp_cert_brotli, TLSEXT_comp_cert_zlib, TLSEXT_comp_cert_zstd };
+
+    /* one of these *must* be defined! */
+#ifndef OPENSSL_NO_BROTLI
+    expected_server = TLSEXT_comp_cert_brotli;
+    expected_client = TLSEXT_comp_cert_brotli;
+#endif
+#ifndef OPENSSL_NO_ZLIB
+    expected_server = TLSEXT_comp_cert_zlib;
+    if (expected_client == TLSEXT_comp_cert_none)
+        expected_client = TLSEXT_comp_cert_zlib;
+#endif
+#ifndef OPENSSL_NO_ZSTD
+    expected_server = TLSEXT_comp_cert_zstd;
+    if (expected_client == TLSEXT_comp_cert_none)
+        expected_client = TLSEXT_comp_cert_zstd;
+#endif
+    /* if there's only one comp algorithm, pref won't do much */
+    if (test == 3 && expected_client == expected_server) {
+        TEST_info("Only one compression algorithm configured");
+        return 1;
+    }
+
+    if (!TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(),
+                                       TLS_client_method(),
+                                       TLS1_3_VERSION, 0,
+                                       &sctx, &cctx, cert, privkey)))
+        goto end;
+    if (test == 3) {
+        server_pref[0] = expected_server;
+        server_pref[1] = expected_client;
+        if (!TEST_true(SSL_CTX_set1_cert_comp_preference(sctx, server_pref, 2)))
+            goto end;
+        client_pref[0] = expected_client;
+        if (!TEST_true(SSL_CTX_set1_cert_comp_preference(cctx, client_pref, 1)))
+            goto end;
+    } else {
+        if (!TEST_true(SSL_CTX_set1_cert_comp_preference(sctx, server_pref, OSSL_NELEM(server_pref))))
+            goto end;
+        if (!TEST_true(SSL_CTX_set1_cert_comp_preference(cctx, client_pref, OSSL_NELEM(client_pref))))
+            goto end;
+    }
+    if (test == 2) {
+        /* Use callbacks from test_client_cert_cb() */
+        SSL_CTX_set_client_cert_cb(cctx, client_cert_cb);
+        SSL_CTX_set_verify(sctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
+    }
+
+    if (test == 1 || test== 2 || test == 3) {
+        if (!TEST_true(SSL_CTX_compress_certs(sctx, expected_server)))
+            goto end;
+    }
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                      NULL, NULL)))
+        goto end;
+
+    if (!TEST_true(SSL_set_app_data(clientssl, &client_seen)))
+        goto end;
+    if (!TEST_true(SSL_set_app_data(serverssl, &server_seen)))
+        goto end;
+    SSL_set_info_callback(clientssl, cert_comp_info_cb);
+    SSL_set_info_callback(serverssl, cert_comp_info_cb);
+
+    if (test == 0) {
+        if (!TEST_int_eq(ssl_comp_cert(serverssl, expected_server), expected_server))
+            goto end;
+    }
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
+        goto end;
+    if (test == 3) {
+        SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(serverssl);
+
+        /* expect that the pre-compressed cert won't be used */
+        if (!TEST_int_eq(sc->cert->key->cert_comp_used, 0))
+            goto end;
+
+        if (!TEST_false(*(int*)SSL_get_app_data(clientssl)))
+            goto end;
+    } else {
+        SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(serverssl);
+
+        if (!TEST_int_gt(sc->cert->key->cert_comp_used, 0))
+            goto end;
+
+        if (!TEST_true(*(int*)SSL_get_app_data(clientssl)))
+            goto end;
+    }
+
+    if (test == 2) {
+        /* Only for client auth */
+        if (!TEST_true(*(int*)SSL_get_app_data(serverssl)))
+            goto end;
+    }
+
+    testresult = 1;
+
+ end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+#endif
+
+OPT_TEST_DECLARE_USAGE("certdir\n")
+
+int setup_tests(void)
+{
+#if !defined(OSSL_NO_USEABLE_TLS1_3)
+    if (!test_skip_common_options()) {
+        TEST_error("Error parsing test options\n");
+        return 0;
+    }
+
+    if (!TEST_ptr(certsdir = test_get_argument(0)))
+        return 0;
+
+    cert = test_mk_file_path(certsdir, "servercert.pem");
+    if (cert == NULL)
+        goto err;
+
+    privkey = test_mk_file_path(certsdir, "serverkey.pem");
+    if (privkey == NULL)
+        goto err;
+
+    ADD_ALL_TESTS(test_ssl_cert_comp, 4);
+    return 1;
+
+ err:
+    OPENSSL_free(cert);
+    OPENSSL_free(privkey);
+    return 0;
+#else
+    return 1;
+#endif
+}
+
+void cleanup_tests(void)
+{
+#if !defined(OSSL_NO_USEABLE_TLS1_3)
+    OPENSSL_free(cert);
+    OPENSSL_free(privkey);
+#endif
+}
index dc1420aba81d5169be8c5256e9e5cca6caa555e6..97a538abf9d4b890e187ff2d7e2da90a37e0361f 100644 (file)
@@ -67,6 +67,7 @@ static EXT_LIST ext_list[] = {
     EXT_ENTRY(key_share),
     EXT_ENTRY(cookie),
     EXT_ENTRY(cryptopro_bug),
+    EXT_ENTRY(compress_certificate),
     EXT_ENTRY(early_data),
     EXT_ENTRY(certificate_authorities),
     EXT_ENTRY(padding),
index fc7f0263001df25befc264ccdf054f6a456766dd..e0422469e4c223d08449c73734128d9ec97494fb 100644 (file)
@@ -695,6 +695,14 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
                                          server2_ctx_data, client_ctx_data))
         goto err;
 #endif  /* !OPENSSL_NO_SRP */
+#ifndef OPENSSL_NO_COMP_ALG
+    if (test->compress_certificates) {
+        if (!TEST_true(SSL_CTX_compress_certs(server_ctx, 0)))
+            goto err;
+        if (server2_ctx != NULL && !TEST_true(SSL_CTX_compress_certs(server2_ctx, 0)))
+            goto err;
+    }
+#endif
     return 1;
 err:
     return 0;
index 69baa0b49fa144364ab779ea6c087261b9f770a6..1919481b02ab3de9a3ca7be177acdd89372fc6be 100644 (file)
@@ -446,6 +446,7 @@ const char *ssl_ct_validation_name(ssl_ct_validation_t mode)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, resumption_expected)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_SERVER_CONF, server, broken_session_ticket)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, use_sctp)
+IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, compress_certificates)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, enable_client_sctp_label_bug)
 IMPLEMENT_SSL_TEST_BOOL_OPTION(SSL_TEST_CTX, test, enable_server_sctp_label_bug)
 
@@ -686,6 +687,7 @@ static const ssl_test_ctx_option ssl_test_ctx_options[] = {
     { "ExpectedClientSignType", &parse_expected_client_sign_type },
     { "ExpectedClientCANames", &parse_expected_client_ca_names },
     { "UseSCTP", &parse_test_use_sctp },
+    { "CompressCertificates", &parse_test_compress_certificates },
     { "EnableClientSCTPLabelBug", &parse_test_enable_client_sctp_label_bug },
     { "EnableServerSCTPLabelBug", &parse_test_enable_server_sctp_label_bug },
     { "ExpectedCipher", &parse_test_expected_cipher },
index c7820d9764b8416acc47245c9b16f52009cdc8c3..4baf657f6da1b1c3aaf061d4d510b6882544f558 100644 (file)
@@ -218,6 +218,8 @@ typedef struct {
     STACK_OF(X509_NAME) *expected_client_ca_names;
     /* Whether to use SCTP for the transport */
     int use_sctp;
+    /* Whether to pre-compress server certificates */
+    int compress_certificates;
     /* Enable SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG on client side */
     int enable_client_sctp_label_bug;
     /* Enable SSL_MODE_DTLS_SCTP_LABEL_LENGTH_BUG on server side */
diff --git a/test/recipes/70-test_tls13certcomp.t b/test/recipes/70-test_tls13certcomp.t
new file mode 100644 (file)
index 0000000..bc960c8
--- /dev/null
@@ -0,0 +1,295 @@
+#! /usr/bin/env perl
+# Copyright 2015-2020 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
+
+use strict;
+use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file srctop_dir bldtop_dir/;
+use OpenSSL::Test::Utils;
+use File::Temp qw(tempfile);
+use TLSProxy::Proxy;
+use checkhandshake qw(checkhandshake @handmessages @extensions);
+
+my $test_name = "test_tls13certcomp";
+setup($test_name);
+
+plan skip_all => "TLSProxy isn't usable on $^O"
+    if $^O =~ /^(VMS)$/;
+
+plan skip_all => "$test_name needs the dynamic engine feature enabled"
+    if disabled("engine") || disabled("dynamic-engine");
+
+plan skip_all => "$test_name needs the sock feature enabled"
+    if disabled("sock");
+
+plan skip_all => "$test_name needs TLSv1.3 enabled"
+    if disabled("tls1_3");
+
+plan skip_all => "$test_name needs EC enabled"
+    if disabled("ec");
+
+plan skip_all => "$test_name needs compression and algorithms enabled"
+    if disabled("comp") || (disabled("brotli") && disabled("zlib") && disabled("zstd"));
+
+@handmessages = (
+    [TLSProxy::Message::MT_CLIENT_HELLO,
+        checkhandshake::ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_SERVER_HELLO,
+        checkhandshake::ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS,
+        checkhandshake::ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_CERTIFICATE_REQUEST,
+        checkhandshake::CERT_COMP_CLI_HANDSHAKE | checkhandshake::CERT_COMP_BOTH_HANDSHAKE],
+    [TLSProxy::Message::MT_CERTIFICATE,
+        checkhandshake::ALL_HANDSHAKES & ~(checkhandshake::CERT_COMP_SRV_HANDSHAKE | checkhandshake::CERT_COMP_BOTH_HANDSHAKE)],
+    [TLSProxy::Message::MT_COMPRESSED_CERTIFICATE,
+        checkhandshake::CERT_COMP_SRV_HANDSHAKE | checkhandshake::CERT_COMP_BOTH_HANDSHAKE],
+    [TLSProxy::Message::MT_CERTIFICATE_VERIFY,
+        checkhandshake::ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_FINISHED,
+        checkhandshake::ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_COMPRESSED_CERTIFICATE,
+        checkhandshake::CERT_COMP_CLI_HANDSHAKE | checkhandshake::CERT_COMP_BOTH_HANDSHAKE],
+    [TLSProxy::Message::MT_CERTIFICATE_VERIFY,
+        checkhandshake::CERT_COMP_CLI_HANDSHAKE | checkhandshake::CERT_COMP_BOTH_HANDSHAKE],
+    [TLSProxy::Message::MT_FINISHED,
+        checkhandshake::ALL_HANDSHAKES],
+    [0, 0]
+);
+
+@extensions = (
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::SERVER_NAME_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::STATUS_REQUEST_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::ALPN_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::SCT_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK_KEX_MODES,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::PSK_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_POST_HANDSHAKE_AUTH,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_COMPRESS_CERTIFICATE,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::CERT_COMP_CLI_EXTENSION],
+
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        TLSProxy::Message::SERVER,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
+        TLSProxy::Message::SERVER,
+        checkhandshake::KEY_SHARE_HRR_EXTENSION],
+
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::SERVER_NAME_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_STATUS_REQUEST,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::STATUS_REQUEST_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EC_POINT_FORMATS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SIG_ALGS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ALPN,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::ALPN_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SCT,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::SCT_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_ENCRYPT_THEN_MAC,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_EXTENDED_MASTER_SECRET,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SESSION_TICKET,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK_KEX_MODES,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::PSK_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_POST_HANDSHAKE_AUTH,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::POST_HANDSHAKE_AUTH_CLI_EXTENSION],
+    [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_COMPRESS_CERTIFICATE,
+        TLSProxy::Message::CLIENT,
+        checkhandshake::CERT_COMP_CLI_EXTENSION],
+
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        TLSProxy::Message::SERVER,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
+        TLSProxy::Message::SERVER,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_PSK,
+        TLSProxy::Message::SERVER,
+        checkhandshake::PSK_SRV_EXTENSION],
+
+    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_SERVER_NAME,
+        TLSProxy::Message::SERVER,
+        checkhandshake::SERVER_NAME_SRV_EXTENSION],
+    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_ALPN,
+        TLSProxy::Message::SERVER,
+        checkhandshake::ALPN_SRV_EXTENSION],
+    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_SUPPORTED_GROUPS,
+        TLSProxy::Message::SERVER,
+        checkhandshake::SUPPORTED_GROUPS_SRV_EXTENSION],
+
+    [TLSProxy::Message::MT_CERTIFICATE_REQUEST, TLSProxy::Message::EXT_SIG_ALGS,
+        TLSProxy::Message::SERVER,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_CERTIFICATE_REQUEST, TLSProxy::Message::EXT_COMPRESS_CERTIFICATE,
+        TLSProxy::Message::SERVER,
+        checkhandshake::CERT_COMP_SRV_EXTENSION],
+
+    [TLSProxy::Message::MT_CERTIFICATE, TLSProxy::Message::EXT_STATUS_REQUEST,
+        TLSProxy::Message::SERVER,
+        checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
+    [TLSProxy::Message::MT_CERTIFICATE, TLSProxy::Message::EXT_SCT,
+        TLSProxy::Message::SERVER,
+        checkhandshake::SCT_SRV_EXTENSION],
+
+    [0,0,0,0]
+);
+
+my $proxy = TLSProxy::Proxy->new(
+    undef,
+    cmdstr(app(["openssl"]), display => 1),
+    srctop_file("apps", "server.pem"),
+    (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
+);
+
+
+#Test 1: Client sends cert comp, but no client auth
+$proxy->serverconnects(2);
+$proxy->clear();
+$proxy->serverflags("-no_tx_cert_comp -no_rx_cert_comp");
+# One final skip check
+$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
+plan tests => 8;
+checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_CLI_EXTENSION,
+               "Client supports certificate compression");
+
+#Test 2: Server sends cert comp, no client auth
+$proxy->clear();
+$proxy->clientflags("-no_tx_cert_comp -no_rx_cert_comp");
+$proxy->serverflags("-cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_SRV_EXTENSION,
+               "Server supports certificate compression, but no client auth");
+
+#Test 3: Both send cert comp, no client auth
+$proxy->clear();
+$proxy->serverflags("-cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::CERT_COMP_SRV_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_CLI_EXTENSION
+               | checkhandshake::CERT_COMP_SRV_EXTENSION,
+               "Both support certificate compression, but no client auth");
+
+#Test 4: Both send cert comp, with client auth
+$proxy->clear();
+$proxy->clientflags("-cert ".srctop_file("apps", "server.pem"));
+$proxy->serverflags("-Verify 5 -cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::CERT_COMP_BOTH_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_CLI_EXTENSION
+               | checkhandshake::CERT_COMP_SRV_EXTENSION,
+               "Both support certificate compression, with client auth");
+
+#Test 5: Client-to-server-only certificate compression, with client auth
+$proxy->clear();
+$proxy->clientflags("-no_rx_cert_comp -cert ".srctop_file("apps", "server.pem"));
+$proxy->serverflags("-no_tx_cert_comp -Verify 5 -cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::CERT_COMP_CLI_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_SRV_EXTENSION,
+               "Client-to-server-only certificate compression, with client auth");
+
+#Test 6: Server-to-client-only certificate compression
+$proxy->clear();
+$proxy->clientflags("-no_tx_cert_comp");
+$proxy->serverflags("-no_rx_cert_comp -cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::CERT_COMP_SRV_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_CLI_EXTENSION,
+               "Server-to-client-only certificate compression");
+
+#Test 7: Neither side wants to send a compressed cert, but will accept one
+$proxy->clear();
+$proxy->clientflags("-no_tx_cert_comp");
+$proxy->serverflags("-no_tx_cert_comp -cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS
+               | checkhandshake::CERT_COMP_CLI_EXTENSION
+               | checkhandshake::CERT_COMP_SRV_EXTENSION,
+               "Accept but not send compressed certificates");
+
+#Test 8: Neither side wants to receive a compressed cert, but will send one
+$proxy->clear();
+$proxy->clientflags("-no_rx_cert_comp");
+$proxy->serverflags("-no_rx_cert_comp -cert_comp");
+$proxy->start();
+checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
+               checkhandshake::DEFAULT_EXTENSIONS,
+               "Send but not accept compressed certificates");
index 0d0681a838eed596fcbc234145864b737a75369b..1c2c18cc113d1c41881ad5812009babb756f3094 100644 (file)
@@ -187,8 +187,8 @@ my $proxy = TLSProxy::Proxy->new(
 
 #Test 1: First get a session
 (undef, my $session) = tempfile();
-$proxy->clientflags("-sess_out ".$session);
-$proxy->serverflags("-servername localhost");
+$proxy->clientflags("-no_rx_cert_comp -sess_out ".$session);
+$proxy->serverflags("-no_rx_cert_comp -servername localhost");
 $proxy->sessionfile($session);
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
 plan tests => 11;
@@ -197,7 +197,7 @@ ok(TLSProxy::Message->success(), "Initial connection");
 #Test 2: Attempt a resume with no kex modes extension. Should fail (server
 #        MUST abort handshake with pre_shared key and no psk_kex_modes)
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
 my $testtype = DELETE_EXTENSION;
 $proxy->filter(\&modify_kex_modes_filter);
 $proxy->start();
@@ -206,7 +206,7 @@ ok(TLSProxy::Message->fail(), "Resume with no kex modes");
 #Test 3: Attempt a resume with empty kex modes extension. Should fail (empty
 #        extension is invalid)
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
 $testtype = EMPTY_EXTENSION;
 $proxy->start();
 ok(TLSProxy::Message->fail(), "Resume with empty kex modes");
@@ -214,8 +214,8 @@ ok(TLSProxy::Message->fail(), "Resume with empty kex modes");
 #Test 4: Attempt a resume with non-dhe kex mode only. Should resume without a
 #        key_share
 $proxy->clear();
-$proxy->clientflags("-allow_no_dhe_kex -sess_in ".$session);
-$proxy->serverflags("-allow_no_dhe_kex");
+$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -sess_in ".$session);
+$proxy->serverflags("-no_rx_cert_comp -allow_no_dhe_kex");
 $testtype = NON_DHE_KEX_MODE_ONLY;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
@@ -227,7 +227,7 @@ checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
 
 #Test 5: Attempt a resume with dhe kex mode only. Should resume with a key_share
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
 $testtype = DHE_KEX_MODE_ONLY;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
@@ -241,7 +241,7 @@ checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
 #Test 6: Attempt a resume with only unrecognised kex modes. Should not resume
 #        but rather fall back to full handshake
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
 $testtype = UNKNOWN_KEX_MODES;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
@@ -254,7 +254,7 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 #Test 7: Attempt a resume with both non-dhe and dhe kex mode. Should resume with
 #        a key_share
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
 $testtype = BOTH_KEX_MODES;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
@@ -268,8 +268,8 @@ checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
 #Test 8: Attempt a resume with both non-dhe and dhe kex mode, but unacceptable
 #        initial key_share. Should resume with a key_share following an HRR
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
-$proxy->serverflags("-curves P-256");
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
+$proxy->serverflags("-no_rx_cert_comp -curves P-256");
 $testtype = BOTH_KEX_MODES;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
@@ -284,8 +284,8 @@ checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
 #Test 9: Attempt a resume with dhe kex mode only and an unacceptable initial
 #        key_share. Should resume with a key_share following an HRR
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
-$proxy->serverflags("-curves P-256");
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
+$proxy->serverflags("-no_rx_cert_comp -curves P-256");
 $testtype = DHE_KEX_MODE_ONLY;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
@@ -301,8 +301,8 @@ checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
 #         initial key_share and no overlapping groups. Should resume without a
 #         key_share
 $proxy->clear();
-$proxy->clientflags("-allow_no_dhe_kex -curves P-384 -sess_in ".$session);
-$proxy->serverflags("-allow_no_dhe_kex -curves P-256");
+$proxy->clientflags("-no_rx_cert_comp -allow_no_dhe_kex -curves P-384 -sess_in ".$session);
+$proxy->serverflags("-no_rx_cert_comp -allow_no_dhe_kex -curves P-256");
 $testtype = BOTH_KEX_MODES;
 $proxy->start();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
@@ -315,8 +315,8 @@ checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
 #Test 11: Attempt a resume with dhe kex mode only, unacceptable
 #         initial key_share and no overlapping groups. Should fail
 $proxy->clear();
-$proxy->clientflags("-curves P-384 -sess_in ".$session);
-$proxy->serverflags("-curves P-256");
+$proxy->clientflags("-no_rx_cert_comp -curves P-384 -sess_in ".$session);
+$proxy->serverflags("-no_rx_cert_comp -curves P-256");
 $testtype = DHE_KEX_MODE_ONLY;
 $proxy->start();
 ok(TLSProxy::Message->fail(), "Resume with dhe kex mode, no overlapping groups");
index 58b88a3ca8da71ca923bca245af9647969c6d3f0..b2f9819f02c80ba76a8d9d3e5d7cd61913b7c350 100644 (file)
@@ -203,7 +203,7 @@ my $proxy = TLSProxy::Proxy->new(
 #Test 1: Check we get all the right messages for a default handshake
 (undef, my $session) = tempfile();
 $proxy->serverconnects(2);
-$proxy->clientflags("-sess_out ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_out ".$session);
 $proxy->sessionfile($session);
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
 plan tests => 17;
@@ -213,7 +213,7 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 
 #Test 2: Resumption handshake
 $proxy->clearClient();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
 $proxy->clientstart();
 checkhandshake($proxy, checkhandshake::RESUME_HANDSHAKE,
                (checkhandshake::DEFAULT_EXTENSIONS
@@ -226,7 +226,7 @@ SKIP: {
         if disabled("ct") || disabled("ec") || disabled("ocsp");
     #Test 3: A status_request handshake (client request only)
     $proxy->clear();
-    $proxy->clientflags("-status");
+    $proxy->clientflags("-no_rx_cert_comp -status");
     $proxy->start();
     checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                    checkhandshake::DEFAULT_EXTENSIONS
@@ -235,7 +235,8 @@ SKIP: {
 
     #Test 4: A status_request handshake (server support only)
     $proxy->clear();
-    $proxy->serverflags("-status_file "
+    $proxy->clientflags("-no_rx_cert_comp");
+    $proxy->serverflags("-no_rx_cert_comp -status_file "
                         .srctop_file("test", "recipes", "ocsp-response.der"));
     $proxy->start();
     checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
@@ -244,8 +245,8 @@ SKIP: {
 
     #Test 5: A status_request handshake (client and server)
     $proxy->clear();
-    $proxy->clientflags("-status");
-    $proxy->serverflags("-status_file "
+    $proxy->clientflags("-no_rx_cert_comp -status");
+    $proxy->serverflags("-no_rx_cert_comp -status_file "
                         .srctop_file("test", "recipes", "ocsp-response.der"));
     $proxy->start();
     checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
@@ -256,9 +257,9 @@ SKIP: {
 
     #Test 6: A status_request handshake (client and server) with client auth
     $proxy->clear();
-    $proxy->clientflags("-status -enable_pha -cert "
+    $proxy->clientflags("-no_rx_cert_comp -status -enable_pha -cert "
                         .srctop_file("apps", "server.pem"));
-    $proxy->serverflags("-Verify 5 -status_file "
+    $proxy->serverflags("-no_rx_cert_comp -Verify 5 -status_file "
                         .srctop_file("test", "recipes", "ocsp-response.der"));
     $proxy->start();
     checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
@@ -271,8 +272,8 @@ SKIP: {
 
 #Test 7: A client auth handshake
 $proxy->clear();
-$proxy->clientflags("-enable_pha -cert ".srctop_file("apps", "server.pem"));
-$proxy->serverflags("-Verify 5");
+$proxy->clientflags("-no_rx_cert_comp -enable_pha -cert ".srctop_file("apps", "server.pem"));
+$proxy->serverflags("-no_rx_cert_comp -Verify 5");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS |
@@ -281,7 +282,7 @@ checkhandshake($proxy, checkhandshake::CLIENT_AUTH_HANDSHAKE,
 
 #Test 8: Server name handshake (no client request)
 $proxy->clear();
-$proxy->clientflags("-noservername");
+$proxy->clientflags("-no_rx_cert_comp -noservername");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
@@ -290,8 +291,8 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 
 #Test 9: Server name handshake (server support only)
 $proxy->clear();
-$proxy->clientflags("-noservername");
-$proxy->serverflags("-servername testhost");
+$proxy->clientflags("-no_rx_cert_comp -noservername");
+$proxy->serverflags("-no_rx_cert_comp -servername testhost");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
@@ -300,8 +301,8 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 
 #Test 10: Server name handshake (client and server)
 $proxy->clear();
-$proxy->clientflags("-servername testhost");
-$proxy->serverflags("-servername testhost");
+$proxy->clientflags("-no_rx_cert_comp -servername testhost");
+$proxy->serverflags("-no_rx_cert_comp -servername testhost");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
@@ -310,7 +311,7 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 
 #Test 11: ALPN handshake (client request only)
 $proxy->clear();
-$proxy->clientflags("-alpn test");
+$proxy->clientflags("-no_rx_cert_comp -alpn test");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
@@ -319,7 +320,8 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 
 #Test 12: ALPN handshake (server support only)
 $proxy->clear();
-$proxy->serverflags("-alpn test");
+$proxy->clientflags("-no_rx_cert_comp");
+$proxy->serverflags("-no_rx_cert_comp -alpn test");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS,
@@ -327,8 +329,8 @@ checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
 
 #Test 13: ALPN handshake (client and server)
 $proxy->clear();
-$proxy->clientflags("-alpn test");
-$proxy->serverflags("-alpn test");
+$proxy->clientflags("-no_rx_cert_comp -alpn test");
+$proxy->serverflags("-no_rx_cert_comp -alpn test");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
@@ -343,8 +345,8 @@ SKIP: {
     #Test 14: SCT handshake (client request only)
     $proxy->clear();
     #Note: -ct also sends status_request
-    $proxy->clientflags("-ct");
-    $proxy->serverflags("-status_file "
+    $proxy->clientflags("-no_rx_cert_comp -ct");
+    $proxy->serverflags("-no_rx_cert_comp -status_file "
                         .srctop_file("test", "recipes", "ocsp-response.der")
                         ." -serverinfo ".srctop_file("test", "serverinfo2.pem"));
     $proxy->start();
@@ -359,7 +361,8 @@ SKIP: {
 
 #Test 15: HRR Handshake
 $proxy->clear();
-$proxy->serverflags("-curves P-256");
+$proxy->clientflags("-no_rx_cert_comp");
+$proxy->serverflags("-no_rx_cert_comp -curves P-256");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::HRR_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
@@ -368,8 +371,8 @@ checkhandshake($proxy, checkhandshake::HRR_HANDSHAKE,
 
 #Test 16: Resumption handshake with HRR
 $proxy->clear();
-$proxy->clientflags("-sess_in ".$session);
-$proxy->serverflags("-curves P-256");
+$proxy->clientflags("-no_rx_cert_comp -sess_in ".$session);
+$proxy->serverflags("-no_rx_cert_comp -curves P-256");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
                (checkhandshake::DEFAULT_EXTENSIONS
@@ -380,7 +383,7 @@ checkhandshake($proxy, checkhandshake::HRR_RESUME_HANDSHAKE,
 
 #Test 17: Acceptable but non preferred key_share
 $proxy->clear();
-$proxy->clientflags("-curves P-256");
+$proxy->clientflags("-no_rx_cert_comp -curves P-256");
 $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
index 1783916b9b4d0c1f177baee588985e7302152590..39690e55ef15770a8b0bc4dfb66b994eacb56ba8 100644 (file)
@@ -42,7 +42,7 @@ if (defined $ENV{SSL_TESTS}) {
     @conf_srcs = glob(srctop_file("test", "ssl-tests", "*.cnf.in"));
     # We hard-code the number of tests to double-check that the globbing above
     # finds all files as expected.
-    plan tests => 31;
+    plan tests => 32;
 }
 map { s/;.*// } @conf_srcs if $^O eq "VMS";
 my @conf_files = map { basename($_, ".in") } @conf_srcs;
@@ -93,6 +93,7 @@ my %conf_dependent_tests = (
   "27-ticket-appdata.cnf" => !$is_default_tls,
   "28-seclevel.cnf" => disabled("tls1_2") || $no_ec,
   "30-extended-master-secret.cnf" => disabled("tls1_2"),
+  "32-compressed-certificate.cnf" => disabled("comp") || disabled("tls1_3"),
 );
 
 # Add your test here if it should be skipped for some compile-time
@@ -127,7 +128,8 @@ my %skip = (
   "25-cipher.cnf" => disabled("ec") || disabled("tls1_2"),
   "26-tls13_client_auth.cnf" => disabled("tls1_3") || ($no_ec && $no_dh),
   "29-dtls-sctp-label-bug.cnf" => disabled("sctp") || disabled("sock"),
-  "31-quic.cnf" => $no_quic || $no_ec
+  "31-quic.cnf" => $no_quic || $no_ec,
+  "32-compressed-certificate.cnf" => disabled("comp") || disabled("tls1_3"),
 );
 
 foreach my $conf (@conf_files) {
diff --git a/test/recipes/90-test_cert_comp.t b/test/recipes/90-test_cert_comp.t
new file mode 100644 (file)
index 0000000..a1421e6
--- /dev/null
@@ -0,0 +1,24 @@
+#! /usr/bin/env perl
+# Copyright 2016-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
+
+use OpenSSL::Test::Utils;
+use OpenSSL::Test qw/:DEFAULT srctop_dir bldtop_dir/;
+
+BEGIN {
+    setup("test_cert_comp");
+}
+
+use lib srctop_dir('Configurations');
+use lib bldtop_dir('.');
+
+plan skip_all => "Certificate compression is disabled in this OpenSSL build"
+    if disabled("comp") || (disabled("brotli") && disabled("zstd") && disabled("zlib"));
+
+plan tests => 1;
+
+ok(run(test(["cert_comp_test", srctop_dir("test", "certs")])), "running cert_comp_test");
diff --git a/test/ssl-tests/32-compressed-certificate.cnf b/test/ssl-tests/32-compressed-certificate.cnf
new file mode 100644 (file)
index 0000000..d189893
--- /dev/null
@@ -0,0 +1,244 @@
+# Generated with generate_ssl_tests.pl
+
+num_tests = 8
+
+test-0 = 0-no-compressed-certificates
+test-1 = 1-server-compressed-certificates
+test-2 = 2-client-compressed-certificates
+test-3 = 3-both-compressed-certificates
+test-4 = 4-no-compressed-certificates-mtls
+test-5 = 5-server-compressed-certificates-mtls
+test-6 = 6-client-compressed-certificates-mtls
+test-7 = 7-both-compressed-certificates-mtls
+# ===========================================================
+
+[0-no-compressed-certificates]
+ssl_conf = 0-no-compressed-certificates-ssl
+
+[0-no-compressed-certificates-ssl]
+server = 0-no-compressed-certificates-server
+client = 0-no-compressed-certificates-client
+
+[0-no-compressed-certificates-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = -TxCertificateCompression,-RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[0-no-compressed-certificates-client]
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = -TxCertificateCompression,-RxCertificateCompression
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-0]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[1-server-compressed-certificates]
+ssl_conf = 1-server-compressed-certificates-ssl
+
+[1-server-compressed-certificates-ssl]
+server = 1-server-compressed-certificates-server
+client = 1-server-compressed-certificates-client
+
+[1-server-compressed-certificates-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = TxCertificateCompression,RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[1-server-compressed-certificates-client]
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = -TxCertificateCompression,-RxCertificateCompression
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-1]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[2-client-compressed-certificates]
+ssl_conf = 2-client-compressed-certificates-ssl
+
+[2-client-compressed-certificates-ssl]
+server = 2-client-compressed-certificates-server
+client = 2-client-compressed-certificates-client
+
+[2-client-compressed-certificates-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = -TxCertificateCompression,-RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[2-client-compressed-certificates-client]
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = TxCertificateCompression,RxCertificateCompression
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-2]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[3-both-compressed-certificates]
+ssl_conf = 3-both-compressed-certificates-ssl
+
+[3-both-compressed-certificates-ssl]
+server = 3-both-compressed-certificates-server
+client = 3-both-compressed-certificates-client
+
+[3-both-compressed-certificates-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = TxCertificateCompression,RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[3-both-compressed-certificates-client]
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = TxCertificateCompression,RxCertificateCompression
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-3]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[4-no-compressed-certificates-mtls]
+ssl_conf = 4-no-compressed-certificates-mtls-ssl
+
+[4-no-compressed-certificates-mtls-ssl]
+server = 4-no-compressed-certificates-mtls-server
+client = 4-no-compressed-certificates-mtls-client
+
+[4-no-compressed-certificates-mtls-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = -TxCertificateCompression,-RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Request
+
+[4-no-compressed-certificates-mtls-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = -TxCertificateCompression,-RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-4]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[5-server-compressed-certificates-mtls]
+ssl_conf = 5-server-compressed-certificates-mtls-ssl
+
+[5-server-compressed-certificates-mtls-ssl]
+server = 5-server-compressed-certificates-mtls-server
+client = 5-server-compressed-certificates-mtls-client
+
+[5-server-compressed-certificates-mtls-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = TxCertificateCompression,RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Request
+
+[5-server-compressed-certificates-mtls-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = -TxCertificateCompression,-RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-5]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[6-client-compressed-certificates-mtls]
+ssl_conf = 6-client-compressed-certificates-mtls-ssl
+
+[6-client-compressed-certificates-mtls-ssl]
+server = 6-client-compressed-certificates-mtls-server
+client = 6-client-compressed-certificates-mtls-client
+
+[6-client-compressed-certificates-mtls-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = -TxCertificateCompression,-RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Request
+
+[6-client-compressed-certificates-mtls-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = TxCertificateCompression,RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-6]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
+# ===========================================================
+
+[7-both-compressed-certificates-mtls]
+ssl_conf = 7-both-compressed-certificates-mtls-ssl
+
+[7-both-compressed-certificates-mtls-ssl]
+server = 7-both-compressed-certificates-mtls-server
+client = 7-both-compressed-certificates-mtls-client
+
+[7-both-compressed-certificates-mtls-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+Options = TxCertificateCompression,RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/root-cert.pem
+VerifyMode = Request
+
+[7-both-compressed-certificates-mtls-client]
+Certificate = ${ENV::TEST_CERTS_DIR}/ee-client-chain.pem
+CipherString = DEFAULT
+MinProtocol = TLSv1.3
+Options = TxCertificateCompression,RxCertificateCompression
+PrivateKey = ${ENV::TEST_CERTS_DIR}/ee-key.pem
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-7]
+CompressCertificates = Yes
+ExpectedResult = Success
+
+
diff --git a/test/ssl-tests/32-compressed-certificate.cnf.in b/test/ssl-tests/32-compressed-certificate.cnf.in
new file mode 100644 (file)
index 0000000..780c982
--- /dev/null
@@ -0,0 +1,147 @@
+# -*- mode: perl; -*-
+# Copyright 2016-2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (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
+
+
+## SSL test configurations
+
+package ssltests;
+
+use OpenSSL::Test::Utils;
+
+our @tests = ();
+
+our @tests = (
+    {
+        name => "no-compressed-certificates",
+        server => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+        },
+        client => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+                "MinProtocol" => "TLSv1.3",
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "server-compressed-certificates",
+        server => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+        },
+        client => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "client-compressed-certificates",
+        server => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+        },
+        client => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "both-compressed-certificates",
+        server => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+        },
+        client => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "no-compressed-certificates-mtls",
+        server => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey"  => test_pem("ee-key.pem"),
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "server-compressed-certificates-mtls",
+        server => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey"  => test_pem("ee-key.pem"),
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "client-compressed-certificates-mtls",
+        server => {
+            "Options" => "-TxCertificateCompression,-RxCertificateCompression",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey"  => test_pem("ee-key.pem"),
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+    {
+        name => "both-compressed-certificates-mtls",
+        server => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+            "VerifyCAFile" => test_pem("root-cert.pem"),
+            "VerifyMode" => "Request",
+        },
+        client => {
+            "Options" => "TxCertificateCompression,RxCertificateCompression",
+            "MinProtocol" => "TLSv1.3",
+            "Certificate" => test_pem("ee-client-chain.pem"),
+            "PrivateKey"  => test_pem("ee-key.pem"),
+        },
+        test   => {
+            "ExpectedResult" => "Success",
+            "CompressCertificates" => "Yes",
+        },
+    },
+);
index 1be4f7e14e2087517e8d83729a12c8528fd87b5a..7d3432de28d56cbfd2c447db0bc134c87ed735b4 100644 (file)
@@ -7164,6 +7164,38 @@ static struct info_cb_states_st {
         {SSL_CB_LOOP, "TWFIN"}, {SSL_CB_HANDSHAKE_DONE, NULL},
         {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "SSLOK"},
         {SSL_CB_LOOP, "TRST"}, {SSL_CB_EXIT, NULL}, {0, NULL},
+    }, {
+        /* TLSv1.3 server, certificate compression, followed by resumption */
+        {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT"},
+        {SSL_CB_LOOP, "PINIT"}, {SSL_CB_LOOP, "TRCH"}, {SSL_CB_LOOP, "TWSH"},
+        {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWEE"}, {SSL_CB_LOOP, "TWSCC"},
+        {SSL_CB_LOOP, "TWSCV"}, {SSL_CB_LOOP, "TWFIN"}, {SSL_CB_LOOP, "TED"},
+        {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TED"}, {SSL_CB_LOOP, "TRFIN"},
+        {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_LOOP, "TWST"},
+        {SSL_CB_LOOP, "TWST"}, {SSL_CB_EXIT, NULL}, {SSL_CB_ALERT, NULL},
+        {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT"},
+        {SSL_CB_LOOP, "PINIT"}, {SSL_CB_LOOP, "TRCH"}, {SSL_CB_LOOP, "TWSH"},
+        {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWEE"}, {SSL_CB_LOOP, "TWFIN"},
+        {SSL_CB_LOOP, "TED"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TED"},
+        {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_HANDSHAKE_DONE, NULL},
+        {SSL_CB_LOOP, "TWST"}, {SSL_CB_EXIT, NULL}, {0, NULL},
+    }, {
+        /* TLSv1.3 client, certificate compression, followed by resumption */
+        {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT"},
+        {SSL_CB_LOOP, "TWCH"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TWCH"},
+        {SSL_CB_LOOP, "TRSH"}, {SSL_CB_LOOP, "TREE"}, {SSL_CB_LOOP, "TRSCC"},
+        {SSL_CB_LOOP, "TRSCV"}, {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_LOOP, "TWCCS"},
+        {SSL_CB_LOOP, "TWFIN"},  {SSL_CB_HANDSHAKE_DONE, NULL},
+        {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "SSLOK"},
+        {SSL_CB_LOOP, "TRST"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "SSLOK"},
+        {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "TRST"}, {SSL_CB_EXIT, NULL},
+        {SSL_CB_ALERT, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
+        {SSL_CB_LOOP, "PINIT"}, {SSL_CB_LOOP, "TWCH"}, {SSL_CB_EXIT, NULL},
+        {SSL_CB_LOOP, "TWCH"}, {SSL_CB_LOOP, "TRSH"},  {SSL_CB_LOOP, "TREE"},
+        {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWFIN"},
+        {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_EXIT, NULL},
+        {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "TRST"},
+        {SSL_CB_EXIT, NULL}, {0, NULL},
     }, {
         {0, NULL},
     }
@@ -7221,6 +7253,8 @@ static void sslapi_info_callback(const SSL *s, int where, int ret)
  * Test 3: TLSv1.3, client
  * Test 4: TLSv1.3, server, early_data
  * Test 5: TLSv1.3, client, early_data
+ * Test 6: TLSv1.3, server, compressed certificate
+ * Test 7: TLSv1.3, client, compressed certificate
  */
 static int test_info_callback(int tst)
 {
@@ -7252,7 +7286,7 @@ static int test_info_callback(int tst)
     info_cb_offset = tst;
 
 #ifndef OSSL_NO_USABLE_TLS1_3
-    if (tst >= 4) {
+    if (tst >= 4 && tst < 6) {
         SSL_SESSION *sess = NULL;
         size_t written, readbytes;
         unsigned char buf[80];
@@ -7303,6 +7337,10 @@ static int test_info_callback(int tst)
      */
     SSL_CTX_set_info_callback((tst % 2) == 0 ? sctx : cctx,
                               sslapi_info_callback);
+    if (tst >= 6) {
+        if (!SSL_CTX_compress_certs(sctx, 0))
+            goto end;
+    }
 
     if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
                                           &clientssl, NULL, NULL))
@@ -10339,7 +10377,12 @@ int setup_tests(void)
 #if !defined(OPENSSL_NO_SRP) && !defined(OPENSSL_NO_TLS1_2)
     ADD_ALL_TESTS(test_srp, 6);
 #endif
+#if !defined(OPENSSL_NO_COMP_ALG)
+    /* Add compression case */
+    ADD_ALL_TESTS(test_info_callback, 8);
+#else
     ADD_ALL_TESTS(test_info_callback, 6);
+#endif
     ADD_ALL_TESTS(test_ssl_pending, 2);
     ADD_ALL_TESTS(test_ssl_get_shared_ciphers, OSSL_NELEM(shared_ciphers_data));
     ADD_ALL_TESTS(test_ticket_callbacks, 20);
index adb095bd53801ea9c5e43f17ca1b44431d72acbf..c275cffcd039ee8b79466793da2e360ae8128f8f 100644 (file)
@@ -524,3 +524,11 @@ SSL_client_hello_get_extension_order    ?  3_2_0   EXIST::FUNCTION:
 OSSL_QUIC_client_method                 ?      3_2_0   EXIST::FUNCTION:QUIC
 OSSL_QUIC_client_thread_method          ?      3_2_0   EXIST::FUNCTION:QUIC
 OSSL_QUIC_server_method                 ?      3_2_0   EXIST::FUNCTION:QUIC
+SSL_CTX_set1_cert_comp_preference       ?      3_2_0   EXIST::FUNCTION:
+SSL_set1_cert_comp_preference           ?      3_2_0   EXIST::FUNCTION:
+SSL_CTX_compress_certs                  ?      3_2_0   EXIST::FUNCTION:
+SSL_compress_certs                      ?      3_2_0   EXIST::FUNCTION:
+SSL_CTX_set1_compressed_cert            ?      3_2_0   EXIST::FUNCTION:
+SSL_set1_compressed_cert                ?      3_2_0   EXIST::FUNCTION:
+SSL_CTX_get1_compressed_cert            ?      3_2_0   EXIST::FUNCTION:
+SSL_get1_compressed_cert                ?      3_2_0   EXIST::FUNCTION:
index 2c1bdb3837e2597d3f9e079f182c92b49c4b2951..648d986342cd9e8c4a1f219e999ca4e012c4fb53 100644 (file)
@@ -28,6 +28,7 @@ use constant {
     MT_CLIENT_KEY_EXCHANGE => 16,
     MT_FINISHED => 20,
     MT_CERTIFICATE_STATUS => 22,
+    MT_COMPRESSED_CERTIFICATE => 25,
     MT_NEXT_PROTO => 67
 };
 
@@ -59,6 +60,7 @@ my %message_type = (
     MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange",
     MT_FINISHED, "Finished",
     MT_CERTIFICATE_STATUS, "CertificateStatus",
+    MT_COMPRESSED_CERTIFICATE, "CompressedCertificate",
     MT_NEXT_PROTO, "NextProto"
 );
 
@@ -76,6 +78,7 @@ use constant {
     EXT_PADDING => 21,
     EXT_ENCRYPT_THEN_MAC => 22,
     EXT_EXTENDED_MASTER_SECRET => 23,
+    EXT_COMPRESS_CERTIFICATE => 27,
     EXT_SESSION_TICKET => 35,
     EXT_KEY_SHARE => 51,
     EXT_PSK => 41,
index a2ae24ee2a0a171098451dc56a02c73c5b6de5c7..618ee99577f9b6b4b67f54b2338230cd524029b5 100644 (file)
@@ -26,8 +26,11 @@ use constant {
     EC_HANDSHAKE => 64,
     HRR_HANDSHAKE => 128,
     HRR_RESUME_HANDSHAKE => 256,
+    CERT_COMP_SRV_HANDSHAKE => 512,
+    CERT_COMP_CLI_HANDSHAKE => 1024,
+    CERT_COMP_BOTH_HANDSHAKE => 2048,
 
-    ALL_HANDSHAKES => 511
+    ALL_HANDSHAKES => 4095
 };
 
 use constant {
@@ -54,7 +57,9 @@ use constant {
     PSK_KEX_MODES_EXTENSION => 0x00040000,
     KEY_SHARE_HRR_EXTENSION => 0x00080000,
     SUPPORTED_GROUPS_SRV_EXTENSION => 0x00100000,
-    POST_HANDSHAKE_AUTH_CLI_EXTENSION => 0x00200000
+    POST_HANDSHAKE_AUTH_CLI_EXTENSION => 0x00200000,
+    CERT_COMP_CLI_EXTENSION => 0x00400000,
+    CERT_COMP_SRV_EXTENSION => 0x00800000
 };
 
 our @handmessages = ();