From 56986f905f973d478b0ba44ccdc569be0074d5fb Mon Sep 17 00:00:00 2001 From: Matthew Braithwaite Date: Thu, 22 Mar 2018 11:48:33 -0700 Subject: [PATCH] Hand back ECDHE split handshakes after the first server message. This changes the contract for split handshakes such that on the receiving side, the connection is to be driven until it returns |SSL_ERROR_HANDBACK|, rather than until SSL_do_handshake() returns success. Change-Id: Idd1ebfbd943d88474d7c934f4c0ae757ff3c0f37 Reviewed-on: https://boringssl-review.googlesource.com/26864 Commit-Queue: Matt Braithwaite CQ-Verified: CQ bot account: commit-bot@chromium.org Reviewed-by: Adam Langley --- include/openssl/ssl.h | 12 +-- ssl/handoff.cc | 146 +++++++++++++++++++++--------- ssl/handshake.cc | 5 ++ ssl/handshake_server.cc | 190 ++++++++++++++++++---------------------- ssl/internal.h | 42 +++++++++ ssl/ssl_key_share.cc | 54 ++++++++++++ ssl/ssl_lib.cc | 3 + ssl/ssl_test.cc | 9 +- ssl/ssl_transcript.cc | 4 - ssl/test/bssl_shim.cc | 177 ++++++++++++++++++++++--------------- 10 files changed, 419 insertions(+), 223 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 8e7d5f529..2cc34cff1 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -535,6 +535,7 @@ OPENSSL_EXPORT int SSL_get_error(const SSL *ssl, int ret_code); #define SSL_ERROR_WANT_CERTIFICATE_VERIFY 16 #define SSL_ERROR_HANDOFF 17 +#define SSL_ERROR_HANDBACK 18 // SSL_set_mtu sets the |ssl|'s MTU in DTLS to |mtu|. It returns one on success // and zero on failure. @@ -3926,6 +3927,7 @@ OPENSSL_EXPORT void SSL_CTX_set_client_cert_cb( #define SSL_EARLY_DATA_REJECTED 11 #define SSL_CERTIFICATE_VERIFY 12 #define SSL_HANDOFF 13 +#define SSL_HANDBACK 14 // SSL_want returns one of the above values to determine what the most recent // operation on |ssl| was blocked on. Use |SSL_get_error| instead. @@ -4478,10 +4480,10 @@ OPENSSL_EXPORT bool SealRecord(SSL *ssl, Span out_prefix, // state of the connection. // // Elsewhere, a fresh |SSL| can be used with |SSL_apply_handoff| to continue -// the connection. The connection from the client is fed into this |SSL| until -// the handshake completes normally. At this point (and only at this point), -// |SSL_serialize_handback| can be called to serialize the result of the -// handshake. +// the connection. The connection from the client is fed into this |SSL|, and +// the handshake resumed. When the handshake stops again and |SSL_get_error| +// indicates |SSL_ERROR_HANDBACK|, |SSL_serialize_handback| should be called to +// serialize the state of the handshake again. // // Back at the first location, a fresh |SSL| can be used with // |SSL_apply_handback|. Then the client's connection can be processed mostly @@ -4489,7 +4491,7 @@ OPENSSL_EXPORT bool SealRecord(SSL *ssl, Span out_prefix, // // Lastly, when a connection is in the handoff state, whether or not // |SSL_serialize_handoff| is called, |SSL_decline_handoff| will move it back -// into a normal state where the connection can procede without impact. +// into a normal state where the connection can proceed without impact. // // WARNING: Currently only works with TLS 1.0–1.2. // WARNING: The serialisation formats are not yet stable: version skew may be diff --git a/ssl/handoff.cc b/ssl/handoff.cc index b19d44322..dd73c83ec 100644 --- a/ssl/handoff.cc +++ b/ssl/handoff.cc @@ -93,17 +93,17 @@ bool SSL_apply_handoff(SSL *ssl, Span handoff) { if (CBS_len(&transcript) != 0) { s3->hs->transcript.Update(transcript); s3->is_v2_hello = true; - ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */, transcript); } + ssl->handback = true; return true; } bool SSL_serialize_handback(const SSL *ssl, CBB *out) { if (!ssl->server || - !ssl->s3->initial_handshake_complete || - ssl->method->is_dtls || - ssl->version < TLS1_VERSION) { + (ssl->s3->hs->state != state12_finish_server_handshake && + ssl->s3->hs->state != state12_read_client_certificate) || + ssl->method->is_dtls || ssl->version < TLS1_VERSION) { return false; } @@ -115,14 +115,22 @@ bool SSL_serialize_handback(const SSL *ssl, CBB *out) { size_t iv_len = 0; const uint8_t *read_iv = nullptr, *write_iv = nullptr; - if (ssl->version == TLS1_VERSION && - SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) && - (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) || - !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) { - return false; + Span transcript; + if (ssl->s3->hs->state == state12_finish_server_handshake) { + if (ssl->version == TLS1_VERSION && + SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) && + (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) || + !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) { + return false; + } + } else { + transcript = s3->hs->transcript.buffer(); } - CBB seq; + // TODO(mab): make sure everything is serialized. + CBB seq, key_share; + SSL_SESSION *session = + s3->session_reused ? ssl->session : s3->hs->new_session.get(); if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) || !CBB_add_asn1_uint64(&seq, kHandbackVersion) || !CBB_add_asn1_uint64(&seq, ssl->version) || @@ -142,7 +150,7 @@ bool SSL_serialize_handback(const SSL *ssl, CBB *out) { !CBB_add_asn1_bool(&seq, s3->session_reused) || !CBB_add_asn1_bool(&seq, s3->send_connection_binding) || !CBB_add_asn1_bool(&seq, s3->tlsext_channel_id_valid) || - !ssl_session_serialize(s3->established_session.get(), &seq) || + !ssl_session_serialize(session, &seq) || !CBB_add_asn1_octet_string(&seq, s3->next_proto_negotiated.data(), s3->next_proto_negotiated.size()) || !CBB_add_asn1_octet_string(&seq, s3->alpn_selected.data(), @@ -158,11 +166,22 @@ bool SSL_serialize_handback(const SSL *ssl, CBB *out) { !CBB_add_asn1_bool(&seq, ssl->quiet_shutdown) || !CBB_add_asn1_bool(&seq, ssl->tlsext_channel_id_enabled) || !CBB_add_asn1_bool(&seq, ssl->retain_only_sha256_of_client_certs) || - !CBB_flush(out)) { + !CBB_add_asn1_bool(&seq, ssl->token_binding_negotiated) || + !CBB_add_asn1_uint64(&seq, ssl->negotiated_token_binding_param) || + !CBB_add_asn1_bool(&seq, s3->hs->next_proto_neg_seen) || + !CBB_add_asn1_bool(&seq, s3->hs->cert_request) || + !CBB_add_asn1_bool(&seq, s3->hs->extended_master_secret) || + !CBB_add_asn1_bool(&seq, s3->hs->ticket_expected) || + !CBB_add_asn1_uint64(&seq, SSL_CIPHER_get_id(s3->hs->new_cipher)) || + !CBB_add_asn1_octet_string(&seq, transcript.data(), transcript.size()) || + !CBB_add_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) { return false; } - - return true; + if (ssl->s3->hs->state == state12_read_client_certificate && + !s3->hs->key_share->Serialize(&key_share)) { + return false; + } + return CBB_flush(out); } bool SSL_apply_handback(SSL *ssl, Span handback) { @@ -173,11 +192,16 @@ bool SSL_apply_handback(SSL *ssl, Span handback) { SSL3_STATE *const s3 = ssl->s3; uint64_t handback_version, version, conf_max_version, conf_min_version, - max_send_fragment, options, mode, max_cert_list; + max_send_fragment, options, mode, max_cert_list, + negotiated_token_binding_param, cipher; + CBS seq, read_seq, write_seq, server_rand, client_rand, read_iv, write_iv, - next_proto, alpn, hostname, channel_id; - int session_reused, send_connection_binding, channel_id_valid, - quiet_shutdown, channel_id_enabled, retain_only_sha256; + next_proto, alpn, hostname, channel_id, transcript, key_share; + int session_reused, send_connection_binding, channel_id_valid, quiet_shutdown, + channel_id_enabled, retain_only_sha256, cert_request, + extended_master_secret, ticket_expected, token_binding_negotiated, + next_proto_neg_seen; + SSL_SESSION *session = nullptr; CBS handback_cbs(handback); if (!CBS_get_asn1(&handback_cbs, &seq, CBS_ASN1_SEQUENCE) || @@ -210,11 +234,19 @@ bool SSL_apply_handback(SSL *ssl, Span handback) { return false; } - s3->established_session = - SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool); + s3->hs = ssl_handshake_new(ssl); + if (session_reused) { + ssl->session = + SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool) + .release(); + session = ssl->session; + } else { + s3->hs->new_session = + SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool); + session = s3->hs->new_session.get(); + } - if (!s3->established_session || - !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) || + if (!session || !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) || !CBS_get_asn1(&seq, &alpn, CBS_ASN1_OCTETSTRING) || !CBS_get_asn1(&seq, &hostname, CBS_ASN1_OCTETSTRING) || !CBS_get_asn1(&seq, &channel_id, CBS_ASN1_OCTETSTRING) || @@ -226,7 +258,22 @@ bool SSL_apply_handback(SSL *ssl, Span handback) { !CBS_get_asn1_uint64(&seq, &max_cert_list) || !CBS_get_asn1_bool(&seq, &quiet_shutdown) || !CBS_get_asn1_bool(&seq, &channel_id_enabled) || - !CBS_get_asn1_bool(&seq, &retain_only_sha256)) { + !CBS_get_asn1_bool(&seq, &retain_only_sha256) || + !CBS_get_asn1_bool(&seq, &token_binding_negotiated) || + !CBS_get_asn1_uint64(&seq, &negotiated_token_binding_param) || + !CBS_get_asn1_bool(&seq, &next_proto_neg_seen) || + !CBS_get_asn1_bool(&seq, &cert_request) || + !CBS_get_asn1_bool(&seq, &extended_master_secret) || + !CBS_get_asn1_bool(&seq, &ticket_expected) || + !CBS_get_asn1_uint64(&seq, &cipher)) { + return false; + } + if ((s3->hs->new_cipher = + SSL_get_cipher_by_value(static_cast(cipher))) == nullptr) { + return false; + } + if (!CBS_get_asn1(&seq, &transcript, CBS_ASN1_OCTETSTRING) || + !CBS_get_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) { return false; } @@ -240,9 +287,9 @@ bool SSL_apply_handback(SSL *ssl, Span handback) { ssl->mode = mode; ssl->max_cert_list = max_cert_list; - s3->hs.reset(); s3->have_version = true; - s3->initial_handshake_complete = true; + s3->hs->state = CBS_len(&transcript) == 0 ? state12_finish_server_handshake + : state12_read_client_certificate; s3->session_reused = session_reused; s3->send_connection_binding = send_connection_binding; s3->tlsext_channel_id_valid = channel_id_valid; @@ -263,23 +310,44 @@ bool SSL_apply_handback(SSL *ssl, Span handback) { ssl->quiet_shutdown = quiet_shutdown; ssl->tlsext_channel_id_enabled = channel_id_enabled; ssl->retain_only_sha256_of_client_certs = retain_only_sha256; + ssl->token_binding_negotiated = token_binding_negotiated; + ssl->negotiated_token_binding_param = + static_cast(negotiated_token_binding_param); + s3->hs->next_proto_neg_seen = next_proto_neg_seen; + s3->hs->wait = ssl_hs_flush; + s3->hs->extended_master_secret = extended_master_secret; + s3->hs->ticket_expected = ticket_expected; + s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version); + s3->hs->cert_request = cert_request; - Array key_block; - if (!tls1_configure_aead(ssl, evp_aead_open, &key_block, - s3->established_session->cipher, read_iv) || - !tls1_configure_aead(ssl, evp_aead_seal, &key_block, - s3->established_session->cipher, write_iv)) { - return false; + if (s3->hs->state == state12_finish_server_handshake) { + Array key_block; + if (!tls1_configure_aead(ssl, evp_aead_open, &key_block, session->cipher, + read_iv) || + !tls1_configure_aead(ssl, evp_aead_seal, &key_block, session->cipher, + write_iv)) { + return false; + } + + if (!CBS_copy_bytes(&read_seq, s3->read_sequence, + sizeof(s3->read_sequence)) || + !CBS_copy_bytes(&write_seq, s3->write_sequence, + sizeof(s3->write_sequence))) { + return false; + } + } else { + if (!s3->hs->transcript.Init() || + !s3->hs->transcript.InitHash(ssl_protocol_version(ssl), + s3->hs->new_cipher) || + !s3->hs->transcript.Update(transcript)) { + return false; + } + if ((s3->hs->key_share = SSLKeyShare::Create(&key_share)) == nullptr) { + return false; + } } - if (!CBS_copy_bytes(&read_seq, s3->read_sequence, - sizeof(s3->read_sequence)) || - !CBS_copy_bytes(&write_seq, s3->write_sequence, - sizeof(s3->write_sequence))) { - return false; - } - - return true; + return CBS_len(&seq) == 0; } } // namespace bssl diff --git a/ssl/handshake.cc b/ssl/handshake.cc index 64324245d..00a2cc5d7 100644 --- a/ssl/handshake.cc +++ b/ssl/handshake.cc @@ -565,6 +565,11 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { hs->wait = ssl_hs_ok; return -1; + case ssl_hs_handback: + ssl->s3->rwstate = SSL_HANDBACK; + hs->wait = ssl_hs_handback; + return -1; + case ssl_hs_x509_lookup: ssl->s3->rwstate = SSL_X509_LOOKUP; hs->wait = ssl_hs_ok; diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc index fa8a24104..5f2f41fd8 100644 --- a/ssl/handshake_server.cc +++ b/ssl/handshake_server.cc @@ -172,30 +172,6 @@ namespace bssl { -enum ssl_server_hs_state_t { - state_start_accept = 0, - state_read_client_hello, - state_select_certificate, - state_tls13, - state_select_parameters, - state_send_server_hello, - state_send_server_certificate, - state_send_server_key_exchange, - state_send_server_hello_done, - state_read_client_certificate, - state_verify_client_certificate, - state_read_client_key_exchange, - state_read_client_certificate_verify, - state_read_change_cipher_spec, - state_process_change_cipher_spec, - state_read_next_proto, - state_read_channel_id, - state_read_client_finished, - state_send_server_finished, - state_finish_server_handshake, - state_done, -}; - int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello, uint16_t id) { CBS cipher_suites; @@ -425,7 +401,7 @@ static const SSL_CIPHER *ssl3_choose_cipher( static enum ssl_hs_wait_t do_start_accept(SSL_HANDSHAKE *hs) { ssl_do_info_callback(hs->ssl, SSL_CB_HANDSHAKE_START, 1); - hs->state = state_read_client_hello; + hs->state = state12_read_client_hello; return ssl_hs_ok; } @@ -505,7 +481,7 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - hs->state = state_select_certificate; + hs->state = state12_select_certificate; return ssl_hs_ok; } @@ -536,7 +512,7 @@ static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) { if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) { // Jump to the TLS 1.3 state machine. - hs->state = state_tls13; + hs->state = state12_tls13; return ssl_hs_ok; } @@ -555,14 +531,14 @@ static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - hs->state = state_select_parameters; + hs->state = state12_select_parameters; return ssl_hs_ok; } static enum ssl_hs_wait_t do_tls13(SSL_HANDSHAKE *hs) { enum ssl_hs_wait_t wait = tls13_server_handshake(hs); if (wait == ssl_hs_ok) { - hs->state = state_finish_server_handshake; + hs->state = state12_finish_server_handshake; return ssl_hs_ok; } @@ -672,14 +648,15 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - // Release the handshake buffer if client authentication isn't required. - if (!hs->cert_request) { + // Handback includes the whole handshake transcript, so we cannot free the + // transcript buffer in the handback case. + if (!hs->cert_request && !hs->ssl->handback) { hs->transcript.FreeBuffer(); } ssl->method->next_message(ssl); - hs->state = state_send_server_hello; + hs->state = state12_send_server_hello; return ssl_hs_ok; } @@ -744,9 +721,9 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { } if (ssl->session != NULL) { - hs->state = state_send_server_finished; + hs->state = state12_send_server_finished; } else { - hs->state = state_send_server_certificate; + hs->state = state12_send_server_certificate; } return ssl_hs_ok; } @@ -835,7 +812,7 @@ static enum ssl_hs_wait_t do_send_server_certificate(SSL_HANDSHAKE *hs) { } } - hs->state = state_send_server_key_exchange; + hs->state = state12_send_server_key_exchange; return ssl_hs_ok; } @@ -843,7 +820,7 @@ static enum ssl_hs_wait_t do_send_server_key_exchange(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; if (hs->server_params.size() == 0) { - hs->state = state_send_server_hello_done; + hs->state = state12_send_server_hello_done; return ssl_hs_ok; } @@ -907,7 +884,7 @@ static enum ssl_hs_wait_t do_send_server_key_exchange(SSL_HANDSHAKE *hs) { hs->server_params.Reset(); - hs->state = state_send_server_hello_done; + hs->state = state12_send_server_hello_done; return ssl_hs_ok; } @@ -942,15 +919,18 @@ static enum ssl_hs_wait_t do_send_server_hello_done(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - hs->state = state_read_client_certificate; + hs->state = state12_read_client_certificate; return ssl_hs_flush; } static enum ssl_hs_wait_t do_read_client_certificate(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; + if (ssl->handback && hs->new_cipher->algorithm_mkey == SSL_kECDHE) { + return ssl_hs_handback; + } if (!hs->cert_request) { - hs->state = state_verify_client_certificate; + hs->state = state12_verify_client_certificate; return ssl_hs_ok; } @@ -973,7 +953,7 @@ static enum ssl_hs_wait_t do_read_client_certificate(SSL_HANDSHAKE *hs) { // OpenSSL returns X509_V_OK when no certificates are received. This is // classed by them as a bug, but it's assumed by at least NGINX. hs->new_session->verify_result = X509_V_OK; - hs->state = state_verify_client_certificate; + hs->state = state12_verify_client_certificate; return ssl_hs_ok; } @@ -1035,7 +1015,7 @@ static enum ssl_hs_wait_t do_read_client_certificate(SSL_HANDSHAKE *hs) { } ssl->method->next_message(ssl); - hs->state = state_verify_client_certificate; + hs->state = state12_verify_client_certificate; return ssl_hs_ok; } @@ -1051,7 +1031,7 @@ static enum ssl_hs_wait_t do_verify_client_certificate(SSL_HANDSHAKE *hs) { } } - hs->state = state_read_client_key_exchange; + hs->state = state12_read_client_key_exchange; return ssl_hs_ok; } @@ -1262,7 +1242,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; ssl->method->next_message(ssl); - hs->state = state_read_client_certificate_verify; + hs->state = state12_read_client_certificate_verify; return ssl_hs_ok; } @@ -1273,7 +1253,7 @@ static enum ssl_hs_wait_t do_read_client_certificate_verify(SSL_HANDSHAKE *hs) { // CertificateVerify is required if and only if there's a client certificate. if (!hs->peer_pubkey) { hs->transcript.FreeBuffer(); - hs->state = state_read_change_cipher_spec; + hs->state = state12_read_change_cipher_spec; return ssl_hs_ok; } @@ -1358,12 +1338,12 @@ static enum ssl_hs_wait_t do_read_client_certificate_verify(SSL_HANDSHAKE *hs) { } ssl->method->next_message(ssl); - hs->state = state_read_change_cipher_spec; + hs->state = state12_read_change_cipher_spec; return ssl_hs_ok; } static enum ssl_hs_wait_t do_read_change_cipher_spec(SSL_HANDSHAKE *hs) { - hs->state = state_process_change_cipher_spec; + hs->state = state12_process_change_cipher_spec; return ssl_hs_read_change_cipher_spec; } @@ -1372,7 +1352,7 @@ static enum ssl_hs_wait_t do_process_change_cipher_spec(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - hs->state = state_read_next_proto; + hs->state = state12_read_next_proto; return ssl_hs_ok; } @@ -1380,7 +1360,7 @@ static enum ssl_hs_wait_t do_read_next_proto(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; if (!hs->next_proto_neg_seen) { - hs->state = state_read_channel_id; + hs->state = state12_read_channel_id; return ssl_hs_ok; } @@ -1408,7 +1388,7 @@ static enum ssl_hs_wait_t do_read_next_proto(SSL_HANDSHAKE *hs) { } ssl->method->next_message(ssl); - hs->state = state_read_channel_id; + hs->state = state12_read_channel_id; return ssl_hs_ok; } @@ -1416,7 +1396,7 @@ static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; if (!ssl->s3->tlsext_channel_id_valid) { - hs->state = state_read_client_finished; + hs->state = state12_read_client_finished; return ssl_hs_ok; } @@ -1432,7 +1412,7 @@ static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) { } ssl->method->next_message(ssl); - hs->state = state_read_client_finished; + hs->state = state12_read_client_finished; return ssl_hs_ok; } @@ -1444,9 +1424,9 @@ static enum ssl_hs_wait_t do_read_client_finished(SSL_HANDSHAKE *hs) { } if (ssl->session != NULL) { - hs->state = state_finish_server_handshake; + hs->state = state12_finish_server_handshake; } else { - hs->state = state_send_server_finished; + hs->state = state12_send_server_finished; } // If this is a full handshake with ChannelID then record the handshake @@ -1501,9 +1481,9 @@ static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) { } if (ssl->session != NULL) { - hs->state = state_read_change_cipher_spec; + hs->state = state12_read_change_cipher_spec; } else { - hs->state = state_finish_server_handshake; + hs->state = state12_finish_server_handshake; } return ssl_hs_flush; } @@ -1511,6 +1491,10 @@ static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) { static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; + if (ssl->handback) { + return ssl_hs_handback; + } + ssl->method->on_handshake_complete(ssl); // If we aren't retaining peer certificates then we can discard it now. @@ -1532,77 +1516,77 @@ static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) { ssl->s3->initial_handshake_complete = true; ssl_update_cache(hs, SSL_SESS_CACHE_SERVER); - hs->state = state_done; + hs->state = state12_done; return ssl_hs_ok; } enum ssl_hs_wait_t ssl_server_handshake(SSL_HANDSHAKE *hs) { - while (hs->state != state_done) { + while (hs->state != state12_done) { enum ssl_hs_wait_t ret = ssl_hs_error; - enum ssl_server_hs_state_t state = - static_cast(hs->state); + enum tls12_server_hs_state_t state = + static_cast(hs->state); switch (state) { - case state_start_accept: + case state12_start_accept: ret = do_start_accept(hs); break; - case state_read_client_hello: + case state12_read_client_hello: ret = do_read_client_hello(hs); break; - case state_select_certificate: + case state12_select_certificate: ret = do_select_certificate(hs); break; - case state_tls13: + case state12_tls13: ret = do_tls13(hs); break; - case state_select_parameters: + case state12_select_parameters: ret = do_select_parameters(hs); break; - case state_send_server_hello: + case state12_send_server_hello: ret = do_send_server_hello(hs); break; - case state_send_server_certificate: + case state12_send_server_certificate: ret = do_send_server_certificate(hs); break; - case state_send_server_key_exchange: + case state12_send_server_key_exchange: ret = do_send_server_key_exchange(hs); break; - case state_send_server_hello_done: + case state12_send_server_hello_done: ret = do_send_server_hello_done(hs); break; - case state_read_client_certificate: + case state12_read_client_certificate: ret = do_read_client_certificate(hs); break; - case state_verify_client_certificate: + case state12_verify_client_certificate: ret = do_verify_client_certificate(hs); break; - case state_read_client_key_exchange: + case state12_read_client_key_exchange: ret = do_read_client_key_exchange(hs); break; - case state_read_client_certificate_verify: + case state12_read_client_certificate_verify: ret = do_read_client_certificate_verify(hs); break; - case state_read_change_cipher_spec: + case state12_read_change_cipher_spec: ret = do_read_change_cipher_spec(hs); break; - case state_process_change_cipher_spec: + case state12_process_change_cipher_spec: ret = do_process_change_cipher_spec(hs); break; - case state_read_next_proto: + case state12_read_next_proto: ret = do_read_next_proto(hs); break; - case state_read_channel_id: + case state12_read_channel_id: ret = do_read_channel_id(hs); break; - case state_read_client_finished: + case state12_read_client_finished: ret = do_read_client_finished(hs); break; - case state_send_server_finished: + case state12_send_server_finished: ret = do_send_server_finished(hs); break; - case state_finish_server_handshake: + case state12_finish_server_handshake: ret = do_finish_server_handshake(hs); break; - case state_done: + case state12_done: ret = ssl_hs_ok; break; } @@ -1621,50 +1605,50 @@ enum ssl_hs_wait_t ssl_server_handshake(SSL_HANDSHAKE *hs) { } const char *ssl_server_handshake_state(SSL_HANDSHAKE *hs) { - enum ssl_server_hs_state_t state = - static_cast(hs->state); + enum tls12_server_hs_state_t state = + static_cast(hs->state); switch (state) { - case state_start_accept: + case state12_start_accept: return "TLS server start_accept"; - case state_read_client_hello: + case state12_read_client_hello: return "TLS server read_client_hello"; - case state_select_certificate: + case state12_select_certificate: return "TLS server select_certificate"; - case state_tls13: + case state12_tls13: return tls13_server_handshake_state(hs); - case state_select_parameters: + case state12_select_parameters: return "TLS server select_parameters"; - case state_send_server_hello: + case state12_send_server_hello: return "TLS server send_server_hello"; - case state_send_server_certificate: + case state12_send_server_certificate: return "TLS server send_server_certificate"; - case state_send_server_key_exchange: + case state12_send_server_key_exchange: return "TLS server send_server_key_exchange"; - case state_send_server_hello_done: + case state12_send_server_hello_done: return "TLS server send_server_hello_done"; - case state_read_client_certificate: + case state12_read_client_certificate: return "TLS server read_client_certificate"; - case state_verify_client_certificate: + case state12_verify_client_certificate: return "TLS server verify_client_certificate"; - case state_read_client_key_exchange: + case state12_read_client_key_exchange: return "TLS server read_client_key_exchange"; - case state_read_client_certificate_verify: + case state12_read_client_certificate_verify: return "TLS server read_client_certificate_verify"; - case state_read_change_cipher_spec: + case state12_read_change_cipher_spec: return "TLS server read_change_cipher_spec"; - case state_process_change_cipher_spec: + case state12_process_change_cipher_spec: return "TLS server process_change_cipher_spec"; - case state_read_next_proto: + case state12_read_next_proto: return "TLS server read_next_proto"; - case state_read_channel_id: + case state12_read_channel_id: return "TLS server read_channel_id"; - case state_read_client_finished: + case state12_read_client_finished: return "TLS server read_client_finished"; - case state_send_server_finished: + case state12_send_server_finished: return "TLS server send_server_finished"; - case state_finish_server_handshake: + case state12_finish_server_handshake: return "TLS server finish_server_handshake"; - case state_done: + case state12_done: return "TLS server done"; } diff --git a/ssl/internal.h b/ssl/internal.h index d13d5f2c9..10716ea44 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -929,6 +929,10 @@ class SSLKeyShare { // nullptr on error. static UniquePtr Create(uint16_t group_id); + // Create deserializes an SSLKeyShare instance previously serialized by + // |Serialize|. + static UniquePtr Create(CBS *in); + // GroupID returns the group ID. virtual uint16_t GroupID() const PURE_VIRTUAL; @@ -952,6 +956,14 @@ class SSLKeyShare { // send to the peer. virtual bool Finish(Array *out_secret, uint8_t *out_alert, Span peer_key) PURE_VIRTUAL; + + // Serialize writes the state of the key exchange to |out|, returning true if + // successful and false otherwise. + virtual bool Serialize(CBB *out) { return false; } + + // Deserialize initializes the state of the key exchange from |in|, returning + // true if successful and false otherwise. It is called by |Create|. + virtual bool Deserialize(CBS *in) { return false; } }; // ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it @@ -1258,6 +1270,7 @@ enum ssl_hs_wait_t { ssl_hs_flush, ssl_hs_certificate_selection_pending, ssl_hs_handoff, + ssl_hs_handback, ssl_hs_x509_lookup, ssl_hs_channel_id_lookup, ssl_hs_private_key_operation, @@ -1280,6 +1293,30 @@ enum ssl_grease_index_t { ssl_grease_last_index = ssl_grease_ticket_extension, }; +enum tls12_server_hs_state_t { + state12_start_accept = 0, + state12_read_client_hello, + state12_select_certificate, + state12_tls13, + state12_select_parameters, + state12_send_server_hello, + state12_send_server_certificate, + state12_send_server_key_exchange, + state12_send_server_hello_done, + state12_read_client_certificate, + state12_verify_client_certificate, + state12_read_client_key_exchange, + state12_read_client_certificate_verify, + state12_read_change_cipher_spec, + state12_process_change_cipher_spec, + state12_read_next_proto, + state12_read_channel_id, + state12_read_client_finished, + state12_send_server_finished, + state12_finish_server_handshake, + state12_done, +}; + struct SSL_HANDSHAKE { explicit SSL_HANDSHAKE(SSL *ssl); ~SSL_HANDSHAKE(); @@ -2676,6 +2713,11 @@ struct SSLConnection { // element of the same name and may be cleared if the handoff is declined. bool handoff:1; + // handback indicates that a server should pause the handshake after + // finishing operations that require private key material, in such a way that + // |SSL_get_error| returns |SSL_HANDBACK|. It is set by |SSL_apply_handoff|. + bool handback : 1; + // did_dummy_pq_padding is only valid for a client. In that context, it is // true iff the client observed the server echoing a dummy PQ padding // extension. diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc index 4d76bb233..2a076c339 100644 --- a/ssl/ssl_key_share.cc +++ b/ssl/ssl_key_share.cc @@ -124,6 +124,32 @@ class ECKeyShare : public SSLKeyShare { return true; } + bool Serialize(CBB *out) override { + assert(private_key_); + CBB cbb; + UniquePtr group(EC_GROUP_new_by_curve_name(nid_)); + // Padding is added to avoid leaking the length. + size_t len = BN_num_bytes(EC_GROUP_get0_order(group.get())); + if (!CBB_add_asn1_uint64(out, group_id_) || + !CBB_add_asn1(out, &cbb, CBS_ASN1_OCTETSTRING) || + !BN_bn2cbb_padded(&cbb, len, private_key_.get()) || + !CBB_flush(out)) { + return false; + } + return true; + } + + bool Deserialize(CBS *in) override { + assert(!private_key_); + CBS private_key; + if (!CBS_get_asn1(in, &private_key, CBS_ASN1_OCTETSTRING)) { + return false; + } + private_key_.reset(BN_bin2bn(CBS_data(&private_key), + CBS_len(&private_key), nullptr)); + return private_key_ != nullptr; + } + private: UniquePtr private_key_; int nid_; @@ -166,6 +192,21 @@ class X25519KeyShare : public SSLKeyShare { return true; } + bool Serialize(CBB *out) override { + return (CBB_add_asn1_uint64(out, GroupID()) && + CBB_add_asn1_octet_string(out, private_key_, sizeof(private_key_))); + } + + bool Deserialize(CBS *in) override { + CBS key; + if (!CBS_get_asn1(in, &key, CBS_ASN1_OCTETSTRING) || + CBS_len(&key) != sizeof(private_key_) || + !CBS_copy_bytes(&key, private_key_, sizeof(private_key_))) { + return false; + } + return true; + } + private: uint8_t private_key_[32]; }; @@ -205,6 +246,19 @@ UniquePtr SSLKeyShare::Create(uint16_t group_id) { } } +UniquePtr SSLKeyShare::Create(CBS *in) { + uint64_t group; + if (!CBS_get_asn1_uint64(in, &group)) { + return nullptr; + } + UniquePtr key_share = Create(static_cast(group)); + if (!key_share->Deserialize(in)) { + return nullptr; + } + return key_share; +} + + bool SSLKeyShare::Accept(CBB *out_public_key, Array *out_secret, uint8_t *out_alert, Span peer_key) { *out_alert = SSL_AD_INTERNAL_ERROR; diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index ef7983119..631250426 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc @@ -1274,6 +1274,9 @@ int SSL_get_error(const SSL *ssl, int ret_code) { case SSL_HANDOFF: return SSL_ERROR_HANDOFF; + case SSL_HANDBACK: + return SSL_ERROR_HANDBACK; + case SSL_READING: { BIO *bio = SSL_get_rbio(ssl); if (BIO_should_read(bio)) { diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 9f77f1443..12f044c84 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -3949,9 +3949,13 @@ TEST(SSLTest, Handoff) { int handshake_ret = SSL_do_handshake(handshaker.get()); int handshake_err = SSL_get_error(handshaker.get(), handshake_ret); - ASSERT_EQ(handshake_err, SSL_ERROR_WANT_READ); + ASSERT_EQ(handshake_err, SSL_ERROR_HANDBACK); - ASSERT_TRUE(CompleteHandshakes(client.get(), handshaker.get())); + // Double-check that additional calls to |SSL_do_handshake| continue + // to get |SSL_ERRROR_HANDBACK|. + handshake_ret = SSL_do_handshake(handshaker.get()); + handshake_err = SSL_get_error(handshaker.get(), handshake_ret); + ASSERT_EQ(handshake_err, SSL_ERROR_HANDBACK); ScopedCBB cbb_handback; Array handback; @@ -3963,6 +3967,7 @@ TEST(SSLTest, Handoff) { ASSERT_TRUE(SSL_apply_handback(server2.get(), handback)); MoveBIOs(server2.get(), handshaker.get()); + ASSERT_TRUE(CompleteHandshakes(client.get(), server2.get())); uint8_t byte = 42; EXPECT_EQ(SSL_write(client.get(), &byte, 1), 1); diff --git a/ssl/ssl_transcript.cc b/ssl/ssl_transcript.cc index 2033dfd47..345f9d311 100644 --- a/ssl/ssl_transcript.cc +++ b/ssl/ssl_transcript.cc @@ -368,10 +368,6 @@ bool SSLTranscript::GetFinishedMAC(uint8_t *out, size_t *out_len, return true; } - // At this point, the handshake should have released the handshake buffer on - // its own. - assert(!buffer_); - static const char kClientLabel[] = "client finished"; static const char kServerLabel[] = "server finished"; auto label = from_server diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index ae26deda1..107de52c6 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc @@ -1937,40 +1937,34 @@ static bool WriteSettings(int i, const TestConfig *config, return fwrite(settings, settings_len, 1, file.get()) == 1; } -static bool DoExchange(bssl::UniquePtr *out_session, - bssl::UniquePtr *ssl_uniqueptr, - const TestConfig *config, bool is_resume, bool is_retry); - -// DoConnection tests an SSL connection against the peer. On success, it returns -// true and sets |*out_session| to the negotiated SSL session. If the test is a -// resumption attempt, |is_resume| is true and |session| is the session from the -// previous exchange. -static bool DoConnection(bssl::UniquePtr *out_session, - SSL_CTX *ssl_ctx, const TestConfig *config, - const TestConfig *retry_config, bool is_resume, - SSL_SESSION *session) { +static bssl::UniquePtr NewSSL(SSL_CTX *ssl_ctx, const TestConfig *config, + SSL_SESSION *session, bool is_resume, + std::unique_ptr test_state) { bssl::UniquePtr ssl(SSL_new(ssl_ctx)); if (!ssl) { - return false; + return nullptr; } - if (!SetTestConfig(ssl.get(), config) || - !SetTestState(ssl.get(), std::unique_ptr(new TestState))) { - return false; + if (!SetTestConfig(ssl.get(), config)) { + return nullptr; + } + if (test_state != nullptr) { + if (!SetTestState(ssl.get(), std::move(test_state))) { + return nullptr; + } + GetTestState(ssl.get())->is_resume = is_resume; } - - GetTestState(ssl.get())->is_resume = is_resume; if (config->fallback_scsv && !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) { - return false; + return nullptr; } // Install the certificate synchronously if nothing else will handle it. if (!config->use_early_callback && !config->use_old_client_cert_callback && !config->async && !InstallCertificate(ssl.get())) { - return false; + return nullptr; } if (!config->use_old_client_cert_callback) { SSL_set_cert_cb(ssl.get(), CertCallback, nullptr); @@ -2027,7 +2021,7 @@ static bool DoConnection(bssl::UniquePtr *out_session, // The async case will be supplied by |ChannelIdCallback|. bssl::UniquePtr pkey = LoadPrivateKey(config->send_channel_id); if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) { - return false; + return nullptr; } } } @@ -2039,13 +2033,13 @@ static bool DoConnection(bssl::UniquePtr *out_session, } if (!config->host_name.empty() && !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) { - return false; + return nullptr; } if (!config->advertise_alpn.empty() && SSL_set_alpn_protos(ssl.get(), (const uint8_t *)config->advertise_alpn.data(), config->advertise_alpn.size()) != 0) { - return false; + return nullptr; } if (!config->psk.empty()) { SSL_set_psk_client_callback(ssl.get(), PskClientCallback); @@ -2053,11 +2047,11 @@ static bool DoConnection(bssl::UniquePtr *out_session, } if (!config->psk_identity.empty() && !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) { - return false; + return nullptr; } if (!config->srtp_profiles.empty() && !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) { - return false; + return nullptr; } if (config->enable_ocsp_stapling) { SSL_enable_ocsp_stapling(ssl.get()); @@ -2067,11 +2061,11 @@ static bool DoConnection(bssl::UniquePtr *out_session, } if (config->min_version != 0 && !SSL_set_min_proto_version(ssl.get(), (uint16_t)config->min_version)) { - return false; + return nullptr; } if (config->max_version != 0 && !SSL_set_max_proto_version(ssl.get(), (uint16_t)config->max_version)) { - return false; + return nullptr; } if (config->mtu != 0) { SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU); @@ -2095,7 +2089,7 @@ static bool DoConnection(bssl::UniquePtr *out_session, if (config->p384_only) { int nid = NID_secp384r1; if (!SSL_set1_curves(ssl.get(), &nid, 1)) { - return false; + return nullptr; } } if (config->enable_all_curves) { @@ -2105,7 +2099,7 @@ static bool DoConnection(bssl::UniquePtr *out_session, }; if (!SSL_set1_curves(ssl.get(), kAllCurves, OPENSSL_ARRAY_SIZE(kAllCurves))) { - return false; + return nullptr; } } if (config->initial_timeout_duration_ms > 0) { @@ -2123,7 +2117,7 @@ static bool DoConnection(bssl::UniquePtr *out_session, } if (config->dummy_pq_padding_len > 0 && !SSL_set_dummy_pq_padding_size(ssl.get(), config->dummy_pq_padding_len)) { - return false; + return nullptr; } if (!config->quic_transport_params.empty()) { if (!SSL_set_quic_transport_params( @@ -2131,10 +2125,55 @@ static bool DoConnection(bssl::UniquePtr *out_session, reinterpret_cast( config->quic_transport_params.data()), config->quic_transport_params.size())) { - return false; + return nullptr; } } + if (session != NULL) { + if (!config->is_server) { + if (SSL_set_session(ssl.get(), session) != 1) { + return nullptr; + } + } else if (config->async) { + // The internal session cache is disabled, so install the session + // manually. + SSL_SESSION_up_ref(session); + GetTestState(ssl.get())->pending_session.reset(session); + } + } + + if (SSL_get_current_cipher(ssl.get()) != nullptr) { + fprintf(stderr, "non-null cipher before handshake\n"); + return nullptr; + } + + return ssl; +} + +static bool DoExchange(bssl::UniquePtr *out_session, + bssl::UniquePtr *ssl_uniqueptr, + const TestConfig *config, bool is_resume, bool is_retry); + +// DoConnection tests an SSL connection against the peer. On success, it returns +// true and sets |*out_session| to the negotiated SSL session. If the test is a +// resumption attempt, |is_resume| is true and |session| is the session from the +// previous exchange. +static bool DoConnection(bssl::UniquePtr *out_session, + SSL_CTX *ssl_ctx, const TestConfig *config, + const TestConfig *retry_config, bool is_resume, + SSL_SESSION *session) { + bssl::UniquePtr ssl = NewSSL(ssl_ctx, config, session, is_resume, + std::unique_ptr(new TestState)); + if (!ssl) { + return false; + } + if (config->is_server) { + SSL_set_accept_state(ssl.get()); + } else { + SSL_set_connect_state(ssl.get()); + } + + int sock = Connect(config->port); if (sock == -1) { return false; @@ -2167,30 +2206,6 @@ static bool DoConnection(bssl::UniquePtr *out_session, SSL_set_bio(ssl.get(), bio.get(), bio.get()); bio.release(); // SSL_set_bio takes ownership. - if (session != NULL) { - if (!config->is_server) { - if (SSL_set_session(ssl.get(), session) != 1) { - return false; - } - } else if (config->async) { - // The internal session cache is disabled, so install the session - // manually. - SSL_SESSION_up_ref(session); - GetTestState(ssl.get())->pending_session.reset(session); - } - } - - if (SSL_get_current_cipher(ssl.get()) != nullptr) { - fprintf(stderr, "non-null cipher before handshake\n"); - return false; - } - - if (config->is_server) { - SSL_set_accept_state(ssl.get()); - } else { - SSL_set_connect_state(ssl.get()); - } - bool ret = DoExchange(out_session, &ssl, config, is_resume, false); if (!config->is_server && is_resume && config->expect_reject_early_data) { // We must have failed due to an early data rejection. @@ -2253,22 +2268,28 @@ static bool HandoffReady(SSL *ssl, int ret) { return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF; } +static bool HandbackReady(SSL *ssl, int ret) { + return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK; +} + static bool DoExchange(bssl::UniquePtr *out_session, bssl::UniquePtr *ssl_uniqueptr, const TestConfig *config, bool is_resume, bool is_retry) { int ret; SSL *ssl = ssl_uniqueptr->get(); + SSL_CTX *session_ctx = ssl->ctx; if (!config->implicit_handshake) { if (config->handoff) { - bssl::UniquePtr ctx_handoff(SSL_CTX_new(TLSv1_method())); + bssl::UniquePtr ctx_handoff = SetupCtx(ssl->ctx, config); if (!ctx_handoff) { return false; } SSL_CTX_set_handoff_mode(ctx_handoff.get(), 1); - bssl::UniquePtr ssl_handoff(SSL_new(ctx_handoff.get())); + bssl::UniquePtr ssl_handoff = + NewSSL(ctx_handoff.get(), config, nullptr, false, nullptr); if (!ssl_handoff) { return false; } @@ -2318,12 +2339,12 @@ static bool DoExchange(bssl::UniquePtr *out_session, }); } while (config->async && RetryAsync(ssl, ret)); - if (ret != 1 || - !CheckHandshakeProperties(ssl, is_resume, config)) { - return false; - } - if (config->handoff) { + if (!HandbackReady(ssl, ret)) { + fprintf(stderr, "Connection failed to handback.\n"); + return false; + } + bssl::ScopedCBB cbb; bssl::Array handback; if (!CBB_init(cbb.get(), 512) || @@ -2333,26 +2354,42 @@ static bool DoExchange(bssl::UniquePtr *out_session, return false; } - bssl::UniquePtr ctx_handback(SSL_CTX_new(TLSv1_method())); - SSL_CTX_set_msg_callback(ctx_handback.get(), MessageCallback); - bssl::UniquePtr ssl_handback(SSL_new(ctx_handback.get())); + bssl::UniquePtr ctx_handback = SetupCtx(ssl->ctx, config); + if (!ctx_handback) { + return false; + } + bssl::UniquePtr ssl_handback = + NewSSL(ctx_handback.get(), config, nullptr, false, nullptr); if (!ssl_handback) { return false; } - if (!SSL_apply_handback(ssl_handback.get(), handback)) { - fprintf(stderr, "Applying handback failed.\n"); - return false; - } - MoveBIOs(ssl_handback.get(), ssl); if (!MoveExData(ssl_handback.get(), ssl)) { return false; } + if (!SSL_apply_handback(ssl_handback.get(), handback)) { + fprintf(stderr, "Applying handback failed.\n"); + return false; + } + *ssl_uniqueptr = std::move(ssl_handback); ssl = ssl_uniqueptr->get(); + + do { + ret = CheckIdempotentError("SSL_do_handshake", ssl, [&]() -> int { + return SSL_do_handshake(ssl); + }); + } while (config->async && RetryAsync(ssl, ret)); } + if (ret != 1 || !CheckHandshakeProperties(ssl, is_resume, config)) { + return false; + } + + lh_SSL_SESSION_doall_arg(ssl->ctx->sessions, ssl_ctx_add_session, + session_ctx); + if (is_resume && !is_retry && !config->is_server && config->expect_no_offer_early_data && SSL_in_early_data(ssl)) { fprintf(stderr, "Client unexpectedly offered early data.\n");