diff options
Diffstat (limited to 'deps/boringssl/src/ssl/handshake_server.cc')
-rw-r--r-- | deps/boringssl/src/ssl/handshake_server.cc | 209 |
1 files changed, 173 insertions, 36 deletions
diff --git a/deps/boringssl/src/ssl/handshake_server.cc b/deps/boringssl/src/ssl/handshake_server.cc index bc0a0d1..fdf9511 100644 --- a/deps/boringssl/src/ssl/handshake_server.cc +++ b/deps/boringssl/src/ssl/handshake_server.cc @@ -154,6 +154,8 @@ #include <openssl/bn.h> #include <openssl/bytestring.h> #include <openssl/cipher.h> +#include <openssl/curve25519.h> +#include <openssl/digest.h> #include <openssl/ec.h> #include <openssl/ecdsa.h> #include <openssl/err.h> @@ -502,6 +504,91 @@ static bool is_probably_jdk11_with_tls13(const SSL_CLIENT_HELLO *client_hello) { return true; } +static bool decrypt_ech(SSL_HANDSHAKE *hs, uint8_t *out_alert, + const SSL_CLIENT_HELLO *client_hello) { + SSL *const ssl = hs->ssl; + CBS body; + if (!ssl_client_hello_get_extension(client_hello, &body, + TLSEXT_TYPE_encrypted_client_hello)) { + return true; + } + uint8_t type; + if (!CBS_get_u8(&body, &type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return false; + } + if (type != ECH_CLIENT_OUTER) { + return true; + } + // This is a ClientHelloOuter ECH extension. Attempt to decrypt it. + uint8_t config_id; + uint16_t kdf_id, aead_id; + CBS enc, payload; + if (!CBS_get_u16(&body, &kdf_id) || // + !CBS_get_u16(&body, &aead_id) || // + !CBS_get_u8(&body, &config_id) || + !CBS_get_u16_length_prefixed(&body, &enc) || + !CBS_get_u16_length_prefixed(&body, &payload) || // + CBS_len(&body) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return false; + } + + { + MutexReadLock lock(&ssl->ctx->lock); + hs->ech_keys = UpRef(ssl->ctx->ech_keys); + } + + if (!hs->ech_keys) { + ssl->s3->ech_status = ssl_ech_rejected; + return true; + } + + for (const auto &config : hs->ech_keys->configs) { + hs->ech_hpke_ctx.Reset(); + if (config_id != config->ech_config().config_id || + !config->SetupContext(hs->ech_hpke_ctx.get(), kdf_id, aead_id, enc)) { + // Ignore the error and try another ECHConfig. + ERR_clear_error(); + continue; + } + Array<uint8_t> encoded_client_hello_inner; + bool is_decrypt_error; + if (!ssl_client_hello_decrypt(hs->ech_hpke_ctx.get(), + &encoded_client_hello_inner, + &is_decrypt_error, client_hello, payload)) { + if (is_decrypt_error) { + // Ignore the error and try another ECHConfig. + ERR_clear_error(); + continue; + } + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED); + return false; + } + + // Recover the ClientHelloInner from the EncodedClientHelloInner. + bssl::Array<uint8_t> client_hello_inner; + if (!ssl_decode_client_hello_inner(ssl, out_alert, &client_hello_inner, + encoded_client_hello_inner, + client_hello)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return false; + } + hs->ech_client_hello_buf = std::move(client_hello_inner); + hs->ech_config_id = config_id; + ssl->s3->ech_status = ssl_ech_accepted; + return true; + } + + // If we did not accept ECH, proceed with the ClientHelloOuter. Note this + // could be key mismatch or ECH GREASE, so we must complete the handshake + // as usual, except EncryptedExtensions will contain retry configs. + ssl->s3->ech_status = ssl_ech_rejected; + return true; +} + static bool extract_sni(SSL_HANDSHAKE *hs, uint8_t *out_alert, const SSL_CLIENT_HELLO *client_hello) { SSL *const ssl = hs->ssl; @@ -563,7 +650,7 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { } SSL_CLIENT_HELLO client_hello; - if (!ssl_client_hello_init(ssl, &client_hello, msg)) { + if (!ssl_client_hello_init(ssl, &client_hello, msg.body)) { OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); return ssl_hs_error; @@ -582,11 +669,36 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { } uint8_t alert = SSL_AD_DECODE_ERROR; + if (!decrypt_ech(hs, &alert, &client_hello)) { + ssl_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + // ECH may have changed which ClientHello we process. Update |msg| and + // |client_hello| in case. + if (!hs->GetClientHello(&msg, &client_hello)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return ssl_hs_error; + } + if (!extract_sni(hs, &alert, &client_hello)) { ssl_send_alert(ssl, SSL3_AL_FATAL, alert); return ssl_hs_error; } + hs->state = state12_read_client_hello_after_ech; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_read_client_hello_after_ech(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + + SSLMessage msg_unused; + SSL_CLIENT_HELLO client_hello; + if (!hs->GetClientHello(&msg_unused, &client_hello)) { + return ssl_hs_error; + } + // Run the early callback. if (ssl->ctx->select_certificate_cb != NULL) { switch (ssl->ctx->select_certificate_cb(&client_hello)) { @@ -614,6 +726,7 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { hs->apply_jdk11_workaround = true; } + uint8_t alert = SSL_AD_DECODE_ERROR; if (!negotiate_version(hs, &alert, &client_hello)) { ssl_send_alert(ssl, SSL3_AL_FATAL, alert); return ssl_hs_error; @@ -644,12 +757,6 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - if (hs->ech_present && hs->ech_is_inner_present) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); - return ssl_hs_error; - } - hs->state = state12_select_certificate; return ssl_hs_ok; } @@ -657,11 +764,6 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; - SSLMessage msg; - if (!ssl->method->get_message(ssl, &msg)) { - return ssl_hs_read_message; - } - // Call |cert_cb| to update server certificates if required. if (hs->config->cert->cert_cb != NULL) { int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg); @@ -701,10 +803,22 @@ static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) { return ssl_hs_ok; } + // It should not be possible to negotiate TLS 1.2 with ECH. The + // ClientHelloInner decoding function rejects ClientHellos which offer TLS 1.2 + // or below. + assert(ssl->s3->ech_status != ssl_ech_accepted); + + // TODO(davidben): Also compute hints for TLS 1.2. When doing so, update the + // check in bssl_shim.cc to test this. + if (hs->hints_requested) { + return ssl_hs_hints_ready; + } + ssl->s3->early_data_reason = ssl_early_data_protocol_version; + SSLMessage msg_unused; SSL_CLIENT_HELLO client_hello; - if (!ssl_client_hello_init(ssl, &client_hello, msg)) { + if (!hs->GetClientHello(&msg_unused, &client_hello)) { return ssl_hs_error; } @@ -743,10 +857,15 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { } SSL_CLIENT_HELLO client_hello; - if (!ssl_client_hello_init(ssl, &client_hello, msg)) { + if (!ssl_client_hello_init(ssl, &client_hello, msg.body)) { return ssl_hs_error; } + hs->session_id_len = client_hello.session_id_len; + // This is checked in |ssl_client_hello_init|. + assert(hs->session_id_len <= sizeof(hs->session_id)); + OPENSSL_memcpy(hs->session_id, client_hello.session_id, hs->session_id_len); + // Determine whether we are doing session resumption. UniquePtr<SSL_SESSION> session; bool tickets_supported = false, renew_ticket = false; @@ -778,16 +897,20 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { hs->ticket_expected = renew_ticket; ssl->session = std::move(session); ssl->s3->session_reused = true; + hs->can_release_private_key = true; } else { hs->ticket_expected = tickets_supported; - ssl_set_session(ssl, NULL); - if (!ssl_get_new_session(hs, 1 /* server */)) { + ssl_set_session(ssl, nullptr); + if (!ssl_get_new_session(hs)) { return ssl_hs_error; } - // Clear the session ID if we want the session to be single-use. - if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) { - hs->new_session->session_id_length = 0; + // Assign a session ID if not using session tickets. + if (!hs->ticket_expected && + (ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) { + hs->new_session->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; + RAND_bytes(hs->new_session->session_id, + hs->new_session->session_id_length); } } @@ -806,7 +929,7 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER); // Only request a certificate if Channel ID isn't negotiated. if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) && - ssl->s3->channel_id_valid) { + hs->channel_id_negotiated) { hs->cert_request = false; } // CertificateRequest may only be sent in certificate-based ciphers. @@ -850,8 +973,7 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { } static void copy_suffix(Span<uint8_t> out, Span<const uint8_t> in) { - out = out.subspan(out.size() - in.size()); - assert(out.size() == in.size()); + out = out.last(in.size()); OPENSSL_memcpy(out.data(), in.data(), in.size()); } @@ -860,9 +982,9 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { // We only accept ChannelIDs on connections with ECDHE in order to avoid a // known attack while we fix ChannelID itself. - if (ssl->s3->channel_id_valid && + if (hs->channel_id_negotiated && (hs->new_cipher->algorithm_mkey & SSL_kECDHE) == 0) { - ssl->s3->channel_id_valid = false; + hs->channel_id_negotiated = false; } // If this is a resumption and the original handshake didn't support @@ -870,7 +992,7 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { // session and so cannot resume with ChannelIDs. if (ssl->session != NULL && ssl->session->original_handshake_hash_len == 0) { - ssl->s3->channel_id_valid = false; + hs->channel_id_negotiated = false; } struct OPENSSL_timeval now; @@ -901,19 +1023,22 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { } } - const SSL_SESSION *session = hs->new_session.get(); + Span<const uint8_t> session_id; if (ssl->session != nullptr) { - session = ssl->session.get(); + // Echo the session ID from the ClientHello to indicate resumption. + session_id = MakeConstSpan(hs->session_id, hs->session_id_len); + } else { + session_id = MakeConstSpan(hs->new_session->session_id, + hs->new_session->session_id_length); } ScopedCBB cbb; - CBB body, session_id; + CBB body, session_id_bytes; if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_SERVER_HELLO) || !CBB_add_u16(&body, ssl->version) || !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) || - !CBB_add_u8_length_prefixed(&body, &session_id) || - !CBB_add_bytes(&session_id, session->session_id, - session->session_id_length) || + !CBB_add_u8_length_prefixed(&body, &session_id_bytes) || + !CBB_add_bytes(&session_id_bytes, session_id.data(), session_id.size()) || !CBB_add_u16(&body, SSL_CIPHER_get_protocol_id(hs->new_cipher)) || !CBB_add_u8(&body, 0 /* no compression */) || !ssl_add_serverhello_tlsext(hs, &body) || @@ -1083,6 +1208,7 @@ static enum ssl_hs_wait_t do_send_server_key_exchange(SSL_HANDSHAKE *hs) { } } + hs->can_release_private_key = true; if (!ssl_add_message_cbb(ssl, cbb.get())) { return ssl_hs_error; } @@ -1415,6 +1541,7 @@ static enum ssl_hs_wait_t do_read_client_key_exchange(SSL_HANDSHAKE *hs) { } hs->new_session->extended_master_secret = hs->extended_master_secret; CONSTTIME_DECLASSIFY(hs->new_session->secret, hs->new_session->secret_length); + hs->can_release_private_key = true; ssl->method->next_message(ssl); hs->state = state12_read_client_certificate_verify; @@ -1556,7 +1683,7 @@ static enum ssl_hs_wait_t do_read_next_proto(SSL_HANDSHAKE *hs) { static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; - if (!ssl->s3->channel_id_valid) { + if (!hs->channel_id_negotiated) { hs->state = state12_read_client_finished; return ssl_hs_ok; } @@ -1666,16 +1793,21 @@ static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) { ssl->ctx->x509_method->session_clear(hs->new_session.get()); } - if (ssl->session != NULL) { - ssl->s3->established_session = UpRef(ssl->session); - } else { + bool has_new_session = hs->new_session != nullptr; + if (has_new_session) { + assert(ssl->session == nullptr); ssl->s3->established_session = std::move(hs->new_session); ssl->s3->established_session->not_resumable = false; + } else { + assert(ssl->session != nullptr); + ssl->s3->established_session = UpRef(ssl->session); } hs->handshake_finalized = true; ssl->s3->initial_handshake_complete = true; - ssl_update_cache(hs, SSL_SESS_CACHE_SERVER); + if (has_new_session) { + ssl_update_cache(ssl); + } hs->state = state12_done; return ssl_hs_ok; @@ -1693,6 +1825,9 @@ enum ssl_hs_wait_t ssl_server_handshake(SSL_HANDSHAKE *hs) { case state12_read_client_hello: ret = do_read_client_hello(hs); break; + case state12_read_client_hello_after_ech: + ret = do_read_client_hello_after_ech(hs); + break; case state12_select_certificate: ret = do_select_certificate(hs); break; @@ -1773,6 +1908,8 @@ const char *ssl_server_handshake_state(SSL_HANDSHAKE *hs) { return "TLS server start_accept"; case state12_read_client_hello: return "TLS server read_client_hello"; + case state12_read_client_hello_after_ech: + return "TLS server read_client_hello_after_ech"; case state12_select_certificate: return "TLS server select_certificate"; case state12_tls13: |