diff options
author | Todd Short <tshort@akamai.com> | 2021-08-09 16:56:50 -0400 |
---|---|---|
committer | Todd Short <todd.short@me.com> | 2022-10-18 09:30:22 -0400 |
commit | b67cb09f8ddf258cf326f3e7b20be095fb53457c (patch) | |
tree | b31a978e8c71e972e84fd03b4de92491deff032a /ssl/statem | |
parent | 59d21298df9176b64b41cc8583c7024f7f5895d4 (diff) | |
download | openssl-new-b67cb09f8ddf258cf326f3e7b20be095fb53457c.tar.gz |
Add support for compressed certificates (RFC8879)
* Compressed Certificate extension (server/client)
* Server certificates (send/receive)
* Client certificate (send/receive)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18186)
Diffstat (limited to 'ssl/statem')
-rw-r--r-- | ssl/statem/extensions.c | 125 | ||||
-rw-r--r-- | ssl/statem/extensions_cust.c | 1 | ||||
-rw-r--r-- | ssl/statem/statem.c | 10 | ||||
-rw-r--r-- | ssl/statem/statem.h | 1 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 162 | ||||
-rw-r--r-- | ssl/statem/statem_lib.c | 73 | ||||
-rw-r--r-- | ssl/statem/statem_local.h | 23 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 117 |
8 files changed, 503 insertions, 9 deletions
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index 880189d998..2cfc9f2b7d 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -62,6 +62,13 @@ static int final_maxfragmentlen(SSL_CONNECTION *s, unsigned int context, int sent); static int init_post_handshake_auth(SSL_CONNECTION *s, unsigned int context); static int final_psk(SSL_CONNECTION *s, unsigned int context, int sent); +static int tls_init_compress_certificate(SSL_CONNECTION *sc, unsigned int context); +static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET *pkt, + unsigned int context, + X509 *x, size_t chainidx); +static int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, + unsigned int context, + X509 *x, size_t chainidx); /* Structure to define a built-in extension */ typedef struct extensions_definition_st { @@ -359,6 +366,15 @@ static const EXTENSION_DEFINITION ext_defs[] = { NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL }, { + TLSEXT_TYPE_compress_certificate, + SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST + | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY, + tls_init_compress_certificate, + tls_parse_compress_certificate, tls_parse_compress_certificate, + tls_construct_compress_certificate, tls_construct_compress_certificate, + NULL + }, + { TLSEXT_TYPE_early_data, SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS | SSL_EXT_TLS1_3_NEW_SESSION_TICKET | SSL_EXT_TLS1_3_ONLY, @@ -1746,3 +1762,112 @@ static int final_psk(SSL_CONNECTION *s, unsigned int context, int sent) return 1; } + +static int tls_init_compress_certificate(SSL_CONNECTION *sc, unsigned int context) +{ + memset(sc->ext.compress_certificate_from_peer, 0, + sizeof(sc->ext.compress_certificate_from_peer)); + return 1; +} + +/* The order these are put into the packet imply a preference order: [brotli, zlib, zstd] */ +static EXT_RETURN tls_construct_compress_certificate(SSL_CONNECTION *sc, WPACKET *pkt, + unsigned int context, + X509 *x, size_t chainidx) +{ +#ifndef OPENSSL_NO_COMP_ALG + int i; + + if (!ossl_comp_has_alg(0)) + return EXT_RETURN_NOT_SENT; + + /* Do not indicate we support receiving compressed certificates */ + if ((sc->options & SSL_OP_NO_RX_CERTIFICATE_COMPRESSION) != 0) + return EXT_RETURN_NOT_SENT; + + if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none) + return EXT_RETURN_NOT_SENT; + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_compress_certificate) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_start_sub_packet_u8(pkt)) + goto err; + + for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) { + if (!WPACKET_put_bytes_u16(pkt, sc->cert_comp_prefs[i])) + goto err; + } + if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) + goto err; + + sc->ext.compress_certificate_sent = 1; + return EXT_RETURN_SENT; + err: + SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; +#else + return EXT_RETURN_NOT_SENT; +#endif +} + +#ifndef OPENSSL_NO_COMP_ALG +static int tls_comp_in_pref(SSL_CONNECTION *sc, int alg) +{ + int i; + + /* ossl_comp_has_alg() considers 0 as "any" */ + if (alg == 0) + return 0; + /* Make sure algorithm is enabled */ + if (!ossl_comp_has_alg(alg)) + return 0; + /* If no preferences are set, it's ok */ + if (sc->cert_comp_prefs[0] == TLSEXT_comp_cert_none) + return 1; + /* Find the algorithm */ + for (i = 0; i < TLSEXT_comp_cert_limit; i++) + if (sc->cert_comp_prefs[i] == alg) + return 1; + return 0; +} +#endif + +int tls_parse_compress_certificate(SSL_CONNECTION *sc, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ +#ifndef OPENSSL_NO_COMP_ALG + PACKET supported_comp_algs; + unsigned int comp; + int already_set[TLSEXT_comp_cert_limit]; + int j = 0; + + /* If no algorithms are available, ignore the extension */ + if (!ossl_comp_has_alg(0)) + return 1; + + /* Ignore the extension and don't send compressed certificates */ + if ((sc->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0) + return 1; + + if (!PACKET_as_length_prefixed_1(pkt, &supported_comp_algs) + || PACKET_remaining(&supported_comp_algs) == 0) { + SSLfatal(sc, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); + return 0; + } + + memset(already_set, 0, sizeof(already_set)); + /* + * The preference array has real values, so take a look at each + * value coming in, and make sure it's in our preference list + * The array is 0 (i.e. "none") terminated + * The preference list only contains supported algorithms + */ + while (PACKET_get_1(&supported_comp_algs, &comp)) { + if (tls_comp_in_pref(sc, comp) && !already_set[comp]) { + sc->ext.compress_certificate_from_peer[j++] = comp; + already_set[comp] = 1; + } + } +#endif + return 1; +} diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c index a2c563b86b..57713702f7 100644 --- a/ssl/statem/extensions_cust.c +++ b/ssl/statem/extensions_cust.c @@ -525,6 +525,7 @@ int SSL_extension_supported(unsigned int ext_type) case TLSEXT_TYPE_certificate_authorities: case TLSEXT_TYPE_psk: case TLSEXT_TYPE_post_handshake_auth: + case TLSEXT_TYPE_compress_certificate: return 1; default: return 0; diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index 448d655a17..ba3681285a 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -130,6 +130,7 @@ void ossl_statem_clear(SSL_CONNECTION *s) s->statem.hand_state = TLS_ST_BEFORE; ossl_statem_set_in_init(s, 1); s->statem.no_cert_verify = 0; + s->statem.ignore_fatal = 0; } /* @@ -143,6 +144,15 @@ void ossl_statem_set_renegotiate(SSL_CONNECTION *s) void ossl_statem_send_fatal(SSL_CONNECTION *s, int al) { + /* + * Some public APIs may call internal functions that fatal error, + * which doesn't make sense outside the state machine. Those APIs + * that can handle a failure set this flag to avoid errors sending + * alerts. Example: getting a wire-formatted certificate for + * compression. + */ + if (s->statem.ignore_fatal) + return; /* We shouldn't call SSLfatal() twice. Once is enough */ if (s->statem.in_init && s->statem.state == MSG_FLOW_ERROR) return; diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h index 2b73eba6f6..b60103e6e5 100644 --- a/ssl/statem/statem.h +++ b/ssl/statem/statem.h @@ -96,6 +96,7 @@ struct ossl_statem_st { OSSL_HANDSHAKE_STATE request_state; int in_init; int read_state_first_init; + int ignore_fatal; /* true when we are actually in SSL_accept() or SSL_connect() */ int in_handshake; /* diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 516aaf3984..31f0ebe8e5 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -135,6 +135,13 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt) st->hand_state = TLS_ST_CR_CERT; return 1; } +#ifndef OPENSSL_NO_COMP_ALG + if (mt == SSL3_MT_COMPRESSED_CERTIFICATE + && s->ext.compress_certificate_sent) { + st->hand_state = TLS_ST_CR_COMP_CERT; + return 1; + } +#endif } break; @@ -143,9 +150,17 @@ static int ossl_statem_client13_read_transition(SSL_CONNECTION *s, int mt) st->hand_state = TLS_ST_CR_CERT; return 1; } +#ifndef OPENSSL_NO_COMP_ALG + if (mt == SSL3_MT_COMPRESSED_CERTIFICATE + && s->ext.compress_certificate_sent) { + st->hand_state = TLS_ST_CR_COMP_CERT; + return 1; + } +#endif break; case TLS_ST_CR_CERT: + case TLS_ST_CR_COMP_CERT: if (mt == SSL3_MT_CERTIFICATE_VERIFY) { st->hand_state = TLS_ST_CR_CERT_VRFY; return 1; @@ -309,6 +324,7 @@ int ossl_statem_client_read_transition(SSL_CONNECTION *s, int mt) break; case TLS_ST_CR_CERT: + case TLS_ST_CR_COMP_CERT: /* * The CertificateStatus message is optional even if * |ext.status_expected| is set @@ -425,7 +441,10 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s) case TLS_ST_CR_CERT_REQ: if (s->post_handshake_auth == SSL_PHA_REQUESTED) { - st->hand_state = TLS_ST_CW_CERT; + if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none) + st->hand_state = TLS_ST_CW_COMP_CERT; + else + st->hand_state = TLS_ST_CW_CERT; return WRITE_TRAN_CONTINUE; } /* @@ -447,9 +466,12 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s) else if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0 && s->hello_retry_request == SSL_HRR_NONE) st->hand_state = TLS_ST_CW_CHANGE; + else if (s->s3.tmp.cert_req == 0) + st->hand_state = TLS_ST_CW_FINISHED; + else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none) + st->hand_state = TLS_ST_CW_COMP_CERT; else - st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT - : TLS_ST_CW_FINISHED; + st->hand_state = TLS_ST_CW_CERT; return WRITE_TRAN_CONTINUE; case TLS_ST_PENDING_EARLY_DATA_END: @@ -461,10 +483,15 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL_CONNECTION *s) case TLS_ST_CW_END_OF_EARLY_DATA: case TLS_ST_CW_CHANGE: - st->hand_state = (s->s3.tmp.cert_req != 0) ? TLS_ST_CW_CERT - : TLS_ST_CW_FINISHED; + if (s->s3.tmp.cert_req == 0) + st->hand_state = TLS_ST_CW_FINISHED; + else if (s->ext.compress_certificate_from_peer[0] != TLSEXT_comp_cert_none) + st->hand_state = TLS_ST_CW_COMP_CERT; + else + st->hand_state = TLS_ST_CW_CERT; return WRITE_TRAN_CONTINUE; + case TLS_ST_CW_COMP_CERT: case TLS_ST_CW_CERT: /* If a non-empty Certificate we also send CertificateVerify */ st->hand_state = (s->s3.tmp.cert_req == 1) ? TLS_ST_CW_CERT_VRFY @@ -931,6 +958,13 @@ int ossl_statem_client_construct_message(SSL_CONNECTION *s, *mt = SSL3_MT_CERTIFICATE; break; +#ifndef OPENSSL_NO_COMP_ALG + case TLS_ST_CW_COMP_CERT: + *confunc = tls_construct_client_compressed_certificate; + *mt = SSL3_MT_COMPRESSED_CERTIFICATE; + break; +#endif + case TLS_ST_CW_KEY_EXCH: *confunc = tls_construct_client_key_exchange; *mt = SSL3_MT_CLIENT_KEY_EXCHANGE; @@ -980,6 +1014,7 @@ size_t ossl_statem_client_max_message_size(SSL_CONNECTION *s) case DTLS_ST_CR_HELLO_VERIFY_REQUEST: return HELLO_VERIFY_REQUEST_MAX_LENGTH; + case TLS_ST_CR_COMP_CERT: case TLS_ST_CR_CERT: return s->max_cert_list; @@ -1046,6 +1081,11 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL_CONNECTION *s, case TLS_ST_CR_CERT: return tls_process_server_certificate(s, pkt); +#ifndef OPENSSL_NO_COMP_ALG + case TLS_ST_CR_COMP_CERT: + return tls_process_server_compressed_certificate(s, pkt); +#endif + case TLS_ST_CR_CERT_VRFY: return tls_process_cert_verify(s, pkt); @@ -1097,6 +1137,7 @@ WORK_STATE ossl_statem_client_post_process_message(SSL_CONNECTION *s, return WORK_ERROR; case TLS_ST_CR_CERT: + case TLS_ST_CR_COMP_CERT: return tls_post_process_server_certificate(s, wst); case TLS_ST_CR_CERT_VRFY: @@ -1968,6 +2009,21 @@ WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s, return WORK_FINISHED_CONTINUE; } +#ifndef OPENSSL_NO_COMP_ALG +MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt) +{ + MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR; + PACKET tmppkt; + BUF_MEM *buf = BUF_MEM_new(); + + if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR) + ret = tls_process_server_certificate(sc, &tmppkt); + + BUF_MEM_free(buf); + return ret; +} +#endif + static int tls_process_ske_psk_preamble(SSL_CONNECTION *s, PACKET *pkt) { #ifndef OPENSSL_NO_PSK @@ -3531,6 +3587,7 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst) return WORK_FINISHED_CONTINUE; } else { s->s3.tmp.cert_req = 2; + s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none; if (!ssl3_digest_cached_records(s, 0)) { /* SSLfatal() already called */ return WORK_ERROR; @@ -3538,6 +3595,10 @@ WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst) } } + if (!SSL_CONNECTION_IS_TLS13(s) + || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0) + s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none; + if (s->post_handshake_auth == SSL_PHA_REQUESTED) return WORK_FINISHED_STOP; return WORK_FINISHED_CONTINUE; @@ -3587,6 +3648,97 @@ CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s, return CON_FUNC_SUCCESS; } +#ifndef OPENSSL_NO_COMP_ALG +CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc, + WPACKET *pkt) +{ + SSL *ssl = SSL_CONNECTION_GET_SSL(sc); + WPACKET tmppkt; + BUF_MEM *buf = NULL; + size_t length; + size_t max_length; + COMP_METHOD *method; + COMP_CTX *comp = NULL; + int comp_len; + int ret = 0; + int alg = sc->ext.compress_certificate_from_peer[0]; + + /* Note that sc->s3.tmp.cert_req == 2 is checked in write transition */ + + if ((buf = BUF_MEM_new()) == NULL || !WPACKET_init(&tmppkt, buf)) + goto err; + + /* Use the |tmppkt| for the to-be-compressed data */ + if (sc->pha_context == NULL) { + /* no context available, add 0-length context */ + if (!WPACKET_put_bytes_u8(&tmppkt, 0)) + goto err; + } else if (!WPACKET_sub_memcpy_u8(&tmppkt, sc->pha_context, sc->pha_context_len)) + goto err; + + if (!ssl3_output_cert_chain(sc, &tmppkt, sc->cert->key)) { + /* SSLfatal() already called */ + goto out; + } + + /* continue with the real |pkt| */ + if (!WPACKET_put_bytes_u16(pkt, alg) + || !WPACKET_get_total_written(&tmppkt, &length) + || !WPACKET_put_bytes_u24(pkt, length)) + goto err; + + switch (alg) { + case TLSEXT_comp_cert_zlib: + method = COMP_zlib(); + break; + case TLSEXT_comp_cert_brotli: + method = COMP_brotli_oneshot(); + break; + case TLSEXT_comp_cert_zstd: + method = COMP_zstd_oneshot(); + break; + default: + goto err; + } + max_length = ossl_calculate_comp_expansion(alg, length); + + if (!WPACKET_start_sub_packet_u24(pkt) + || !WPACKET_reserve_bytes(pkt, max_length, NULL) + || (comp = COMP_CTX_new(method)) == NULL) + goto err; + + comp_len = COMP_compress_block(comp, WPACKET_get_curr(pkt), max_length, + (unsigned char *)buf->data, length); + if (comp_len <= 0) + goto err; + + if (!WPACKET_allocate_bytes(pkt, comp_len, NULL) + || !WPACKET_close(pkt)) + goto err; + + if (SSL_IS_FIRST_HANDSHAKE(sc) + && (!ssl->method->ssl3_enc->change_cipher_state(sc, + SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE))) { + /* + * This is a fatal error, which leaves sc->enc_write_ctx in an + * inconsistent state and thus ssl3_send_alert may crash. + */ + SSLfatal(sc, SSL_AD_NO_ALERT, SSL_R_CANNOT_CHANGE_CIPHER); + goto out; + } + ret = 1; + goto out; + + err: + SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + out: + WPACKET_cleanup(&tmppkt); + BUF_MEM_free(buf); + COMP_CTX_free(comp); + return ret; +} +#endif + int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s) { const SSL_CERT_LOOKUP *clu; diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index 8b34c11048..69e900f90e 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -2466,3 +2466,76 @@ int tls13_restore_handshake_digest_for_pha(SSL_CONNECTION *s) } return 1; } + +#ifndef OPENSSL_NO_COMP_ALG +MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc, + PACKET *pkt, + PACKET *tmppkt, + BUF_MEM *buf) +{ + MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR; + int comp_alg; + COMP_METHOD *method = NULL; + COMP_CTX *comp = NULL; + size_t expected_length; + size_t comp_length; + int i; + int found = 0; + + if (buf == NULL) { + SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto err; + } + if (!PACKET_get_net_2(pkt, (unsigned int*)&comp_alg)) { + SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, ERR_R_INTERNAL_ERROR); + goto err; + } + /* If we have a prefs list, make sure the algorithm is in it */ + if (sc->cert_comp_prefs[0] != TLSEXT_comp_cert_none) { + for (i = 0; sc->cert_comp_prefs[i] != TLSEXT_comp_cert_none; i++) { + if (sc->cert_comp_prefs[i] == comp_alg) { + found = 1; + break; + } + } + if (!found) { + SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM); + goto err; + } + } + if (!ossl_comp_has_alg(comp_alg)) { + SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM); + goto err; + } + switch (comp_alg) { + case TLSEXT_comp_cert_zlib: + method = COMP_zlib(); + break; + case TLSEXT_comp_cert_brotli: + method = COMP_brotli_oneshot(); + break; + case TLSEXT_comp_cert_zstd: + method = COMP_zstd_oneshot(); + break; + default: + SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_COMPRESSION_ALGORITHM); + goto err; + } + + if (!PACKET_get_net_3_len(pkt, &expected_length) + || !PACKET_get_net_3_len(pkt, &comp_length) + || PACKET_remaining(pkt) != comp_length + || !BUF_MEM_grow(buf, expected_length) + || !PACKET_buf_init(tmppkt, (unsigned char *)buf->data, expected_length) + || (comp = COMP_CTX_new(method)) == NULL + || COMP_expand_block(comp, (unsigned char *)buf->data, expected_length, + (unsigned char*)PACKET_data(pkt), comp_length) != (int)expected_length) { + SSLfatal(sc, SSL_AD_BAD_CERTIFICATE, SSL_R_BAD_DECOMPRESSION); + goto err; + } + ret = MSG_PROCESS_CONTINUE_PROCESSING; + err: + COMP_CTX_free(comp); + return ret; +} +#endif diff --git a/ssl/statem/statem_local.h b/ssl/statem/statem_local.h index e5c6cfe535..75b7274c2e 100644 --- a/ssl/statem/statem_local.h +++ b/ssl/statem/statem_local.h @@ -129,6 +129,13 @@ __owur WORK_STATE tls_finish_handshake(SSL_CONNECTION *s, WORK_STATE wst, int clearbufs, int stop); __owur WORK_STATE dtls_wait_for_dry(SSL_CONNECTION *s); +#ifndef OPENSSL_NO_COMP_ALG +__owur MSG_PROCESS_RETURN tls13_process_compressed_certificate(SSL_CONNECTION *sc, + PACKET *pkt, + PACKET *tmppkt, + BUF_MEM *buf); +#endif + /* some client-only functions */ __owur CON_FUNC_RETURN tls_construct_client_hello(SSL_CONNECTION *s, WPACKET *pkt); @@ -149,6 +156,10 @@ __owur WORK_STATE tls_prepare_client_certificate(SSL_CONNECTION *s, WORK_STATE wst); __owur CON_FUNC_RETURN tls_construct_client_certificate(SSL_CONNECTION *s, WPACKET *pkt); +#ifndef OPENSSL_NO_COMP_ALG +__owur CON_FUNC_RETURN tls_construct_client_compressed_certificate(SSL_CONNECTION *sc, + WPACKET *pkt); +#endif __owur int ssl_do_client_cert_cb(SSL_CONNECTION *s, X509 **px509, EVP_PKEY **ppkey); __owur CON_FUNC_RETURN tls_construct_client_key_exchange(SSL_CONNECTION *s, @@ -163,6 +174,10 @@ __owur MSG_PROCESS_RETURN tls_process_server_certificate(SSL_CONNECTION *s, PACKET *pkt); __owur WORK_STATE tls_post_process_server_certificate(SSL_CONNECTION *s, WORK_STATE wst); +#ifndef OPENSSL_NO_COMP_ALG +__owur MSG_PROCESS_RETURN tls_process_server_compressed_certificate(SSL_CONNECTION *sc, + PACKET *pkt); +#endif __owur int ssl3_check_cert_and_algorithm(SSL_CONNECTION *s); #ifndef OPENSSL_NO_NEXTPROTONEG __owur CON_FUNC_RETURN tls_construct_next_proto(SSL_CONNECTION *s, WPACKET *pkt); @@ -183,6 +198,10 @@ __owur CON_FUNC_RETURN dtls_construct_hello_verify_request(SSL_CONNECTION *s, WPACKET *pkt); __owur CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt); +#ifndef OPENSSL_NO_COMP_ALG +__owur CON_FUNC_RETURN tls_construct_server_compressed_certificate(SSL_CONNECTION *sc, + WPACKET *pkt); +#endif __owur CON_FUNC_RETURN tls_construct_server_key_exchange(SSL_CONNECTION *s, WPACKET *pkt); __owur CON_FUNC_RETURN tls_construct_certificate_request(SSL_CONNECTION *s, @@ -191,6 +210,10 @@ __owur CON_FUNC_RETURN tls_construct_server_done(SSL_CONNECTION *s, WPACKET *pkt); __owur MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s, PACKET *pkt); +#ifndef OPENSSL_NO_COMP_ALG +__owur MSG_PROCESS_RETURN tls_process_client_compressed_certificate(SSL_CONNECTION *sc, + PACKET *pkt); +#endif __owur MSG_PROCESS_RETURN tls_process_client_key_exchange(SSL_CONNECTION *s, PACKET *pkt); __owur WORK_STATE tls_post_process_client_key_exchange(SSL_CONNECTION *s, diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index ab1574419e..0d68e4b1cf 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -26,6 +26,7 @@ #include <openssl/trace.h> #include <openssl/core_names.h> #include <openssl/asn1t.h> +#include <openssl/comp.h> #define TICKET_NONCE_SIZE 8 @@ -91,6 +92,13 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt) st->hand_state = TLS_ST_SR_CERT; return 1; } +#ifndef OPENSSL_NO_COMP_ALG + if (mt == SSL3_MT_COMPRESSED_CERTIFICATE + && s->ext.compress_certificate_sent) { + st->hand_state = TLS_ST_SR_COMP_CERT; + return 1; + } +#endif } else { if (mt == SSL3_MT_FINISHED) { st->hand_state = TLS_ST_SR_FINISHED; @@ -99,6 +107,7 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt) } break; + case TLS_ST_SR_COMP_CERT: case TLS_ST_SR_CERT: if (s->session->peer == NULL) { if (mt == SSL3_MT_FINISHED) { @@ -128,10 +137,18 @@ static int ossl_statem_server13_read_transition(SSL_CONNECTION *s, int mt) if (s->early_data_state == SSL_EARLY_DATA_READING) break; - if (mt == SSL3_MT_CERTIFICATE - && s->post_handshake_auth == SSL_PHA_REQUESTED) { - st->hand_state = TLS_ST_SR_CERT; - return 1; + if (s->post_handshake_auth == SSL_PHA_REQUESTED) { + if (mt == SSL3_MT_CERTIFICATE) { + st->hand_state = TLS_ST_SR_CERT; + return 1; + } +#ifndef OPENSSL_NO_COMP_ALG + if (mt == SSL3_MT_COMPRESSED_CERTIFICATE + && s->ext.compress_certificate_sent) { + st->hand_state = TLS_ST_SR_COMP_CERT; + return 1; + } +#endif } if (mt == SSL3_MT_KEY_UPDATE) { @@ -357,6 +374,27 @@ static int send_server_key_exchange(SSL_CONNECTION *s) } /* + * Used to determine if we shoud send a CompressedCertificate message + * + * Returns the algorithm to use, TLSEXT_comp_cert_none means no compression + */ +static int get_compressed_certificate_alg(SSL_CONNECTION *sc) +{ +#ifndef OPENSSL_NO_COMP_ALG + int *alg = sc->ext.compress_certificate_from_peer; + + if (sc->s3.tmp.cert == NULL) + return TLSEXT_comp_cert_none; + + for (; *alg != TLSEXT_comp_cert_none; alg++) { + if (sc->s3.tmp.cert->comp_cert[*alg] != NULL) + return *alg; + } +#endif + return TLSEXT_comp_cert_none; +} + +/* * Should we send a CertificateRequest message? * * Valid return values are: @@ -468,6 +506,8 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s) st->hand_state = TLS_ST_SW_FINISHED; else if (send_certificate_request(s)) st->hand_state = TLS_ST_SW_CERT_REQ; + else if (get_compressed_certificate_alg(s) != TLSEXT_comp_cert_none) + st->hand_state = TLS_ST_SW_COMP_CERT; else st->hand_state = TLS_ST_SW_CERT; @@ -477,11 +517,14 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s) if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) { s->post_handshake_auth = SSL_PHA_REQUESTED; st->hand_state = TLS_ST_OK; + } else if (get_compressed_certificate_alg(s) != TLSEXT_comp_cert_none) { + st->hand_state = TLS_ST_SW_COMP_CERT; } else { st->hand_state = TLS_ST_SW_CERT; } return WRITE_TRAN_CONTINUE; + case TLS_ST_SW_COMP_CERT: case TLS_ST_SW_CERT: st->hand_state = TLS_ST_SW_CERT_VRFY; return WRITE_TRAN_CONTINUE; @@ -975,6 +1018,18 @@ WORK_STATE ossl_statem_server_post_work(SSL_CONNECTION *s, WORK_STATE wst) if (s->post_handshake_auth == SSL_PHA_REQUEST_PENDING) { if (statem_flush(s) != 1) return WORK_MORE_A; + } else { + if (!SSL_CONNECTION_IS_TLS13(s) + || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0) + s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none; + } + break; + + case TLS_ST_SW_ENCRYPTED_EXTENSIONS: + if (!s->hit && !send_certificate_request(s)) { + if (!SSL_CONNECTION_IS_TLS13(s) + || (s->options & SSL_OP_NO_TX_CERTIFICATE_COMPRESSION) != 0) + s->ext.compress_certificate_from_peer[0] = TLSEXT_comp_cert_none; } break; @@ -1059,6 +1114,13 @@ int ossl_statem_server_construct_message(SSL_CONNECTION *s, *mt = SSL3_MT_CERTIFICATE; break; +#ifndef OPENSSL_NO_COMP_ALG + case TLS_ST_SW_COMP_CERT: + *confunc = tls_construct_server_compressed_certificate; + *mt = SSL3_MT_COMPRESSED_CERTIFICATE; + break; +#endif + case TLS_ST_SW_CERT_VRFY: *confunc = tls_construct_cert_verify; *mt = SSL3_MT_CERTIFICATE_VERIFY; @@ -1153,6 +1215,7 @@ size_t ossl_statem_server_max_message_size(SSL_CONNECTION *s) case TLS_ST_SR_END_OF_EARLY_DATA: return END_OF_EARLY_DATA_MAX_LENGTH; + case TLS_ST_SR_COMP_CERT: case TLS_ST_SR_CERT: return s->max_cert_list; @@ -1201,6 +1264,11 @@ MSG_PROCESS_RETURN ossl_statem_server_process_message(SSL_CONNECTION *s, case TLS_ST_SR_CERT: return tls_process_client_certificate(s, pkt); +#ifndef OPENSSL_NO_COMP_ALG + case TLS_ST_SR_COMP_CERT: + return tls_process_client_compressed_certificate(s, pkt); +#endif + case TLS_ST_SR_KEY_EXCH: return tls_process_client_key_exchange(s, pkt); @@ -3633,6 +3701,21 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL_CONNECTION *s, return ret; } +#ifndef OPENSSL_NO_COMP_ALG +MSG_PROCESS_RETURN tls_process_client_compressed_certificate(SSL_CONNECTION *sc, PACKET *pkt) +{ + MSG_PROCESS_RETURN ret = MSG_PROCESS_ERROR; + PACKET tmppkt; + BUF_MEM *buf = BUF_MEM_new(); + + if (tls13_process_compressed_certificate(sc, pkt, &tmppkt, buf) != MSG_PROCESS_ERROR) + ret = tls_process_client_certificate(sc, &tmppkt); + + BUF_MEM_free(buf); + return ret; +} +#endif + CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt) { CERT_PKEY *cpk = s->s3.tmp.cert; @@ -3658,6 +3741,32 @@ CON_FUNC_RETURN tls_construct_server_certificate(SSL_CONNECTION *s, WPACKET *pkt return CON_FUNC_SUCCESS; } +#ifndef OPENSSL_NO_COMP_ALG +CON_FUNC_RETURN tls_construct_server_compressed_certificate(SSL_CONNECTION *sc, WPACKET *pkt) +{ + int alg = get_compressed_certificate_alg(sc); + OSSL_COMP_CERT *cc = sc->s3.tmp.cert->comp_cert[alg]; + + if (!ossl_assert(cc != NULL)) { + SSLfatal(sc, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + /* + * Server can't compress on-demand + * Use pre-compressed certificate + */ + if (!WPACKET_put_bytes_u16(pkt, alg) + || !WPACKET_put_bytes_u24(pkt, cc->orig_len) + || !WPACKET_start_sub_packet_u24(pkt) + || !WPACKET_memcpy(pkt, cc->data, cc->len) + || !WPACKET_close(pkt)) + return 0; + + sc->s3.tmp.cert->cert_comp_used++; + return 1; +} +#endif + static int create_ticket_prequel(SSL_CONNECTION *s, WPACKET *pkt, uint32_t age_add, unsigned char *tick_nonce) { |