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_VERSION 0xfeff
|
||||||
#define DTLS1_2_VERSION 0xfefd
|
#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
|
/* SSL_CTX_set_min_version sets the minimum protocol version for |ctx| to
|
||||||
* |version|. */
|
* |version|. */
|
||||||
@ -3702,7 +3702,10 @@ struct ssl_session_st {
|
|||||||
uint8_t original_handshake_hash[EVP_MAX_MD_SIZE];
|
uint8_t original_handshake_hash[EVP_MAX_MD_SIZE];
|
||||||
unsigned original_handshake_hash_len;
|
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
|
/* 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
|
* 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. */
|
/* not_resumable is used to indicate that session resumption is disallowed. */
|
||||||
unsigned not_resumable:1;
|
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
|
/* 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);
|
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||||
goto err;
|
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
|
/* 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
|
* 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);
|
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;
|
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
|
/* Ticket lifetime hint (advisory only): We leave this unspecified for
|
||||||
* resumed session (for simplicity), and guess that tickets for new
|
* resumed session (for simplicity), and guess that tickets for new
|
||||||
* sessions will live as long as their sessions. */
|
* sessions will live as long as their sessions. */
|
||||||
!CBB_add_u32(&body, ssl->session != NULL ? 0 :
|
!CBB_add_u32(&body,
|
||||||
ssl->s3->new_session->timeout) ||
|
ssl->session != NULL ? 0 : ssl->s3->new_session->timeout) ||
|
||||||
!CBB_add_u16_length_prefixed(&body, &ticket)) {
|
!CBB_add_u16_length_prefixed(&body, &ticket) ||
|
||||||
goto err;
|
!ssl_encrypt_ticket(ssl, &ticket, ssl->session != NULL
|
||||||
}
|
? ssl->session
|
||||||
|
: ssl->s3->new_session) ||
|
||||||
/* 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)) ||
|
|
||||||
!ssl->method->finish_message(ssl, &cbb)) {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)) {
|
!ssl->method->finish_message(ssl, &cbb)) {
|
||||||
goto err;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
|
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
|
||||||
ret = ssl->method->write_message(ssl);
|
return ssl->method->write_message(ssl);
|
||||||
|
|
||||||
err:
|
|
||||||
OPENSSL_free(session);
|
|
||||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
|
||||||
HMAC_CTX_cleanup(&hctx);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
@ -891,6 +891,8 @@ struct ssl_handshake_st {
|
|||||||
|
|
||||||
uint8_t *cert_context;
|
uint8_t *cert_context;
|
||||||
size_t cert_context_len;
|
size_t cert_context_len;
|
||||||
|
|
||||||
|
uint8_t session_tickets_sent;
|
||||||
} /* SSL_HANDSHAKE */;
|
} /* SSL_HANDSHAKE */;
|
||||||
|
|
||||||
SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl));
|
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(
|
enum ssl_private_key_result_t tls13_prepare_certificate_verify(
|
||||||
SSL *ssl, int is_first_run);
|
SSL *ssl, int is_first_run);
|
||||||
int tls13_prepare_finished(SSL *ssl);
|
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,
|
int ssl_ext_key_share_parse_serverhello(SSL *ssl, uint8_t **out_secret,
|
||||||
size_t *out_secret_len,
|
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 TLSv1_enc_data;
|
||||||
extern const SSL3_ENC_METHOD SSLv3_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);
|
int ssl_clear_bad_session(SSL *ssl);
|
||||||
CERT *ssl_cert_new(void);
|
CERT *ssl_cert_new(void);
|
||||||
CERT *ssl_cert_dup(CERT *cert);
|
CERT *ssl_cert_dup(CERT *cert);
|
||||||
void ssl_cert_clear_certs(CERT *c);
|
void ssl_cert_clear_certs(CERT *c);
|
||||||
void ssl_cert_free(CERT *c);
|
void ssl_cert_free(CERT *c);
|
||||||
int ssl_get_new_session(SSL *ssl, int is_server);
|
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 {
|
enum ssl_session_result_t {
|
||||||
ssl_session_success,
|
ssl_session_success,
|
||||||
|
@ -120,6 +120,8 @@
|
|||||||
* extendedMasterSecret [17] BOOLEAN OPTIONAL,
|
* extendedMasterSecret [17] BOOLEAN OPTIONAL,
|
||||||
* keyExchangeInfo [18] INTEGER OPTIONAL,
|
* keyExchangeInfo [18] INTEGER OPTIONAL,
|
||||||
* certChain [19] SEQUENCE OF Certificate OPTIONAL,
|
* certChain [19] SEQUENCE OF Certificate OPTIONAL,
|
||||||
|
* ticketFlags [20] INTEGER OPTIONAL,
|
||||||
|
* ticketAgeAdd [21] OCTET STRING OPTIONAL,
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* Note: historically this serialization has included other 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;
|
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18;
|
||||||
static const int kCertChainTag =
|
static const int kCertChainTag =
|
||||||
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19;
|
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,
|
static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
|
||||||
size_t *out_len, int for_ticket) {
|
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) ||
|
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);
|
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||||
goto err;
|
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)) {
|
if (!CBB_finish(&cbb, out_data, out_len)) {
|
||||||
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
|
||||||
goto err;
|
goto err;
|
||||||
@ -573,7 +596,7 @@ static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) {
|
|||||||
kHostNameTag) ||
|
kHostNameTag) ||
|
||||||
!SSL_SESSION_parse_string(&session, &ret->psk_identity,
|
!SSL_SESSION_parse_string(&session, &ret->psk_identity,
|
||||||
kPSKIdentityTag) ||
|
kPSKIdentityTag) ||
|
||||||
!SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint,
|
!SSL_SESSION_parse_u32(&session, &ret->ticket_lifetime_hint,
|
||||||
kTicketLifetimeHintTag, 0) ||
|
kTicketLifetimeHintTag, 0) ||
|
||||||
!SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick,
|
!SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick,
|
||||||
&ret->tlsext_ticklen, kTicketTag)) {
|
&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) {
|
if (CBS_len(&session) != 0) {
|
||||||
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
|
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -219,8 +219,8 @@ SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int include_ticket) {
|
|||||||
}
|
}
|
||||||
if (include_ticket) {
|
if (include_ticket) {
|
||||||
if (session->tlsext_tick != NULL) {
|
if (session->tlsext_tick != NULL) {
|
||||||
new_session->tlsext_tick = BUF_memdup(session->tlsext_tick,
|
new_session->tlsext_tick =
|
||||||
session->tlsext_ticklen);
|
BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
|
||||||
if (new_session->tlsext_tick == NULL) {
|
if (new_session->tlsext_tick == NULL) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -252,7 +252,9 @@ SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int include_ticket) {
|
|||||||
session->original_handshake_hash_len);
|
session->original_handshake_hash_len);
|
||||||
new_session->original_handshake_hash_len =
|
new_session->original_handshake_hash_len =
|
||||||
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->extended_master_secret = session->extended_master_secret;
|
||||||
new_session->peer_sha256_valid = session->peer_sha256_valid;
|
new_session->peer_sha256_valid = session->peer_sha256_valid;
|
||||||
new_session->not_resumable = 1;
|
new_session->not_resumable = 1;
|
||||||
@ -468,6 +470,93 @@ err:
|
|||||||
return 0;
|
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
|
/* 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
|
* |*out_session| to an |SSL_SESSION| object if found. The caller takes
|
||||||
* ownership of the result. */
|
* ownership of the result. */
|
||||||
|
@ -1028,6 +1028,14 @@ static int DoSendFatalAlert(SSL *ssl, uint8_t alert) {
|
|||||||
return ret;
|
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
|
// CheckHandshakeProperties checks, immediately after |ssl| completes its
|
||||||
// initial handshake (or False Starts), whether all the properties are
|
// initial handshake (or False Starts), whether all the properties are
|
||||||
// consistent with the test configuration and invariants.
|
// consistent with the test configuration and invariants.
|
||||||
@ -1057,8 +1065,8 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
|
|||||||
bool expect_new_session =
|
bool expect_new_session =
|
||||||
!config->expect_no_session &&
|
!config->expect_no_session &&
|
||||||
(!SSL_session_reused(ssl) || config->expect_ticket_renewal) &&
|
(!SSL_session_reused(ssl) || config->expect_ticket_renewal) &&
|
||||||
/* TODO(svaldez): Implement Session Resumption. */
|
// Session tickets are sent post-handshake in TLS 1.3.
|
||||||
SSL_version(ssl) != TLS1_3_VERSION;
|
GetProtocolVersion(ssl) < TLS1_3_VERSION;
|
||||||
if (expect_new_session != GetTestState(ssl)->got_new_session) {
|
if (expect_new_session != GetTestState(ssl)->got_new_session) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"new session was%s cached, but we expected the opposite\n",
|
"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 &&
|
if (!config->is_server && !config->false_start &&
|
||||||
!config->implicit_handshake &&
|
!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) {
|
GetTestState(ssl.get())->got_new_session) {
|
||||||
fprintf(stderr, "new session was established after the handshake\n");
|
fprintf(stderr, "new session was established after the handshake\n");
|
||||||
return false;
|
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) {
|
if (out_session) {
|
||||||
out_session->reset(SSL_get1_session(ssl.get()));
|
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 &&
|
if (ssl->s3->tmp.message_type == SSL3_MT_NEW_SESSION_TICKET &&
|
||||||
!ssl->server) {
|
!ssl->server) {
|
||||||
// TODO(svaldez): Handle NewSessionTicket.
|
return tls13_process_new_session_ticket(ssl);
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(svaldez): Handle post-handshake authentication.
|
// TODO(svaldez): Handle post-handshake authentication.
|
||||||
|
@ -566,3 +566,38 @@ enum ssl_hs_wait_t tls13_client_handshake(SSL *ssl) {
|
|||||||
|
|
||||||
return ssl_hs_ok;
|
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,
|
||||||
state_process_client_certificate_verify,
|
state_process_client_certificate_verify,
|
||||||
state_process_client_finished,
|
state_process_client_finished,
|
||||||
|
state_send_new_session_ticket,
|
||||||
|
state_flush_new_session_ticket,
|
||||||
state_done,
|
state_done,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -506,10 +508,55 @@ static enum ssl_hs_wait_t do_process_client_finished(SSL *ssl,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ssl->method->received_flight(ssl);
|
ssl->method->received_flight(ssl);
|
||||||
hs->state = state_done;
|
hs->state = state_send_new_session_ticket;
|
||||||
return ssl_hs_ok;
|
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) {
|
enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl) {
|
||||||
SSL_HANDSHAKE *hs = ssl->s3->hs;
|
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:
|
case state_process_client_finished:
|
||||||
ret = do_process_client_finished(ssl, hs);
|
ret = do_process_client_finished(ssl, hs);
|
||||||
break;
|
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:
|
case state_done:
|
||||||
ret = ssl_hs_ok;
|
ret = ssl_hs_ok;
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user