Deduplicate our three ServerHello parsers.

We do this enough that it's worth extracting a common parser. And this
gives a struct we can pass around. Note this moves the server extensions
block parsing out of ssl_scan_serverhello_tlsext.

I've also consolidated a few error conditions to tighten the code up a
bit: the TLS 1.2 code distinguishes unknown from unadvertised cipher,
while the TLS 1.3 code didn't. And seeing the wrong legacy version
number in TLS 1.3 is really just a syntax error since it's not the
version field anymore. (RFC8446 specifies the value.)

Change-Id: Ia2f44ff9a3899b5a594569f1b258f2b487930496
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48908
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
grpc-202302
David Benjamin 4 years ago committed by Boringssl LUCI CQ
parent 61f320874b
commit e2cb423767
  1. 16
      ssl/extensions.cc
  2. 143
      ssl/handshake_client.cc
  3. 17
      ssl/internal.h
  4. 4
      ssl/test/runner/runner.go
  5. 105
      ssl/tls13_client.cc

@ -3716,18 +3716,10 @@ bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
return true;
}
static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs,
int *out_alert) {
SSL *const ssl = hs->ssl;
// Before TLS 1.3, ServerHello extensions blocks may be omitted if empty.
if (CBS_len(cbs) == 0 && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
return true;
}
// Decode the extensions block and check it is valid.
CBS extensions;
if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
!tls1_check_duplicate_extensions(&extensions)) {
CBS extensions = *cbs;
if (!tls1_check_duplicate_extensions(&extensions)) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
@ -3849,7 +3841,7 @@ static bool ssl_check_serverhello_tlsext(SSL_HANDSHAKE *hs) {
return true;
}
bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs) {
SSL *const ssl = hs->ssl;
int alert = SSL_AD_DECODE_ERROR;
if (!ssl_scan_serverhello_tlsext(hs, cbs, &alert)) {

@ -354,42 +354,36 @@ bool ssl_add_client_hello(SSL_HANDSHAKE *hs) {
return ssl->method->add_message(ssl, std::move(msg));
}
static bool parse_supported_versions(SSL_HANDSHAKE *hs, uint16_t *version,
const CBS *in) {
// If the outer version is not TLS 1.2, or there is no extensions block, use
// the outer version.
if (*version != TLS1_2_VERSION || CBS_len(in) == 0) {
static bool parse_server_version(const SSL_HANDSHAKE *hs, uint16_t *out_version,
uint8_t *out_alert,
const ParsedServerHello &server_hello) {
// If the outer version is not TLS 1.2, use it.
// TODO(davidben): This function doesn't quite match the RFC8446 formulation.
if (server_hello.legacy_version != TLS1_2_VERSION) {
*out_version = server_hello.legacy_version;
return true;
}
SSL *const ssl = hs->ssl;
CBS copy = *in, extensions;
if (!CBS_get_u16_length_prefixed(&copy, &extensions) ||
CBS_len(&copy) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return false;
}
bool have_supported_versions;
CBS supported_versions;
const SSL_EXTENSION_TYPE ext_types[] = {
{TLSEXT_TYPE_supported_versions, &have_supported_versions,
&supported_versions},
};
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ssl_parse_extensions(&extensions, &alert, ext_types,
CBS extensions = server_hello.extensions;
if (!ssl_parse_extensions(&extensions, out_alert, ext_types,
/*ignore_unknown=*/true)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return false;
}
// Override the outer version with the extension, if present.
if (have_supported_versions &&
(!CBS_get_u16(&supported_versions, version) ||
CBS_len(&supported_versions) != 0)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
if (!have_supported_versions) {
*out_version = server_hello.legacy_version;
return true;
}
if (!CBS_get_u16(&supported_versions, out_version) ||
CBS_len(&supported_versions) != 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
@ -657,6 +651,37 @@ static enum ssl_hs_wait_t do_read_hello_verify_request(SSL_HANDSHAKE *hs) {
return ssl_hs_flush;
}
bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
const SSLMessage &msg) {
if (msg.type != SSL3_MT_SERVER_HELLO) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
*out_alert = SSL_AD_UNEXPECTED_MESSAGE;
return false;
}
CBS body = msg.body;
if (!CBS_get_u16(&body, &out->legacy_version) ||
!CBS_get_bytes(&body, &out->random, SSL3_RANDOM_SIZE) ||
!CBS_get_u8_length_prefixed(&body, &out->session_id) ||
CBS_len(&out->session_id) > SSL3_SESSION_ID_SIZE ||
!CBS_get_u16(&body, &out->cipher_suite) ||
!CBS_get_u8(&body, &out->compression_method)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
// In TLS 1.2 and below, empty extensions blocks may be omitted. In TLS 1.3,
// ServerHellos always have extensions, so this can be applied generically.
CBS_init(&out->extensions, nullptr, 0);
if ((CBS_len(&body) != 0 &&
!CBS_get_u16_length_prefixed(&body, &out->extensions)) ||
CBS_len(&body) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
return true;
}
static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
SSLMessage msg;
@ -664,26 +689,12 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
return ssl_hs_read_server_hello;
}
if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
return ssl_hs_error;
}
CBS server_hello = msg.body, server_random, session_id;
uint16_t server_version, cipher_suite;
uint8_t compression_method;
if (!CBS_get_u16(&server_hello, &server_version) ||
!CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
!CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
!CBS_get_u16(&server_hello, &cipher_suite) ||
!CBS_get_u8(&server_hello, &compression_method)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return ssl_hs_error;
}
// Use the supported_versions extension if applicable.
if (!parse_supported_versions(hs, &server_version, &server_hello)) {
ParsedServerHello server_hello;
uint16_t server_version;
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ssl_parse_server_hello(&server_hello, &alert, msg) ||
!parse_server_version(hs, &server_version, &alert, server_hello)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
@ -737,7 +748,7 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
}
// Copy over the server random.
OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
SSL3_RANDOM_SIZE);
// Enforce the TLS 1.3 anti-downgrade feature.
@ -760,28 +771,26 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
}
}
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
if (cipher == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
hs->new_cipher = cipher;
// The cipher must be allowed in the selected version and enabled.
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
uint32_t mask_a, mask_k;
ssl_get_client_disabled(hs, &mask_a, &mask_k);
if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
if (cipher == nullptr ||
(cipher->algorithm_mkey & mask_k) ||
(cipher->algorithm_auth & mask_a) ||
SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
!sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, cipher)) {
!sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), nullptr, cipher)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
hs->new_cipher = cipher;
if (hs->session_id_len != 0 &&
CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len)) {
CBS_mem_equal(&server_hello.session_id, hs->session_id,
hs->session_id_len)) {
// Echoing the ClientHello session ID in TLS 1.2, whether from the session
// or a synthetic one, indicates resumption. If there was no session (or if
// the session was only offered in ECH ClientHelloInner), this was the
@ -799,7 +808,7 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
if (ssl->session->cipher != cipher) {
if (ssl->session->cipher != hs->new_cipher) {
OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
@ -822,10 +831,11 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
return ssl_hs_error;
}
// Note: session_id could be empty.
hs->new_session->session_id_length = CBS_len(&session_id);
OPENSSL_memcpy(hs->new_session->session_id, CBS_data(&session_id),
CBS_len(&session_id));
hs->new_session->cipher = cipher;
hs->new_session->session_id_length = CBS_len(&server_hello.session_id);
OPENSSL_memcpy(hs->new_session->session_id,
CBS_data(&server_hello.session_id),
CBS_len(&server_hello.session_id));
hs->new_session->cipher = hs->new_cipher;
}
// Now that the cipher is known, initialize the handshake hash and hash the
@ -845,26 +855,17 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
}
// Only the NULL compression algorithm is supported.
if (compression_method != 0) {
if (server_hello.compression_method != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
// TLS extensions
if (!ssl_parse_serverhello_tlsext(hs, &server_hello)) {
if (!ssl_parse_serverhello_tlsext(hs, &server_hello.extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
return ssl_hs_error;
}
// There should be nothing left over in the record.
if (CBS_len(&server_hello) != 0) {
// wrong packet length
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return ssl_hs_error;
}
if (ssl->session != NULL &&
hs->extended_master_secret != ssl->session->extended_master_secret) {
if (ssl->session->extended_master_secret) {

@ -2163,6 +2163,21 @@ bool ssl_write_client_hello_without_extensions(const SSL_HANDSHAKE *hs,
// flight. It returns true on success and false on error.
bool ssl_add_client_hello(SSL_HANDSHAKE *hs);
struct ParsedServerHello {
uint16_t legacy_version = 0;
CBS random;
CBS session_id;
uint16_t cipher_suite = 0;
uint8_t compression_method = 0;
CBS extensions;
};
// ssl_parse_server_hello parses |msg| as a ServerHello. On success, it writes
// the result to |*out| and returns true. Otherwise, it returns false and sets
// |*out_alert| to an alert to send to the peer.
bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
const SSLMessage &msg);
enum ssl_cert_verify_context_t {
ssl_cert_verify_server,
ssl_cert_verify_client,
@ -3304,7 +3319,7 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded,
bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
const SSL_CLIENT_HELLO *client_hello);
bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *extensions);
#define tlsext_tick_md EVP_sha256

@ -3833,7 +3833,7 @@ func addCipherSuiteTests() {
},
},
shouldFail: true,
expectedError: ":UNKNOWN_CIPHER_RETURNED:",
expectedError: ":WRONG_CIPHER_RETURNED:",
})
testCases = append(testCases, testCase{
name: "ServerHelloBogusCipher-TLS13",
@ -13884,7 +13884,7 @@ func addTLS13HandshakeTests() {
},
},
shouldFail: true,
expectedError: ":WRONG_VERSION_NUMBER:",
expectedError: ":DECODE_ERROR:",
})
testCases = append(testCases, testCase{

@ -101,6 +101,25 @@ static bool close_early_data(SSL_HANDSHAKE *hs, ssl_encryption_level_t level) {
return true;
}
static bool parse_server_hello_tls13(const SSL_HANDSHAKE *hs,
ParsedServerHello *out, uint8_t *out_alert,
const SSLMessage &msg) {
if (!ssl_parse_server_hello(out, out_alert, msg)) {
return false;
}
// The RFC8446 version of the structure fixes some legacy values.
// Additionally, the session ID must echo the original one.
if (out->legacy_version != TLS1_2_VERSION ||
out->compression_method != 0 ||
!CBS_mem_equal(&out->session_id, hs->session_id, hs->session_id_len) ||
CBS_len(&out->extensions) == 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return false;
}
return true;
}
static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
assert(ssl->s3->have_version);
@ -117,32 +136,17 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
return ssl_hs_error;
}
if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
return ssl_hs_error;
}
CBS body = msg.body, extensions, server_random, session_id;
uint16_t server_version, cipher_suite;
uint8_t compression_method;
if (!CBS_get_u16(&body, &server_version) ||
!CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
!CBS_get_u8_length_prefixed(&body, &session_id) ||
!CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
!CBS_get_u16(&body, &cipher_suite) ||
!CBS_get_u8(&body, &compression_method) ||
compression_method != 0 ||
!CBS_get_u16_length_prefixed(&body, &extensions) ||
CBS_len(&extensions) == 0 ||
CBS_len(&body) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
ParsedServerHello server_hello;
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
// The cipher suite must be one we offered. We currently offer all supported
// TLS 1.3 ciphers, so check the version.
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
if (cipher == NULL ||
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
if (cipher == nullptr ||
SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
@ -152,7 +156,8 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
hs->new_cipher = cipher;
if (!CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
if (!CBS_mem_equal(&server_hello.random, kHelloRetryRequest,
SSL3_RANDOM_SIZE)) {
hs->tls13_state = state_read_server_hello;
return ssl_hs_ok;
}
@ -166,8 +171,7 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
&supported_versions},
};
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ssl_parse_extensions(&extensions, &alert, ext_types,
if (!ssl_parse_extensions(&server_hello.extensions, &alert, ext_types,
/*ignore_unknown=*/false)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
@ -287,49 +291,29 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
if (!ssl->method->get_message(ssl, &msg)) {
return ssl_hs_read_message;
}
if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
return ssl_hs_error;
}
CBS body = msg.body, server_random, session_id, extensions;
uint16_t server_version;
uint16_t cipher_suite;
uint8_t compression_method;
if (!CBS_get_u16(&body, &server_version) ||
!CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
!CBS_get_u8_length_prefixed(&body, &session_id) ||
!CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
!CBS_get_u16(&body, &cipher_suite) ||
!CBS_get_u8(&body, &compression_method) ||
compression_method != 0 ||
!CBS_get_u16_length_prefixed(&body, &extensions) ||
CBS_len(&body) != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return ssl_hs_error;
}
if (server_version != TLS1_2_VERSION) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
ParsedServerHello server_hello;
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
}
// Forbid a second HelloRetryRequest.
if (CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
if (CBS_mem_equal(&server_hello.random, kHelloRetryRequest,
SSL3_RANDOM_SIZE)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
return ssl_hs_error;
}
// Check the cipher suite, in case this is after HelloRetryRequest.
if (SSL_CIPHER_get_value(hs->new_cipher) != cipher_suite) {
if (SSL_CIPHER_get_value(hs->new_cipher) != server_hello.cipher_suite) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
SSL3_RANDOM_SIZE);
// Parse out the extensions.
@ -343,8 +327,7 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
&supported_versions},
};
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ssl_parse_extensions(&extensions, &alert, ext_types,
if (!ssl_parse_extensions(&server_hello.extensions, &alert, ext_types,
/*ignore_unknown=*/false)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
@ -521,17 +504,19 @@ static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) {
return ssl_hs_error;
}
CBS body = msg.body;
if (!ssl_parse_serverhello_tlsext(hs, &body)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
return ssl_hs_error;
}
if (CBS_len(&body) != 0) {
CBS body = msg.body, extensions;
if (!CBS_get_u16_length_prefixed(&body, &extensions) ||
CBS_len(&body) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return ssl_hs_error;
}
if (!ssl_parse_serverhello_tlsext(hs, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
return ssl_hs_error;
}
if (ssl->s3->early_data_accepted) {
// The extension parser checks the server resumed the session.
assert(ssl->s3->session_reused);

Loading…
Cancel
Save