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 <mab@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: Adam Langley <agl@google.com>kris/onging/CECPQ3_patch15
@@ -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<uint8_t> 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<uint8_t> 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 | |||
@@ -93,17 +93,17 @@ bool SSL_apply_handoff(SSL *ssl, Span<const uint8_t> 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<const uint8_t> 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<const uint8_t> handback) { | |||
@@ -173,11 +192,16 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> 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<const uint8_t> 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<const uint8_t> 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<uint16_t>(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<const uint8_t> 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<const uint8_t> 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<uint8_t>(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; | |||
if (s3->hs->state == state12_finish_server_handshake) { | |||
Array<uint8_t> 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; | |||
} | |||
Array<uint8_t> 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 (!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; | |||
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; | |||
} | |||
} | |||
return true; | |||
return CBS_len(&seq) == 0; | |||
} | |||
} // namespace bssl |
@@ -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; | |||
@@ -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<enum ssl_server_hs_state_t>(hs->state); | |||
enum tls12_server_hs_state_t state = | |||
static_cast<enum tls12_server_hs_state_t>(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<enum ssl_server_hs_state_t>(hs->state); | |||
enum tls12_server_hs_state_t state = | |||
static_cast<enum tls12_server_hs_state_t>(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"; | |||
} | |||
@@ -929,6 +929,10 @@ class SSLKeyShare { | |||
// nullptr on error. | |||
static UniquePtr<SSLKeyShare> Create(uint16_t group_id); | |||
// Create deserializes an SSLKeyShare instance previously serialized by | |||
// |Serialize|. | |||
static UniquePtr<SSLKeyShare> 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<uint8_t> *out_secret, uint8_t *out_alert, | |||
Span<const uint8_t> 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. | |||
@@ -124,6 +124,32 @@ class ECKeyShare : public SSLKeyShare { | |||
return true; | |||
} | |||
bool Serialize(CBB *out) override { | |||
assert(private_key_); | |||
CBB cbb; | |||
UniquePtr<EC_GROUP> 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<BIGNUM> 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> SSLKeyShare::Create(uint16_t group_id) { | |||
} | |||
} | |||
UniquePtr<SSLKeyShare> SSLKeyShare::Create(CBS *in) { | |||
uint64_t group; | |||
if (!CBS_get_asn1_uint64(in, &group)) { | |||
return nullptr; | |||
} | |||
UniquePtr<SSLKeyShare> key_share = Create(static_cast<uint64_t>(group)); | |||
if (!key_share->Deserialize(in)) { | |||
return nullptr; | |||
} | |||
return key_share; | |||
} | |||
bool SSLKeyShare::Accept(CBB *out_public_key, Array<uint8_t> *out_secret, | |||
uint8_t *out_alert, Span<const uint8_t> peer_key) { | |||
*out_alert = SSL_AD_INTERNAL_ERROR; | |||
@@ -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)) { | |||
@@ -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<uint8_t> 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); | |||
@@ -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 | |||
@@ -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<SSL_SESSION> *out_session, | |||
bssl::UniquePtr<SSL> *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<SSL_SESSION> *out_session, | |||
SSL_CTX *ssl_ctx, const TestConfig *config, | |||
const TestConfig *retry_config, bool is_resume, | |||
SSL_SESSION *session) { | |||
static bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, const TestConfig *config, | |||
SSL_SESSION *session, bool is_resume, | |||
std::unique_ptr<TestState> test_state) { | |||
bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx)); | |||
if (!ssl) { | |||
return false; | |||
return nullptr; | |||
} | |||
if (!SetTestConfig(ssl.get(), config) || | |||
!SetTestState(ssl.get(), std::unique_ptr<TestState>(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<SSL_SESSION> *out_session, | |||
// The async case will be supplied by |ChannelIdCallback|. | |||
bssl::UniquePtr<EVP_PKEY> 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<SSL_SESSION> *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<SSL_SESSION> *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<SSL_SESSION> *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<SSL_SESSION> *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<SSL_SESSION> *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<SSL_SESSION> *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<SSL_SESSION> *out_session, | |||
reinterpret_cast<const uint8_t *>( | |||
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<SSL_SESSION> *out_session, | |||
bssl::UniquePtr<SSL> *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<SSL_SESSION> *out_session, | |||
SSL_CTX *ssl_ctx, const TestConfig *config, | |||
const TestConfig *retry_config, bool is_resume, | |||
SSL_SESSION *session) { | |||
bssl::UniquePtr<SSL> ssl = NewSSL(ssl_ctx, config, session, is_resume, | |||
std::unique_ptr<TestState>(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<SSL_SESSION> *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<SSL_SESSION> *out_session, | |||
bssl::UniquePtr<SSL> *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<SSL_CTX> ctx_handoff(SSL_CTX_new(TLSv1_method())); | |||
bssl::UniquePtr<SSL_CTX> ctx_handoff = SetupCtx(ssl->ctx, config); | |||
if (!ctx_handoff) { | |||
return false; | |||
} | |||
SSL_CTX_set_handoff_mode(ctx_handoff.get(), 1); | |||
bssl::UniquePtr<SSL> ssl_handoff(SSL_new(ctx_handoff.get())); | |||
bssl::UniquePtr<SSL> ssl_handoff = | |||
NewSSL(ctx_handoff.get(), config, nullptr, false, nullptr); | |||
if (!ssl_handoff) { | |||
return false; | |||
} | |||
@@ -2318,12 +2339,12 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *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<uint8_t> handback; | |||
if (!CBB_init(cbb.get(), 512) || | |||
@@ -2333,26 +2354,42 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, | |||
return false; | |||
} | |||
bssl::UniquePtr<SSL_CTX> ctx_handback(SSL_CTX_new(TLSv1_method())); | |||
SSL_CTX_set_msg_callback(ctx_handback.get(), MessageCallback); | |||
bssl::UniquePtr<SSL> ssl_handback(SSL_new(ctx_handback.get())); | |||
if (!ssl_handback) { | |||
bssl::UniquePtr<SSL_CTX> ctx_handback = SetupCtx(ssl->ctx, config); | |||
if (!ctx_handback) { | |||
return false; | |||
} | |||
if (!SSL_apply_handback(ssl_handback.get(), handback)) { | |||
fprintf(stderr, "Applying handback failed.\n"); | |||
bssl::UniquePtr<SSL> ssl_handback = | |||
NewSSL(ctx_handback.get(), config, nullptr, false, nullptr); | |||
if (!ssl_handback) { | |||
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"); | |||