Adding NewSessionTicket.
We will now send tickets as a server and accept them as a client. Correctly offering and resuming them in the handshake will be implemented in a follow-up. Now that we're actually processing draft 14 tickets, bump the draft version. Change-Id: I304320a29c4ffe564fa9c00642a4ace96ff8d871 Reviewed-on: https://boringssl-review.googlesource.com/8982 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
parent
e8e84b9008
commit
1e6f11a7ff
@ -572,7 +572,7 @@ OPENSSL_EXPORT int DTLSv1_handle_timeout(SSL *ssl);
|
||||
#define DTLS1_VERSION 0xfeff
|
||||
#define DTLS1_2_VERSION 0xfefd
|
||||
|
||||
#define TLS1_3_DRAFT_VERSION 13
|
||||
#define TLS1_3_DRAFT_VERSION 14
|
||||
|
||||
/* SSL_CTX_set_min_version sets the minimum protocol version for |ctx| to
|
||||
* |version|. */
|
||||
@ -3702,7 +3702,10 @@ struct ssl_session_st {
|
||||
uint8_t original_handshake_hash[EVP_MAX_MD_SIZE];
|
||||
unsigned original_handshake_hash_len;
|
||||
|
||||
uint32_t tlsext_tick_lifetime_hint; /* Session lifetime hint in seconds */
|
||||
uint32_t ticket_lifetime_hint; /* Session lifetime hint in seconds */
|
||||
|
||||
uint32_t ticket_flags;
|
||||
uint32_t ticket_age_add;
|
||||
|
||||
/* extended_master_secret is true if the master secret in this session was
|
||||
* generated using EMS and thus isn't vulnerable to the Triple Handshake
|
||||
@ -3714,6 +3717,9 @@ struct ssl_session_st {
|
||||
|
||||
/* not_resumable is used to indicate that session resumption is disallowed. */
|
||||
unsigned not_resumable:1;
|
||||
|
||||
/* ticket_age_add_valid is non-zero if |ticket_age_add| is valid. */
|
||||
unsigned ticket_age_add_valid:1;
|
||||
};
|
||||
|
||||
/* ssl_cipher_preference_list_st contains a list of SSL_CIPHERs with
|
||||
|
@ -1953,7 +1953,7 @@ static int ssl3_get_new_session_ticket(SSL *ssl) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
|
||||
session->ticket_lifetime_hint = ticket_lifetime_hint;
|
||||
|
||||
/* Generate a session ID for this session based on the session ticket. We use
|
||||
* the session ID mechanism for detecting ticket resumption. This also fits in
|
||||
|
@ -1867,107 +1867,22 @@ static int ssl3_send_new_session_ticket(SSL *ssl) {
|
||||
return ssl->method->write_message(ssl);
|
||||
}
|
||||
|
||||
/* Serialize the SSL_SESSION to be encoded into the ticket. */
|
||||
uint8_t *session = NULL;
|
||||
size_t session_len;
|
||||
if (!SSL_SESSION_to_bytes_for_ticket(
|
||||
ssl->session != NULL ? ssl->session : ssl->s3->new_session,
|
||||
&session, &session_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
HMAC_CTX hctx;
|
||||
HMAC_CTX_init(&hctx);
|
||||
|
||||
int ret = -1;
|
||||
CBB cbb, body, ticket;
|
||||
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) ||
|
||||
if (!ssl->method->init_message(ssl, &cbb, &body,
|
||||
SSL3_MT_NEW_SESSION_TICKET) ||
|
||||
/* Ticket lifetime hint (advisory only): We leave this unspecified for
|
||||
* resumed session (for simplicity), and guess that tickets for new
|
||||
* sessions will live as long as their sessions. */
|
||||
!CBB_add_u32(&body, ssl->session != NULL ? 0 :
|
||||
ssl->s3->new_session->timeout) ||
|
||||
!CBB_add_u16_length_prefixed(&body, &ticket)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* If the session is too long, emit a dummy value rather than abort the
|
||||
* connection. */
|
||||
const size_t max_ticket_overhead =
|
||||
16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
|
||||
if (session_len > 0xffff - max_ticket_overhead) {
|
||||
static const char kTicketPlaceholder[] = "TICKET TOO LARGE";
|
||||
|
||||
if (!CBB_add_bytes(&ticket, (const uint8_t *)kTicketPlaceholder,
|
||||
strlen(kTicketPlaceholder)) ||
|
||||
!CBB_add_u32(&body,
|
||||
ssl->session != NULL ? 0 : ssl->s3->new_session->timeout) ||
|
||||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
|
||||
!ssl_encrypt_ticket(ssl, &ticket, ssl->session != NULL
|
||||
? ssl->session
|
||||
: ssl->s3->new_session) ||
|
||||
!ssl->method->finish_message(ssl, &cbb)) {
|
||||
goto err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
|
||||
ret = 1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Initialize HMAC and cipher contexts. If callback present it does all the
|
||||
* work otherwise use generated values from parent ctx. */
|
||||
SSL_CTX *tctx = ssl->initial_ctx;
|
||||
uint8_t iv[EVP_MAX_IV_LENGTH];
|
||||
uint8_t key_name[16];
|
||||
if (tctx->tlsext_ticket_key_cb != NULL) {
|
||||
if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
|
||||
1 /* encrypt */) < 0) {
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
if (!RAND_bytes(iv, 16) ||
|
||||
!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
|
||||
tctx->tlsext_tick_aes_key, iv) ||
|
||||
!HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
|
||||
NULL)) {
|
||||
goto err;
|
||||
}
|
||||
memcpy(key_name, tctx->tlsext_tick_key_name, 16);
|
||||
}
|
||||
|
||||
uint8_t *ptr;
|
||||
if (!CBB_add_bytes(&ticket, key_name, 16) ||
|
||||
!CBB_add_bytes(&ticket, iv, EVP_CIPHER_CTX_iv_length(&ctx)) ||
|
||||
!CBB_reserve(&ticket, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
int len;
|
||||
size_t total = 0;
|
||||
if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session, session_len)) {
|
||||
goto err;
|
||||
}
|
||||
total += len;
|
||||
if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) {
|
||||
goto err;
|
||||
}
|
||||
total += len;
|
||||
if (!CBB_did_write(&ticket, total)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
unsigned hlen;
|
||||
if (!HMAC_Update(&hctx, CBB_data(&ticket), CBB_len(&ticket)) ||
|
||||
!CBB_reserve(&ticket, &ptr, EVP_MAX_MD_SIZE) ||
|
||||
!HMAC_Final(&hctx, ptr, &hlen) ||
|
||||
!CBB_did_write(&ticket, hlen) ||
|
||||
!ssl->method->finish_message(ssl, &cbb)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
|
||||
ret = ssl->method->write_message(ssl);
|
||||
|
||||
err:
|
||||
OPENSSL_free(session);
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
HMAC_CTX_cleanup(&hctx);
|
||||
return ret;
|
||||
return ssl->method->write_message(ssl);
|
||||
}
|
||||
|
@ -891,6 +891,8 @@ struct ssl_handshake_st {
|
||||
|
||||
uint8_t *cert_context;
|
||||
size_t cert_context_len;
|
||||
|
||||
uint8_t session_tickets_sent;
|
||||
} /* SSL_HANDSHAKE */;
|
||||
|
||||
SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl));
|
||||
@ -925,6 +927,7 @@ int tls13_prepare_certificate(SSL *ssl);
|
||||
enum ssl_private_key_result_t tls13_prepare_certificate_verify(
|
||||
SSL *ssl, int is_first_run);
|
||||
int tls13_prepare_finished(SSL *ssl);
|
||||
int tls13_process_new_session_ticket(SSL *ssl);
|
||||
|
||||
int ssl_ext_key_share_parse_serverhello(SSL *ssl, uint8_t **out_secret,
|
||||
size_t *out_secret_len,
|
||||
@ -1202,12 +1205,18 @@ typedef struct dtls1_state_st {
|
||||
extern const SSL3_ENC_METHOD TLSv1_enc_data;
|
||||
extern const SSL3_ENC_METHOD SSLv3_enc_data;
|
||||
|
||||
/* From draft-ietf-tls-tls13-14, used in determining ticket validity. */
|
||||
#define SSL_TICKET_ALLOW_EARLY_DATA 1
|
||||
#define SSL_TICKET_ALLOW_DHE_RESUMPTION 2
|
||||
#define SSL_TICKET_ALLOW_PSK_RESUMPTION 4
|
||||
|
||||
int ssl_clear_bad_session(SSL *ssl);
|
||||
CERT *ssl_cert_new(void);
|
||||
CERT *ssl_cert_dup(CERT *cert);
|
||||
void ssl_cert_clear_certs(CERT *c);
|
||||
void ssl_cert_free(CERT *c);
|
||||
int ssl_get_new_session(SSL *ssl, int is_server);
|
||||
int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
|
||||
|
||||
enum ssl_session_result_t {
|
||||
ssl_session_success,
|
||||
|
@ -120,6 +120,8 @@
|
||||
* extendedMasterSecret [17] BOOLEAN OPTIONAL,
|
||||
* keyExchangeInfo [18] INTEGER OPTIONAL,
|
||||
* certChain [19] SEQUENCE OF Certificate OPTIONAL,
|
||||
* ticketFlags [20] INTEGER OPTIONAL,
|
||||
* ticketAgeAdd [21] OCTET STRING OPTIONAL,
|
||||
* }
|
||||
*
|
||||
* Note: historically this serialization has included other optional
|
||||
@ -164,6 +166,10 @@ static const int kKeyExchangeInfoTag =
|
||||
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18;
|
||||
static const int kCertChainTag =
|
||||
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
|
||||
static const int kTicketFlagsTag =
|
||||
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 20;
|
||||
static const int kTicketAgeAddTag =
|
||||
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 21;
|
||||
|
||||
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
|
||||
size_t *out_len, int for_ticket) {
|
||||
@ -255,9 +261,9 @@ static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
|
||||
}
|
||||
}
|
||||
|
||||
if (in->tlsext_tick_lifetime_hint > 0) {
|
||||
if (in->ticket_lifetime_hint > 0) {
|
||||
if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) ||
|
||||
!CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) {
|
||||
!CBB_add_asn1_uint64(&child, in->ticket_lifetime_hint)) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
@ -341,6 +347,23 @@ static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
|
||||
}
|
||||
}
|
||||
|
||||
if (in->ticket_flags > 0) {
|
||||
if (!CBB_add_asn1(&session, &child, kTicketFlagsTag) ||
|
||||
!CBB_add_asn1_uint64(&child, in->ticket_flags)) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (in->ticket_age_add_valid) {
|
||||
if (!CBB_add_asn1(&session, &child, kTicketAgeAddTag) ||
|
||||
!CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
|
||||
!CBB_add_u32(&child2, in->ticket_age_add)) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CBB_finish(&cbb, out_data, out_len)) {
|
||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
@ -573,7 +596,7 @@ static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) {
|
||||
kHostNameTag) ||
|
||||
!SSL_SESSION_parse_string(&session, &ret->psk_identity,
|
||||
kPSKIdentityTag) ||
|
||||
!SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint,
|
||||
!SSL_SESSION_parse_u32(&session, &ret->ticket_lifetime_hint,
|
||||
kTicketLifetimeHintTag, 0) ||
|
||||
!SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick,
|
||||
&ret->tlsext_ticklen, kTicketTag)) {
|
||||
@ -652,6 +675,19 @@ static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) {
|
||||
}
|
||||
}
|
||||
|
||||
CBS age_add;
|
||||
int age_add_present;
|
||||
if (!SSL_SESSION_parse_u32(&session, &ret->ticket_flags,
|
||||
kTicketFlagsTag, 0) ||
|
||||
!CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present,
|
||||
kTicketAgeAddTag) ||
|
||||
(age_add_present &&
|
||||
!CBS_get_u32(&age_add, &ret->ticket_age_add)) ||
|
||||
CBS_len(&age_add) != 0) {
|
||||
goto err;
|
||||
}
|
||||
ret->ticket_age_add_valid = age_add_present;
|
||||
|
||||
if (CBS_len(&session) != 0) {
|
||||
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
|
||||
goto err;
|
||||
|
@ -219,8 +219,8 @@ SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int include_ticket) {
|
||||
}
|
||||
if (include_ticket) {
|
||||
if (session->tlsext_tick != NULL) {
|
||||
new_session->tlsext_tick = BUF_memdup(session->tlsext_tick,
|
||||
session->tlsext_ticklen);
|
||||
new_session->tlsext_tick =
|
||||
BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
|
||||
if (new_session->tlsext_tick == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@ -252,7 +252,9 @@ SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int include_ticket) {
|
||||
session->original_handshake_hash_len);
|
||||
new_session->original_handshake_hash_len =
|
||||
session->original_handshake_hash_len;
|
||||
new_session->tlsext_tick_lifetime_hint = session->tlsext_tick_lifetime_hint;
|
||||
new_session->ticket_lifetime_hint = session->ticket_lifetime_hint;
|
||||
new_session->ticket_flags = session->ticket_flags;
|
||||
new_session->ticket_age_add = session->ticket_age_add;
|
||||
new_session->extended_master_secret = session->extended_master_secret;
|
||||
new_session->peer_sha256_valid = session->peer_sha256_valid;
|
||||
new_session->not_resumable = 1;
|
||||
@ -468,6 +470,93 @@ err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
|
||||
int ret = 0;
|
||||
|
||||
/* Serialize the SSL_SESSION to be encoded into the ticket. */
|
||||
uint8_t *session_buf = NULL;
|
||||
size_t session_len;
|
||||
if (!SSL_SESSION_to_bytes_for_ticket(session, &session_buf, &session_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
HMAC_CTX hctx;
|
||||
HMAC_CTX_init(&hctx);
|
||||
|
||||
/* If the session is too long, emit a dummy value rather than abort the
|
||||
* connection. */
|
||||
static const size_t kMaxTicketOverhead =
|
||||
16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
|
||||
if (session_len > 0xffff - kMaxTicketOverhead) {
|
||||
static const char kTicketPlaceholder[] = "TICKET TOO LARGE";
|
||||
if (CBB_add_bytes(out, (const uint8_t *)kTicketPlaceholder,
|
||||
strlen(kTicketPlaceholder))) {
|
||||
ret = 1;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Initialize HMAC and cipher contexts. If callback present it does all the
|
||||
* work otherwise use generated values from parent ctx. */
|
||||
SSL_CTX *tctx = ssl->initial_ctx;
|
||||
uint8_t iv[EVP_MAX_IV_LENGTH];
|
||||
uint8_t key_name[16];
|
||||
if (tctx->tlsext_ticket_key_cb != NULL) {
|
||||
if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
|
||||
1 /* encrypt */) < 0) {
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
if (!RAND_bytes(iv, 16) ||
|
||||
!EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
|
||||
tctx->tlsext_tick_aes_key, iv) ||
|
||||
!HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
|
||||
NULL)) {
|
||||
goto err;
|
||||
}
|
||||
memcpy(key_name, tctx->tlsext_tick_key_name, 16);
|
||||
}
|
||||
|
||||
uint8_t *ptr;
|
||||
if (!CBB_add_bytes(out, key_name, 16) ||
|
||||
!CBB_add_bytes(out, iv, EVP_CIPHER_CTX_iv_length(&ctx)) ||
|
||||
!CBB_reserve(out, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
int len;
|
||||
size_t total = 0;
|
||||
if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session_buf, session_len)) {
|
||||
goto err;
|
||||
}
|
||||
total += len;
|
||||
if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) {
|
||||
goto err;
|
||||
}
|
||||
total += len;
|
||||
if (!CBB_did_write(out, total)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
unsigned hlen;
|
||||
if (!HMAC_Update(&hctx, CBB_data(out), CBB_len(out)) ||
|
||||
!CBB_reserve(out, &ptr, EVP_MAX_MD_SIZE) ||
|
||||
!HMAC_Final(&hctx, ptr, &hlen) ||
|
||||
!CBB_did_write(out, hlen)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = 1;
|
||||
|
||||
err:
|
||||
OPENSSL_free(session_buf);
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
HMAC_CTX_cleanup(&hctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ssl_lookup_session looks up |session_id| in the session cache and sets
|
||||
* |*out_session| to an |SSL_SESSION| object if found. The caller takes
|
||||
* ownership of the result. */
|
||||
|
@ -1028,6 +1028,14 @@ static int DoSendFatalAlert(SSL *ssl, uint8_t alert) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint16_t GetProtocolVersion(const SSL *ssl) {
|
||||
uint16_t version = SSL_version(ssl);
|
||||
if (!SSL_is_dtls(ssl)) {
|
||||
return version;
|
||||
}
|
||||
return 0x0201 + ~version;
|
||||
}
|
||||
|
||||
// CheckHandshakeProperties checks, immediately after |ssl| completes its
|
||||
// initial handshake (or False Starts), whether all the properties are
|
||||
// consistent with the test configuration and invariants.
|
||||
@ -1057,8 +1065,8 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
|
||||
bool expect_new_session =
|
||||
!config->expect_no_session &&
|
||||
(!SSL_session_reused(ssl) || config->expect_ticket_renewal) &&
|
||||
/* TODO(svaldez): Implement Session Resumption. */
|
||||
SSL_version(ssl) != TLS1_3_VERSION;
|
||||
// Session tickets are sent post-handshake in TLS 1.3.
|
||||
GetProtocolVersion(ssl) < TLS1_3_VERSION;
|
||||
if (expect_new_session != GetTestState(ssl)->got_new_session) {
|
||||
fprintf(stderr,
|
||||
"new session was%s cached, but we expected the opposite\n",
|
||||
@ -1569,11 +1577,24 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
|
||||
|
||||
if (!config->is_server && !config->false_start &&
|
||||
!config->implicit_handshake &&
|
||||
// Session tickets are sent post-handshake in TLS 1.3.
|
||||
GetProtocolVersion(ssl.get()) < TLS1_3_VERSION &&
|
||||
GetTestState(ssl.get())->got_new_session) {
|
||||
fprintf(stderr, "new session was established after the handshake\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetProtocolVersion(ssl.get()) >= TLS1_3_VERSION && !config->is_server) {
|
||||
bool expect_new_session =
|
||||
!config->expect_no_session && !config->shim_shuts_down;
|
||||
if (expect_new_session != GetTestState(ssl.get())->got_new_session) {
|
||||
fprintf(stderr,
|
||||
"new session was%s cached, but we expected the opposite\n",
|
||||
GetTestState(ssl.get())->got_new_session ? "" : " not");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_session) {
|
||||
out_session->reset(SSL_get1_session(ssl.get()));
|
||||
}
|
||||
|
@ -458,8 +458,7 @@ int tls13_post_handshake(SSL *ssl) {
|
||||
|
||||
if (ssl->s3->tmp.message_type == SSL3_MT_NEW_SESSION_TICKET &&
|
||||
!ssl->server) {
|
||||
// TODO(svaldez): Handle NewSessionTicket.
|
||||
return 1;
|
||||
return tls13_process_new_session_ticket(ssl);
|
||||
}
|
||||
|
||||
// TODO(svaldez): Handle post-handshake authentication.
|
||||
|
@ -566,3 +566,38 @@ enum ssl_hs_wait_t tls13_client_handshake(SSL *ssl) {
|
||||
|
||||
return ssl_hs_ok;
|
||||
}
|
||||
|
||||
int tls13_process_new_session_ticket(SSL *ssl) {
|
||||
SSL_SESSION *session = SSL_SESSION_dup(ssl->s3->established_session,
|
||||
0 /* don't include ticket */);
|
||||
if (session == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
CBS cbs, extensions, ticket;
|
||||
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
|
||||
if (!CBS_get_u32(&cbs, &session->ticket_lifetime_hint) ||
|
||||
!CBS_get_u32(&cbs, &session->ticket_flags) ||
|
||||
!CBS_get_u32(&cbs, &session->ticket_age_add) ||
|
||||
!CBS_get_u16_length_prefixed(&cbs, &extensions) ||
|
||||
!CBS_get_u16_length_prefixed(&cbs, &ticket) ||
|
||||
!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
|
||||
CBS_len(&cbs) != 0) {
|
||||
SSL_SESSION_free(session);
|
||||
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
|
||||
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
session->ticket_age_add_valid = 1;
|
||||
session->not_resumable = 0;
|
||||
|
||||
if (ssl->ctx->new_session_cb != NULL &&
|
||||
ssl->ctx->new_session_cb(ssl, session)) {
|
||||
/* |new_session_cb|'s return value signals that it took ownership. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_SESSION_free(session);
|
||||
return 1;
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ enum server_hs_state_t {
|
||||
state_process_client_certificate,
|
||||
state_process_client_certificate_verify,
|
||||
state_process_client_finished,
|
||||
state_send_new_session_ticket,
|
||||
state_flush_new_session_ticket,
|
||||
state_done,
|
||||
};
|
||||
|
||||
@ -506,10 +508,55 @@ static enum ssl_hs_wait_t do_process_client_finished(SSL *ssl,
|
||||
}
|
||||
|
||||
ssl->method->received_flight(ssl);
|
||||
hs->state = state_done;
|
||||
hs->state = state_send_new_session_ticket;
|
||||
return ssl_hs_ok;
|
||||
}
|
||||
|
||||
static enum ssl_hs_wait_t do_send_new_session_ticket(SSL *ssl,
|
||||
SSL_HANDSHAKE *hs) {
|
||||
SSL_SESSION *session = ssl->s3->new_session;
|
||||
session->ticket_lifetime_hint = session->timeout;
|
||||
session->ticket_flags = SSL_TICKET_ALLOW_DHE_RESUMPTION;
|
||||
if (!RAND_bytes((uint8_t *)&session->ticket_age_add,
|
||||
sizeof(session->ticket_age_add))) {
|
||||
return 0;
|
||||
}
|
||||
session->ticket_age_add_valid = 1;
|
||||
|
||||
CBB cbb, body, ticket;
|
||||
if (!ssl->method->init_message(ssl, &cbb, &body,
|
||||
SSL3_MT_NEW_SESSION_TICKET) ||
|
||||
!CBB_add_u32(&body, session->ticket_lifetime_hint) ||
|
||||
!CBB_add_u32(&body, session->ticket_flags) ||
|
||||
!CBB_add_u32(&body, session->ticket_age_add) ||
|
||||
!CBB_add_u16(&body, 0 /* no ticket extensions */) ||
|
||||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
|
||||
!ssl_encrypt_ticket(ssl, &ticket, session) ||
|
||||
!ssl->method->finish_message(ssl, &cbb)) {
|
||||
CBB_cleanup(&cbb);
|
||||
return ssl_hs_error;
|
||||
}
|
||||
|
||||
hs->session_tickets_sent++;
|
||||
|
||||
hs->state = state_flush_new_session_ticket;
|
||||
return ssl_hs_write_message;
|
||||
}
|
||||
|
||||
/* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the
|
||||
* client makes several connections before getting a renewal. */
|
||||
static const int kNumTickets = 2;
|
||||
|
||||
static enum ssl_hs_wait_t do_flush_new_session_ticket(SSL *ssl,
|
||||
SSL_HANDSHAKE *hs) {
|
||||
if (hs->session_tickets_sent >= kNumTickets) {
|
||||
hs->state = state_done;
|
||||
} else {
|
||||
hs->state = state_send_new_session_ticket;
|
||||
}
|
||||
return ssl_hs_flush;
|
||||
}
|
||||
|
||||
enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl) {
|
||||
SSL_HANDSHAKE *hs = ssl->s3->hs;
|
||||
|
||||
@ -562,6 +609,12 @@ enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl) {
|
||||
case state_process_client_finished:
|
||||
ret = do_process_client_finished(ssl, hs);
|
||||
break;
|
||||
case state_send_new_session_ticket:
|
||||
ret = do_send_new_session_ticket(ssl, hs);
|
||||
break;
|
||||
case state_flush_new_session_ticket:
|
||||
ret = do_flush_new_session_ticket(ssl, hs);
|
||||
break;
|
||||
case state_done:
|
||||
ret = ssl_hs_ok;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user