### 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.
### 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
OPT_S_RECORD_PADDING, OPT_S_DEBUGBROKE, OPT_S_COMP, \
OPT_S_MINPROTO, OPT_S_MAXPROTO, \
OPT_S_NO_RENEGOTIATION, OPT_S_NO_MIDDLEBOX, OPT_S_NO_ETM, \
- OPT_S_NO_EMS, OPT_S__LAST
+ OPT_S_NO_EMS, \
+ OPT_S_NO_TX_CERT_COMP, \
+ OPT_S_NO_RX_CERT_COMP, \
+ OPT_S__LAST
# define OPT_S_OPTIONS \
OPT_SECTION("TLS/SSL"), \
{"bugs", OPT_S_BUGS, '-', "Turn on SSL bug compatibility"}, \
{"no_comp", OPT_S_NO_COMP, '-', "Disable SSL/TLS compression (default)" }, \
{"comp", OPT_S_COMP, '-', "Use SSL/TLS-level compression" }, \
+ {"no_tx_cert_comp", OPT_S_NO_TX_CERT_COMP, '-', "Disable sending TLSv1.3 compressed certificates" }, \
+ {"no_rx_cert_comp", OPT_S_NO_RX_CERT_COMP, '-', "Disable receiving TLSv1.3 compressed certificates" }, \
{"no_ticket", OPT_S_NOTICKET, '-', \
"Disable use of TLS session tickets"}, \
{"serverpref", OPT_S_SERVERPREF, '-', "Use server's cipher preferences"}, \
case OPT_S_BUGS: \
case OPT_S_NO_COMP: \
case OPT_S_COMP: \
+ case OPT_S_NO_TX_CERT_COMP: \
+ case OPT_S_NO_RX_CERT_COMP: \
case OPT_S_NOTICKET: \
case OPT_S_SERVERPREF: \
case OPT_S_LEGACYRENEG: \
{", 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
#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},
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,
"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"),
int enable_ktls = 0;
#endif
int tfo = 0;
+ int cert_comp = 0;
/* Init of few remaining global variables */
local_argc = argc;
case OPT_TFO:
tfo = 1;
break;
+ case OPT_CERT_COMP:
+ cert_comp = 1;
+ break;
}
}
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)
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
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
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),
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
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
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 \
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 \
[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>]
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.
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
[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>]
[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 -}
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.
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 -}
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
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
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
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
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
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.
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.
--- /dev/null
+=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
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.
* 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
# 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
* 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."
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,
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,
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,
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
# 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
# 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
/* 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
# 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
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
{
CERT *ret = OPENSSL_zalloc(sizeof(*ret));
int i;
+#ifndef OPENSSL_NO_COMP_ALG
+ int j;
+#endif
if (ret == NULL)
return NULL;
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);
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 */
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++) {
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
}
}
--- /dev/null
+/*
+ * 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
+}
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;
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),
{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 */
{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"},
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;
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))
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);
ERR_clear_error();
}
# endif
+#endif
+
+#ifndef OPENSSL_NO_COMP_ALG
+ /*
+ * Set the default order: brotli, zlib, zstd
+ * Including only those enabled algorithms
+ */
+ memset(ret->cert_comp_prefs, 0, sizeof(ret->cert_comp_prefs));
+ i = 0;
+ if (ossl_comp_has_alg(TLSEXT_comp_cert_brotli))
+ ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_brotli;
+ if (ossl_comp_has_alg(TLSEXT_comp_cert_zlib))
+ ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_zlib;
+ if (ossl_comp_has_alg(TLSEXT_comp_cert_zstd))
+ ret->cert_comp_prefs[i++] = TLSEXT_comp_cert_zstd;
#endif
/*
* Disable compression by default to prevent CRIME. Applications can
# 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>
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,
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;
* 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;
/*
*/
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) \
# 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;
*/
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)
}
}
+int ossl_comp_has_alg(int a);
+size_t ossl_calculate_comp_expansion(int alg, size_t length);
+
#endif
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:
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:
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:
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:
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:
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:
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:
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:
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 {
| SSL_EXT_TLS1_2_AND_BELOW_ONLY,
NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL
},
+ {
+ TLSEXT_TYPE_compress_certificate,
+ SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
+ | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
+ tls_init_compress_certificate,
+ tls_parse_compress_certificate, tls_parse_compress_certificate,
+ tls_construct_compress_certificate, tls_construct_compress_certificate,
+ NULL
+ },
{
TLSEXT_TYPE_early_data,
SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
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;
+}
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;
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;
}
/*
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;
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;
/*
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;
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;
break;
case TLS_ST_CR_CERT:
+ case TLS_ST_CR_COMP_CERT:
/*
* The CertificateStatus message is optional even if
* |ext.status_expected| is set
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;
}
/*
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:
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
*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;
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;
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);
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:
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
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;
}
}
+ 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;
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;
}
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
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);
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,
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);
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,
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,
#include <openssl/trace.h>
#include <openssl/core_names.h>
#include <openssl/asn1t.h>
+#include <openssl/comp.h>
#define TICKET_NONCE_SIZE 8
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;
}
break;
+ case TLS_ST_SR_COMP_CERT:
case TLS_ST_SR_CERT:
if (s->session->peer == NULL) {
if (mt == SSL3_MT_FINISHED) {
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) {
return 0;
}
+/*
+ * Used to determine if we shoud send a CompressedCertificate message
+ *
+ * Returns the algorithm to use, TLSEXT_comp_cert_none means no compression
+ */
+static int get_compressed_certificate_alg(SSL_CONNECTION *sc)
+{
+#ifndef OPENSSL_NO_COMP_ALG
+ int *alg = sc->ext.compress_certificate_from_peer;
+
+ if (sc->s3.tmp.cert == NULL)
+ return TLSEXT_comp_cert_none;
+
+ for (; *alg != TLSEXT_comp_cert_none; alg++) {
+ if (sc->s3.tmp.cert->comp_cert[*alg] != NULL)
+ return *alg;
+ }
+#endif
+ return TLSEXT_comp_cert_none;
+}
+
/*
* Should we send a CertificateRequest message?
*
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;
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;
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;
*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;
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;
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);
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;
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)
{
{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
{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"},
{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)
{
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;
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)
{
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;
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())))
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
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;
--- /dev/null
+/*
+ * 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
+}
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),
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;
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)
{ "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 },
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 */
--- /dev/null
+#! /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");
#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;
#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();
#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");
#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,
#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,
#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,
#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,
#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,
#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,
# 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,
#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");
#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;
#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
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
#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,
#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,
#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,
#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 |
#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
#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
#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
#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
#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,
#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
#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();
#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
#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
#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
@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;
"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
"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) {
--- /dev/null
+#! /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");
--- /dev/null
+# 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
+
+
--- /dev/null
+# -*- 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",
+ },
+ },
+);
{SSL_CB_LOOP, "TWFIN"}, {SSL_CB_HANDSHAKE_DONE, NULL},
{SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "SSLOK"},
{SSL_CB_LOOP, "TRST"}, {SSL_CB_EXIT, NULL}, {0, NULL},
+ }, {
+ /* TLSv1.3 server, certificate compression, followed by resumption */
+ {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT"},
+ {SSL_CB_LOOP, "PINIT"}, {SSL_CB_LOOP, "TRCH"}, {SSL_CB_LOOP, "TWSH"},
+ {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWEE"}, {SSL_CB_LOOP, "TWSCC"},
+ {SSL_CB_LOOP, "TWSCV"}, {SSL_CB_LOOP, "TWFIN"}, {SSL_CB_LOOP, "TED"},
+ {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TED"}, {SSL_CB_LOOP, "TRFIN"},
+ {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_LOOP, "TWST"},
+ {SSL_CB_LOOP, "TWST"}, {SSL_CB_EXIT, NULL}, {SSL_CB_ALERT, NULL},
+ {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT"},
+ {SSL_CB_LOOP, "PINIT"}, {SSL_CB_LOOP, "TRCH"}, {SSL_CB_LOOP, "TWSH"},
+ {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWEE"}, {SSL_CB_LOOP, "TWFIN"},
+ {SSL_CB_LOOP, "TED"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TED"},
+ {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_HANDSHAKE_DONE, NULL},
+ {SSL_CB_LOOP, "TWST"}, {SSL_CB_EXIT, NULL}, {0, NULL},
+ }, {
+ /* TLSv1.3 client, certificate compression, followed by resumption */
+ {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT"},
+ {SSL_CB_LOOP, "TWCH"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TWCH"},
+ {SSL_CB_LOOP, "TRSH"}, {SSL_CB_LOOP, "TREE"}, {SSL_CB_LOOP, "TRSCC"},
+ {SSL_CB_LOOP, "TRSCV"}, {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_LOOP, "TWCCS"},
+ {SSL_CB_LOOP, "TWFIN"}, {SSL_CB_HANDSHAKE_DONE, NULL},
+ {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "SSLOK"},
+ {SSL_CB_LOOP, "TRST"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "SSLOK"},
+ {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "TRST"}, {SSL_CB_EXIT, NULL},
+ {SSL_CB_ALERT, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
+ {SSL_CB_LOOP, "PINIT"}, {SSL_CB_LOOP, "TWCH"}, {SSL_CB_EXIT, NULL},
+ {SSL_CB_LOOP, "TWCH"}, {SSL_CB_LOOP, "TRSH"}, {SSL_CB_LOOP, "TREE"},
+ {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWFIN"},
+ {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_EXIT, NULL},
+ {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "SSLOK"}, {SSL_CB_LOOP, "TRST"},
+ {SSL_CB_EXIT, NULL}, {0, NULL},
}, {
{0, NULL},
}
* 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)
{
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];
*/
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))
#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);
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:
MT_CLIENT_KEY_EXCHANGE => 16,
MT_FINISHED => 20,
MT_CERTIFICATE_STATUS => 22,
+ MT_COMPRESSED_CERTIFICATE => 25,
MT_NEXT_PROTO => 67
};
MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange",
MT_FINISHED, "Finished",
MT_CERTIFICATE_STATUS, "CertificateStatus",
+ MT_COMPRESSED_CERTIFICATE, "CompressedCertificate",
MT_NEXT_PROTO, "NextProto"
);
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,
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 {
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 = ();