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>
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index 5553400..cb1edb9 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -219,8 +219,8 @@
}
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 @@
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 @@
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. */