Support asynchronous ticket decryption with TLS 1.0–1.2. This change adds support for setting an |SSL_TICKET_AEAD_METHOD| which allows a caller to control ticket encryption and decryption to a greater extent than previously possible and also permits asynchronous ticket decryption. This change only includes partial support: TLS 1.3 work remains to be done. Change-Id: Ia2e10ebb3257e1a119630c463b6bf389cf20ef18 Reviewed-on: https://boringssl-review.googlesource.com/14144 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index e3a4e51..81e45ef 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c
@@ -930,6 +930,9 @@ case ssl_session_retry: ssl->rwstate = SSL_PENDING_SESSION; goto err; + case ssl_session_ticket_retry: + ssl->rwstate = SSL_PENDING_TICKET; + goto err; } if (session != NULL) {
diff --git a/ssl/internal.h b/ssl/internal.h index bc72239..b93a3e4 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -1991,13 +1991,15 @@ ssl_session_success, ssl_session_error, ssl_session_retry, + ssl_session_ticket_retry, }; /* ssl_get_prev_session looks up the previous session based on |client_hello|. * On success, it sets |*out_session| to the session or NULL if none was found. * If the session could not be looked up synchronously, it returns - * |ssl_session_retry| and should be called again. Otherwise, it returns - * |ssl_session_error|. */ + * |ssl_session_retry| and should be called again. If a ticket could not be + * decrypted immediately it returns |ssl_session_ticket_retry| and should also + * be called again. Otherwise, it returns |ssl_session_error|. */ enum ssl_session_result_t ssl_get_prev_session( SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported, int *out_renew_ticket, const SSL_CLIENT_HELLO *client_hello); @@ -2165,15 +2167,19 @@ #define tlsext_tick_md EVP_sha256 -/* tls_process_ticket processes a session ticket from the client. On success, - * it sets |*out_session| to the decrypted session or NULL if the ticket was - * rejected. If the ticket was valid, it sets |*out_renew_ticket| to whether - * the ticket should be renewed. It returns one on success and zero on fatal - * error. */ -int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session, - int *out_renew_ticket, const uint8_t *ticket, - size_t ticket_len, const uint8_t *session_id, - size_t session_id_len); +/* ssl_process_ticket processes a session ticket from the client. It returns + * one of: + * |ssl_ticket_aead_success|: |*out_session| is set to the parsed session and + * |*out_renew_ticket| is set to whether the ticket should be renewed. + * |ssl_ticket_aead_ignore_ticket|: |*out_renew_ticket| is set to whether a + * fresh ticket should be sent, but the given ticket cannot be used. + * |ssl_ticket_aead_retry|: the ticket could not be immediately decrypted. + * Retry later. + * |ssl_ticket_aead_error|: an error occured that is fatal to the connection. */ +enum ssl_ticket_aead_result_t ssl_process_ticket( + SSL *ssl, SSL_SESSION **out_session, int *out_renew_ticket, + const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id, + size_t session_id_len); /* tls1_verify_channel_id processes the current message as a Channel ID message, * and verifies the signature. If the key is valid, it saves the Channel ID and
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 95ea170..d16c952 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c
@@ -910,6 +910,9 @@ case SSL_PRIVATE_KEY_OPERATION: return SSL_ERROR_WANT_PRIVATE_KEY_OPERATION; + + case SSL_PENDING_TICKET: + return SSL_ERROR_PENDING_TICKET; } return SSL_ERROR_SYSCALL; @@ -2699,3 +2702,8 @@ int SSL_set_max_version(SSL *ssl, uint16_t version) { return SSL_set_max_proto_version(ssl, version); } + +void SSL_CTX_set_ticket_aead_method(SSL_CTX *ctx, + const SSL_TICKET_AEAD_METHOD *aead_method) { + ctx->ticket_aead_method = aead_method; +}
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c index e11238f..05ae059 100644 --- a/ssl/ssl_session.c +++ b/ssl/ssl_session.c
@@ -581,16 +581,11 @@ return 0; } -int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) { +static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out, + const uint8_t *session_buf, + size_t session_len) { 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; @@ -667,12 +662,60 @@ ret = 1; err: - OPENSSL_free(session_buf); EVP_CIPHER_CTX_cleanup(&ctx); HMAC_CTX_cleanup(&hctx); return ret; } +static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out, + const uint8_t *session_buf, + size_t session_len) { + const SSL_TICKET_AEAD_METHOD *method = ssl->session_ctx->ticket_aead_method; + const size_t max_overhead = method->max_overhead(ssl); + const size_t max_out = session_len + max_overhead; + if (max_out < max_overhead) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + + uint8_t *ptr; + if (!CBB_reserve(out, &ptr, max_out)) { + return 0; + } + + size_t out_len; + if (!method->seal(ssl, ptr, &out_len, max_out, session_buf, session_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TICKET_ENCRYPTION_FAILED); + return 0; + } + + if (!CBB_did_write(out, out_len)) { + return 0; + } + + return 1; +} + +int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) { + /* 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; + } + + int ret = 0; + if (ssl->session_ctx->ticket_aead_method) { + ret = ssl_encrypt_ticket_with_method(ssl, out, session_buf, session_len); + } else { + ret = + ssl_encrypt_ticket_with_cipher_ctx(ssl, out, session_buf, session_len); + } + + OPENSSL_free(session_buf); + return ret; +} + int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) { if (session == NULL) { return 0; @@ -811,10 +854,18 @@ SSL_early_callback_ctx_extension_get( client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len); if (tickets_supported && ticket_len > 0) { - if (!tls_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len, - client_hello->session_id, - client_hello->session_id_len)) { - return ssl_session_error; + switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len, + client_hello->session_id, + client_hello->session_id_len)) { + case ssl_ticket_aead_success: + break; + case ssl_ticket_aead_ignore_ticket: + assert(session == NULL); + break; + case ssl_ticket_aead_error: + return ssl_session_error; + case ssl_ticket_aead_retry: + return ssl_session_ticket_retry; } } else { /* The client didn't send a ticket, so the session ID is a real ID. */
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 5f89b81..719bdab 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -1352,7 +1352,8 @@ int client_err = SSL_get_error(client, client_ret); if (client_err != SSL_ERROR_NONE && client_err != SSL_ERROR_WANT_READ && - client_err != SSL_ERROR_WANT_WRITE) { + client_err != SSL_ERROR_WANT_WRITE && + client_err != SSL_ERROR_PENDING_TICKET) { fprintf(stderr, "Client error: %d\n", client_err); return false; } @@ -1361,7 +1362,8 @@ int server_err = SSL_get_error(server, server_ret); if (server_err != SSL_ERROR_NONE && server_err != SSL_ERROR_WANT_READ && - server_err != SSL_ERROR_WANT_WRITE) { + server_err != SSL_ERROR_WANT_WRITE && + server_err != SSL_ERROR_PENDING_TICKET) { fprintf(stderr, "Server error: %d\n", server_err); return false; } @@ -3246,6 +3248,238 @@ EXPECT_EQ(0u, sk_SSL_CIPHER_num(SSL_CTX_get_ciphers(ctx.get()))); } +// ssl_test_ticket_aead_failure_mode enumerates the possible ways in which the +// test |SSL_TICKET_AEAD_METHOD| can fail. +enum ssl_test_ticket_aead_failure_mode { + ssl_test_ticket_aead_ok = 0, + ssl_test_ticket_aead_seal_fail, + ssl_test_ticket_aead_open_soft_fail, + ssl_test_ticket_aead_open_hard_fail, +}; + +struct ssl_test_ticket_aead_state { + unsigned retry_count; + ssl_test_ticket_aead_failure_mode failure_mode; +}; + +static int ssl_test_ticket_aead_ex_index_dup(CRYPTO_EX_DATA *to, + const CRYPTO_EX_DATA *from, + void **from_d, int index, + long argl, void *argp) { + abort(); +} + +static void ssl_test_ticket_aead_ex_index_free(void *parent, void *ptr, + CRYPTO_EX_DATA *ad, int index, + long argl, void *argp) { + auto state = reinterpret_cast<ssl_test_ticket_aead_state*>(ptr); + if (state == nullptr) { + return; + } + + OPENSSL_free(state); +} + +static CRYPTO_once_t g_ssl_test_ticket_aead_ex_index_once = CRYPTO_ONCE_INIT; +static int g_ssl_test_ticket_aead_ex_index; + +static int ssl_test_ticket_aead_get_ex_index() { + CRYPTO_once(&g_ssl_test_ticket_aead_ex_index_once, [] { + g_ssl_test_ticket_aead_ex_index = SSL_get_ex_new_index( + 0, nullptr, nullptr, ssl_test_ticket_aead_ex_index_dup, + ssl_test_ticket_aead_ex_index_free); + }); + return g_ssl_test_ticket_aead_ex_index; +} + +static size_t ssl_test_ticket_aead_max_overhead(SSL *ssl) { + return 1; +} + +static int ssl_test_ticket_aead_seal(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out_len, const uint8_t *in, + size_t in_len) { + auto state = reinterpret_cast<ssl_test_ticket_aead_state *>( + SSL_get_ex_data(ssl, ssl_test_ticket_aead_get_ex_index())); + + if (state->failure_mode == ssl_test_ticket_aead_seal_fail || + max_out_len < in_len + 1) { + return 0; + } + + OPENSSL_memmove(out, in, in_len); + out[in_len] = 0xff; + *out_len = in_len + 1; + + return 1; +} + +static ssl_ticket_aead_result_t ssl_test_ticket_aead_open( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len, + const uint8_t *in, size_t in_len) { + auto state = reinterpret_cast<ssl_test_ticket_aead_state *>( + SSL_get_ex_data(ssl, ssl_test_ticket_aead_get_ex_index())); + + if (state->retry_count > 0) { + state->retry_count--; + return ssl_ticket_aead_retry; + } + + switch (state->failure_mode) { + case ssl_test_ticket_aead_ok: + break; + case ssl_test_ticket_aead_seal_fail: + // If |seal| failed then there shouldn't be any ticket to try and + // decrypt. + abort(); + break; + case ssl_test_ticket_aead_open_soft_fail: + return ssl_ticket_aead_ignore_ticket; + case ssl_test_ticket_aead_open_hard_fail: + return ssl_ticket_aead_error; + } + + if (in_len == 0 || in[in_len - 1] != 0xff) { + return ssl_ticket_aead_ignore_ticket; + } + + if (max_out_len < in_len - 1) { + return ssl_ticket_aead_error; + } + + OPENSSL_memmove(out, in, in_len - 1); + *out_len = in_len - 1; + return ssl_ticket_aead_success; +} + +static const SSL_TICKET_AEAD_METHOD kSSLTestTicketMethod = { + ssl_test_ticket_aead_max_overhead, + ssl_test_ticket_aead_seal, + ssl_test_ticket_aead_open, +}; + +static void ConnectClientAndServerWithTicketMethod( + bssl::UniquePtr<SSL> *out_client, bssl::UniquePtr<SSL> *out_server, + SSL_CTX *client_ctx, SSL_CTX *server_ctx, unsigned retry_count, + ssl_test_ticket_aead_failure_mode failure_mode, SSL_SESSION *session) { + bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx)); + ASSERT_TRUE(client); + ASSERT_TRUE(server); + SSL_set_connect_state(client.get()); + SSL_set_accept_state(server.get()); + + auto state = reinterpret_cast<ssl_test_ticket_aead_state *>( + OPENSSL_malloc(sizeof(ssl_test_ticket_aead_state))); + ASSERT_TRUE(state); + OPENSSL_memset(state, 0, sizeof(ssl_test_ticket_aead_state)); + state->retry_count = retry_count; + state->failure_mode = failure_mode; + + ASSERT_TRUE(SSL_set_ex_data(server.get(), ssl_test_ticket_aead_get_ex_index(), + state)); + + SSL_set_session(client.get(), session); + + BIO *bio1, *bio2; + ASSERT_TRUE(BIO_new_bio_pair(&bio1, 0, &bio2, 0)); + + // SSL_set_bio takes ownership. + SSL_set_bio(client.get(), bio1, bio1); + SSL_set_bio(server.get(), bio2, bio2); + + if (CompleteHandshakes(client.get(), server.get())) { + *out_client = std::move(client); + *out_server = std::move(server); + } else { + out_client->reset(); + out_server->reset(); + } +} + +class TicketAEADMethodTest + : public ::testing::TestWithParam<testing::tuple< + uint16_t, unsigned, ssl_test_ticket_aead_failure_mode>> {}; + +TEST_P(TicketAEADMethodTest, Resume) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + ASSERT_TRUE(cert); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + ASSERT_TRUE(key); + + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(server_ctx); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(client_ctx); + + const uint16_t version = testing::get<0>(GetParam()); + const unsigned retry_count = testing::get<1>(GetParam()); + const ssl_test_ticket_aead_failure_mode failure_mode = + testing::get<2>(GetParam()); + + ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get())); + ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())); + ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), version)); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), version)); + ASSERT_TRUE(SSL_CTX_set_min_proto_version(server_ctx.get(), version)); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), version)); + + SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_current_time_cb(client_ctx.get(), FrozenTimeCallback); + SSL_CTX_set_current_time_cb(server_ctx.get(), FrozenTimeCallback); + + SSL_CTX_set_ticket_aead_method(server_ctx.get(), &kSSLTestTicketMethod); + + bssl::UniquePtr<SSL> client, server; + ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(), + server_ctx.get(), retry_count, + failure_mode, nullptr); + switch (failure_mode) { + case ssl_test_ticket_aead_ok: + case ssl_test_ticket_aead_open_hard_fail: + case ssl_test_ticket_aead_open_soft_fail: + ASSERT_TRUE(client); + break; + case ssl_test_ticket_aead_seal_fail: + EXPECT_FALSE(client); + return; + } + EXPECT_FALSE(SSL_session_reused(client.get())); + EXPECT_FALSE(SSL_session_reused(server.get())); + + SSL_SESSION *session = SSL_get_session(client.get()); + ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(), + server_ctx.get(), retry_count, + failure_mode, session); + switch (failure_mode) { + case ssl_test_ticket_aead_ok: + ASSERT_TRUE(client); + EXPECT_TRUE(SSL_session_reused(client.get())); + EXPECT_TRUE(SSL_session_reused(server.get())); + break; + case ssl_test_ticket_aead_seal_fail: + abort(); + break; + case ssl_test_ticket_aead_open_hard_fail: + EXPECT_FALSE(client); + break; + case ssl_test_ticket_aead_open_soft_fail: + ASSERT_TRUE(client); + EXPECT_FALSE(SSL_session_reused(client.get())); + EXPECT_FALSE(SSL_session_reused(server.get())); + } +} + +INSTANTIATE_TEST_CASE_P( + TicketAEADMethodTests, TicketAEADMethodTest, + testing::Combine( + testing::Values(TLS1_2_VERSION /*, TLS1_3_VERSION TODO: enable */), + testing::Values(0, 1, 2), + testing::Values(ssl_test_ticket_aead_ok, + ssl_test_ticket_aead_seal_fail, + ssl_test_ticket_aead_open_soft_fail, + ssl_test_ticket_aead_open_hard_fail))); + // TODO(davidben): Convert this file to GTest properly. TEST(SSLTest, AllTests) { if (!TestCipherRules() ||
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 34478ed..e3d0a9e 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c
@@ -2017,10 +2017,18 @@ /* TLS 1.3 session tickets are renewed separately as part of the * NewSessionTicket. */ int unused_renew; - if (!tls_process_ticket(ssl, out_session, &unused_renew, CBS_data(&ticket), - CBS_len(&ticket), NULL, 0)) { - *out_alert = SSL_AD_INTERNAL_ERROR; - return 0; + switch (ssl_process_ticket(ssl, out_session, &unused_renew, CBS_data(&ticket), + CBS_len(&ticket), NULL, 0)) { + case ssl_ticket_aead_success: + break; + case ssl_ticket_aead_ignore_ticket: + assert(*out_session == NULL); + break; + case ssl_ticket_aead_retry: + /* TODO: async tickets for TLS 1.3. */ + case ssl_ticket_aead_error: + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; } return 1; @@ -3043,12 +3051,12 @@ return 1; } -int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session, - int *out_renew_ticket, const uint8_t *ticket, - size_t ticket_len, const uint8_t *session_id, - size_t session_id_len) { - int ret = 1; /* Most errors are non-fatal. */ - SSL_CTX *ssl_ctx = ssl->session_ctx; +static enum ssl_ticket_aead_result_t +ssl_decrypt_ticket_with_cipher_ctx(SSL *ssl, uint8_t **out, size_t *out_len, + int *out_renew_ticket, const uint8_t *ticket, + size_t ticket_len) { + enum ssl_ticket_aead_result_t ret = ssl_ticket_aead_ignore_ticket; + const SSL_CTX *const ssl_ctx = ssl->session_ctx; uint8_t *plaintext = NULL; HMAC_CTX hmac_ctx; @@ -3056,23 +3064,12 @@ EVP_CIPHER_CTX cipher_ctx; EVP_CIPHER_CTX_init(&cipher_ctx); - *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; - } - /* Ensure there is room for the key name and the largest IV * |tlsext_ticket_key_cb| may try to consume. The real limit may be lower, but * the maximum IV length should be well under the minimum size for the * session material and HMAC. */ if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) { - goto done; + goto out; } const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN; @@ -3081,28 +3078,26 @@ ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, &cipher_ctx, &hmac_ctx, 0 /* decrypt */); if (cb_ret < 0) { - ret = 0; - goto done; - } - if (cb_ret == 0) { - goto done; - } - if (cb_ret == 2) { + ret = ssl_ticket_aead_error; + goto out; + } else if (cb_ret == 0) { + goto out; + } else if (cb_ret == 2) { *out_renew_ticket = 1; } } else { /* Check the key name matches. */ if (OPENSSL_memcmp(ticket, ssl_ctx->tlsext_tick_key_name, SSL_TICKET_KEY_NAME_LEN) != 0) { - goto done; + goto out; } if (!HMAC_Init_ex(&hmac_ctx, ssl_ctx->tlsext_tick_hmac_key, sizeof(ssl_ctx->tlsext_tick_hmac_key), tlsext_tick_md(), NULL) || !EVP_DecryptInit_ex(&cipher_ctx, EVP_aes_128_cbc(), NULL, ssl_ctx->tlsext_tick_aes_key, iv)) { - ret = 0; - goto done; + ret = ssl_ticket_aead_error; + goto out; } } size_t iv_len = EVP_CIPHER_CTX_iv_length(&cipher_ctx); @@ -3112,7 +3107,7 @@ size_t mac_len = HMAC_size(&hmac_ctx); if (ticket_len < SSL_TICKET_KEY_NAME_LEN + iv_len + 1 + mac_len) { /* The ticket must be large enough for key name, IV, data, and MAC. */ - goto done; + goto out; } HMAC_Update(&hmac_ctx, ticket, ticket_len - mac_len); HMAC_Final(&hmac_ctx, mac, NULL); @@ -3122,7 +3117,7 @@ mac_ok = 1; #endif if (!mac_ok) { - goto done; + goto out; } /* Decrypt the session data. */ @@ -3131,8 +3126,8 @@ mac_len; plaintext = OPENSSL_malloc(ciphertext_len); if (plaintext == NULL) { - ret = 0; - goto done; + ret = ssl_ticket_aead_error; + goto out; } size_t plaintext_len; #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) @@ -3140,24 +3135,89 @@ plaintext_len = ciphertext_len; #else if (ciphertext_len >= INT_MAX) { - goto done; + goto out; } int len1, len2; if (!EVP_DecryptUpdate(&cipher_ctx, plaintext, &len1, ciphertext, (int)ciphertext_len) || !EVP_DecryptFinal_ex(&cipher_ctx, plaintext + len1, &len2)) { - ERR_clear_error(); /* Don't leave an error on the queue. */ - goto done; + ERR_clear_error(); + goto out; } - plaintext_len = (size_t)(len1 + len2); + plaintext_len = (size_t)(len1) + len2; #endif + *out = plaintext; + plaintext = NULL; + *out_len = plaintext_len; + ret = ssl_ticket_aead_success; + +out: + OPENSSL_free(plaintext); + HMAC_CTX_cleanup(&hmac_ctx); + EVP_CIPHER_CTX_cleanup(&cipher_ctx); + return ret; +} + +static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_method( + SSL *ssl, uint8_t **out, size_t *out_len, int *out_renew_ticket, + const uint8_t *ticket, size_t ticket_len) { + uint8_t *plaintext = OPENSSL_malloc(ticket_len); + if (plaintext == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return ssl_ticket_aead_error; + } + + size_t plaintext_len; + const enum ssl_ticket_aead_result_t result = + ssl->session_ctx->ticket_aead_method->open( + ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len); + + if (result == ssl_ticket_aead_success) { + *out = plaintext; + plaintext = NULL; + *out_len = plaintext_len; + } + + OPENSSL_free(plaintext); + return result; +} + +enum ssl_ticket_aead_result_t ssl_process_ticket( + SSL *ssl, SSL_SESSION **out_session, int *out_renew_ticket, + const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id, + size_t session_id_len) { + *out_renew_ticket = 0; + *out_session = NULL; + + if ((SSL_get_options(ssl) & SSL_OP_NO_TICKET) || + session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { + return ssl_ticket_aead_ignore_ticket; + } + + uint8_t *plaintext = NULL; + size_t plaintext_len; + enum ssl_ticket_aead_result_t result; + if (ssl->session_ctx->ticket_aead_method != NULL) { + result = ssl_decrypt_ticket_with_method( + ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len); + } else { + result = ssl_decrypt_ticket_with_cipher_ctx( + ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len); + } + + if (result != ssl_ticket_aead_success) { + return result; + } + /* Decode the session. */ SSL_SESSION *session = SSL_SESSION_from_bytes(plaintext, plaintext_len, ssl->ctx); + OPENSSL_free(plaintext); + if (session == NULL) { ERR_clear_error(); /* Don't leave an error on the queue. */ - goto done; + return ssl_ticket_aead_ignore_ticket; } /* Copy the client's session ID into the new session, to denote the ticket has @@ -3166,12 +3226,7 @@ session->session_id_length = session_id_len; *out_session = session; - -done: - OPENSSL_free(plaintext); - HMAC_CTX_cleanup(&hmac_ctx); - EVP_CIPHER_CTX_cleanup(&cipher_ctx); - return ret; + return ssl_ticket_aead_success; } int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *in_sigalgs) {