Adding code to send session as PSK Identity.
BUG=75
Change-Id: Ied864cfccbc0e68d71c55c5ab563da27b7253463
Reviewed-on: https://boringssl-review.googlesource.com/9043
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>
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 34c1adc..d7b1a2c 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -514,7 +514,7 @@
* of the new established_session due to False Start. The caller may
* have taken a reference to the temporary session. */
ssl->s3->established_session =
- SSL_SESSION_dup(ssl->s3->new_session, 1 /* include ticket */);
+ SSL_SESSION_dup(ssl->s3->new_session, SSL_SESSION_DUP_ALL);
if (ssl->s3->established_session == NULL) {
/* Do not stay in SSL_ST_OK, to avoid confusing |SSL_in_init|
* callers. */
@@ -605,6 +605,16 @@
if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
return 0;
}
+ /* Add PSK ciphers for TLS 1.3 resumption. */
+ if (ssl->session != NULL &&
+ ssl->method->version_from_wire(ssl->session->ssl_version) >=
+ TLS1_3_VERSION) {
+ uint16_t resumption_cipher;
+ if (ssl_cipher_get_ecdhe_psk_cipher(cipher, &resumption_cipher) &&
+ !CBB_add_u16(&child, resumption_cipher)) {
+ return 0;
+ }
+ }
}
/* If all ciphers were disabled, return the error to the caller. */
@@ -708,10 +718,10 @@
if (ssl->session != NULL) {
uint16_t session_version =
ssl->method->version_from_wire(ssl->session->ssl_version);
- struct timeval now;
- ssl_get_current_time(ssl, &now);
- if (ssl->session->session_id_length == 0 || ssl->session->not_resumable ||
- ssl->session->timeout < (long)now.tv_sec - ssl->session->time ||
+ if ((session_version < TLS1_3_VERSION &&
+ ssl->session->session_id_length == 0) ||
+ ssl->session->not_resumable ||
+ !ssl_session_is_time_valid(ssl, ssl->session) ||
session_version < min_version || session_version > max_version) {
SSL_set_session(ssl, NULL);
}
@@ -885,18 +895,11 @@
goto f_err;
}
- assert(ssl->session == NULL || ssl->session->session_id_length > 0);
if (!ssl->s3->initial_handshake_complete && ssl->session != NULL &&
+ ssl->session->session_id_length != 0 &&
CBS_mem_equal(&session_id, ssl->session->session_id,
ssl->session->session_id_length)) {
- if (ssl->sid_ctx_length != ssl->session->sid_ctx_length ||
- memcmp(ssl->session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length)) {
- /* actually a client application bug */
- al = SSL_AD_ILLEGAL_PARAMETER;
- OPENSSL_PUT_ERROR(SSL,
- SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
- goto f_err;
- }
+ ssl->s3->session_reused = 1;
} else {
/* The session wasn't resumed. Create a fresh SSL_SESSION to
* fill out. */
@@ -946,6 +949,13 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
goto f_err;
}
+ if (!ssl_session_is_context_valid(ssl, ssl->session)) {
+ /* This is actually a client application bug. */
+ al = SSL_AD_ILLEGAL_PARAMETER;
+ OPENSSL_PUT_ERROR(SSL,
+ SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
+ goto f_err;
+ }
} else {
ssl->s3->new_session->cipher = c;
}
@@ -1935,8 +1945,7 @@
/* The server is sending a new ticket for an existing session. Sessions are
* immutable once established, so duplicate all but the ticket of the
* existing session. */
- session = SSL_SESSION_dup(ssl->session,
- 0 /* Don't duplicate session ticket */);
+ session = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
if (session == NULL) {
/* This should never happen. */
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 4e7aae2..4818fe5 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -713,6 +713,7 @@
ssl->session = session;
session = NULL;
ssl->verify_result = ssl->session->verify_result;
+ ssl->s3->session_reused = 1;
} else {
SSL_set_session(ssl, NULL);
if (!ssl_get_new_session(ssl, 1 /* server */)) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 4fcf4b9..ade9416 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -241,6 +241,11 @@
/* ssl_cipher_get_value returns the cipher suite id of |cipher|. */
uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher);
+/* ssl_cipher_get_resumption_cipher returns the cipher suite id of the cipher
+ * matching |cipher| with PSK enabled. */
+int ssl_cipher_get_ecdhe_psk_cipher(const SSL_CIPHER *cipher,
+ uint16_t *out_cipher);
+
/* ssl_cipher_get_key_type returns the |EVP_PKEY_*| value corresponding to the
* server key used in |cipher| or |EVP_PKEY_NONE| if there is none. */
int ssl_cipher_get_key_type(const SSL_CIPHER *cipher);
@@ -848,6 +853,18 @@
* 0 for the Client Finished. */
int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server);
+/* tls13_resumption_psk calculates the PSK to use for the resumption of
+ * |session| and stores the result in |out|. It returns one on success, and
+ * zero on failure. */
+int tls13_resumption_psk(SSL *ssl, uint8_t *out, size_t out_len,
+ const SSL_SESSION *session);
+
+/* tls13_resumption_context derives the context to be used for the handshake
+ * transcript on the resumption of |session|. It returns one on success, and
+ * zero on failure. */
+int tls13_resumption_context(SSL *ssl, uint8_t *out, size_t out_len,
+ const SSL_SESSION *session);
+
/* Handshake functions. */
@@ -938,6 +955,13 @@
uint8_t *out_alert, CBS *contents);
int ssl_ext_key_share_add_serverhello(SSL *ssl, CBB *out);
+int ssl_ext_pre_shared_key_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+ CBS *contents);
+int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
+ SSL_SESSION **out_session,
+ uint8_t *out_alert, CBS *contents);
+int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out);
+
int ssl_add_client_hello_body(SSL *ssl, CBB *body);
@@ -1232,6 +1256,14 @@
int ssl_get_new_session(SSL *ssl, int is_server);
int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
+/* ssl_session_is_context_valid returns one if |session|'s session ID context
+ * matches the one set on |ssl| and zero otherwise. */
+int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session);
+
+/* ssl_session_is_time_valid returns one if |session| is still valid and zero if
+ * it has expired. */
+int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session);
+
enum ssl_session_result_t {
ssl_session_success,
ssl_session_error,
@@ -1248,11 +1280,18 @@
SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
const struct ssl_early_callback_ctx *ctx);
+/* The following flags determine which parts of the session are duplicated. */
+#define SSL_SESSION_DUP_AUTH_ONLY 0x0
+#define SSL_SESSION_INCLUDE_TICKET 0x1
+#define SSL_SESSION_INCLUDE_NONAUTH 0x2
+#define SSL_SESSION_DUP_ALL \
+ (SSL_SESSION_INCLUDE_TICKET | SSL_SESSION_INCLUDE_NONAUTH)
+
/* SSL_SESSION_dup returns a newly-allocated |SSL_SESSION| with a copy of the
* fields in |session| or NULL on error. The new session is non-resumable and
* must be explicitly marked resumable once it has been filled in. */
OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session,
- int include_ticket);
+ int dup_flags);
void ssl_cipher_preference_list_free(
struct ssl_cipher_preference_list_st *cipher_list);
diff --git a/ssl/ssl_cipher.c b/ssl/ssl_cipher.c
index 3810667..079c823 100644
--- a/ssl/ssl_cipher.c
+++ b/ssl/ssl_cipher.c
@@ -1660,6 +1660,30 @@
return id & 0xffff;
}
+int ssl_cipher_get_ecdhe_psk_cipher(const SSL_CIPHER *cipher,
+ uint16_t *out_cipher) {
+ switch (cipher->id) {
+ case TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256:
+ *out_cipher = TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 & 0xffff;
+ return 1;
+
+ case TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256:
+ *out_cipher = TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256 & 0xffff;
+ return 1;
+
+ case TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case TLS1_CK_ECDHE_PSK_WITH_AES_256_GCM_SHA384:
+ *out_cipher = TLS1_CK_ECDHE_PSK_WITH_AES_256_GCM_SHA384 & 0xffff;
+ return 1;
+ }
+ return 0;
+}
+
int SSL_CIPHER_is_AES(const SSL_CIPHER *cipher) {
return (cipher->algorithm_enc & SSL_AES) != 0;
}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 1a950ad..e7830a1 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2162,7 +2162,7 @@
}
int SSL_session_reused(const SSL *ssl) {
- return ssl->session != NULL;
+ return ssl->s3->session_reused;
}
const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; }
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index 1d634d8..e26866b 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -175,22 +175,23 @@
return session;
}
-SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int include_ticket) {
+SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int dup_flags) {
SSL_SESSION *new_session = SSL_SESSION_new();
if (new_session == NULL) {
goto err;
}
new_session->ssl_version = session->ssl_version;
- new_session->key_exchange_info = session->key_exchange_info;
+ new_session->sid_ctx_length = session->sid_ctx_length;
+ memcpy(new_session->sid_ctx, session->sid_ctx, session->sid_ctx_length);
+
+ /* Copy the key material. */
new_session->master_key_length = session->master_key_length;
memcpy(new_session->master_key, session->master_key,
session->master_key_length);
- new_session->session_id_length = session->session_id_length;
- memcpy(new_session->session_id, session->session_id,
- session->session_id_length);
- new_session->sid_ctx_length = session->sid_ctx_length;
- memcpy(new_session->sid_ctx, session->sid_ctx, session->sid_ctx_length);
+ new_session->cipher = session->cipher;
+
+ /* Copy authentication state. */
if (session->psk_identity != NULL) {
new_session->psk_identity = BUF_strdup(session->psk_identity);
if (new_session->psk_identity == NULL) {
@@ -208,26 +209,15 @@
}
}
new_session->verify_result = session->verify_result;
- new_session->timeout = session->timeout;
- new_session->time = session->time;
- new_session->cipher = session->cipher;
- /* The new_session does not get a copy of the ex_data. */
- if (session->tlsext_hostname != NULL) {
- new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
- if (new_session->tlsext_hostname == NULL) {
+
+ new_session->ocsp_response_length = session->ocsp_response_length;
+ if (session->ocsp_response != NULL) {
+ new_session->ocsp_response = BUF_memdup(session->ocsp_response,
+ session->ocsp_response_length);
+ if (new_session->ocsp_response == NULL) {
goto err;
}
}
- if (include_ticket) {
- if (session->tlsext_tick != NULL) {
- new_session->tlsext_tick =
- BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
- if (new_session->tlsext_tick == NULL) {
- goto err;
- }
- }
- new_session->tlsext_ticklen = session->tlsext_ticklen;
- }
new_session->tlsext_signed_cert_timestamp_list_length =
session->tlsext_signed_cert_timestamp_list_length;
@@ -239,25 +229,52 @@
goto err;
}
}
- new_session->ocsp_response_length = session->ocsp_response_length;
- if (session->ocsp_response != NULL) {
- new_session->ocsp_response = BUF_memdup(session->ocsp_response,
- session->ocsp_response_length);
- if (new_session->ocsp_response == NULL) {
- goto err;
- }
- }
+
memcpy(new_session->peer_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH);
- memcpy(new_session->original_handshake_hash,
- session->original_handshake_hash,
- 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_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;
+
+ /* Copy non-authentication connection properties. */
+ if (dup_flags & SSL_SESSION_INCLUDE_NONAUTH) {
+ new_session->session_id_length = session->session_id_length;
+ memcpy(new_session->session_id, session->session_id,
+ session->session_id_length);
+
+ new_session->key_exchange_info = session->key_exchange_info;
+ new_session->timeout = session->timeout;
+ new_session->time = session->time;
+
+ if (session->tlsext_hostname != NULL) {
+ new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
+ if (new_session->tlsext_hostname == NULL) {
+ goto err;
+ }
+ }
+
+ memcpy(new_session->original_handshake_hash,
+ session->original_handshake_hash,
+ 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_flags = session->ticket_flags;
+ new_session->ticket_age_add = session->ticket_age_add;
+ new_session->extended_master_secret = session->extended_master_secret;
+ }
+
+ /* Copy the ticket. */
+ if (dup_flags & SSL_SESSION_INCLUDE_TICKET) {
+ if (session->tlsext_tick != NULL) {
+ new_session->tlsext_tick =
+ BUF_memdup(session->tlsext_tick, session->tlsext_ticklen);
+ if (new_session->tlsext_tick == NULL) {
+ goto err;
+ }
+ }
+ new_session->tlsext_ticklen = session->tlsext_ticklen;
+ }
+
+ /* The new_session does not get a copy of the ex_data. */
+
new_session->not_resumable = 1;
return new_session;
@@ -564,6 +581,25 @@
return ret;
}
+int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) {
+ if (session == NULL) {
+ return 0;
+ }
+
+ return session->sid_ctx_length == ssl->sid_ctx_length &&
+ memcmp(session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length) == 0;
+}
+
+int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session) {
+ if (session == NULL) {
+ return 0;
+ }
+
+ struct timeval now;
+ ssl_get_current_time(ssl, &now);
+ return session->timeout >= (long)now.tv_sec - session->time;
+}
+
/* 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. */
@@ -576,7 +612,7 @@
return ssl_session_success;
}
- SSL_SESSION *session;
+ SSL_SESSION *session = NULL;
/* Try the internal cache, if it exists. */
if (!(ssl->initial_ctx->session_cache_mode &
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
@@ -592,39 +628,48 @@
}
/* TODO(davidben): This should probably move it to the front of the list. */
CRYPTO_MUTEX_unlock_read(&ssl->initial_ctx->lock);
-
- if (session != NULL) {
- *out_session = session;
- return ssl_session_success;
- }
}
/* Fall back to the external cache, if it exists. */
- if (ssl->initial_ctx->get_session_cb == NULL) {
- return ssl_session_success;
+ if (session == NULL &&
+ ssl->initial_ctx->get_session_cb != NULL) {
+ int copy = 1;
+ session = ssl->initial_ctx->get_session_cb(ssl, (uint8_t *)session_id,
+ session_id_len, ©);
+
+ if (session == SSL_magic_pending_session_ptr()) {
+ return ssl_session_retry;
+ }
+
+ /* Increment reference count now if the session callback asks us to do so
+ * (note that if the session structures returned by the callback are shared
+ * between threads, it must handle the reference count itself [i.e. copy ==
+ * 0], or things won't be thread-safe). */
+ if (copy) {
+ SSL_SESSION_up_ref(session);
+ }
+
+ /* Add the externally cached session to the internal cache if necessary. */
+ if (session != NULL &&
+ !(ssl->initial_ctx->session_cache_mode &
+ SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
+ SSL_CTX_add_session(ssl->initial_ctx, session);
+ }
}
- int copy = 1;
- session = ssl->initial_ctx->get_session_cb(ssl, (uint8_t *)session_id,
- session_id_len, ©);
+
if (session == NULL) {
return ssl_session_success;
}
- if (session == SSL_magic_pending_session_ptr()) {
- return ssl_session_retry;
- }
- /* Increment reference count now if the session callback asks us to do so
- * (note that if the session structures returned by the callback are shared
- * between threads, it must handle the reference count itself [i.e. copy ==
- * 0], or things won't be thread-safe). */
- if (copy) {
- SSL_SESSION_up_ref(session);
- }
-
- /* Add the externally cached session to the internal cache if necessary. */
- if (!(ssl->initial_ctx->session_cache_mode &
- SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
- SSL_CTX_add_session(ssl->initial_ctx, session);
+ if (!ssl_session_is_context_valid(ssl, session)) {
+ /* The client did not offer a suitable ticket or session ID. */
+ SSL_SESSION_free(session);
+ session = NULL;
+ } else if (!ssl_session_is_time_valid(ssl, session)) {
+ /* The session was from the cache, so remove it. */
+ SSL_CTX_remove_session(ssl->initial_ctx, session);
+ SSL_SESSION_free(session);
+ session = NULL;
}
*out_session = session;
@@ -647,7 +692,6 @@
ssl->version > SSL3_VERSION &&
SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_session_ticket,
&ticket, &ticket_len);
- int from_cache = 0;
if (tickets_supported && ticket_len > 0) {
if (!tls_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
ctx->session_id, ctx->session_id_len)) {
@@ -660,35 +704,14 @@
if (lookup_ret != ssl_session_success) {
return lookup_ret;
}
- from_cache = 1;
- }
-
- if (session == NULL ||
- session->sid_ctx_length != ssl->sid_ctx_length ||
- memcmp(session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length) != 0) {
- /* The client did not offer a suitable ticket or session ID. If supported,
- * the new session should use a ticket. */
- goto no_session;
- }
-
- struct timeval now;
- ssl_get_current_time(ssl, &now);
- if (session->timeout < (long)now.tv_sec - session->time) {
- if (from_cache) {
- /* The session was from the cache, so remove it. */
- SSL_CTX_remove_session(ssl->initial_ctx, session);
- }
- goto no_session;
}
*out_session = session;
- *out_send_ticket = renew_ticket;
- return ssl_session_success;
-
-no_session:
- *out_session = NULL;
- *out_send_ticket = tickets_supported;
- SSL_SESSION_free(session);
+ if (session != NULL) {
+ *out_send_ticket = renew_ticket;
+ } else {
+ *out_send_ticket = tickets_supported;
+ }
return ssl_session_success;
}
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 4c4f6d9..f73c37d 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1301,7 +1301,7 @@
}
SSL_SESSION *session0 = SSL_get_session(client.get());
- ScopedSSL_SESSION session1(SSL_SESSION_dup(session0, 1));
+ ScopedSSL_SESSION session1(SSL_SESSION_dup(session0, SSL_SESSION_DUP_ALL));
if (!session1) {
return false;
}
@@ -1796,11 +1796,6 @@
static const uint8_t kContext2[] = {2};
for (uint16_t version : kVersions) {
- // TODO(davidben): Enable this when TLS 1.3 resumption is implemented.
- if (version == TLS1_3_VERSION) {
- continue;
- }
-
ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
if (!server_ctx || !client_ctx ||
@@ -1864,11 +1859,6 @@
}
for (uint16_t version : kVersions) {
- // TODO(davidben): Enable this when TLS 1.3 resumption is implemented.
- if (version == TLS1_3_VERSION) {
- continue;
- }
-
ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
if (!server_ctx || !client_ctx ||
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index bb345b4..dc6c6dd 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1030,7 +1030,10 @@
* without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */
if (!ssl->s3->initial_handshake_complete &&
ssl->session != NULL &&
- ssl->session->tlsext_tick != NULL) {
+ ssl->session->tlsext_tick != NULL &&
+ /* Don't send TLS 1.3 session tickets in the ticket extension. */
+ ssl->method->version_from_wire(ssl->session->ssl_version) <
+ TLS1_3_VERSION) {
ticket_data = ssl->session->tlsext_tick;
ticket_len = ssl->session->tlsext_ticklen;
}
@@ -1428,7 +1431,7 @@
}
/* Session resumption uses the original session information. */
- if (ssl->session == NULL &&
+ if (!ssl->s3->session_reused &&
!CBS_stow(
contents,
&ssl->s3->new_session->tlsext_signed_cert_timestamp_list,
@@ -1447,7 +1450,7 @@
static int ext_sct_add_serverhello(SSL *ssl, CBB *out) {
/* The extension shouldn't be sent when resuming sessions. */
- if (ssl->session != NULL ||
+ if (ssl->s3->session_reused ||
ssl->ctx->signed_cert_timestamp_list_length == 0) {
return 1;
}
@@ -1972,6 +1975,89 @@
}
+/* Pre Shared Key
+ *
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-14 */
+
+static int ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
+ uint16_t min_version, max_version;
+ if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+ return 0;
+ }
+
+ if (max_version < TLS1_3_VERSION || ssl->session == NULL ||
+ ssl->method->version_from_wire(ssl->session->ssl_version) <
+ TLS1_3_VERSION) {
+ return 1;
+ }
+
+ CBB contents, identities, identity;
+ if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
+ !CBB_add_u16_length_prefixed(out, &contents) ||
+ !CBB_add_u16_length_prefixed(&contents, &identities) ||
+ !CBB_add_u16_length_prefixed(&identities, &identity) ||
+ !CBB_add_bytes(&identity, ssl->session->tlsext_tick,
+ ssl->session->tlsext_ticklen)) {
+ return 0;
+ }
+
+ return CBB_flush(out);
+}
+
+int ssl_ext_pre_shared_key_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+ CBS *contents) {
+ uint16_t psk_id;
+ if (!CBS_get_u16(contents, &psk_id) ||
+ CBS_len(contents) != 0) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ if (psk_id != 0) {
+ *out_alert = SSL_AD_UNKNOWN_PSK_IDENTITY;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
+ SSL_SESSION **out_session,
+ uint8_t *out_alert,
+ CBS *contents) {
+ CBS identities, identity;
+ if (!CBS_get_u16_length_prefixed(contents, &identities) ||
+ !CBS_get_u16_length_prefixed(&identities, &identity) ||
+ CBS_len(contents) != 0) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ /* TLS 1.3 session tickets are renewed separately as part of the
+ * NewSessionTicket. */
+ int renew;
+ return tls_process_ticket(ssl, out_session, &renew, CBS_data(&identity),
+ CBS_len(&identity), NULL, 0);
+}
+
+int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out) {
+ if (!ssl->s3->session_reused) {
+ return 1;
+ }
+
+ CBB contents;
+ if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
+ !CBB_add_u16_length_prefixed(out, &contents) ||
+ /* We only consider the first identity for resumption */
+ !CBB_add_u16(&contents, 0) ||
+ !CBB_flush(out)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
/* Key Share
*
* https://tools.ietf.org/html/draft-ietf-tls-tls13-12 */
@@ -2361,6 +2447,14 @@
ignore_parse_clienthello,
dont_add_serverhello,
},
+ {
+ TLSEXT_TYPE_pre_shared_key,
+ NULL,
+ ext_pre_shared_key_add_clienthello,
+ forbid_parse_serverhello,
+ ignore_parse_clienthello,
+ dont_add_serverhello,
+ },
/* The final extension must be non-empty. WebSphere Application Server 7.0 is
* intolerant to the last extension being zero-length. See
* https://crbug.com/363583. */
@@ -2786,6 +2880,10 @@
*out_renew_ticket = 0;
*out_session = NULL;
+ if (SSL_get_options(ssl) & SSL_OP_NO_TICKET) {
+ goto done;
+ }
+
if (session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
goto done;
}
@@ -2875,6 +2973,12 @@
memcpy(session->session_id, session_id, session_id_len);
session->session_id_length = session_id_len;
+ if (!ssl_session_is_context_valid(ssl, session) ||
+ !ssl_session_is_time_valid(ssl, session)) {
+ SSL_SESSION_free(session);
+ session = NULL;
+ }
+
*out_session = session;
done:
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 2a4db6b..13229e9 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -101,6 +101,7 @@
// operation has been retried.
unsigned private_key_retries = 0;
bool got_new_session = false;
+ ScopedSSL_SESSION new_session;
bool ticket_decrypt_done = false;
bool alpn_select_done = false;
};
@@ -645,8 +646,7 @@
static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
GetTestState(ssl)->got_new_session = true;
- // BoringSSL passes a reference to |session|.
- SSL_SESSION_free(session);
+ GetTestState(ssl)->new_session.reset(session);
return 1;
}
@@ -1621,7 +1621,7 @@
}
if (out_session) {
- out_session->reset(SSL_get1_session(ssl.get()));
+ *out_session = std::move(GetTestState(ssl.get())->new_session);
}
ret = DoShutdown(ssl.get());
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 45d3e13..d064919 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2313,9 +2313,6 @@
expectedClientError = ":WRONG_CIPHER_RETURNED:"
}
- // TODO(davidben,svaldez): Implement resumption for TLS 1.3.
- resumeSession := ver.version < VersionTLS13
-
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
@@ -2336,7 +2333,7 @@
certFile: certFile,
keyFile: keyFile,
flags: flags,
- resumeSession: resumeSession,
+ resumeSession: true,
shouldFail: shouldServerFail,
expectedError: expectedServerError,
})
@@ -2358,7 +2355,7 @@
},
},
flags: flags,
- resumeSession: resumeSession,
+ resumeSession: true,
shouldFail: shouldClientFail,
expectedError: expectedClientError,
})
@@ -3426,9 +3423,7 @@
base64.StdEncoding.EncodeToString(testOCSPResponse),
"-verify-peer",
},
- // TODO(davidben): Enable this when resumption is implemented
- // in TLS 1.3.
- resumeSession: false,
+ resumeSession: true,
})
tests = append(tests, testCase{
testType: serverTest,
@@ -3441,9 +3436,7 @@
"-ocsp-response",
base64.StdEncoding.EncodeToString(testOCSPResponse),
},
- // TODO(davidben): Enable this when resumption is implemented
- // in TLS 1.3.
- resumeSession: false,
+ resumeSession: true,
})
// Certificate verification tests.
@@ -3474,9 +3467,7 @@
flag,
"-expect-verify-result",
},
- // TODO(davidben): Enable this when resumption is
- // implemented in TLS 1.3.
- resumeSession: vers.version != VersionTLS13,
+ resumeSession: true,
})
tests = append(tests, testCase{
testType: testType,
@@ -3507,9 +3498,7 @@
"-verify-fail",
"-expect-verify-result",
},
- // TODO(davidben): Enable this when resumption is
- // implemented in TLS 1.3.
- resumeSession: vers.version != VersionTLS13,
+ resumeSession: true,
})
}
@@ -3822,7 +3811,6 @@
func addDDoSCallbackTests() {
// DDoS callback.
- // TODO(davidben): Implement DDoS resumption tests for TLS 1.3.
for _, resume := range []bool{false, true} {
suffix := "Resume"
if resume {
@@ -3838,17 +3826,15 @@
flags: []string{"-install-ddos-callback"},
resumeSession: resume,
})
- if !resume {
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Server-DDoS-OK-" + suffix + "-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- },
- flags: []string{"-install-ddos-callback"},
- resumeSession: resume,
- })
- }
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Server-DDoS-OK-" + suffix + "-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ flags: []string{"-install-ddos-callback"},
+ resumeSession: resume,
+ })
failFlag := "-fail-ddos-callback"
if resume {
@@ -3865,19 +3851,17 @@
shouldFail: true,
expectedError: ":CONNECTION_REJECTED:",
})
- if !resume {
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Server-DDoS-Reject-" + suffix + "-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- },
- flags: []string{"-install-ddos-callback", failFlag},
- resumeSession: resume,
- shouldFail: true,
- expectedError: ":CONNECTION_REJECTED:",
- })
- }
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Server-DDoS-Reject-" + suffix + "-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ flags: []string{"-install-ddos-callback", failFlag},
+ resumeSession: resume,
+ shouldFail: true,
+ expectedError: ":CONNECTION_REJECTED:",
+ })
}
}
@@ -4223,9 +4207,6 @@
continue
}
- // TODO(davidben): Implement resumption in TLS 1.3.
- resumeSession := ver.version < VersionTLS13
-
// Test that duplicate extensions are rejected.
testCases = append(testCases, testCase{
testType: clientTest,
@@ -4297,7 +4278,7 @@
ServerName: "example.com",
},
flags: []string{"-expect-server-name", "example.com"},
- resumeSession: resumeSession,
+ resumeSession: true,
})
// Test ALPN.
@@ -4314,7 +4295,7 @@
},
expectedNextProto: "foo",
expectedNextProtoType: alpn,
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
testType: clientTest,
@@ -4345,7 +4326,7 @@
},
expectedNextProto: "foo",
expectedNextProtoType: alpn,
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
testType: serverTest,
@@ -4356,7 +4337,7 @@
},
flags: []string{"-decline-alpn"},
expectNoNextProto: true,
- resumeSession: resumeSession,
+ resumeSession: true,
})
// Test ALPN in async mode as well to ensure that extensions callbacks are only
@@ -4375,7 +4356,7 @@
},
expectedNextProto: "foo",
expectedNextProtoType: alpn,
- resumeSession: resumeSession,
+ resumeSession: true,
})
var emptyString string
@@ -4430,7 +4411,7 @@
},
expectedNextProto: "foo",
expectedNextProtoType: alpn,
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
testType: serverTest,
@@ -4449,7 +4430,7 @@
},
expectedNextProto: "foo",
expectedNextProtoType: alpn,
- resumeSession: resumeSession,
+ resumeSession: true,
})
// Test that negotiating both NPN and ALPN is forbidden.
@@ -4503,66 +4484,65 @@
}
// Test ticket behavior.
- //
- // TODO(davidben): Add TLS 1.3 versions of these.
+
+ // Resume with a corrupt ticket.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CorruptTicket-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ CorruptTicket: true,
+ },
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+ // Test the ticket callback, with and without renewal.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TicketCallback-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ },
+ resumeSession: true,
+ flags: []string{"-use-ticket-callback"},
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TicketCallback-Renew-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ },
+ },
+ flags: []string{"-use-ticket-callback", "-renew-ticket"},
+ resumeSession: true,
+ })
+
+ // Test that the ticket callback is only called once when everything before
+ // it in the ClientHello is asynchronous. This corrupts the ticket so
+ // certificate selection callbacks run.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TicketCallback-SingleCall-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ CorruptTicket: true,
+ },
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ flags: []string{
+ "-use-ticket-callback",
+ "-async",
+ },
+ })
+
+ // Resume with an oversized session id.
if ver.version < VersionTLS13 {
- // Resume with a corrupt ticket.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "CorruptTicket-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- CorruptTicket: true,
- },
- },
- resumeSession: true,
- expectResumeRejected: true,
- })
- // Test the ticket callback, with and without renewal.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketCallback-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- },
- resumeSession: true,
- flags: []string{"-use-ticket-callback"},
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketCallback-Renew-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- ExpectNewTicket: true,
- },
- },
- flags: []string{"-use-ticket-callback", "-renew-ticket"},
- resumeSession: true,
- })
-
- // Test that the ticket callback is only called once when everything before
- // it in the ClientHello is asynchronous. This corrupts the ticket so
- // certificate selection callbacks run.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TicketCallback-SingleCall-" + ver.name,
- config: Config{
- MaxVersion: ver.version,
- Bugs: ProtocolBugs{
- CorruptTicket: true,
- },
- },
- resumeSession: true,
- expectResumeRejected: true,
- flags: []string{
- "-use-ticket-callback",
- "-async",
- },
- })
-
- // Resume with an oversized session id.
testCases = append(testCases, testCase{
testType: serverTest,
name: "OversizedSessionId-" + ver.name,
@@ -4674,7 +4654,7 @@
"-expect-signed-cert-timestamps",
base64.StdEncoding.EncodeToString(testSCTList),
},
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
name: "SendSCTListOnResume-" + ver.name,
@@ -4689,7 +4669,7 @@
"-expect-signed-cert-timestamps",
base64.StdEncoding.EncodeToString(testSCTList),
},
- resumeSession: resumeSession,
+ resumeSession: true,
})
testCases = append(testCases, testCase{
name: "SignedCertificateTimestampList-Server-" + ver.name,
@@ -4702,7 +4682,7 @@
base64.StdEncoding.EncodeToString(testSCTList),
},
expectedSCTList: testSCTList,
- resumeSession: resumeSession,
+ resumeSession: true,
})
}
@@ -4874,14 +4854,7 @@
func addResumptionVersionTests() {
for _, sessionVers := range tlsVersions {
- // TODO(davidben,svaldez): Implement resumption in TLS 1.3.
- if sessionVers.version >= VersionTLS13 {
- continue
- }
for _, resumeVers := range tlsVersions {
- if resumeVers.version >= VersionTLS13 {
- continue
- }
cipher := TLS_RSA_WITH_AES_128_CBC_SHA
if sessionVers.version >= VersionTLS13 || resumeVers.version >= VersionTLS13 {
// TLS 1.3 only shares ciphers with TLS 1.2, so
@@ -4916,6 +4889,14 @@
expectedResumeVersion: resumeVers.version,
})
} else {
+ var localError, error string
+ if (resumeVers.version >= VersionTLS13) != (sessionVers.version >= VersionTLS13) {
+ // TLS 1.3 sessions are incompatible with TLS 1.2 sessions.
+ localError = "didResume is false, but we expected the opposite"
+ } else {
+ error = ":OLD_SESSION_VERSION_NOT_RETURNED:"
+ }
+
testCases = append(testCases, testCase{
protocol: protocol,
name: "Resume-Client-Mismatch" + suffix,
@@ -4934,7 +4915,8 @@
},
expectedResumeVersion: resumeVers.version,
shouldFail: true,
- expectedError: ":OLD_SESSION_VERSION_NOT_RETURNED:",
+ expectedLocalError: localError,
+ expectedError: error,
})
}
@@ -4977,7 +4959,6 @@
}
}
- // TODO(davidben): This test should have a TLS 1.3 variant later.
testCases = append(testCases, testCase{
name: "Resume-Client-CipherMismatch",
resumeSession: true,
@@ -4995,6 +4976,24 @@
shouldFail: true,
expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
})
+
+ testCases = append(testCases, testCase{
+ name: "Resume-Client-CipherMismatch-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ SendCipherSuite: TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
+ })
}
func addRenegotiationTests() {
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 61e1414..d58f72d 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -151,8 +151,8 @@
}
/* Parse out the extensions. */
- int have_key_share = 0;
- CBS key_share;
+ int have_key_share = 0, have_pre_shared_key = 0;
+ CBS key_share, pre_shared_key;
while (CBS_len(&extensions) != 0) {
uint16_t type;
CBS extension;
@@ -173,6 +173,15 @@
key_share = extension;
have_key_share = 1;
break;
+ case TLSEXT_TYPE_pre_shared_key:
+ if (have_pre_shared_key) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return ssl_hs_error;
+ }
+ pre_shared_key = extension;
+ have_pre_shared_key = 1;
+ break;
default:
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
@@ -183,10 +192,48 @@
assert(ssl->s3->have_version);
memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE);
- SSL_set_session(ssl, NULL);
- if (!ssl_get_new_session(ssl, 0)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ if (have_pre_shared_key) {
+ if (ssl->session == NULL) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+ return ssl_hs_error;
+ }
+
+ if (!ssl_ext_pre_shared_key_parse_serverhello(ssl, &alert,
+ &pre_shared_key)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+ }
+
+ if (ssl->session->ssl_version != ssl->version) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return ssl_hs_error;
+ }
+
+ if (!ssl_session_is_context_valid(ssl, ssl->session)) {
+ /* This is actually a client application bug. */
+ OPENSSL_PUT_ERROR(SSL,
+ SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return ssl_hs_error;
+ }
+
+ ssl->s3->session_reused = 1;
+ /* Only authentication information carries over in TLS 1.3. */
+ ssl->s3->new_session =
+ SSL_SESSION_dup(ssl->session, SSL_SESSION_DUP_AUTH_ONLY);
+ if (ssl->s3->new_session == NULL) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+ SSL_set_session(ssl, NULL);
+ } else {
+ if (!ssl_get_new_session(ssl, 0)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
}
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
@@ -196,15 +243,26 @@
return ssl_hs_error;
}
- /* Check if the cipher is disabled. */
- if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
- (cipher->algorithm_auth & ssl->cert->mask_a) ||
- SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
- SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
- !sk_SSL_CIPHER_find(ssl_get_ciphers_by_id(ssl), NULL, cipher)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
+ if (!ssl->s3->session_reused) {
+ /* Check if the cipher is disabled. */
+ if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
+ (cipher->algorithm_auth & ssl->cert->mask_a) ||
+ SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
+ SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
+ !sk_SSL_CIPHER_find(ssl_get_ciphers_by_id(ssl), NULL, cipher)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return ssl_hs_error;
+ }
+ } else {
+ uint16_t resumption_cipher;
+ if (!ssl_cipher_get_ecdhe_psk_cipher(ssl->s3->new_session->cipher,
+ &resumption_cipher) ||
+ resumption_cipher != ssl_cipher_get_value(cipher)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ return ssl_hs_error;
+ }
}
ssl->s3->new_session->cipher = cipher;
@@ -212,18 +270,35 @@
/* The PRF hash is now known. Set up the key schedule. */
static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
- size_t hash_len =
+ size_t resumption_ctx_len =
EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
- if (!tls13_init_key_schedule(ssl, kZeroes, hash_len)) {
+ if (ssl->s3->session_reused) {
+ uint8_t resumption_ctx[EVP_MAX_MD_SIZE];
+ if (!tls13_resumption_context(ssl, resumption_ctx, resumption_ctx_len,
+ ssl->s3->new_session) ||
+ !tls13_init_key_schedule(ssl, resumption_ctx, resumption_ctx_len)) {
+ return ssl_hs_error;
+ }
+ } else if (!tls13_init_key_schedule(ssl, kZeroes, resumption_ctx_len)) {
return ssl_hs_error;
}
/* Resolve PSK and incorporate it into the secret. */
if (cipher->algorithm_auth == SSL_aPSK) {
- /* TODO(davidben): Support PSK. */
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return ssl_hs_error;
- } else if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) {
+ if (!ssl->s3->session_reused) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+
+ uint8_t resumption_psk[EVP_MAX_MD_SIZE];
+ if (!tls13_resumption_psk(ssl, resumption_psk, hs->hash_len,
+ ssl->s3->new_session) ||
+ !tls13_advance_key_schedule(ssl, resumption_psk, hs->hash_len)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return ssl_hs_error;
+ }
+ } else if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len)) {
return ssl_hs_error;
}
@@ -237,7 +312,6 @@
uint8_t *dhe_secret;
size_t dhe_secret_len;
- uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ssl_ext_key_share_parse_serverhello(ssl, &dhe_secret, &dhe_secret_len,
&alert, &key_share)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -255,7 +329,7 @@
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
return ssl_hs_error;
}
- if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) {
+ if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len)) {
return ssl_hs_error;
}
}
@@ -568,8 +642,9 @@
}
int tls13_process_new_session_ticket(SSL *ssl) {
- SSL_SESSION *session = SSL_SESSION_dup(ssl->s3->established_session,
- 0 /* don't include ticket */);
+ SSL_SESSION *session =
+ SSL_SESSION_dup(ssl->s3->established_session,
+ SSL_SESSION_INCLUDE_NONAUTH);
if (session == NULL) {
return 0;
}
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 70b041a..518d2d3 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -351,6 +351,28 @@
return 1;
}
+static const char kTLS13LabelResumptionPSK[] = "resumption psk";
+static const char kTLS13LabelResumptionContext[] = "resumption context";
+
+int tls13_resumption_psk(SSL *ssl, uint8_t *out, size_t out_len,
+ const SSL_SESSION *session) {
+ const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+ return hkdf_expand_label(out, digest, session->master_key,
+ session->master_key_length,
+ (const uint8_t *)kTLS13LabelResumptionPSK,
+ strlen(kTLS13LabelResumptionPSK), NULL, 0, out_len);
+}
+
+int tls13_resumption_context(SSL *ssl, uint8_t *out, size_t out_len,
+ const SSL_SESSION *session) {
+ const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+ return hkdf_expand_label(out, digest, session->master_key,
+ session->master_key_length,
+ (const uint8_t *)kTLS13LabelResumptionContext,
+ strlen(kTLS13LabelResumptionContext), NULL, 0,
+ out_len);
+}
+
int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
const char *label, size_t label_len,
const uint8_t *context, size_t context_len,
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index a1aeeea..1f4475e 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -58,9 +58,14 @@
return tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len);
}
- /* TODO(davidben): Support PSK. */
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
+ uint8_t resumption_psk[EVP_MAX_MD_SIZE];
+ if (!tls13_resumption_psk(ssl, resumption_psk, hs->hash_len,
+ ssl->s3->new_session) ||
+ !tls13_advance_key_schedule(ssl, resumption_psk, hs->hash_len)) {
+ return 0;
+ }
+
+ return 1;
}
static int resolve_ecdhe_secret(SSL *ssl, int *out_need_retry,
@@ -123,10 +128,45 @@
}
memcpy(ssl->s3->client_random, client_hello.random, client_hello.random_len);
- SSL_set_session(ssl, NULL);
- if (!ssl_get_new_session(ssl, 1 /* server */)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ SSL_SESSION *session = NULL;
+ CBS pre_shared_key;
+ if (ssl_early_callback_get_extension(&client_hello, &pre_shared_key,
+ TLSEXT_TYPE_pre_shared_key) &&
+ !ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &alert,
+ &pre_shared_key)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return 0;
+ }
+
+ uint16_t resumption_cipher;
+ if (session != NULL &&
+ /* We currently only support ECDHE-PSK resumption. */
+ ((session->ticket_flags & SSL_TICKET_ALLOW_DHE_RESUMPTION) == 0 ||
+ /* Only resume if the session's version matches. */
+ session->ssl_version != ssl->version ||
+ !ssl_cipher_get_ecdhe_psk_cipher(session->cipher, &resumption_cipher) ||
+ !ssl_client_cipher_list_contains_cipher(&client_hello,
+ resumption_cipher))) {
+ SSL_SESSION_free(session);
+ session = NULL;
+ }
+
+ if (session == NULL) {
+ if (!ssl_get_new_session(ssl, 1 /* server */)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+ } else {
+ /* Only authentication information carries over in TLS 1.3. */
+ ssl->s3->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY);
+ if (ssl->s3->new_session == NULL) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+ ssl->s3->session_reused = 1;
+ ssl->verify_result = session->verify_result;
+ SSL_SESSION_free(session);
}
if (ssl->ctx->dos_protection_cb != NULL &&
@@ -156,17 +196,19 @@
}
static enum ssl_hs_wait_t do_select_parameters(SSL *ssl, SSL_HANDSHAKE *hs) {
- /* Call |cert_cb| to update server certificates if required. */
- if (ssl->cert->cert_cb != NULL) {
- int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
- if (rv == 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
- }
- if (rv < 0) {
- hs->state = state_select_parameters;
- return ssl_hs_x509_lookup;
+ if (!ssl->s3->session_reused) {
+ /* Call |cert_cb| to update server certificates if required. */
+ if (ssl->cert->cert_cb != NULL) {
+ int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+ if (rv == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+ if (rv < 0) {
+ hs->state = state_select_parameters;
+ return ssl_hs_x509_lookup;
+ }
}
}
@@ -178,25 +220,45 @@
return ssl_hs_error;
}
- const SSL_CIPHER *cipher =
- ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
- if (cipher == NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
- return ssl_hs_error;
- }
+ if (!ssl->s3->session_reused) {
+ const SSL_CIPHER *cipher =
+ ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
+ if (cipher == NULL) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+ return ssl_hs_error;
+ }
- ssl->s3->new_session->cipher = cipher;
- ssl->s3->tmp.new_cipher = cipher;
+ ssl->s3->new_session->cipher = cipher;
+ ssl->s3->tmp.new_cipher = cipher;
+ } else {
+ uint16_t resumption_cipher;
+ if (!ssl_cipher_get_ecdhe_psk_cipher(ssl->s3->new_session->cipher,
+ &resumption_cipher)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+ return ssl_hs_error;
+ }
+ ssl->s3->tmp.new_cipher = SSL_get_cipher_by_value(resumption_cipher);
+ }
ssl->method->received_flight(ssl);
/* The PRF hash is now known. Set up the key schedule and hash the
* ClientHello. */
- size_t hash_len =
+ size_t resumption_ctx_len =
EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
- if (!tls13_init_key_schedule(ssl, kZeroes, hash_len)) {
- return ssl_hs_error;
+ if (ssl->s3->session_reused) {
+ uint8_t resumption_ctx[EVP_MAX_MD_SIZE];
+ if (!tls13_resumption_context(ssl, resumption_ctx, resumption_ctx_len,
+ ssl->s3->new_session) ||
+ !tls13_init_key_schedule(ssl, resumption_ctx, resumption_ctx_len)) {
+ return ssl_hs_error;
+ }
+ } else {
+ if (!tls13_init_key_schedule(ssl, kZeroes, resumption_ctx_len)) {
+ return ssl_hs_error;
+ }
}
/* Resolve PSK and incorporate it into the secret. */
@@ -285,6 +347,7 @@
!CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
!CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
!CBB_add_u16_length_prefixed(&body, &extensions) ||
+ !ssl_ext_pre_shared_key_add_serverhello(ssl, &extensions) ||
!ssl_ext_key_share_add_serverhello(ssl, &extensions) ||
!ssl->method->finish_message(ssl, &cbb)) {
CBB_cleanup(&cbb);
@@ -428,7 +491,7 @@
SSL_HANDSHAKE *hs) {
if (!ssl->s3->tmp.cert_request) {
/* Skip this state. */
- hs->state = state_process_client_certificate_verify;
+ hs->state = state_process_client_finished;
return ssl_hs_ok;
}