2016-07-11 18:19:03 +01:00
|
|
|
/* Copyright (c) 2016, Google Inc.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2016-10-31 23:20:42 +00:00
|
|
|
#include <openssl/aead.h>
|
2016-07-11 18:19:03 +01:00
|
|
|
#include <openssl/bytestring.h>
|
|
|
|
#include <openssl/digest.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
#include <openssl/mem.h>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#include <openssl/stack.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
enum server_hs_state_t {
|
|
|
|
state_process_client_hello = 0,
|
2016-08-10 01:00:32 +01:00
|
|
|
state_select_parameters,
|
2016-07-18 17:40:30 +01:00
|
|
|
state_send_hello_retry_request,
|
|
|
|
state_flush_hello_retry_request,
|
|
|
|
state_process_second_client_hello,
|
2016-07-11 18:19:03 +01:00
|
|
|
state_send_server_hello,
|
|
|
|
state_send_encrypted_extensions,
|
|
|
|
state_send_certificate_request,
|
|
|
|
state_send_server_certificate,
|
|
|
|
state_send_server_certificate_verify,
|
|
|
|
state_complete_server_certificate_verify,
|
|
|
|
state_send_server_finished,
|
|
|
|
state_flush,
|
|
|
|
state_process_client_certificate,
|
|
|
|
state_process_client_certificate_verify,
|
2016-09-24 00:25:11 +01:00
|
|
|
state_process_channel_id,
|
2016-07-11 18:19:03 +01:00
|
|
|
state_process_client_finished,
|
2016-07-27 16:10:52 +01:00
|
|
|
state_send_new_session_ticket,
|
2016-11-03 21:19:16 +00:00
|
|
|
state_flush_new_session_tickets,
|
2016-07-11 18:19:03 +01:00
|
|
|
state_done,
|
|
|
|
};
|
|
|
|
|
2016-07-18 17:40:30 +01:00
|
|
|
static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
|
|
|
|
|
2016-11-17 07:43:08 +00:00
|
|
|
static int resolve_ecdhe_secret(SSL_HANDSHAKE *hs, int *out_need_retry,
|
2016-07-18 17:40:30 +01:00
|
|
|
struct ssl_early_callback_ctx *early_ctx) {
|
2016-11-17 07:43:08 +00:00
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-18 17:40:30 +01:00
|
|
|
*out_need_retry = 0;
|
|
|
|
|
2016-09-06 19:13:43 +01:00
|
|
|
/* We only support connections that include an ECDHE key exchange. */
|
2016-07-18 17:40:30 +01:00
|
|
|
CBS key_share;
|
2016-08-02 22:41:33 +01:00
|
|
|
if (!ssl_early_callback_get_extension(early_ctx, &key_share,
|
|
|
|
TLSEXT_TYPE_key_share)) {
|
2016-07-18 17:40:30 +01:00
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
|
2016-11-16 10:10:08 +00:00
|
|
|
return 0;
|
2016-07-18 17:40:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int found_key_share;
|
|
|
|
uint8_t *dhe_secret;
|
|
|
|
size_t dhe_secret_len;
|
2016-09-21 00:24:40 +01:00
|
|
|
uint8_t alert = SSL_AD_DECODE_ERROR;
|
2016-11-17 08:11:16 +00:00
|
|
|
if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share, &dhe_secret,
|
2016-08-02 21:55:05 +01:00
|
|
|
&dhe_secret_len, &alert,
|
|
|
|
&key_share)) {
|
2016-07-18 17:40:30 +01:00
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found_key_share) {
|
|
|
|
*out_need_retry = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-17 07:43:08 +00:00
|
|
|
int ok = tls13_advance_key_schedule(hs, dhe_secret, dhe_secret_len);
|
2016-07-18 17:40:30 +01:00
|
|
|
OPENSSL_free(dhe_secret);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_process_client_hello(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-11 18:19:03 +01:00
|
|
|
if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-08-09 21:21:24 +01:00
|
|
|
struct ssl_early_callback_ctx client_hello;
|
|
|
|
if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
|
2016-07-20 18:05:22 +01:00
|
|
|
ssl->init_num)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(ssl->s3->have_version);
|
|
|
|
|
|
|
|
/* Load the client random. */
|
2016-08-09 21:21:24 +01:00
|
|
|
if (client_hello.random_len != SSL3_RANDOM_SIZE) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
2016-11-16 10:10:08 +00:00
|
|
|
return ssl_hs_error;
|
2016-08-09 21:21:24 +01:00
|
|
|
}
|
|
|
|
memcpy(ssl->s3->client_random, client_hello.random, client_hello.random_len);
|
2016-07-11 18:19:03 +01:00
|
|
|
|
2016-11-16 08:08:23 +00:00
|
|
|
/* TLS 1.3 requires the peer only advertise the null compression. */
|
|
|
|
if (client_hello.compression_methods_len != 1 ||
|
|
|
|
client_hello.compression_methods[0] != 0) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
|
2016-11-16 10:10:08 +00:00
|
|
|
return ssl_hs_error;
|
2016-07-29 19:32:55 +01:00
|
|
|
}
|
|
|
|
|
2016-11-16 08:08:23 +00:00
|
|
|
/* TLS extensions. */
|
2016-12-03 07:20:34 +00:00
|
|
|
if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
|
2016-11-16 08:08:23 +00:00
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-11-16 10:07:53 +00:00
|
|
|
hs->state = state_select_parameters;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const SSL_CIPHER *choose_tls13_cipher(
|
|
|
|
const SSL *ssl, const struct ssl_early_callback_ctx *client_hello) {
|
|
|
|
if (client_hello->cipher_suites_len % 2 != 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CBS cipher_suites;
|
|
|
|
CBS_init(&cipher_suites, client_hello->cipher_suites,
|
|
|
|
client_hello->cipher_suites_len);
|
|
|
|
|
|
|
|
const int aes_is_fine = EVP_has_aes_hardware();
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
const uint16_t version = ssl3_protocol_version(ssl);
|
2016-11-16 10:07:53 +00:00
|
|
|
|
|
|
|
const SSL_CIPHER *best = NULL;
|
|
|
|
while (CBS_len(&cipher_suites) > 0) {
|
|
|
|
uint16_t cipher_suite;
|
|
|
|
if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
/* Limit to TLS 1.3 ciphers we know about. */
|
2016-11-16 10:07:53 +00:00
|
|
|
const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite);
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
if (candidate == NULL ||
|
|
|
|
SSL_CIPHER_get_min_version(candidate) > version ||
|
|
|
|
SSL_CIPHER_get_max_version(candidate) < version) {
|
2016-11-16 10:07:53 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TLS 1.3 removes legacy ciphers, so honor the client order, but prefer
|
|
|
|
* ChaCha20 if we do not have AES hardware. */
|
|
|
|
if (aes_is_fine) {
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (candidate->algorithm_enc == SSL_CHACHA20POLY1305) {
|
|
|
|
return candidate;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (best == NULL) {
|
|
|
|
best = candidate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-11-16 10:07:53 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ssl_early_callback_ctx client_hello;
|
|
|
|
if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
|
|
|
|
ssl->init_num)) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
/* Negotiate the cipher suite. */
|
|
|
|
ssl->s3->tmp.new_cipher = choose_tls13_cipher(ssl, &client_hello);
|
|
|
|
if (ssl->s3->tmp.new_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode the ticket if we agree on a PSK key exchange mode. */
|
2016-11-16 08:08:23 +00:00
|
|
|
uint8_t alert = SSL_AD_DECODE_ERROR;
|
2016-11-01 17:39:36 +00:00
|
|
|
SSL_SESSION *session = NULL;
|
2016-11-16 08:08:23 +00:00
|
|
|
CBS pre_shared_key, binders;
|
|
|
|
if (hs->accept_psk_mode &&
|
|
|
|
ssl_early_callback_get_extension(&client_hello, &pre_shared_key,
|
|
|
|
TLSEXT_TYPE_pre_shared_key)) {
|
|
|
|
/* Verify that the pre_shared_key extension is the last extension in
|
|
|
|
* ClientHello. */
|
|
|
|
if (CBS_data(&pre_shared_key) + CBS_len(&pre_shared_key) !=
|
|
|
|
client_hello.extensions + client_hello.extensions_len) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_PRE_SHARED_KEY_MUST_BE_LAST);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
2016-11-01 17:39:36 +00:00
|
|
|
|
2016-11-17 08:11:16 +00:00
|
|
|
if (!ssl_ext_pre_shared_key_parse_clienthello(hs, &session, &binders,
|
2016-11-16 08:08:23 +00:00
|
|
|
&alert, &pre_shared_key)) {
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
|
|
|
|
return ssl_hs_error;
|
2016-11-01 17:39:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 19:32:55 +01:00
|
|
|
if (session != NULL &&
|
2016-11-12 03:36:06 +00:00
|
|
|
!ssl_session_is_resumable(ssl, session)) {
|
2016-07-29 19:32:55 +01:00
|
|
|
SSL_SESSION_free(session);
|
|
|
|
session = NULL;
|
|
|
|
}
|
|
|
|
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
/* Set up the new session, either using the original one as a template or
|
|
|
|
* creating a fresh one. */
|
2016-07-29 19:32:55 +01:00
|
|
|
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;
|
|
|
|
}
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
|
|
|
|
ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
|
|
|
|
|
|
|
|
/* On new sessions, stash the SNI value in the session. */
|
2016-11-17 07:43:08 +00:00
|
|
|
if (hs->hostname != NULL) {
|
|
|
|
ssl->s3->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
if (ssl->s3->new_session->tlsext_hostname == NULL) {
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
}
|
2016-07-29 19:32:55 +01:00
|
|
|
} else {
|
2016-11-01 17:39:36 +00:00
|
|
|
/* Check the PSK binder. */
|
|
|
|
if (!tls13_verify_psk_binder(ssl, session, &binders)) {
|
|
|
|
SSL_SESSION_free(session);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-07-29 19:32:55 +01:00
|
|
|
/* 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_SESSION_free(session);
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ssl->ctx->dos_protection_cb != NULL &&
|
2016-08-09 21:21:24 +01:00
|
|
|
ssl->ctx->dos_protection_cb(&client_hello) == 0) {
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Connection rejected for DOS reasons. */
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
|
2016-09-16 20:58:00 +01:00
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
/* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
|
|
|
|
* deferred. Complete it now. */
|
2016-10-31 22:01:13 +00:00
|
|
|
if (!ssl_negotiate_alpn(ssl, &alert, &client_hello)) {
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
2016-09-06 19:13:43 +01:00
|
|
|
|
2016-11-30 16:24:40 +00:00
|
|
|
/* The PRF hash is now known. Set up the key schedule. */
|
2016-09-06 19:13:43 +01:00
|
|
|
size_t hash_len =
|
2016-07-11 18:19:03 +01:00
|
|
|
EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!tls13_init_key_schedule(hs)) {
|
2016-11-30 16:24:40 +00:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
2016-09-06 19:13:43 +01:00
|
|
|
|
2016-11-30 16:24:40 +00:00
|
|
|
/* Incorporate the PSK into the running secret. */
|
2016-07-29 19:32:55 +01:00
|
|
|
if (ssl->s3->session_reused) {
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!tls13_advance_key_schedule(hs, ssl->s3->new_session->master_key,
|
2016-11-30 16:24:40 +00:00
|
|
|
ssl->s3->new_session->master_key_length)) {
|
2016-07-29 19:32:55 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
2016-11-17 07:43:08 +00:00
|
|
|
} else if (!tls13_advance_key_schedule(hs, kZeroes, hash_len)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
Negotiate ciphers before resumption.
This changes our resumption strategy. Before, we would negotiate ciphers
only on fresh handshakes. On resumption, we would blindly use whatever
was in the session.
Instead, evaluate cipher suite preferences on every handshake.
Resumption requires that the saved cipher suite match the one that would
have been negotiated anyway. If client or server preferences changed
sufficiently, we decline the session.
This is much easier to reason about (we always pick the best cipher
suite), simpler, and avoids getting stuck under old preferences if
tickets are continuously renewed. Notably, although TLS 1.2 ticket
renewal does not work in practice, TLS 1.3 will renew tickets like
there's no tomorrow.
It also means we don't need dedicated code to avoid resuming a cipher
which has since been disabled. (That dedicated code was a little odd
anyway since the mask_k, etc., checks didn't occur. When cert_cb was
skipped on resumption, one could resume without ever configuring a
certificate! So we couldn't know whether to mask off RSA or ECDSA cipher
suites.)
Add tests which assert on this new arrangement.
BUG=116
Change-Id: Id40d851ccd87e06c46c6ec272527fd8ece8abfc6
Reviewed-on: https://boringssl-review.googlesource.com/11847
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
2016-11-16 10:05:33 +00:00
|
|
|
ssl->method->received_flight(ssl);
|
|
|
|
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Resolve ECDHE and incorporate it into the secret. */
|
2016-07-18 17:40:30 +01:00
|
|
|
int need_retry;
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
|
2016-07-18 17:40:30 +01:00
|
|
|
if (need_retry) {
|
|
|
|
hs->state = state_send_hello_retry_request;
|
|
|
|
return ssl_hs_ok;
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
2016-07-18 17:40:30 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
2016-07-11 18:19:03 +01:00
|
|
|
|
2016-07-18 17:40:30 +01:00
|
|
|
hs->state = state_send_server_hello;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
2016-07-11 18:19:03 +01:00
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_hello_retry_request(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-18 17:40:30 +01:00
|
|
|
CBB cbb, body, extensions;
|
|
|
|
uint16_t group_id;
|
|
|
|
if (!ssl->method->init_message(ssl, &cbb, &body,
|
|
|
|
SSL3_MT_HELLO_RETRY_REQUEST) ||
|
|
|
|
!CBB_add_u16(&body, ssl->version) ||
|
|
|
|
!tls1_get_shared_group(ssl, &group_id) ||
|
|
|
|
!CBB_add_u16_length_prefixed(&body, &extensions) ||
|
2016-10-08 02:10:38 +01:00
|
|
|
!CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
|
|
|
|
!CBB_add_u16(&extensions, 2 /* length */) ||
|
|
|
|
!CBB_add_u16(&extensions, group_id) ||
|
2016-11-12 03:23:25 +00:00
|
|
|
!ssl_complete_message(ssl, &cbb)) {
|
2016-07-18 17:40:30 +01:00
|
|
|
CBB_cleanup(&cbb);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_flush_hello_retry_request;
|
|
|
|
return ssl_hs_write_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_flush_hello_retry_request(SSL_HANDSHAKE *hs) {
|
2016-07-18 17:40:30 +01:00
|
|
|
hs->state = state_process_second_client_hello;
|
|
|
|
return ssl_hs_flush_and_read_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_process_second_client_hello(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-18 17:40:30 +01:00
|
|
|
if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-08-09 21:21:24 +01:00
|
|
|
struct ssl_early_callback_ctx client_hello;
|
|
|
|
if (!ssl_early_callback_init(ssl, &client_hello, ssl->init_msg,
|
2016-07-20 18:05:22 +01:00
|
|
|
ssl->init_num)) {
|
2016-07-18 17:40:30 +01:00
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int need_retry;
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
|
2016-07-18 17:40:30 +01:00
|
|
|
if (need_retry) {
|
|
|
|
/* Only send one HelloRetryRequest. */
|
|
|
|
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
2016-07-18 17:40:30 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-11-14 08:12:11 +00:00
|
|
|
if (!ssl_hash_current_message(ssl)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-07-22 16:39:29 +01:00
|
|
|
ssl->method->received_flight(ssl);
|
2016-07-11 18:19:03 +01:00
|
|
|
hs->state = state_send_server_hello;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-11 18:19:03 +01:00
|
|
|
CBB cbb, body, extensions;
|
|
|
|
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
|
|
|
|
!CBB_add_u16(&body, ssl->version) ||
|
|
|
|
!RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) ||
|
|
|
|
!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) ||
|
2016-11-17 08:11:16 +00:00
|
|
|
!ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
|
|
|
|
!ssl_ext_key_share_add_serverhello(hs, &extensions) ||
|
2016-11-01 17:39:36 +00:00
|
|
|
!ssl_complete_message(ssl, &cbb)) {
|
2016-09-06 19:13:43 +01:00
|
|
|
goto err;
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_send_encrypted_extensions;
|
|
|
|
return ssl_hs_write_message;
|
2016-09-06 19:13:43 +01:00
|
|
|
|
|
|
|
err:
|
|
|
|
CBB_cleanup(&cbb);
|
|
|
|
return ssl_hs_error;
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_encrypted_extensions(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!tls13_set_handshake_traffic(hs)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
CBB cbb, body;
|
|
|
|
if (!ssl->method->init_message(ssl, &cbb, &body,
|
|
|
|
SSL3_MT_ENCRYPTED_EXTENSIONS) ||
|
2016-12-03 07:20:34 +00:00
|
|
|
!ssl_add_serverhello_tlsext(hs, &body) ||
|
2016-11-12 03:23:25 +00:00
|
|
|
!ssl_complete_message(ssl, &cbb)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
CBB_cleanup(&cbb);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_send_certificate_request;
|
|
|
|
return ssl_hs_write_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_certificate_request(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Determine whether to request a client certificate. */
|
2016-11-14 01:32:04 +00:00
|
|
|
hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
|
2016-09-06 19:13:43 +01:00
|
|
|
/* CertificateRequest may only be sent in non-resumption handshakes. */
|
|
|
|
if (ssl->s3->session_reused) {
|
2016-11-14 01:32:04 +00:00
|
|
|
hs->cert_request = 0;
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
if (!hs->cert_request) {
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Skip this state. */
|
|
|
|
hs->state = state_send_server_certificate;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
CBB cbb, body, sigalgs_cbb;
|
|
|
|
if (!ssl->method->init_message(ssl, &cbb, &body,
|
|
|
|
SSL3_MT_CERTIFICATE_REQUEST) ||
|
|
|
|
!CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint16_t *sigalgs;
|
2016-10-17 22:59:54 +01:00
|
|
|
size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
|
2016-07-11 18:19:03 +01:00
|
|
|
if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2016-08-17 20:29:46 +01:00
|
|
|
for (size_t i = 0; i < num_sigalgs; i++) {
|
2016-07-11 18:19:03 +01:00
|
|
|
if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ssl_add_client_CA_list(ssl, &body) ||
|
|
|
|
!CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
|
2016-11-12 03:23:25 +00:00
|
|
|
!ssl_complete_message(ssl, &cbb)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_send_server_certificate;
|
|
|
|
return ssl_hs_write_message;
|
|
|
|
|
|
|
|
err:
|
|
|
|
CBB_cleanup(&cbb);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_server_certificate(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-09-06 19:13:43 +01:00
|
|
|
if (ssl->s3->session_reused) {
|
2016-07-11 18:19:03 +01:00
|
|
|
hs->state = state_send_server_finished;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ssl_has_certificate(ssl)) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!tls13_prepare_certificate(hs)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_send_server_certificate_verify;
|
|
|
|
return ssl_hs_write_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs,
|
2016-07-11 18:19:03 +01:00
|
|
|
int is_first_run) {
|
2016-11-17 07:43:08 +00:00
|
|
|
switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
case ssl_private_key_success:
|
|
|
|
hs->state = state_send_server_finished;
|
|
|
|
return ssl_hs_write_message;
|
|
|
|
|
|
|
|
case ssl_private_key_retry:
|
|
|
|
hs->state = state_complete_server_certificate_verify;
|
|
|
|
return ssl_hs_private_key_operation;
|
|
|
|
|
|
|
|
case ssl_private_key_failure:
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(0);
|
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!tls13_prepare_finished(hs)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_flush;
|
|
|
|
return ssl_hs_write_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_flush(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Update the secret to the master secret and derive traffic keys. */
|
2016-11-17 07:43:08 +00:00
|
|
|
if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
|
|
|
|
!tls13_derive_application_secrets(hs) ||
|
2016-11-01 17:39:36 +00:00
|
|
|
!tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
|
|
|
|
hs->hash_len)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
hs->state = state_process_client_certificate;
|
2016-07-18 21:25:05 +01:00
|
|
|
return ssl_hs_flush_and_read_message;
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_process_client_certificate(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
|
|
|
if (!hs->cert_request) {
|
2016-08-02 00:16:46 +01:00
|
|
|
/* OpenSSL returns X509_V_OK when no certificates are requested. This is
|
2016-08-18 21:40:28 +01:00
|
|
|
* classed by them as a bug, but it's assumed by at least NGINX. */
|
2016-08-02 00:16:46 +01:00
|
|
|
ssl->s3->new_session->verify_result = X509_V_OK;
|
|
|
|
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Skip this state. */
|
2016-09-24 00:25:11 +01:00
|
|
|
hs->state = state_process_channel_id;
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
2016-08-02 01:16:31 +01:00
|
|
|
const int allow_anonymous =
|
|
|
|
(ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
|
|
|
|
|
2016-07-11 18:19:03 +01:00
|
|
|
if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
|
2016-08-02 01:16:31 +01:00
|
|
|
!tls13_process_certificate(ssl, allow_anonymous) ||
|
2016-11-14 08:12:11 +00:00
|
|
|
!ssl_hash_current_message(ssl)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-08-02 00:41:34 +01:00
|
|
|
/* For historical reasons, the server's copy of the chain does not include the
|
|
|
|
* leaf while the client's does. */
|
2016-11-07 20:02:35 +00:00
|
|
|
if (sk_X509_num(ssl->s3->new_session->x509_chain) > 0) {
|
|
|
|
X509_free(sk_X509_shift(ssl->s3->new_session->x509_chain));
|
2016-08-02 00:41:34 +01:00
|
|
|
}
|
|
|
|
|
2016-07-11 18:19:03 +01:00
|
|
|
hs->state = state_process_client_certificate_verify;
|
|
|
|
return ssl_hs_read_message;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum ssl_hs_wait_t do_process_client_certificate_verify(
|
2016-11-14 01:32:04 +00:00
|
|
|
SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-11-07 20:02:35 +00:00
|
|
|
if (ssl->s3->new_session->x509_peer == NULL) {
|
2016-07-11 18:19:03 +01:00
|
|
|
/* Skip this state. */
|
2016-09-24 00:25:11 +01:00
|
|
|
hs->state = state_process_channel_id;
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
|
|
|
|
!tls13_process_certificate_verify(ssl) ||
|
2016-11-14 08:12:11 +00:00
|
|
|
!ssl_hash_current_message(ssl)) {
|
2016-11-16 10:10:08 +00:00
|
|
|
return ssl_hs_error;
|
2016-07-11 18:19:03 +01:00
|
|
|
}
|
|
|
|
|
2016-09-24 00:25:11 +01:00
|
|
|
hs->state = state_process_channel_id;
|
|
|
|
return ssl_hs_read_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_process_channel_id(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-09-24 00:25:11 +01:00
|
|
|
if (!ssl->s3->tlsext_channel_id_valid) {
|
|
|
|
hs->state = state_process_client_finished;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tls13_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
|
|
|
|
!tls1_verify_channel_id(ssl) ||
|
2016-11-14 08:12:11 +00:00
|
|
|
!ssl_hash_current_message(ssl)) {
|
2016-09-24 00:25:11 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-07-11 18:19:03 +01:00
|
|
|
hs->state = state_process_client_finished;
|
|
|
|
return ssl_hs_read_message;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_process_client_finished(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-07-11 18:19:03 +01:00
|
|
|
if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
|
2016-11-17 07:43:08 +00:00
|
|
|
!tls13_process_finished(hs) ||
|
2016-11-14 08:12:11 +00:00
|
|
|
!ssl_hash_current_message(ssl) ||
|
2016-07-11 18:19:03 +01:00
|
|
|
/* evp_aead_seal keys have already been switched. */
|
2016-11-01 17:39:36 +00:00
|
|
|
!tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
|
|
|
|
hs->hash_len) ||
|
2016-11-17 07:43:08 +00:00
|
|
|
!tls13_derive_resumption_secret(hs)) {
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_error;
|
|
|
|
}
|
|
|
|
|
2016-07-22 16:39:29 +01:00
|
|
|
ssl->method->received_flight(ssl);
|
2016-11-03 20:59:25 +00:00
|
|
|
|
|
|
|
/* Refresh the session timestamp so that it is measured from ticket
|
|
|
|
* issuance. */
|
|
|
|
ssl_session_refresh_time(ssl, ssl->s3->new_session);
|
2016-07-27 16:10:52 +01:00
|
|
|
hs->state = state_send_new_session_ticket;
|
2016-07-11 18:19:03 +01:00
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
2016-11-03 21:19:16 +00:00
|
|
|
/* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the
|
|
|
|
* client makes several connections before getting a renewal. */
|
|
|
|
static const int kNumTickets = 2;
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_send_new_session_ticket(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
2016-11-01 17:39:36 +00:00
|
|
|
/* If the client doesn't accept resumption with PSK_DHE_KE, don't send a
|
|
|
|
* session ticket. */
|
|
|
|
if (!hs->accept_psk_mode) {
|
|
|
|
hs->state = state_done;
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|
|
|
|
|
2016-07-27 16:10:52 +01:00
|
|
|
SSL_SESSION *session = ssl->s3->new_session;
|
2016-11-01 17:39:36 +00:00
|
|
|
if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
|
|
|
|
goto err;
|
|
|
|
}
|
2016-07-27 16:10:52 +01:00
|
|
|
|
2016-09-01 17:29:49 +01:00
|
|
|
/* TODO(svaldez): Add support for sending 0RTT through TicketEarlyDataInfo
|
|
|
|
* extension. */
|
|
|
|
|
2016-11-01 17:39:36 +00:00
|
|
|
CBB cbb, body, ticket, extensions;
|
2016-07-27 16:10:52 +01:00
|
|
|
if (!ssl->method->init_message(ssl, &cbb, &body,
|
|
|
|
SSL3_MT_NEW_SESSION_TICKET) ||
|
2016-11-03 20:59:25 +00:00
|
|
|
!CBB_add_u32(&body, session->timeout) ||
|
2016-11-01 17:39:36 +00:00
|
|
|
!CBB_add_u32(&body, session->ticket_age_add) ||
|
2016-07-27 16:10:52 +01:00
|
|
|
!CBB_add_u16_length_prefixed(&body, &ticket) ||
|
|
|
|
!ssl_encrypt_ticket(ssl, &ticket, session) ||
|
2016-10-07 20:19:18 +01:00
|
|
|
!CBB_add_u16_length_prefixed(&body, &extensions)) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a fake extension. See draft-davidben-tls-grease-01. */
|
2016-10-20 18:19:20 +01:00
|
|
|
if (!CBB_add_u16(&extensions,
|
|
|
|
ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
|
|
|
|
!CBB_add_u16(&extensions, 0 /* empty */)) {
|
|
|
|
goto err;
|
2016-10-07 20:19:18 +01:00
|
|
|
}
|
|
|
|
|
2016-11-12 03:23:25 +00:00
|
|
|
if (!ssl_complete_message(ssl, &cbb)) {
|
2016-10-07 20:19:18 +01:00
|
|
|
goto err;
|
2016-07-27 16:10:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hs->session_tickets_sent++;
|
2016-11-03 21:19:16 +00:00
|
|
|
if (hs->session_tickets_sent >= kNumTickets) {
|
|
|
|
hs->state = state_flush_new_session_tickets;
|
|
|
|
} else {
|
|
|
|
hs->state = state_send_new_session_ticket;
|
|
|
|
}
|
2016-07-27 16:10:52 +01:00
|
|
|
|
|
|
|
return ssl_hs_write_message;
|
2016-10-07 20:19:18 +01:00
|
|
|
|
|
|
|
err:
|
|
|
|
CBB_cleanup(&cbb);
|
|
|
|
return ssl_hs_error;
|
2016-07-27 16:10:52 +01:00
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL_HANDSHAKE *hs) {
|
2016-11-03 21:19:16 +00:00
|
|
|
hs->state = state_done;
|
2016-07-27 16:10:52 +01:00
|
|
|
return ssl_hs_flush;
|
|
|
|
}
|
|
|
|
|
2016-11-14 01:32:04 +00:00
|
|
|
enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
|
2016-07-11 18:19:03 +01:00
|
|
|
while (hs->state != state_done) {
|
|
|
|
enum ssl_hs_wait_t ret = ssl_hs_error;
|
|
|
|
enum server_hs_state_t state = hs->state;
|
|
|
|
switch (state) {
|
|
|
|
case state_process_client_hello:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_process_client_hello(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
2016-08-10 01:00:32 +01:00
|
|
|
case state_select_parameters:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_select_parameters(hs);
|
2016-08-10 01:00:32 +01:00
|
|
|
break;
|
2016-07-18 17:40:30 +01:00
|
|
|
case state_send_hello_retry_request:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_hello_retry_request(hs);
|
2016-07-18 17:40:30 +01:00
|
|
|
break;
|
|
|
|
case state_flush_hello_retry_request:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_flush_hello_retry_request(hs);
|
2016-07-18 17:40:30 +01:00
|
|
|
break;
|
|
|
|
case state_process_second_client_hello:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_process_second_client_hello(hs);
|
2016-07-18 17:40:30 +01:00
|
|
|
break;
|
2016-07-11 18:19:03 +01:00
|
|
|
case state_send_server_hello:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_server_hello(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_send_encrypted_extensions:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_encrypted_extensions(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_send_certificate_request:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_certificate_request(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_send_server_certificate:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_server_certificate(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_send_server_certificate_verify:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_server_certificate_verify(hs, 1 /* first run */);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_complete_server_certificate_verify:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_server_certificate_verify(hs, 0 /* complete */);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_send_server_finished:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_server_finished(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_flush:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_flush(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_process_client_certificate:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_process_client_certificate(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
|
|
|
case state_process_client_certificate_verify:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_process_client_certificate_verify(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
2016-09-24 00:25:11 +01:00
|
|
|
case state_process_channel_id:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_process_channel_id(hs);
|
2016-09-24 00:25:11 +01:00
|
|
|
break;
|
2016-07-11 18:19:03 +01:00
|
|
|
case state_process_client_finished:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_process_client_finished(hs);
|
2016-07-11 18:19:03 +01:00
|
|
|
break;
|
2016-07-27 16:10:52 +01:00
|
|
|
case state_send_new_session_ticket:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_send_new_session_ticket(hs);
|
2016-07-27 16:10:52 +01:00
|
|
|
break;
|
2016-11-03 21:19:16 +00:00
|
|
|
case state_flush_new_session_tickets:
|
2016-11-14 01:32:04 +00:00
|
|
|
ret = do_flush_new_session_tickets(hs);
|
2016-07-27 16:10:52 +01:00
|
|
|
break;
|
2016-07-11 18:19:03 +01:00
|
|
|
case state_done:
|
|
|
|
ret = ssl_hs_ok;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != ssl_hs_ok) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssl_hs_ok;
|
|
|
|
}
|