summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Short <tshort@akamai.com>2021-08-09 16:56:50 -0400
committerTodd Short <todd.short@me.com>2022-10-18 09:30:22 -0400
commitb67cb09f8ddf258cf326f3e7b20be095fb53457c (patch)
treeb31a978e8c71e972e84fd03b4de92491deff032a
parent59d21298df9176b64b41cc8583c7024f7f5895d4 (diff)
downloadopenssl-new-b67cb09f8ddf258cf326f3e7b20be095fb53457c.tar.gz
Add support for compressed certificates (RFC8879)
* Compressed Certificate extension (server/client) * Server certificates (send/receive) * Client certificate (send/receive) Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/18186)
-rw-r--r--CHANGES.md5
-rw-r--r--NEWS.md2
-rw-r--r--apps/include/opt.h9
-rw-r--r--apps/lib/s_cb.c2
-rw-r--r--apps/s_server.c17
-rw-r--r--crypto/comp/c_brotli.c6
-rw-r--r--crypto/comp/c_zstd.c6
-rw-r--r--crypto/comp/comp_err.c4
-rw-r--r--crypto/err/openssl.txt1
-rw-r--r--doc/build.info6
-rw-r--r--doc/man1/openssl-s_client.pod.in14
-rw-r--r--doc/man1/openssl-s_server.pod.in17
-rw-r--r--doc/man3/COMP_CTX_new.pod12
-rw-r--r--doc/man3/SSL_CONF_cmd.pod29
-rw-r--r--doc/man3/SSL_CTX_set1_cert_comp_preference.pod160
-rw-r--r--doc/man3/SSL_CTX_set_options.pod16
-rw-r--r--include/openssl/comperr.h14
-rw-r--r--include/openssl/configuration.h.in6
-rw-r--r--include/openssl/ssl.h.in29
-rw-r--r--include/openssl/ssl3.h1
-rw-r--r--include/openssl/sslerr.h1
-rw-r--r--include/openssl/tls1.h12
-rw-r--r--ssl/build.info1
-rw-r--r--ssl/ssl_cert.c35
-rw-r--r--ssl/ssl_cert_comp.c479
-rw-r--r--ssl/ssl_conf.c12
-rw-r--r--ssl/ssl_err.c2
-rw-r--r--ssl/ssl_lib.c25
-rw-r--r--ssl/ssl_local.h41
-rw-r--r--ssl/ssl_stat.c16
-rw-r--r--ssl/statem/extensions.c125
-rw-r--r--ssl/statem/extensions_cust.c1
-rw-r--r--ssl/statem/statem.c10
-rw-r--r--ssl/statem/statem.h1
-rw-r--r--ssl/statem/statem_clnt.c162
-rw-r--r--ssl/statem/statem_lib.c73
-rw-r--r--ssl/statem/statem_local.h23
-rw-r--r--ssl/statem/statem_srvr.c117
-rw-r--r--ssl/t1_trce.c89
-rw-r--r--test/bio_comp_test.c2
-rw-r--r--test/build.info8
-rw-r--r--test/cert_comp_test.c281
-rw-r--r--test/ext_internal_test.c1
-rw-r--r--test/helpers/handshake.c8
-rw-r--r--test/helpers/ssl_test_ctx.c2
-rw-r--r--test/helpers/ssl_test_ctx.h2
-rw-r--r--test/recipes/70-test_tls13certcomp.t295
-rw-r--r--test/recipes/70-test_tls13kexmodes.t34
-rw-r--r--test/recipes/70-test_tls13messages.t53
-rw-r--r--test/recipes/80-test_ssl_new.t6
-rw-r--r--test/recipes/90-test_cert_comp.t24
-rw-r--r--test/ssl-tests/32-compressed-certificate.cnf244
-rw-r--r--test/ssl-tests/32-compressed-certificate.cnf.in147
-rw-r--r--test/sslapitest.c45
-rw-r--r--util/libssl.num8
-rw-r--r--util/perl/TLSProxy/Message.pm3
-rw-r--r--util/perl/checkhandshake.pm9
57 files changed, 2661 insertions, 92 deletions
diff --git a/CHANGES.md b/CHANGES.md
index fac9d80cc0..2fc80041a3 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -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 3df3bbc007..b934aaa0a8 100644
--- 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
diff --git a/apps/include/opt.h b/apps/include/opt.h
index 849e10c09e..787dac5468 100644
--- a/apps/include/opt.h
+++ b/apps/include/opt.h
@@ -164,7 +164,10 @@
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"), \
@@ -176,6 +179,8 @@
{"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"}, \
@@ -233,6 +238,8 @@
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: \
diff --git a/apps/lib/s_cb.c b/apps/lib/s_cb.c
index 8f20113cd3..8325e7371f 100644
--- a/apps/lib/s_cb.c
+++ b/apps/lib/s_cb.c
@@ -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},
diff --git a/apps/s_server.c b/apps/s_server.c
index 8a573d4414..f519505ade 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -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)
diff --git a/crypto/comp/c_brotli.c b/crypto/comp/c_brotli.c
index ace6f221b8..377ea2b8d0 100644
--- a/crypto/comp/c_brotli.c
+++ b/crypto/comp/c_brotli.c
@@ -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
diff --git a/crypto/comp/c_zstd.c b/crypto/comp/c_zstd.c
index 99d326219e..15b826c589 100644
--- a/crypto/comp/c_zstd.c
+++ b/crypto/comp/c_zstd.c
@@ -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
diff --git a/crypto/comp/comp_err.c b/crypto/comp/comp_err.c
index 10a9e66860..2345da693e 100644
--- a/crypto/comp/comp_err.c
+++ b/crypto/comp/comp_err.c
@@ -19,12 +19,8 @@
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),
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index ef02a097e4..653b775330 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -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
diff --git a/doc/build.info b/doc/build.info
index 8884b0dd5f..71c7f3aa40 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -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 \
diff --git a/doc/man1/openssl-s_client.pod.in b/doc/man1/openssl-s_client.pod.in
index 5f6f74c51d..86b9aff91e 100644
--- a/doc/man1/openssl-s_client.pod.in
+++ b/doc/man1/openssl-s_client.pod.in
@@ -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
diff --git a/doc/man1/openssl-s_server.pod.in b/doc/man1/openssl-s_server.pod.in
index 8fa041c2fe..94f3b4b46c 100644
--- a/doc/man1/openssl-s_server.pod.in
+++ b/doc/man1/openssl-s_server.pod.in
@@ -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
diff --git a/doc/man3/COMP_CTX_new.pod b/doc/man3/COMP_CTX_new.pod
index 1fe1d691df..7e1c8c4a83 100644
--- a/doc/man3/COMP_CTX_new.pod
+++ b/doc/man3/COMP_CTX_new.pod
@@ -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
diff --git a/doc/man3/SSL_CONF_cmd.pod b/doc/man3/SSL_CONF_cmd.pod
index 400bd223c6..c20df37e3b 100644
--- a/doc/man3/SSL_CONF_cmd.pod
+++ b/doc/man3/SSL_CONF_cmd.pod
@@ -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
index 0000000000..2fe35873f7
--- /dev/null
+++ b/doc/man3/SSL_CTX_set1_cert_comp_preference.pod
@@ -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
diff --git a/doc/man3/SSL_CTX_set_options.pod b/doc/man3/SSL_CTX_set_options.pod
index 08522522cd..1da057adb8 100644
--- a/doc/man3/SSL_CTX_set_options.pod
+++ b/doc/man3/SSL_CTX_set_options.pod
@@ -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.
diff --git a/include/openssl/comperr.h b/include/openssl/comperr.h
index e075506e68..1948d37f1a 100644
--- a/include/openssl/comperr.h
+++ b/include/openssl/comperr.h
@@ -24,17 +24,15 @@
* 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
diff --git a/include/openssl/configuration.h.in b/include/openssl/configuration.h.in
index b84dc1dfe3..86077d0a6f 100644
--- a/include/openssl/configuration.h.in
+++ b/include/openssl/configuration.h.in
@@ -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
diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in
index d2cad44b91..4ae96cd1ee 100644
--- a/include/openssl/ssl.h.in
+++ b/include/openssl/ssl.h.in
@@ -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
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index 49bd51f24e..6c172f45f9 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -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
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index 8248b641d7..98bddbb857 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -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
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index c516291695..0f1b854d42 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -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
diff --git a/ssl/build.info b/ssl/build.info
index 81631c7497..00e9e8fd7d 100644
--- a/ssl/build.info
+++ b/ssl/build.info
@@ -19,6 +19,7 @@ SOURCE[../libssl]=\
ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c ssl_mcnf.c \
bio_ssl.c ssl_err.c ssl_err_legacy.c tls_srp.c t1_trce.c ssl_utst.c \
statem/statem.c \
+ ssl_cert_comp.c \
tls_depr.c
# For shared builds we need to include the libcrypto packet.c and quic_vlint.c
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 8ec36b9c19..d2bc61476d 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -74,6 +74,9 @@ CERT *ssl_cert_dup(CERT *cert)
{
CERT *ret = OPENSSL_zalloc(sizeof(*ret));
int i;
+#ifndef OPENSSL_NO_COMP_ALG
+ int j;
+#endif
if (ret == NULL)
return NULL;
@@ -98,6 +101,7 @@ CERT *ssl_cert_dup(CERT *cert)
for (i = 0; i < SSL_PKEY_NUM; i++) {
CERT_PKEY *cpk = cert->pkeys + i;
CERT_PKEY *rpk = ret->pkeys + i;
+
if (cpk->x509 != NULL) {
rpk->x509 = cpk->x509;
X509_up_ref(rpk->x509);
@@ -115,16 +119,22 @@ CERT *ssl_cert_dup(CERT *cert)
goto err;
}
}
- if (cert->pkeys[i].serverinfo != NULL) {
+ if (cpk->serverinfo != NULL) {
/* Just copy everything. */
- ret->pkeys[i].serverinfo =
- OPENSSL_malloc(cert->pkeys[i].serverinfo_length);
- if (ret->pkeys[i].serverinfo == NULL)
+ rpk->serverinfo = OPENSSL_memdup(cpk->serverinfo, cpk->serverinfo_length);
+ if (rpk->serverinfo == NULL)
goto err;
- ret->pkeys[i].serverinfo_length = cert->pkeys[i].serverinfo_length;
- memcpy(ret->pkeys[i].serverinfo,
- cert->pkeys[i].serverinfo, cert->pkeys[i].serverinfo_length);
+ rpk->serverinfo_length = cpk->serverinfo_length;
+ }
+#ifndef OPENSSL_NO_COMP_ALG
+ for (j = TLSEXT_comp_cert_none; j < TLSEXT_comp_cert_limit; j++) {
+ if (cpk->comp_cert[j] != NULL) {
+ if (!OSSL_COMP_CERT_up_ref(cpk->comp_cert[j]))
+ goto err;
+ rpk->comp_cert[j] = cpk->comp_cert[j];
+ }
}
+#endif
}
/* Configured sigalgs copied across */
@@ -198,6 +208,10 @@ CERT *ssl_cert_dup(CERT *cert)
void ssl_cert_clear_certs(CERT *c)
{
int i;
+#ifndef OPENSSL_NO_COMP_ALG
+ int j;
+#endif
+
if (c == NULL)
return;
for (i = 0; i < SSL_PKEY_NUM; i++) {
@@ -211,6 +225,13 @@ void ssl_cert_clear_certs(CERT *c)
OPENSSL_free(cpk->serverinfo);
cpk->serverinfo = NULL;
cpk->serverinfo_length = 0;
+#ifndef OPENSSL_NO_COMP_ALG
+ for (j = 0; j < TLSEXT_comp_cert_limit; j++) {
+ OSSL_COMP_CERT_free(cpk->comp_cert[j]);
+ cpk->comp_cert[j] = NULL;
+ cpk->cert_comp_used = 0;
+ }
+#endif
}
}
diff --git a/ssl/ssl_cert_comp.c b/ssl/ssl_cert_comp.c
new file mode 100644
index 0000000000..a86282279c
--- /dev/null
+++ b/ssl/ssl_cert_comp.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdio.h>
+#include "ssl_local.h"
+#include "internal/e_os.h"
+#include "internal/refcount.h"
+
+size_t ossl_calculate_comp_expansion(int alg, size_t length)
+{
+ size_t ret;
+ /*
+ * Uncompressibility expansion:
+ * ZLIB: N + 11 + 5 * (N >> 14)
+ * Brotli: per RFC7932: N + 5 + 3 * (N >> 16)
+ * ZSTD: N + 4 + 14 + 3 * (N >> 17) + 4
+ */
+
+ switch (alg) {
+ case TLSEXT_comp_cert_zlib:
+ ret = length + 11 + 5 * (length >> 14);
+ break;
+ case TLSEXT_comp_cert_brotli:
+ ret = length + 5 + 3 * (length >> 16);
+ break;
+ case TLSEXT_comp_cert_zstd:
+ ret = length + 22 + 3 * (length >> 17);
+ break;
+ default:
+ return 0;
+ }
+ /* Check for overflow */
+ if (ret < length)
+ return 0;
+ return ret;
+}
+
+int ossl_comp_has_alg(int a)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ /* 0 means "any" algorithm */
+ if ((a == 0 || a == TLSEXT_comp_cert_brotli) && BIO_f_brotli() != NULL)
+ return 1;
+ if ((a == 0 || a == TLSEXT_comp_cert_zstd) && BIO_f_zstd() != NULL)
+ return 1;
+ if ((a == 0 || a == TLSEXT_comp_cert_zlib) && BIO_f_zlib() != NULL)
+ return 1;
+#endif
+ return 0;
+}
+
+/* New operation Helper routine */
+#ifndef OPENSSL_NO_COMP_ALG
+static OSSL_COMP_CERT *OSSL_COMP_CERT_new(unsigned char *data, size_t len, size_t orig_len, int alg)
+{
+ OSSL_COMP_CERT *ret = NULL;
+
+ if (!ossl_comp_has_alg(alg)
+ || data == NULL
+ || (ret = OPENSSL_zalloc(sizeof(*ret))) == NULL
+ || (ret->lock = CRYPTO_THREAD_lock_new()) == NULL)
+ goto err;
+
+ ret->references = 1;
+ ret->data = data;
+ ret->len = len;
+ ret->orig_len = orig_len;
+ ret->alg = alg;
+ return ret;
+ err:
+ ERR_raise(ERR_LIB_SSL, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(data);
+ OPENSSL_free(ret);
+ return NULL;
+}
+
+__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_compressed_data(unsigned char *data, size_t len,
+ size_t orig_len, int alg)
+{
+ return OSSL_COMP_CERT_new(OPENSSL_memdup(data, len), len, orig_len, alg);
+}
+
+__owur static OSSL_COMP_CERT *OSSL_COMP_CERT_from_uncompressed_data(unsigned char *data, size_t len,
+ int alg)
+{
+ OSSL_COMP_CERT *ret = NULL;
+ size_t max_length;
+ int comp_length;
+ COMP_METHOD *method;
+ unsigned char *comp_data = NULL;
+ COMP_CTX *comp_ctx = NULL;
+
+ switch (alg) {
+ case TLSEXT_comp_cert_brotli:
+ method = COMP_brotli_oneshot();
+ break;
+ case TLSEXT_comp_cert_zlib:
+ method = COMP_zlib();
+ break;
+ case TLSEXT_comp_cert_zstd:
+ method = COMP_zstd_oneshot();
+ break;
+ default:
+ goto err;
+ }
+
+ if ((max_length = ossl_calculate_comp_expansion(alg, len)) == 0
+ || method == NULL
+ || (comp_ctx = COMP_CTX_new(method)) == NULL
+ || (comp_data = OPENSSL_zalloc(max_length)) == NULL)
+ goto err;
+
+ comp_length = COMP_compress_block(comp_ctx, comp_data, max_length, data, len);
+ if (comp_length <= 0)
+ goto err;
+
+ ret = OSSL_COMP_CERT_new(comp_data, comp_length, len, alg);
+ comp_data = NULL;
+
+ err:
+ OPENSSL_free(comp_data);
+ COMP_CTX_free(comp_ctx);
+ return ret;
+}
+
+void OSSL_COMP_CERT_free(OSSL_COMP_CERT *cc)
+{
+ int i;
+
+ if (cc == NULL)
+ return;
+
+ CRYPTO_DOWN_REF(&cc->references, &i, cc->lock);
+ REF_PRINT_COUNT("OSSL_COMP_CERT", cc);
+ if (i > 0)
+ return;
+ REF_ASSERT_ISNT(i < 0);
+
+ OPENSSL_free(cc->data);
+ CRYPTO_THREAD_lock_free(cc->lock);
+ OPENSSL_free(cc);
+}
+int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *cc)
+{
+ int i;
+
+ if (CRYPTO_UP_REF(&cc->references, &i, cc->lock) <= 0)
+ return 0;
+
+ REF_PRINT_COUNT("OSSL_COMP_CERT", cc);
+ REF_ASSERT_ISNT(i < 2);
+ return ((i > 1) ? 1 : 0);
+}
+
+static int ssl_set_cert_comp_pref(int *prefs, int *algs, size_t len)
+{
+ size_t j = 0;
+ size_t i;
+ int found = 0;
+ int already_set[TLSEXT_comp_cert_limit];
+ int tmp_prefs[TLSEXT_comp_cert_limit];
+
+ /* Note that |len| is the number of |algs| elements */
+ /* clear all algorithms */
+ if (len == 0 || algs == NULL) {
+ memset(prefs, 0, sizeof(tmp_prefs));
+ return 1;
+ }
+
+ /* This will 0-terminate the array */
+ memset(tmp_prefs, 0, sizeof(tmp_prefs));
+ memset(already_set, 0, sizeof(already_set));
+ /* Include only those algorithms we support, ignoring duplicates and unknowns */
+ for (i = 0; i < len; i++) {
+ if (algs[i] != 0 && ossl_comp_has_alg(algs[i])) {
+ /* Check for duplicate */
+ if (already_set[algs[i]])
+ return 0;
+ tmp_prefs[j++] = algs[i];
+ already_set[algs[i]] = 1;
+ found = 1;
+ }
+ }
+ if (found)
+ memcpy(prefs, tmp_prefs, sizeof(tmp_prefs));
+ return found;
+}
+
+static size_t ssl_get_cert_to_compress(SSL *ssl, CERT_PKEY *cpk, unsigned char **data)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+ WPACKET tmppkt;
+ BUF_MEM buf = { 0 };
+ size_t ret = 0;
+ const SSL_METHOD *method = NULL;
+
+ if (sc == NULL
+ || cpk == NULL
+ || !sc->server
+ || !SSL_in_before(ssl))
+ return 0;
+
+ /* Use the |tmppkt| for the to-be-compressed data */
+ if (!WPACKET_init(&tmppkt, &buf))
+ goto out;
+
+ /* no context present, add 0-length context */
+ if (!WPACKET_put_bytes_u8(&tmppkt, 0))
+ goto out;
+
+ /*
+ * ssl3_output_cert_chain() may generate an SSLfata() error,
+ * for this case, we want to ignore it
+ */
+ sc->statem.ignore_fatal = 1;
+ ERR_set_mark();
+ /* Must get the certificate as TLSv1.3, restore before returning */
+ method = SSL_get_ssl_method(ssl);
+ if (!SSL_set_ssl_method(ssl, tlsv1_3_server_method()))
+ goto out;
+
+ if (!ssl3_output_cert_chain(sc, &tmppkt, cpk))
+ goto out;
+ WPACKET_get_total_written(&tmppkt, &ret);
+
+ out:
+ /* Don't leave errors in the queue */
+ ERR_pop_to_mark();
+ sc->statem.ignore_fatal = 0;
+ if (method != NULL && !SSL_set_ssl_method(ssl, method))
+ ret = 0;
+ WPACKET_cleanup(&tmppkt);
+ if (ret != 0 && data != NULL)
+ *data = (unsigned char *)buf.data;
+ else
+ OPENSSL_free(buf.data);
+ return ret;
+}
+
+static int ssl_compress_one_cert(SSL *ssl, CERT_PKEY *cpk, int alg)
+{
+ unsigned char *cert_data = NULL;
+ OSSL_COMP_CERT *comp_cert = NULL;
+ size_t length;
+
+ if (cpk == NULL
+ || alg == TLSEXT_comp_cert_none
+ || !ossl_comp_has_alg(alg))
+ return 0;
+
+ if ((length = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
+ return 0;
+ comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, length, alg);
+ OPENSSL_free(cert_data);
+ if (comp_cert == NULL)
+ return 0;
+
+ OSSL_COMP_CERT_free(cpk->comp_cert[alg]);
+ cpk->comp_cert[alg] = comp_cert;
+ return 1;
+}
+
+/* alg_in can be 0, meaning any/all algorithms */
+static int ssl_compress_certs(SSL *ssl, CERT_PKEY *cpks, int alg_in)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+ int i;
+ int j;
+ int alg;
+ int count = 0;
+
+ if (sc == NULL
+ || cpks == NULL
+ || !ossl_comp_has_alg(alg_in))
+ return 0;
+
+ /* Look through the preferences to see what we have */
+ for (i = 0; i < TLSEXT_comp_cert_limit; i++) {
+ /*
+ * alg = 0 means compress for everything, but only for algorithms enabled
+ * alg != 0 means compress for that algorithm if enabled
+ */
+ alg = sc->cert_comp_prefs[i];
+ if ((alg_in == 0 && alg != TLSEXT_comp_cert_none)
+ || (alg_in != 0 && alg == alg_in)) {
+
+ for (j = 0; j < SSL_PKEY_NUM; j++) {
+ /* No cert, move on */
+ if (cpks[j].x509 == NULL)
+ continue;
+
+ if (!ssl_compress_one_cert(ssl, &cpks[j], alg))
+ return 0;
+
+ /* if the cert expanded, set the value in the CERT_PKEY to NULL */
+ if (cpks[j].comp_cert[alg]->len >= cpks[j].comp_cert[alg]->orig_len) {
+ OSSL_COMP_CERT_free(cpks[j].comp_cert[alg]);
+ cpks[j].comp_cert[alg] = NULL;
+ } else {
+ count++;
+ }
+ }
+ }
+ }
+ return (count > 0);
+}
+
+static size_t ssl_get_compressed_cert(SSL *ssl, CERT_PKEY *cpk, int alg, unsigned char **data,
+ size_t *orig_len)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+ size_t cert_len = 0;
+ size_t comp_len = 0;
+ unsigned char *cert_data = NULL;
+ OSSL_COMP_CERT *comp_cert = NULL;
+
+ if (sc == NULL
+ || cpk == NULL
+ || data == NULL
+ || orig_len == NULL
+ || !sc->server
+ || !SSL_in_before(ssl)
+ || !ossl_comp_has_alg(alg))
+ return 0;
+
+ if ((cert_len = ssl_get_cert_to_compress(ssl, cpk, &cert_data)) == 0)
+ goto err;
+
+ comp_cert = OSSL_COMP_CERT_from_uncompressed_data(cert_data, cert_len, alg);
+ OPENSSL_free(cert_data);
+ if (comp_cert == NULL)
+ goto err;
+
+ comp_len = comp_cert->len;
+ *orig_len = comp_cert->orig_len;
+ *data = comp_cert->data;
+ comp_cert->data = NULL;
+ err:
+ OSSL_COMP_CERT_free(comp_cert);
+ return comp_len;
+}
+
+static int ossl_set1_compressed_cert(CERT *cert, int algorithm,
+ unsigned char *comp_data, size_t comp_length,
+ size_t orig_length)
+{
+ OSSL_COMP_CERT *comp_cert;
+
+ /* No explicit cert set */
+ if (cert == NULL || cert->key == NULL)
+ return 0;
+
+ comp_cert = OSSL_COMP_CERT_from_compressed_data(comp_data, comp_length,
+ orig_length, algorithm);
+ if (comp_cert == NULL)
+ return 0;
+
+ OSSL_COMP_CERT_free(cert->key->comp_cert[algorithm]);
+ cert->key->comp_cert[algorithm] = comp_cert;
+
+ return 1;
+}
+#endif
+
+/*-
+ * Public API
+ */
+int SSL_CTX_set1_cert_comp_preference(SSL_CTX *ctx, int *algs, size_t len)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ return ssl_set_cert_comp_pref(ctx->cert_comp_prefs, algs, len);
+#else
+ return 0;
+#endif
+}
+
+int SSL_set1_cert_comp_preference(SSL *ssl, int *algs, size_t len)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+
+ if (sc == NULL)
+ return 0;
+ return ssl_set_cert_comp_pref(sc->cert_comp_prefs, algs, len);
+#else
+ return 0;
+#endif
+}
+
+int SSL_compress_certs(SSL *ssl, int alg)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+
+ if (sc == NULL || sc->cert == NULL)
+ return 0;
+
+ return ssl_compress_certs(ssl, sc->cert->pkeys, alg);
+#endif
+ return 0;
+}
+
+int SSL_CTX_compress_certs(SSL_CTX *ctx, int alg)
+{
+ int ret = 0;
+#ifndef OPENSSL_NO_COMP_ALG
+ SSL *new = SSL_new(ctx);
+
+ if (new == NULL)
+ return 0;
+
+ ret = ssl_compress_certs(new, ctx->cert->pkeys, alg);
+ SSL_free(new);
+#endif
+ return ret;
+}
+
+size_t SSL_get1_compressed_cert(SSL *ssl, int alg, unsigned char **data, size_t *orig_len)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+ CERT_PKEY *cpk = NULL;
+
+ if (sc->cert != NULL)
+ cpk = sc->cert->key;
+ else
+ cpk = ssl->ctx->cert->key;
+
+ return ssl_get_compressed_cert(ssl, cpk, alg, data, orig_len);
+#else
+ return 0;
+#endif
+}
+
+size_t SSL_CTX_get1_compressed_cert(SSL_CTX *ctx, int alg, unsigned char **data, size_t *orig_len)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ size_t ret;
+ SSL *new = SSL_new(ctx);
+
+ ret = ssl_get_compressed_cert(new, ctx->cert->key, alg, data, orig_len);
+ SSL_free(new);
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+int SSL_CTX_set1_compressed_cert(SSL_CTX *ctx, int algorithm, unsigned char *comp_data,
+ size_t comp_length, size_t orig_length)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ return ossl_set1_compressed_cert(ctx->cert, algorithm, comp_data, comp_length, orig_length);
+#else
+ return 0;
+#endif
+}
+
+int SSL_set1_compressed_cert(SSL *ssl, int algorithm, unsigned char *comp_data,
+ size_t comp_length, size_t orig_length)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(ssl);
+
+ /* Cannot set a pre-compressed certificate on a client */
+ if (sc == NULL || !sc->server)
+ return 0;
+
+ return ossl_set1_compressed_cert(sc->cert, algorithm, comp_data, comp_length, orig_length);
+#else
+ return 0;
+#endif
+}
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c
index e1a4bda9ed..bebfc501a9 100644
--- a/ssl/ssl_conf.c
+++ b/ssl/ssl_conf.c
@@ -397,7 +397,9 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value)
SSL_FLAG_TBL_INV("ExtendedMasterSecret", SSL_OP_NO_EXTENDED_MASTER_SECRET),
SSL_FLAG_TBL_INV("CANames", SSL_OP_DISABLE_TLSEXT_CA_NAMES),
SSL_FLAG_TBL("KTLS", SSL_OP_ENABLE_KTLS),
- SSL_FLAG_TBL_CERT("StrictCertCheck", SSL_CERT_FLAG_TLS_STRICT)
+ SSL_FLAG_TBL_CERT("StrictCertCheck", SSL_CERT_FLAG_TLS_STRICT),
+ SSL_FLAG_TBL_INV("TxCertificateCompression", SSL_OP_NO_TX_CERTIFICATE_COMPRESSION),
+ SSL_FLAG_TBL_INV("RxCertificateCompression", SSL_OP_NO_RX_CERTIFICATE_COMPRESSION),
};
if (value == NULL)
return -3;
@@ -707,6 +709,10 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
SSL_CONF_CMD_SWITCH("bugs", 0),
SSL_CONF_CMD_SWITCH("no_comp", 0),
SSL_CONF_CMD_SWITCH("comp", 0),
+ SSL_CONF_CMD_SWITCH("no_tx_cert_comp", 0),
+ SSL_CONF_CMD_SWITCH("tx_cert_comp", 0),
+ SSL_CONF_CMD_SWITCH("no_rx_cert_comp", 0),
+ SSL_CONF_CMD_SWITCH("rx_cert_comp", 0),
SSL_CONF_CMD_SWITCH("ecdh_single", SSL_CONF_FLAG_SERVER),
SSL_CONF_CMD_SWITCH("no_ticket", 0),
SSL_CONF_CMD_SWITCH("serverpref", SSL_CONF_FLAG_SERVER),
@@ -787,6 +793,10 @@ static const ssl_switch_tbl ssl_cmd_switches[] = {
{SSL_OP_ALL, 0}, /* bugs */
{SSL_OP_NO_COMPRESSION, 0}, /* no_comp */
{SSL_OP_NO_COMPRESSION, SSL_TFLAG_INV}, /* comp */
+ {SSL_OP_NO_TX_CERTIFICATE_COMPRESSION, 0}, /* no_tx_cert_comp */
+ {SSL_OP_NO_TX_CERTIFICATE_COMPRESSION, SSL_TFLAG_INV}, /* tx_cert_comp */
+ {SSL_OP_NO_RX_CERTIFICATE_COMPRESSION, 0}, /* no_rx_cert_comp */
+ {SSL_OP_NO_RX_CERTIFICATE_COMPRESSION, SSL_TFLAG_INV}, /* rx_cert_comp */
{SSL_OP_SINGLE_ECDH_USE, 0}, /* ecdh_single */
{SSL_OP_NO_TICKET, 0}, /* no_ticket */
{SSL_OP_CIPHER_SERVER_PREFERENCE, 0}, /* serverpref */
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index fb825eb371..1a4f441a9f 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -26,6 +26,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHANGE_CIPHER_SPEC),
"bad change cipher spec"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CIPHER), "bad cipher"},
+ {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_COMPRESSION_ALGORITHM),
+ "bad compression algorithm"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA), "bad data"},
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK),
"bad data returned by callback"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index fb43b9b369..186e60f34c 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -615,6 +615,9 @@ int ossl_ssl_connection_reset(SSL *s)
sc->first_packet = 0;
sc->key_update = SSL_KEY_UPDATE_NONE;
+ memset(sc->ext.compress_certificate_from_peer, 0,
+ sizeof(sc->ext.compress_certificate_from_peer));
+ sc->ext.compress_certificate_sent = 0;
EVP_MD_CTX_free(sc->pha_dgst);
sc->pha_dgst = NULL;
@@ -890,6 +893,10 @@ SSL *ossl_ssl_connection_new(SSL_CTX *ctx)
s->job = NULL;
+#ifndef OPENSSL_NO_COMP_ALG
+ memcpy(s->cert_comp_prefs, ctx->cert_comp_prefs, sizeof(s->cert_comp_prefs));
+#endif
+
#ifndef OPENSSL_NO_CT
if (!SSL_set_ct_validation_callback(ssl, ctx->ct_validation_callback,
ctx->ct_validation_callback_arg))
@@ -3658,6 +3665,9 @@ SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq,
const SSL_METHOD *meth)
{
SSL_CTX *ret = NULL;
+#ifndef OPENSSL_NO_COMP_ALG
+ int i;
+#endif
if (meth == NULL) {
ERR_raise(ERR_LIB_SSL, SSL_R_NULL_SSL_METHOD_PASSED);
@@ -3832,6 +3842,21 @@ SSL_CTX *SSL_CTX_new_ex(OSSL_LIB_CTX *libctx, const char *propq,
}
# endif
#endif
+
+#ifndef OPENSSL_NO_COMP_ALG
+ /*
+ * Set the default order: brotli, zlib, zstd
+ * Including only those enabled algorithms
+ */
+ memset(ret->cert_comp_prefs, 0, sizeof(ret->cert_comp_prefs));
+ i = 0;
+ if (ossl_comp_has_alg(TLSEXT_comp_cert_brotli))
+ ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_brotli;
+ if (ossl_comp_has_alg(TLSEXT_comp_cert_zlib))
+ ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_zlib;
+ if (ossl_comp_has_alg(TLSEXT_comp_cert_zstd))
+ ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_zstd;
+#endif
/*
* Disable compression by default to prevent CRIME. Applications can
* re-enable compression by configuring
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 75614c9fc9..2580064ebd 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -19,8 +19,8 @@
# include "internal/common.h" /* for HAS_PREFIX */
# include <openssl/buffer.h>
-# include <openssl/comp.h>
# include <openssl/bio.h>
+# include <openssl/comp.h>
# include <openssl/dsa.h>
# include <openssl/err.h>
# include <openssl/ssl.h>
@@ -771,6 +771,7 @@ typedef enum tlsext_index_en {
TLSEXT_IDX_key_share,
TLSEXT_IDX_cookie,
TLSEXT_IDX_cryptopro_bug,
+ TLSEXT_IDX_compress_certificate,
TLSEXT_IDX_early_data,
TLSEXT_IDX_certificate_authorities,
TLSEXT_IDX_padding,
@@ -1212,6 +1213,11 @@ struct ssl_ctx_st {
uint32_t disabled_mac_mask;
uint32_t disabled_mkey_mask;
uint32_t disabled_auth_mask;
+
+#ifndef OPENSSL_NO_COMP_ALG
+ /* certificate compression preferences */
+ int cert_comp_prefs[TLSEXT_comp_cert_limit];
+#endif
};
typedef struct cert_pkey_st CERT_PKEY;
@@ -1699,6 +1705,11 @@ struct ssl_connection_st {
* selected.
*/
int tick_identity;
+
+ /* This is the list of algorithms the peer supports that we also support */
+ int compress_certificate_from_peer[TLSEXT_comp_cert_limit];
+ /* indicate that we sent the extension, so we'll accept it */
+ int compress_certificate_sent;
} ext;
/*
@@ -1814,6 +1825,11 @@ struct ssl_connection_st {
*/
const struct sigalg_lookup_st **shared_sigalgs;
size_t shared_sigalgslen;
+
+#ifndef OPENSSL_NO_COMP_ALG
+ /* certificate compression preferences */
+ int cert_comp_prefs[TLSEXT_comp_cert_limit];
+#endif
};
# define SSL_CONNECTION_FROM_SSL_ONLY_int(ssl, c) \
@@ -1986,6 +2002,21 @@ typedef struct dtls1_state_st {
# define EXPLICIT_CHAR2_CURVE_TYPE 2
# define NAMED_CURVE_TYPE 3
+# ifndef OPENSSL_NO_COMP_ALG
+struct ossl_comp_cert_st {
+ unsigned char *data;
+ size_t len;
+ size_t orig_len;
+ CRYPTO_REF_COUNT references;
+ CRYPTO_RWLOCK *lock;
+ int alg;
+};
+typedef struct ossl_comp_cert_st OSSL_COMP_CERT;
+
+void OSSL_COMP_CERT_free(OSSL_COMP_CERT *c);
+int OSSL_COMP_CERT_up_ref(OSSL_COMP_CERT *c);
+# endif
+
struct cert_pkey_st {
X509 *x509;
EVP_PKEY *privatekey;
@@ -2000,6 +2031,11 @@ struct cert_pkey_st {
*/
unsigned char *serverinfo;
size_t serverinfo_length;
+# ifndef OPENSSL_NO_COMP_ALG
+ /* Compressed certificate data - index 0 is unused */
+ OSSL_COMP_CERT *comp_cert[TLSEXT_comp_cert_limit];
+ int cert_comp_used;
+# endif
};
/* Retrieve Suite B flags */
# define tls1_suiteb(s) (s->cert->cert_flags & SSL_CERT_FLAG_SUITEB_128_LOS)
@@ -2956,4 +2992,7 @@ static ossl_unused ossl_inline void ssl_tsan_counter(const SSL_CTX *ctx,
}
}
+int ossl_comp_has_alg(int a);
+size_t ossl_calculate_comp_expansion(int alg, size_t length);
+
#endif
diff --git a/ssl/ssl_stat.c b/ssl/ssl_stat.c
index 8854abcbd1..8b93ccd4ac 100644
--- a/ssl/ssl_stat.c
+++ b/ssl/ssl_stat.c
@@ -37,6 +37,8 @@ const char *SSL_state_string_long(const SSL *s)
return "SSLv3/TLS read server hello";
case TLS_ST_CR_CERT:
return "SSLv3/TLS read server certificate";
+ case TLS_ST_CR_COMP_CERT:
+ return "TLSv1.3 read server compressed certificate";
case TLS_ST_CR_KEY_EXCH:
return "SSLv3/TLS read server key exchange";
case TLS_ST_CR_CERT_REQ:
@@ -47,6 +49,8 @@ const char *SSL_state_string_long(const SSL *s)
return "SSLv3/TLS read server done";
case TLS_ST_CW_CERT:
return "SSLv3/TLS write client certificate";
+ case TLS_ST_CW_COMP_CERT:
+ return "TLSv1.3 write client compressed certificate";
case TLS_ST_CW_KEY_EXCH:
return "SSLv3/TLS write client key exchange";
case TLS_ST_CW_CERT_VRFY:
@@ -71,6 +75,8 @@ const char *SSL_state_string_long(const SSL *s)
return "SSLv3/TLS write server hello";
case TLS_ST_SW_CERT:
return "SSLv3/TLS write certificate";
+ case TLS_ST_SW_COMP_CERT:
+ return "TLSv1.3 write server compressed certificate";
case TLS_ST_SW_KEY_EXCH:
return "SSLv3/TLS write key exchange";
case TLS_ST_SW_CERT_REQ:
@@ -81,6 +87,8 @@ const char *SSL_state_string_long(const SSL *s)
return "SSLv3/TLS write server done";
case TLS_ST_SR_CERT:
return "SSLv3/TLS read client certificate";
+ case TLS_ST_SR_COMP_CERT:
+ return "TLSv1.3 read client compressed certificate";
case TLS_ST_SR_KEY_EXCH:
return "SSLv3/TLS read client key exchange";
case TLS_ST_SR_CERT_VRFY:
@@ -150,6 +158,8 @@ const char *SSL_state_string(const SSL *s)
return "TRSH";
case TLS_ST_CR_CERT:
return "TRSC";
+ case TLS_ST_CR_COMP_CERT:
+ return "TRSCC";
case TLS_ST_CR_KEY_EXCH:
return "TRSKE";
case TLS_ST_CR_CERT_REQ:
@@ -158,6 +168,8 @@ const char *SSL_state_string(const SSL *s)
return "TRSD";
case TLS_ST_CW_CERT:
return "TWCC";
+ case TLS_ST_CW_COMP_CERT:
+ return "TWCCC";
case TLS_ST_CW_KEY_EXCH:
return "TWCKE";
case TLS_ST_CW_CERT_VRFY:
@@ -182,6 +194,8 @@ const char *SSL_state_string(const SSL *s)
return "TWSH";
case TLS_ST_SW_CERT:
return "TWSC";
+ case TLS_ST_SW_COMP_CERT:
+ return "TWSCC";
case TLS_ST_SW_KEY_EXCH:
return "TWSKE";
case TLS_ST_SW_CERT_REQ:
@@ -190,6 +204,8 @@ const char *SSL_state_string(const SSL *s)
return "TWSD";
case TLS_ST_SR_CERT:
return "TRCC";
+ case TLS_ST_SR_COMP_CERT:
+ return "TRCCC";
case TLS_ST_SR_KEY_EXCH:
return "TRCKE";
case TLS_ST_SR_CERT_VRFY:
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 880189d998..2cfc9f2b7d 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -62,6 +62,13 @@ static int final_maxfragmentlen(SSL_CONNECTION *s, unsigned int context,
int sent);
static int init_post_handshake_auth(SSL_CONNECTION *s, unsigned int context);
static int final_psk(SSL_CONNECTION *s, unsigned int context, int sent);
+static int tls_init_compress_certificate(SSL_CONNECTION *sc, unsigned int context);
+static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
+static int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx);
/* Structure to define a built-in extension */
typedef struct extensions_definition_st {
@@ -359,6 +366,15 @@ static const EXTENSION_DEFINITION ext_defs[] = {
NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL
},
{
+ TLSEXT_TYPE_compress_certificate,
+ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
+ | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
+ tls_init_compress_certificate,
+ tls_parse_compress_certificate, tls_parse_compress_certificate,
+ tls_construct_compress_certificate, tls_construct_compress_certificate,
+ NULL
+ },
+ {
TLSEXT_TYPE_early_data,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| SSL_EXT_TLS1_3_NEW_SESSION_TICKET | SSL_EXT_TLS1_3_ONLY,
@@ -1746,3 +1762,112 @@ static int final_psk(SSL_CONNECTION *s, unsigned int context, int sent)
return 1;
}
+
+static int tls_init_compress_certificate(SSL_CONNECTION *sc, unsigned int context)
+{
+ memset(sc->ext.compress_certificate_from_peer, 0,
+ sizeof(sc->ext.compress_certificate_from_peer));
+ return 1;
+}
+
+/* The order these are put into the packet imply a preference order: [brotli, zlib, zstd] */
+static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET *pkt,
+ unsigned int context,
+ X509 *x, size_t chainidx)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ int i;
+
+ if (!ossl_comp_has_alg(0))
+ return EXT_RETURN_NOT_SENT;
+
+ /* Do not indicate we support receiving compressed certificates */
+ if ((sc->options & SSL_OP_NO_RX_CERTIFICATE_COMPRESSION) != 0)
+ return EXT_RETURN_NOT_SENT;
+
+ if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none)
+ return EXT_RETURN_NOT_SENT;
+
+ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_compress_certificate)
+ || !WPACKET_start_sub_packet_u16(pkt)
+ || !WPACKET_start_sub_packet_u8(pkt))
+ goto err;
+
+ for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) {
+ if (!WPACKET_put_bytes_u16(pkt, sc->cert_comp_prefs[i]))
+ goto err;
+ }
+ if (!WPACKET_close(pkt) || !WPACKET_close(pkt))
+ goto err;
+
+ sc->ext.compress_certificate_sent = 1;
+ return EXT_RETURN_SENT;
+ err:
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return EXT_RETURN_FAIL;
+#else
+ return EXT_RETURN_NOT_SENT;
+#endif
+}
+
+#ifndef OPENSSL_NO_COMP_ALG
+static int tls_comp_in_pref(SSL_CONNECTION *sc, int alg)
+{
+ int i;
+
+ /* ossl_comp_has_alg() considers 0 as "any" */
+ if (alg == 0)
+ return 0;
+ /* Make sure algorithm is enabled */
+ if (!ossl_comp_has_alg(alg))
+ return 0;
+ /* If no preferences are set, it's ok */
+ if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none)
+ return 1;
+ /* Find the algorithm */
+ for (i = 0; i < TLSEXT_comp_cert_limit; i++)
+ if (sc->cert_comp_prefs[i] == alg)
+ return 1;
+ return 0;
+}
+#endif
+
+int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, unsigned int context,
+ X509 *x, size_t chainidx)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ PACKET supported_comp_algs;
+ unsigned int comp;
+ int already_set[TLSEXT_comp_cert_limit];
+ int j = 0;
+
+ /* If no algorithms are available, ignore the extension */
+ if (!ossl_comp_has_alg(0))
+ return 1;
+
+ /* Ignore the extension and don't send compressed certificates */
+ if ((sc->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ return 1;
+
+ if (!PACKET_as_length_prefixed_1(pkt, &supported_comp_algs)
+ || PACKET_remaining(&supported_comp_algs) == 0) {
+ SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION);
+ return 0;
+ }
+
+ memset(already_set, 0, sizeof(already_set));
+ /*
+ * The preference array has real values, so take a look at each
+ * value coming in, and make sure it's in our preference list
+ * The array is 0 (i.e. "none") terminated
+ * The preference list only contains supported algorithms
+ */
+ while (PACKET_get_1(&supported_comp_algs, &comp)) {
+ if (tls_comp_in_pref(sc, comp) && !already_set[comp]) {
+ sc->ext.compress_certificate_from_peer[j++] = comp;
+ already_set[comp] = 1;
+ }
+ }
+#endif
+ return 1;
+}
diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c
index a2c563b86b..57713702f7 100644
--- a/ssl/statem/extensions_cust.c
+++ b/ssl/statem/extensions_cust.c
@@ -525,6 +525,7 @@ int SSL_extension_supported(unsigned int ext_type)
case TLSEXT_TYPE_certificate_authorities:
case TLSEXT_TYPE_psk:
case TLSEXT_TYPE_post_handshake_auth:
+ case TLSEXT_TYPE_compress_certificate:
return 1;
default:
return 0;
diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c
index 448d655a17..ba3681285a 100644
--- a/ssl/statem/statem.c
+++ b/ssl/statem/statem.c
@@ -130,6 +130,7 @@ void ossl_statem_clear(SSL_CONNECTION *s)
s->statem.hand_state = TLS_ST_BEFORE;
ossl_statem_set_in_init(s, 1);
s->statem.no_cert_verify = 0;
+ s->statem.ignore_fatal = 0;
}
/*
@@ -143,6 +144,15 @@ void ossl_statem_set_renegotiate(SSL_CONNECTION *s)
void ossl_statem_send_fatal(SSL_CONNECTION *s, int al)
{
+ /*
+ * Some public APIs may call internal functions that fatal error,
+ * which doesn't make sense outside the state machine. Those APIs
+ * that can handle a failure set this flag to avoid errors sending
+ * alerts. Example: getting a wire-formatted certificate for
+ * compression.
+ */
+ if (s->statem.ignore_fatal)
+ return;
/* We shouldn't call SSLfatal() twice. Once is enough */
if (s->statem.in_init && s->statem.state == MSG_FLOW_ERROR)
return;
diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h
index 2b73eba6f6..b60103e6e5 100644
--- a/ssl/statem/statem.h
+++ b/ssl/statem/statem.h
@@ -96,6 +96,7 @@ struct ossl_statem_st {
OSSL_HANDSHAKE_STATE request_state;
int in_init;
int read_state_first_init;
+ int ignore_fatal;
/* true when we are actually in SSL_accept() or SSL_connect() */
int in_handshake;
/*
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 516aaf3984..31f0ebe8e5 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -135,6 +135,13 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
st->hand_state = TLS_ST_CR_CERT;
return 1;
}
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_CR_COMP_CERT;
+ return 1;
+ }
+#endif
}
break;
@@ -143,9 +150,17 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt)
st->hand_state = TLS_ST_CR_CERT;
return 1;
}
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_CR_COMP_CERT;
+ return 1;
+ }
+#endif
break;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
if (mt == SSL3_MT_CERTIFICATE_VERIFY) {
st->hand_state = TLS_ST_CR_CERT_VRFY;
return 1;
@@ -309,6 +324,7 @@ int ossl_statem_client_read_transition(SSL_CONNECTION *s, int mt)
break;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
/*
* The CertificateStatus message is optional even if
* |ext.status_expected| is set
@@ -425,7 +441,10 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
case TLS_ST_CR_CERT_REQ:
if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
- st->hand_state = TLS_ST_CW_CERT;
+ if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_CW_COMP_CERT;
+ else
+ st->hand_state = TLS_ST_CW_CERT;
return WRITE_TRAN_CONTINUE;
}
/*
@@ -447,9 +466,12 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
else if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
&& s->hello_retry_request == SSL_HRR_NONE)
st->hand_state = TLS_ST_CW_CHANGE;
+ else if (s->s3.tmp.cert_req == 0)
+ st->hand_state = TLS_ST_CW_FINISHED;
+ else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_CW_COMP_CERT;
else
- st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT
- : TLS_ST_CW_FINISHED;
+ st->hand_state = TLS_ST_CW_CERT;
return WRITE_TRAN_CONTINUE;
case TLS_ST_PENDING_EARLY_DATA_END:
@@ -461,10 +483,15 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s)
case TLS_ST_CW_END_OF_EARLY_DATA:
case TLS_ST_CW_CHANGE:
- st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT
- : TLS_ST_CW_FINISHED;
+ if (s->s3.tmp.cert_req == 0)
+ st->hand_state = TLS_ST_CW_FINISHED;
+ else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_CW_COMP_CERT;
+ else
+ st->hand_state = TLS_ST_CW_CERT;
return WRITE_TRAN_CONTINUE;
+ case TLS_ST_CW_COMP_CERT:
case TLS_ST_CW_CERT:
/* If a non-empty Certificate we also send CertificateVerify */
st->hand_state = (s->s3.tmp.cert_req == 1) ? TLS_ST_CW_CERT_VRFY
@@ -931,6 +958,13 @@ int ossl_statem_client_construct_message(SSL_CONNECTION *s,
*mt = SSL3_MT_CERTIFICATE;
break;
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_CW_COMP_CERT:
+ *confunc = tls_construct_client_compressed_certificate;
+ *mt = SSL3_MT_COMPRESSED_CERTIFICATE;
+ break;
+#endif
+
case TLS_ST_CW_KEY_EXCH:
*confunc = tls_construct_client_key_exchange;
*mt = SSL3_MT_CLIENT_KEY_EXCHANGE;
@@ -980,6 +1014,7 @@ size_t ossl_statem_client_max_message_size(SSL_CONNECTION *s)
case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
return HELLO_VERIFY_REQUEST_MAX_LENGTH;
+ case TLS_ST_CR_COMP_CERT:
case TLS_ST_CR_CERT:
return s->max_cert_list;
@@ -1046,6 +1081,11 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL_CONNECTION *s,
case TLS_ST_CR_CERT:
return tls_process_server_certificate(s, pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_CR_COMP_CERT:
+ return tls_process_server_compressed_certificate(s, pkt);
+#endif
+
case TLS_ST_CR_CERT_VRFY:
return tls_process_cert_verify(s, pkt);
@@ -1097,6 +1137,7 @@ WORK_STATE ossl_statem_client_post_process_message(SSL_CONNECTION *s,
return WORK_ERROR;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
return tls_post_process_server_certificate(s, wst);
case TLS_ST_CR_CERT_VRFY:
@@ -1968,6 +2009,21 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
return WORK_FINISHED_CONTINUE;
}
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ PACKET tmppkt;
+ BUF_MEM *buf = BUF_MEM_new();
+
+ if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR)
+ ret = tls_process_server_certificate(sc, &tmppkt);
+
+ BUF_MEM_free(buf);
+ return ret;
+}
+#endif
+
static int tls_process_ske_psk_preamble(SSL_CONNECTION *s, PACKET *pkt)
{
#ifndef OPENSSL_NO_PSK
@@ -3531,6 +3587,7 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
return WORK_FINISHED_CONTINUE;
} else {
s->s3.tmp.cert_req = 2;
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
if (!ssl3_digest_cached_records(s, 0)) {
/* SSLfatal() already called */
return WORK_ERROR;
@@ -3538,6 +3595,10 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst)
}
}
+ if (!SSL_CONNECTION_IS_TLS13(s)
+ || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
+
if (s->post_handshake_auth == SSL_PHA_REQUESTED)
return WORK_FINISHED_STOP;
return WORK_FINISHED_CONTINUE;
@@ -3587,6 +3648,97 @@ CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
return CON_FUNC_SUCCESS;
}
+#ifndef OPENSSL_NO_COMP_ALG
+CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc,
+ WPACKET *pkt)
+{
+ SSL *ssl = SSL_CONNECTION_GET_SSL(sc);
+ WPACKET tmppkt;
+ BUF_MEM *buf = NULL;
+ size_t length;
+ size_t max_length;
+ COMP_METHOD *method;
+ COMP_CTX *comp = NULL;
+ int comp_len;
+ int ret = 0;
+ int alg = sc->ext.compress_certificate_from_peer[0];
+
+ /* Note that sc->s3.tmp.cert_req == 2 is checked in write transition */
+
+ if ((buf = BUF_MEM_new()) == NULL || !WPACKET_init(&tmppkt, buf))
+ goto err;
+
+ /* Use the |tmppkt| for the to-be-compressed data */
+ if (sc->pha_context == NULL) {
+ /* no context available, add 0-length context */
+ if (!WPACKET_put_bytes_u8(&tmppkt, 0))
+ goto err;
+ } else if (!WPACKET_sub_memcpy_u8(&tmppkt, sc->pha_context, sc->pha_context_len))
+ goto err;
+
+ if (!ssl3_output_cert_chain(sc, &tmppkt, sc->cert->key)) {
+ /* SSLfatal() already called */
+ goto out;
+ }
+
+ /* continue with the real |pkt| */
+ if (!WPACKET_put_bytes_u16(pkt, alg)
+ || !WPACKET_get_total_written(&tmppkt, &length)
+ || !WPACKET_put_bytes_u24(pkt, length))
+ goto err;
+
+ switch (alg) {
+ case TLSEXT_comp_cert_zlib:
+ method = COMP_zlib();
+ break;
+ case TLSEXT_comp_cert_brotli:
+ method = COMP_brotli_oneshot();
+ break;
+ case TLSEXT_comp_cert_zstd:
+ method = COMP_zstd_oneshot();
+ break;
+ default:
+ goto err;
+ }
+ max_length = ossl_calculate_comp_expansion(alg, length);
+
+ if (!WPACKET_start_sub_packet_u24(pkt)
+ || !WPACKET_reserve_bytes(pkt, max_length, NULL)
+ || (comp = COMP_CTX_new(method)) == NULL)
+ goto err;
+
+ comp_len = COMP_compress_block(comp, WPACKET_get_curr(pkt), max_length,
+ (unsigned char *)buf->data, length);
+ if (comp_len <= 0)
+ goto err;
+
+ if (!WPACKET_allocate_bytes(pkt, comp_len, NULL)
+ || !WPACKET_close(pkt))
+ goto err;
+
+ if (SSL_IS_FIRST_HANDSHAKE(sc)
+ && (!ssl->method->ssl3_enc->change_cipher_state(sc,
+ SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE))) {
+ /*
+ * This is a fatal error, which leaves sc->enc_write_ctx in an
+ * inconsistent state and thus ssl3_send_alert may crash.
+ */
+ SSLfatal(sc, SSL_AD_NO_ALERT, SSL_R_CANNOT_CHANGE_CIPHER);
+ goto out;
+ }
+ ret = 1;
+ goto out;
+
+ err:
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ out:
+ WPACKET_cleanup(&tmppkt);
+ BUF_MEM_free(buf);
+ COMP_CTX_free(comp);
+ return ret;
+}
+#endif
+
int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s)
{
const SSL_CERT_LOOKUP *clu;
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 8b34c11048..69e900f90e 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -2466,3 +2466,76 @@ int tls13_restore_handshake_digest_for_pha(SSL_CONNECTION *s)
}
return 1;
}
+
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt,
+ PACKET *tmppkt,
+ BUF_MEM *buf)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ int comp_alg;
+ COMP_METHOD *method = NULL;
+ COMP_CTX *comp = NULL;
+ size_t expected_length;
+ size_t comp_length;
+ int i;
+ int found = 0;
+
+ if (buf == NULL) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ if (!PACKET_get_net_2(pkt, (unsigned int*)&comp_alg)) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ /* If we have a prefs list, make sure the algorithm is in it */
+ if (sc->cert_comp_prefs[0] != TLSEXT_comp_cert_none) {
+ for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) {
+ if (sc->cert_comp_prefs[i] == comp_alg) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM);
+ goto err;
+ }
+ }
+ if (!ossl_comp_has_alg(comp_alg)) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM);
+ goto err;
+ }
+ switch (comp_alg) {
+ case TLSEXT_comp_cert_zlib:
+ method = COMP_zlib();
+ break;
+ case TLSEXT_comp_cert_brotli:
+ method = COMP_brotli_oneshot();
+ break;
+ case TLSEXT_comp_cert_zstd:
+ method = COMP_zstd_oneshot();
+ break;
+ default:
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM);
+ goto err;
+ }
+
+ if (!PACKET_get_net_3_len(pkt, &expected_length)
+ || !PACKET_get_net_3_len(pkt, &comp_length)
+ || PACKET_remaining(pkt) != comp_length
+ || !BUF_MEM_grow(buf, expected_length)
+ || !PACKET_buf_init(tmppkt, (unsigned char *)buf->data, expected_length)
+ || (comp = COMP_CTX_new(method)) == NULL
+ || COMP_expand_block(comp, (unsigned char *)buf->data, expected_length,
+ (unsigned char*)PACKET_data(pkt), comp_length) != (int)expected_length) {
+ SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_DECOMPRESSION);
+ goto err;
+ }
+ ret = MSG_PROCESS_CONTINUE_PROCESSING;
+ err:
+ COMP_CTX_free(comp);
+ return ret;
+}
+#endif
diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h
index e5c6cfe535..75b7274c2e 100644
--- a/ssl/statem/statem_local.h
+++ b/ssl/statem/statem_local.h
@@ -129,6 +129,13 @@ __owur WORK_STATE tls_finish_handshake(SSL_CONNECTION *s, WORK_STATE wst,
int clearbufs, int stop);
__owur WORK_STATE dtls_wait_for_dry(SSL_CONNECTION *s);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt,
+ PACKET *tmppkt,
+ BUF_MEM *buf);
+#endif
+
/* some client-only functions */
__owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s,
WPACKET *pkt);
@@ -149,6 +156,10 @@ __owur WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s,
WORK_STATE wst);
__owur CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s,
WPACKET *pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc,
+ WPACKET *pkt);
+#endif
__owur int ssl_do_client_cert_cb(SSL_CONNECTION *s, X509 **px509,
EVP_PKEY **ppkey);
__owur CON_FUNC_RETURN tls_construct_client_key_exchange(SSL_CONNECTION *s,
@@ -163,6 +174,10 @@ __owur MSG_PROCESS_RETURN tls_process_server_certificate(SSL_CONNECTION *s,
PACKET *pkt);
__owur WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s,
WORK_STATE wst);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt);
+#endif
__owur int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s);
#ifndef OPENSSL_NO_NEXTPROTONEG
__owur CON_FUNC_RETURN tls_construct_next_proto(SSL_CONNECTION *s, WPACKET *pkt);
@@ -183,6 +198,10 @@ __owur CON_FUNC_RETURN dtls_construct_hello_verify_request(SSL_CONNECTION *s,
WPACKET *pkt);
__owur CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s,
WPACKET *pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur CON_FUNC_RETURN tls_construct_server_compressed_certificate(SSL_CONNECTION *sc,
+ WPACKET *pkt);
+#endif
__owur CON_FUNC_RETURN tls_construct_server_key_exchange(SSL_CONNECTION *s,
WPACKET *pkt);
__owur CON_FUNC_RETURN tls_construct_certificate_request(SSL_CONNECTION *s,
@@ -191,6 +210,10 @@ __owur CON_FUNC_RETURN tls_construct_server_done(SSL_CONNECTION *s,
WPACKET *pkt);
__owur MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
PACKET *pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+__owur MSG_PROCESS_RETURN tls_process_client_compressed_certificate(SSL_CONNECTION *sc,
+ PACKET *pkt);
+#endif
__owur MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL_CONNECTION *s,
PACKET *pkt);
__owur WORK_STATE tls_post_process_client_key_exchange(SSL_CONNECTION *s,
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index ab1574419e..0d68e4b1cf 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -26,6 +26,7 @@
#include <openssl/trace.h>
#include <openssl/core_names.h>
#include <openssl/asn1t.h>
+#include <openssl/comp.h>
#define TICKET_NONCE_SIZE 8
@@ -91,6 +92,13 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
st->hand_state = TLS_ST_SR_CERT;
return 1;
}
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_SR_COMP_CERT;
+ return 1;
+ }
+#endif
} else {
if (mt == SSL3_MT_FINISHED) {
st->hand_state = TLS_ST_SR_FINISHED;
@@ -99,6 +107,7 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
}
break;
+ case TLS_ST_SR_COMP_CERT:
case TLS_ST_SR_CERT:
if (s->session->peer == NULL) {
if (mt == SSL3_MT_FINISHED) {
@@ -128,10 +137,18 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt)
if (s->early_data_state == SSL_EARLY_DATA_READING)
break;
- if (mt == SSL3_MT_CERTIFICATE
- && s->post_handshake_auth == SSL_PHA_REQUESTED) {
- st->hand_state = TLS_ST_SR_CERT;
- return 1;
+ if (s->post_handshake_auth == SSL_PHA_REQUESTED) {
+ if (mt == SSL3_MT_CERTIFICATE) {
+ st->hand_state = TLS_ST_SR_CERT;
+ return 1;
+ }
+#ifndef OPENSSL_NO_COMP_ALG
+ if (mt == SSL3_MT_COMPRESSED_CERTIFICATE
+ && s->ext.compress_certificate_sent) {
+ st->hand_state = TLS_ST_SR_COMP_CERT;
+ return 1;
+ }
+#endif
}
if (mt == SSL3_MT_KEY_UPDATE) {
@@ -357,6 +374,27 @@ static int send_server_key_exchange(SSL_CONNECTION *s)
}
/*
+ * Used to determine if we shoud send a CompressedCertificate message
+ *
+ * Returns the algorithm to use, TLSEXT_comp_cert_none means no compression
+ */
+static int get_compressed_certificate_alg(SSL_CONNECTION *sc)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ int *alg = sc->ext.compress_certificate_from_peer;
+
+ if (sc->s3.tmp.cert == NULL)
+ return TLSEXT_comp_cert_none;
+
+ for (; *alg != TLSEXT_comp_cert_none; alg++) {
+ if (sc->s3.tmp.cert->comp_cert[*alg] != NULL)
+ return *alg;
+ }
+#endif
+ return TLSEXT_comp_cert_none;
+}
+
+/*
* Should we send a CertificateRequest message?
*
* Valid return values are:
@@ -468,6 +506,8 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
st->hand_state = TLS_ST_SW_FINISHED;
else if (send_certificate_request(s))
st->hand_state = TLS_ST_SW_CERT_REQ;
+ else if (get_compressed_certificate_alg(s) != TLSEXT_comp_cert_none)
+ st->hand_state = TLS_ST_SW_COMP_CERT;
else
st->hand_state = TLS_ST_SW_CERT;
@@ -477,11 +517,14 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
s->post_handshake_auth = SSL_PHA_REQUESTED;
st->hand_state = TLS_ST_OK;
+ } else if (get_compressed_certificate_alg(s) != TLSEXT_comp_cert_none) {
+ st->hand_state = TLS_ST_SW_COMP_CERT;
} else {
st->hand_state = TLS_ST_SW_CERT;
}
return WRITE_TRAN_CONTINUE;
+ case TLS_ST_SW_COMP_CERT:
case TLS_ST_SW_CERT:
st->hand_state = TLS_ST_SW_CERT_VRFY;
return WRITE_TRAN_CONTINUE;
@@ -975,6 +1018,18 @@ WORK_STATE ossl_statem_server_post_work(SSL_CONNECTION *s, WORK_STATE wst)
if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) {
if (statem_flush(s) != 1)
return WORK_MORE_A;
+ } else {
+ if (!SSL_CONNECTION_IS_TLS13(s)
+ || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
+ }
+ break;
+
+ case TLS_ST_SW_ENCRYPTED_EXTENSIONS:
+ if (!s->hit && !send_certificate_request(s)) {
+ if (!SSL_CONNECTION_IS_TLS13(s)
+ || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0)
+ s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none;
}
break;
@@ -1059,6 +1114,13 @@ int ossl_statem_server_construct_message(SSL_CONNECTION *s,
*mt = SSL3_MT_CERTIFICATE;
break;
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_SW_COMP_CERT:
+ *confunc = tls_construct_server_compressed_certificate;
+ *mt = SSL3_MT_COMPRESSED_CERTIFICATE;
+ break;
+#endif
+
case TLS_ST_SW_CERT_VRFY:
*confunc = tls_construct_cert_verify;
*mt = SSL3_MT_CERTIFICATE_VERIFY;
@@ -1153,6 +1215,7 @@ size_t ossl_statem_server_max_message_size(SSL_CONNECTION *s)
case TLS_ST_SR_END_OF_EARLY_DATA:
return END_OF_EARLY_DATA_MAX_LENGTH;
+ case TLS_ST_SR_COMP_CERT:
case TLS_ST_SR_CERT:
return s->max_cert_list;
@@ -1201,6 +1264,11 @@ MSG_PROCESS_RETURN ossl_statem_server_process_message(SSL_CONNECTION *s,
case TLS_ST_SR_CERT:
return tls_process_client_certificate(s, pkt);
+#ifndef OPENSSL_NO_COMP_ALG
+ case TLS_ST_SR_COMP_CERT:
+ return tls_process_client_compressed_certificate(s, pkt);
+#endif
+
case TLS_ST_SR_KEY_EXCH:
return tls_process_client_key_exchange(s, pkt);
@@ -3633,6 +3701,21 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s,
return ret;
}
+#ifndef OPENSSL_NO_COMP_ALG
+MSG_PROCESS_RETURN tls_process_client_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt)
+{
+ MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR;
+ PACKET tmppkt;
+ BUF_MEM *buf = BUF_MEM_new();
+
+ if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR)
+ ret = tls_process_client_certificate(sc, &tmppkt);
+
+ BUF_MEM_free(buf);
+ return ret;
+}
+#endif
+
CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt)
{
CERT_PKEY *cpk = s->s3.tmp.cert;
@@ -3658,6 +3741,32 @@ CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt
return CON_FUNC_SUCCESS;
}
+#ifndef OPENSSL_NO_COMP_ALG
+CON_FUNC_RETURN tls_construct_server_compressed_certificate(SSL_CONNECTION *sc, WPACKET *pkt)
+{
+ int alg = get_compressed_certificate_alg(sc);
+ OSSL_COMP_CERT *cc = sc->s3.tmp.cert->comp_cert[alg];
+
+ if (!ossl_assert(cc != NULL)) {
+ SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ /*
+ * Server can't compress on-demand
+ * Use pre-compressed certificate
+ */
+ if (!WPACKET_put_bytes_u16(pkt, alg)
+ || !WPACKET_put_bytes_u24(pkt, cc->orig_len)
+ || !WPACKET_start_sub_packet_u24(pkt)
+ || !WPACKET_memcpy(pkt, cc->data, cc->len)
+ || !WPACKET_close(pkt))
+ return 0;
+
+ sc->s3.tmp.cert->cert_comp_used++;
+ return 1;
+}
+#endif
+
static int create_ticket_prequel(SSL_CONNECTION *s, WPACKET *pkt,
uint32_t age_add, unsigned char *tick_nonce)
{
diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index 802958c383..1a032b3137 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -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;
diff --git a/test/bio_comp_test.c b/test/bio_comp_test.c
index ab56e981f5..5dd13abd36 100644
--- a/test/bio_comp_test.c
+++ b/test/bio_comp_test.c
@@ -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())))
diff --git a/test/build.info b/test/build.info
index 6994131275..4a6fc835e1 100644
--- a/test/build.info
+++ b/test/build.info
@@ -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
index 0000000000..66320bddd4
--- /dev/null
+++ b/test/cert_comp_test.c
@@ -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
+}
diff --git a/test/ext_internal_test.c b/test/ext_internal_test.c
index dc1420aba8..97a538abf9 100644
--- a/test/ext_internal_test.c
+++ b/test/ext_internal_test.c
@@ -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),
diff --git a/test/helpers/handshake.c b/test/helpers/handshake.c
index fc7f026300..e0422469e4 100644
--- a/test/helpers/handshake.c
+++ b/test/helpers/handshake.c
@@ -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;
diff --git a/test/helpers/ssl_test_ctx.c b/test/helpers/ssl_test_ctx.c
index 69baa0b49f..1919481b02 100644
--- a/test/helpers/ssl_test_ctx.c
+++ b/test/helpers/ssl_test_ctx.c
@@ -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 },
diff --git a/test/helpers/ssl_test_ctx.h b/test/helpers/ssl_test_ctx.h
index c7820d9764..4baf657f6d 100644
--- a/test/helpers/ssl_test_ctx.h
+++ b/test/helpers/ssl_test_ctx.h
@@ -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
index 0000000000..bc960c8b37
--- /dev/null
+++ b/test/recipes/70-test_tls13certcomp.t
@@ -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");
diff --git a/test/recipes/70-test_tls13kexmodes.t b/test/recipes/70-test_tls13kexmodes.t
index 0d0681a838..1c2c18cc11 100644
--- a/test/recipes/70-test_tls13kexmodes.t
+++ b/test/recipes/70-test_tls13kexmodes.t
@@ -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");
diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t
index 58b88a3ca8..b2f9819f02 100644
--- a/test/recipes/70-test_tls13messages.t
+++ b/test/recipes/70-test_tls13messages.t
@@ -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
diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t
index 1783916b9b..39690e55ef 100644
--- a/test/recipes/80-test_ssl_new.t
+++ b/test/recipes/80-test_ssl_new.t
@@ -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
index 0000000000..a1421e60d0
--- /dev/null
+++ b/test/recipes/90-test_cert_comp.t
@@ -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
index 0000000000..d1898934d5
--- /dev/null
+++ b/test/ssl-tests/32-compressed-certificate.cnf
@@ -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
index 0000000000..780c982d3c
--- /dev/null
+++ b/test/ssl-tests/32-compressed-certificate.cnf.in
@@ -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",
+ },
+ },
+);
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 1be4f7e14e..7d3432de28 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -7165,6 +7165,38 @@ static struct info_cb_states_st {
{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);
diff --git a/util/libssl.num b/util/libssl.num
index adb095bd53..c275cffcd0 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -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:
diff --git a/util/perl/TLSProxy/Message.pm b/util/perl/TLSProxy/Message.pm
index 2c1bdb3837..648d986342 100644
--- a/util/perl/TLSProxy/Message.pm
+++ b/util/perl/TLSProxy/Message.pm
@@ -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,
diff --git a/util/perl/checkhandshake.pm b/util/perl/checkhandshake.pm
index a2ae24ee2a..618ee99577 100644
--- a/util/perl/checkhandshake.pm
+++ b/util/perl/checkhandshake.pm
@@ -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 = ();