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/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) {