Pārlūkot izejas kodu

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>
kris/onging/CECPQ3_patch15
Adam Langley pirms 7 gadiem
committed by Adam Langley
vecāks
revīzija
4c341d0299
9 mainītis faili ar 501 papildinājumiem un 74 dzēšanām
  1. +1
    -0
      crypto/err/ssl.errordata
  2. +1
    -0
      include/openssl/base.h
  3. +69
    -1
      include/openssl/ssl.h
  4. +3
    -0
      ssl/handshake_server.c
  5. +17
    -11
      ssl/internal.h
  6. +8
    -0
      ssl/ssl_lib.c
  7. +64
    -13
      ssl/ssl_session.c
  8. +236
    -2
      ssl/ssl_test.cc
  9. +102
    -47
      ssl/t1_lib.c

+ 1
- 0
crypto/err/ssl.errordata Parādīt failu

@@ -149,6 +149,7 @@ SSL,1043,SSLV3_ALERT_UNSUPPORTED_CERTIFICATE
SSL,214,SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION
SSL,215,SSL_HANDSHAKE_FAILURE
SSL,216,SSL_SESSION_ID_CONTEXT_TOO_LONG
SSL,276,TICKET_ENCRYPTION_FAILED
SSL,1049,TLSV1_ALERT_ACCESS_DENIED
SSL,1050,TLSV1_ALERT_DECODE_ERROR
SSL,1021,TLSV1_ALERT_DECRYPTION_FAILED


+ 1
- 0
include/openssl/base.h Parādīt failu

@@ -306,6 +306,7 @@ typedef struct ssl_method_st SSL_METHOD;
typedef struct ssl_private_key_method_st SSL_PRIVATE_KEY_METHOD;
typedef struct ssl_session_st SSL_SESSION;
typedef struct ssl_st SSL;
typedef struct ssl_ticket_aead_method_st SSL_TICKET_AEAD_METHOD;
typedef struct st_ERR_FNS ERR_FNS;
typedef struct v3_ext_ctx X509V3_CTX;
typedef struct x509_attributes_st X509_ATTRIBUTE;


+ 69
- 1
include/openssl/ssl.h Parādīt failu

@@ -506,6 +506,12 @@ OPENSSL_EXPORT int SSL_get_error(const SSL *ssl, int ret_code);
* |SSL_CTX_set_private_key_method|. */
#define SSL_ERROR_WANT_PRIVATE_KEY_OPERATION 13

/* SSL_ERROR_PENDING_TICKET indicates that a ticket decryption is pending. The
* caller may retry the operation when the decryption is ready.
*
* See also |SSL_CTX_set_ticket_aead_method|. */
#define SSL_ERROR_PENDING_TICKET 14

/* SSL_set_mtu sets the |ssl|'s MTU in DTLS to |mtu|. It returns one on success
* and zero on failure. */
OPENSSL_EXPORT int SSL_set_mtu(SSL *ssl, unsigned mtu);
@@ -1955,7 +1961,14 @@ OPENSSL_EXPORT SSL_SESSION *SSL_magic_pending_session_ptr(void);
* On the server, tickets are encrypted and authenticated with a secret key. By
* default, an |SSL_CTX| generates a key on creation. Tickets are minted and
* processed transparently. The following functions may be used to configure a
* persistent key or implement more custom behavior. */
* persistent key or implement more custom behavior. There are three levels of
* customisation possible:
*
* 1) One can simply set the keys with |SSL_CTX_set_tlsext_ticket_keys|.
* 2) One can configure an |EVP_CIPHER_CTX| and |HMAC_CTX| directly for
* encryption and authentication.
* 3) One can configure an |SSL_TICKET_ENCRYPTION_METHOD| to have more control
* and the option of asynchronous decryption. */

/* SSL_CTX_get_tlsext_ticket_keys writes |ctx|'s session ticket key material to
* |len| bytes of |out|. It returns one on success and zero if |len| is not
@@ -2001,6 +2014,55 @@ OPENSSL_EXPORT int SSL_CTX_set_tlsext_ticket_key_cb(
EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
int encrypt));

/* ssl_ticket_aead_result_t enumerates the possible results from decrypting a
* ticket with an |SSL_TICKET_AEAD_METHOD|. */
enum ssl_ticket_aead_result_t {
/* ssl_ticket_aead_success indicates the the ticket was successfully
* decrypted. */
ssl_ticket_aead_success,
/* ssl_ticket_aead_retry indicates that the operation could not be
* immediately completed and must be reattempted, via |open|, at a later
* point. */
ssl_ticket_aead_retry,
/* ssl_ticket_aead_ignore_ticket indicates that the ticket should be ignored
* (i.e. is corrupt or otherwise undecryptable). */
ssl_ticket_aead_ignore_ticket,
/* ssl_ticket_aead_error indicates that a fatal error occured and the
* handshake should be terminated. */
ssl_ticket_aead_error,
};

/* ssl_ticket_aead_method_st (aka |SSL_TICKET_ENCRYPTION_METHOD|) contains
* methods for encrypting and decrypting session tickets. */
struct ssl_ticket_aead_method_st {
/* max_overhead returns the maximum number of bytes of overhead that |seal|
* may add. */
size_t (*max_overhead)(SSL *ssl);

/* seal encrypts and authenticates |in_len| bytes from |in|, writes, at most,
* |max_out_len| bytes to |out|, and puts the number of bytes written in
* |*out_len|. The |in| and |out| buffers may be equal but will not otherwise
* alias. It returns one on success or zero on error. */
int (*seal)(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len,
const uint8_t *in, size_t in_len);

/* open authenticates and decrypts |in_len| bytes from |in|, writes, at most,
* |max_out_len| bytes of plaintext to |out|, and puts the number of bytes
* written in |*out_len|. The |in| and |out| buffers may be equal but will
* not otherwise alias. See |ssl_ticket_aead_result_t| for details of the
* return values. In the case that a retry is indicated, the caller should
* arrange for the high-level operation on |ssl| to be retried when the
* operation is completed, which will result in another call to |open|. */
enum ssl_ticket_aead_result_t (*open)(SSL *ssl, uint8_t *out, size_t *out_len,
size_t max_out_len, const uint8_t *in,
size_t in_len);
};

/* SSL_CTX_set_ticket_aead_method configures a custom ticket AEAD method table
* on |ctx|. |aead_method| must remain valid for the lifetime of |ctx|. */
OPENSSL_EXPORT void SSL_CTX_set_ticket_aead_method(
SSL_CTX *ctx, const SSL_TICKET_AEAD_METHOD *aead_method);


/* Elliptic curve Diffie-Hellman.
*
@@ -3572,6 +3634,7 @@ OPENSSL_EXPORT void SSL_CTX_set_client_cert_cb(
#define SSL_PENDING_SESSION 7
#define SSL_CERTIFICATE_SELECTION_PENDING 8
#define SSL_PRIVATE_KEY_OPERATION 9
#define SSL_PENDING_TICKET 10

/* SSL_want returns one of the above values to determine what the most recent
* operation on |ssl| was blocked on. Use |SSL_get_error| instead. */
@@ -4144,6 +4207,10 @@ struct ssl_ctx_st {
* memory. */
CRYPTO_BUFFER_POOL *pool;

/* ticket_aead_method contains function pointers for opening and sealing
* session tickets. */
const SSL_TICKET_AEAD_METHOD *ticket_aead_method;

/* quiet_shutdown is true if the connection should not send a close_notify on
* shutdown. */
unsigned quiet_shutdown:1;
@@ -4509,6 +4576,7 @@ BORINGSSL_MAKE_DELETER(SSL_SESSION, SSL_SESSION_free)
#define SSL_R_SERVER_CERT_CHANGED 273
#define SSL_R_CERTIFICATE_AND_PRIVATE_KEY_MISMATCH 274
#define SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD 275
#define SSL_R_TICKET_ENCRYPTION_FAILED 276
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020


+ 3
- 0
ssl/handshake_server.c Parādīt failu

@@ -930,6 +930,9 @@ static int ssl3_select_parameters(SSL_HANDSHAKE *hs) {
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) {


+ 17
- 11
ssl/internal.h Parādīt failu

@@ -1991,13 +1991,15 @@ enum ssl_session_result_t {
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 @@ int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);

#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


+ 8
- 0
ssl/ssl_lib.c Parādīt failu

@@ -910,6 +910,9 @@ int SSL_get_error(const SSL *ssl, int ret_code) {

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_min_version(SSL *ssl, uint16_t version) {
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;
}

+ 64
- 13
ssl/ssl_session.c Parādīt failu

@@ -581,16 +581,11 @@ err:
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 @@ int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
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 @@ enum ssl_session_result_t ssl_get_prev_session(
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. */


+ 236
- 2
ssl/ssl_test.cc Parādīt failu

@@ -1352,7 +1352,8 @@ static bool CompleteHandshakes(SSL *client, SSL *server) {
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 @@ static bool CompleteHandshakes(SSL *client, SSL *server) {
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 @@ TEST(SSLTest, EmptyCipherList) {
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() ||


+ 102
- 47
ssl/t1_lib.c Parādīt failu

@@ -2017,10 +2017,18 @@ int ssl_ext_pre_shared_key_parse_clienthello(
/* 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 @@ int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
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 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
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 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
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 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
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 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
mac_ok = 1;
#endif
if (!mac_ok) {
goto done;
goto out;
}

/* Decrypt the session data. */
@@ -3131,8 +3126,8 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
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 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
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 @@ int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
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) {


Notiek ielāde…
Atcelt
Saglabāt