external/boringssl: Sync to 68f37b7a3f451aa1ca8c93669c024d01f6270ae8.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/3ef7697ed30f28367395a5aafb57a12a19906d96..68f37b7a3f451aa1ca8c93669c024d01f6270ae8
Test: cts-tradefed run cts -m CtsLibcoreOkHttpTestCases -a arm64-v8a
Test: cts-tradefed run cts -m CtsLibcoreTestCases -a arm64-v8a
Change-Id: I296d05afab7470335cdda2442414a858df591f6c
diff --git a/src/ssl/custom_extensions.c b/src/ssl/custom_extensions.c
index 780cdc6..46b5efb 100644
--- a/src/ssl/custom_extensions.c
+++ b/src/ssl/custom_extensions.c
@@ -90,7 +90,7 @@
!CBB_add_bytes(&contents_cbb, contents, contents_len) ||
!CBB_flush(extensions)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- ERR_add_error_dataf("extension: %u", (unsigned) ext->value);
+ ERR_add_error_dataf("extension %u", (unsigned) ext->value);
if (ext->free_callback && 0 < contents_len) {
ext->free_callback(ssl, ext->value, contents, ext->add_arg);
}
@@ -113,7 +113,7 @@
default:
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
- ERR_add_error_dataf("extension: %u", (unsigned) ext->value);
+ ERR_add_error_dataf("extension %u", (unsigned) ext->value);
return 0;
}
}
@@ -136,7 +136,7 @@
/* Also, if we didn't send the extension, that's also unacceptable. */
!(ssl->s3->hs->custom_extensions.sent & (1u << index))) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)value);
+ ERR_add_error_dataf("extension %u", (unsigned)value);
*out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
return 0;
}
@@ -145,7 +145,7 @@
!ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
out_alert, ext->parse_arg)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
- ERR_add_error_dataf("extension: %u", (unsigned)ext->value);
+ ERR_add_error_dataf("extension %u", (unsigned)ext->value);
return 0;
}
@@ -169,7 +169,7 @@
!ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension),
out_alert, ext->parse_arg)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR);
- ERR_add_error_dataf("extension: %u", (unsigned)ext->value);
+ ERR_add_error_dataf("extension %u", (unsigned)ext->value);
return 0;
}
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index 7bb2de2..f9bb8f4 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -433,7 +433,7 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
return -1;
}
- if (hash_message == ssl_hash_message && !dtls1_hash_current_message(ssl)) {
+ if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
return -1;
}
@@ -442,13 +442,12 @@
return 1;
}
-int dtls1_hash_current_message(SSL *ssl) {
+void dtls1_get_current_message(const SSL *ssl, CBS *out) {
assert(dtls1_is_current_message_complete(ssl));
hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
SSL_MAX_HANDSHAKE_FLIGHT];
- return ssl3_update_handshake_hash(ssl, frag->data,
- DTLS1_HM_HEADER_LENGTH + frag->msg_len);
+ CBS_init(out, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
}
void dtls1_release_current_message(SSL *ssl, int free_buffer) {
@@ -527,10 +526,10 @@
/* dtls1_max_record_size returns the maximum record body length that may be
* written without exceeding the MTU. It accounts for any buffering installed on
* the write BIO. If no record may be written, it returns zero. */
-static size_t dtls1_max_record_size(SSL *ssl) {
+static size_t dtls1_max_record_size(const SSL *ssl) {
size_t ret = ssl->d1->mtu;
- size_t overhead = ssl_max_seal_overhead(ssl);
+ size_t overhead = SSL_max_seal_overhead(ssl);
if (ret <= overhead) {
return 0;
}
@@ -736,21 +735,23 @@
return 1;
}
-int dtls1_finish_message(SSL *ssl, CBB *cbb) {
- uint8_t *msg = NULL;
- size_t len;
- if (!CBB_finish(cbb, &msg, &len) ||
- len > 0xffffffffu ||
- len < DTLS1_HM_HEADER_LENGTH) {
+int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
+ size_t *out_len) {
+ *out_msg = NULL;
+ if (!CBB_finish(cbb, out_msg, out_len) ||
+ *out_len < DTLS1_HM_HEADER_LENGTH) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- OPENSSL_free(msg);
+ OPENSSL_free(*out_msg);
return 0;
}
/* Fix up the header. Copy the fragment length into the total message
* length. */
- memcpy(msg + 1, msg + DTLS1_HM_HEADER_LENGTH - 3, 3);
+ memcpy(*out_msg + 1, *out_msg + DTLS1_HM_HEADER_LENGTH - 3, 3);
+ return 1;
+}
+int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
ssl3_update_handshake_hash(ssl, msg, len);
ssl->d1->handshake_write_seq++;
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 099de5d..155359c 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -377,7 +377,7 @@
return -1;
}
- size_t max_out = len + ssl_max_seal_overhead(ssl);
+ size_t max_out = len + SSL_max_seal_overhead(ssl);
uint8_t *out;
size_t ciphertext_len;
if (!ssl_write_buffer_init(ssl, &out, max_out) ||
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 9d791b5..8e92cc9 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -132,7 +132,7 @@
dtls1_new,
dtls1_free,
dtls1_get_message,
- dtls1_hash_current_message,
+ dtls1_get_current_message,
dtls1_release_current_message,
dtls1_read_app_data,
dtls1_read_change_cipher_spec,
@@ -142,6 +142,7 @@
dtls1_supports_cipher,
dtls1_init_message,
dtls1_finish_message,
+ dtls1_queue_message,
dtls1_write_message,
dtls1_send_change_cipher_spec,
dtls1_expect_flight,
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index a917e02..70d8d96 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -152,6 +152,7 @@
#include <assert.h>
#include <string.h>
+#include <openssl/aead.h>
#include <openssl/bn.h>
#include <openssl/buf.h>
#include <openssl/bytestring.h>
@@ -447,7 +448,7 @@
goto end;
case SSL3_ST_CR_SESSION_TICKET_A:
- if (ssl->tlsext_ticket_expected) {
+ if (ssl->s3->hs->ticket_expected) {
ret = ssl3_get_new_session_ticket(ssl);
if (ret <= 0) {
goto end;
@@ -506,8 +507,6 @@
break;
case SSL_ST_OK:
- /* Clean a few things up. */
- ssl3_cleanup_key_block(ssl);
ssl->method->release_current_message(ssl, 1 /* free_buffer */);
SSL_SESSION_free(ssl->s3->established_session);
@@ -537,9 +536,6 @@
/* Remove write buffering now. */
ssl_free_wbio_buffer(ssl);
- ssl_handshake_free(ssl->s3->hs);
- ssl->s3->hs = NULL;
-
const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
ssl->s3->initial_handshake_complete = 1;
if (is_initial_handshake) {
@@ -547,6 +543,9 @@
ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
}
+ ssl_handshake_free(ssl->s3->hs);
+ ssl->s3->hs = NULL;
+
ret = 1;
ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
goto end;
@@ -605,30 +604,48 @@
return 0;
}
- STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
-
- int any_enabled = 0;
- for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
- const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
- /* Skip disabled ciphers */
- if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
- (cipher->algorithm_auth & ssl->cert->mask_a)) {
- continue;
+ /* Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on
+ * hardware support. */
+ if (max_version >= TLS1_3_VERSION) {
+ if (!EVP_has_aes_hardware() &&
+ !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
+ return 0;
}
- if (SSL_CIPHER_get_min_version(cipher) > max_version ||
- SSL_CIPHER_get_max_version(cipher) < min_version) {
- continue;
+ if (!CBB_add_u16(&child, TLS1_CK_AES_128_GCM_SHA256 & 0xffff) ||
+ !CBB_add_u16(&child, TLS1_CK_AES_256_GCM_SHA384 & 0xffff)) {
+ return 0;
}
- any_enabled = 1;
- if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
+ if (EVP_has_aes_hardware() &&
+ !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
return 0;
}
}
- /* If all ciphers were disabled, return the error to the caller. */
- if (!any_enabled) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
- return 0;
+ if (min_version < TLS1_3_VERSION) {
+ STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
+ int any_enabled = 0;
+ for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
+ const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
+ /* Skip disabled ciphers */
+ if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
+ (cipher->algorithm_auth & ssl->cert->mask_a)) {
+ continue;
+ }
+ if (SSL_CIPHER_get_min_version(cipher) > max_version ||
+ SSL_CIPHER_get_max_version(cipher) < min_version) {
+ continue;
+ }
+ any_enabled = 1;
+ if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
+ return 0;
+ }
+ }
+
+ /* If all ciphers were disabled, return the error to the caller. */
+ if (!any_enabled && max_version < TLS1_3_VERSION) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
+ return 0;
+ }
}
/* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
@@ -649,43 +666,66 @@
return CBB_flush(out);
}
-int ssl_add_client_hello_body(SSL *ssl, CBB *body) {
+int ssl_write_client_hello(SSL *ssl) {
uint16_t min_version, max_version;
if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
return 0;
}
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO)) {
+ goto err;
+ }
+
/* Renegotiations do not participate in session resumption. */
int has_session = ssl->session != NULL &&
!ssl->s3->initial_handshake_complete;
CBB child;
- if (!CBB_add_u16(body, ssl->client_version) ||
- !CBB_add_bytes(body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
- !CBB_add_u8_length_prefixed(body, &child) ||
+ if (!CBB_add_u16(&body, ssl->client_version) ||
+ !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+ !CBB_add_u8_length_prefixed(&body, &child) ||
(has_session &&
!CBB_add_bytes(&child, ssl->session->session_id,
ssl->session->session_id_length))) {
- return 0;
+ goto err;
}
if (SSL_is_dtls(ssl)) {
- if (!CBB_add_u8_length_prefixed(body, &child) ||
+ if (!CBB_add_u8_length_prefixed(&body, &child) ||
!CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
- return 0;
+ goto err;
}
}
size_t header_len =
SSL_is_dtls(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
- if (!ssl_write_client_cipher_list(ssl, body, min_version, max_version) ||
- !CBB_add_u8(body, 1 /* one compression method */) ||
- !CBB_add_u8(body, 0 /* null compression */) ||
- !ssl_add_clienthello_tlsext(ssl, body, header_len + CBB_len(body))) {
- return 0;
+ if (!ssl_write_client_cipher_list(ssl, &body, min_version, max_version) ||
+ !CBB_add_u8(&body, 1 /* one compression method */) ||
+ !CBB_add_u8(&body, 0 /* null compression */) ||
+ !ssl_add_clienthello_tlsext(ssl, &body, header_len + CBB_len(&body))) {
+ goto err;
}
- return 1;
+ uint8_t *msg = NULL;
+ size_t len;
+ if (!ssl->method->finish_message(ssl, &cbb, &msg, &len)) {
+ goto err;
+ }
+
+ /* Now that the length prefixes have been computed, fill in the placeholder
+ * PSK binder. */
+ if (ssl->s3->hs->needs_psk_binder &&
+ !tls13_write_psk_binder(ssl, msg, len)) {
+ OPENSSL_free(msg);
+ goto err;
+ }
+
+ return ssl->method->queue_message(ssl, msg, len);
+
+ err:
+ CBB_cleanup(&cbb);
+ return 0;
}
static int ssl3_send_client_hello(SSL *ssl) {
@@ -700,12 +740,9 @@
return -1;
}
- CBB cbb;
- CBB_zero(&cbb);
-
uint16_t min_version, max_version;
if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
- goto err;
+ return -1;
}
assert(ssl->state == SSL3_ST_CW_CLNT_HELLO_A);
@@ -741,22 +778,15 @@
* renegerate the client_random. The random must be reused. */
if ((!SSL_is_dtls(ssl) || !ssl->d1->send_cookie) &&
!RAND_bytes(ssl->s3->client_random, sizeof(ssl->s3->client_random))) {
- goto err;
+ return -1;
}
- CBB body;
- if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO) ||
- !ssl_add_client_hello_body(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
- goto err;
+ if (!ssl_write_client_hello(ssl)) {
+ return -1;
}
ssl->state = SSL3_ST_CW_CLNT_HELLO_B;
return ssl->method->write_message(ssl);
-
-err:
- CBB_cleanup(&cbb);
- return -1;
}
static int dtls1_get_hello_verify(SSL *ssl) {
@@ -802,8 +832,6 @@
}
static int ssl3_get_server_hello(SSL *ssl) {
- STACK_OF(SSL_CIPHER) *sk;
- const SSL_CIPHER *c;
CERT *ct = ssl->cert;
int al = SSL_AD_INTERNAL_ERROR;
CBS server_hello, server_random, session_id;
@@ -911,26 +939,19 @@
CBS_len(&session_id));
}
- c = SSL_get_cipher_by_value(cipher_suite);
+ const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite);
if (c == NULL) {
/* unknown cipher */
al = SSL_AD_ILLEGAL_PARAMETER;
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
goto f_err;
}
- /* If the cipher is disabled then we didn't sent it in the ClientHello, so if
- * the server selected it, it's an error. */
+
+ /* The cipher must be allowed in the selected version and enabled. */
if ((c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a) ||
SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
- SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl)) {
- al = SSL_AD_ILLEGAL_PARAMETER;
- OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
- goto f_err;
- }
-
- sk = ssl_get_ciphers_by_id(ssl);
- if (!sk_SSL_CIPHER_find(sk, NULL, c)) {
- /* we did not say we would use this cipher */
+ SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl) ||
+ !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, c)) {
al = SSL_AD_ILLEGAL_PARAMETER;
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
goto f_err;
@@ -1041,14 +1062,14 @@
goto err;
}
- /* NOTE: Unlike the server half, the client's copy of |cert_chain| includes
+ /* NOTE: Unlike the server half, the client's copy of |x509_chain| includes
* the leaf. */
- sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
- ssl->s3->new_session->cert_chain = chain;
+ sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+ ssl->s3->new_session->x509_chain = chain;
- X509_free(ssl->s3->new_session->peer);
+ X509_free(ssl->s3->new_session->x509_peer);
X509_up_ref(leaf);
- ssl->s3->new_session->peer = leaf;
+ ssl->s3->new_session->x509_peer = leaf;
return 1;
@@ -1100,7 +1121,7 @@
static int ssl3_verify_server_cert(SSL *ssl) {
if (!ssl_verify_cert_chain(ssl, &ssl->s3->new_session->verify_result,
- ssl->s3->new_session->cert_chain)) {
+ ssl->s3->new_session->x509_chain)) {
return -1;
}
@@ -1274,7 +1295,7 @@
/* ServerKeyExchange should be signed by the server's public key. */
if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
- pkey = X509_get_pubkey(ssl->s3->new_session->peer);
+ pkey = X509_get_pubkey(ssl->s3->new_session->x509_peer);
if (pkey == NULL) {
goto err;
}
@@ -1455,6 +1476,7 @@
return -1;
}
if (ret == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return -1;
}
@@ -1562,7 +1584,7 @@
goto err;
}
- EVP_PKEY *pkey = X509_get_pubkey(ssl->s3->new_session->peer);
+ EVP_PKEY *pkey = X509_get_pubkey(ssl->s3->new_session->x509_peer);
if (pkey == NULL) {
goto err;
}
@@ -1668,7 +1690,7 @@
/* The message must be added to the finished hash before calculating the
* master secret. */
- if (!ssl->method->finish_message(ssl, &cbb)) {
+ if (!ssl_complete_message(ssl, &cbb)) {
goto err;
}
ssl->state = SSL3_ST_CW_KEY_EXCH_B;
@@ -1785,7 +1807,7 @@
}
if (!CBB_did_write(&child, sig_len) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -1814,7 +1836,7 @@
ssl->s3->next_proto_negotiated_len) ||
!CBB_add_u8_length_prefixed(&body, &child) ||
!CBB_add_bytes(&child, kZero, padding_len) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
@@ -1831,16 +1853,8 @@
assert(ssl->state == SSL3_ST_CW_CHANNEL_ID_A);
- if (ssl->tlsext_channel_id_private == NULL &&
- ssl->ctx->channel_id_cb != NULL) {
- EVP_PKEY *key = NULL;
- ssl->ctx->channel_id_cb(ssl, &key);
- if (key != NULL &&
- !SSL_set1_tls_channel_id(ssl, key)) {
- EVP_PKEY_free(key);
- return -1;
- }
- EVP_PKEY_free(key);
+ if (!ssl_do_channel_id_callback(ssl)) {
+ return -1;
}
if (ssl->tlsext_channel_id_private == NULL) {
@@ -1848,55 +1862,17 @@
return -1;
}
- EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
- if (ec_key == NULL) {
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
+ !tls1_write_channel_id(ssl, &body) ||
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ CBB_cleanup(&cbb);
return -1;
}
- int ret = -1;
- BIGNUM *x = BN_new();
- BIGNUM *y = BN_new();
- ECDSA_SIG *sig = NULL;
- if (x == NULL || y == NULL ||
- !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key),
- EC_KEY_get0_public_key(ec_key),
- x, y, NULL)) {
- goto err;
- }
-
- uint8_t digest[EVP_MAX_MD_SIZE];
- size_t digest_len;
- if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
- goto err;
- }
-
- sig = ECDSA_do_sign(digest, digest_len, ec_key);
- if (sig == NULL) {
- goto err;
- }
-
- CBB cbb, body, child;
- if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
- !CBB_add_u16(&body, TLSEXT_TYPE_channel_id) ||
- !CBB_add_u16_length_prefixed(&body, &child) ||
- !BN_bn2cbb_padded(&child, 32, x) || !BN_bn2cbb_padded(&child, 32, y) ||
- !BN_bn2cbb_padded(&child, 32, sig->r) ||
- !BN_bn2cbb_padded(&child, 32, sig->s) ||
- !ssl->method->finish_message(ssl, &cbb)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- CBB_cleanup(&cbb);
- goto err;
- }
-
ssl->state = SSL3_ST_CW_CHANNEL_ID_B;
- ret = ssl->method->write_message(ssl);
-
-err:
- BN_free(x);
- BN_free(y);
- ECDSA_SIG_free(sig);
- return ret;
+ return ssl->method->write_message(ssl);
}
static int ssl3_get_new_session_ticket(SSL *ssl) {
@@ -1919,10 +1895,9 @@
if (CBS_len(&ticket) == 0) {
/* RFC 5077 allows a server to change its mind and send no ticket after
- * negotiating the extension. The value of |tlsext_ticket_expected| is
- * checked in |ssl_update_cache| so is cleared here to avoid an unnecessary
- * update. */
- ssl->tlsext_ticket_expected = 0;
+ * negotiating the extension. The value of |ticket_expected| is checked in
+ * |ssl_update_cache| so is cleared here to avoid an unnecessary update. */
+ ssl->s3->hs->ticket_expected = 0;
return 1;
}
@@ -1940,6 +1915,9 @@
}
}
+ /* |tlsext_tick_lifetime_hint| is measured from when the ticket was issued. */
+ ssl_session_refresh_time(ssl, session);
+
if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index e018680..3b66ab7 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -231,6 +231,7 @@
case SSL3_ST_SR_CLNT_HELLO_B:
case SSL3_ST_SR_CLNT_HELLO_C:
case SSL3_ST_SR_CLNT_HELLO_D:
+ case SSL3_ST_SR_CLNT_HELLO_E:
ret = ssl3_get_client_hello(ssl);
if (ssl->state == SSL_ST_TLS13) {
break;
@@ -415,7 +416,7 @@
case SSL3_ST_SW_SESSION_TICKET_A:
case SSL3_ST_SW_SESSION_TICKET_B:
- if (ssl->tlsext_ticket_expected) {
+ if (ssl->s3->hs->ticket_expected) {
ret = ssl3_send_new_session_ticket(ssl);
if (ret <= 0) {
goto end;
@@ -476,18 +477,16 @@
break;
case SSL_ST_OK:
- /* Clean a few things up. */
- ssl3_cleanup_key_block(ssl);
ssl->method->release_current_message(ssl, 1 /* free_buffer */);
/* If we aren't retaining peer certificates then we can discard it
* now. */
if (ssl->s3->new_session != NULL &&
- ssl->ctx->retain_only_sha256_of_client_certs) {
- X509_free(ssl->s3->new_session->peer);
- ssl->s3->new_session->peer = NULL;
- sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
- ssl->s3->new_session->cert_chain = NULL;
+ ssl->retain_only_sha256_of_client_certs) {
+ X509_free(ssl->s3->new_session->x509_peer);
+ ssl->s3->new_session->x509_peer = NULL;
+ sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+ ssl->s3->new_session->x509_chain = NULL;
}
SSL_SESSION_free(ssl->s3->established_session);
@@ -503,15 +502,13 @@
/* remove buffering on output */
ssl_free_wbio_buffer(ssl);
+ ssl->s3->initial_handshake_complete = 1;
+ ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
+
ssl_handshake_free(ssl->s3->hs);
ssl->s3->hs = NULL;
- ssl->s3->initial_handshake_complete = 1;
-
- ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
-
ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
-
ret = 1;
goto end;
@@ -556,7 +553,7 @@
}
static int negotiate_version(
- SSL *ssl, int *out_alert,
+ SSL *ssl, uint8_t *out_alert,
const struct ssl_early_callback_ctx *client_hello) {
uint16_t min_version, max_version;
if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
@@ -578,6 +575,9 @@
return 0;
}
+ /* Choose the newest commonly-supported version advertised by the client.
+ * The client orders the versions according to its preferences, but we're
+ * not required to honor the client's preferences. */
int found_version = 0;
while (CBS_len(&versions) != 0) {
uint16_t ext_version;
@@ -590,10 +590,10 @@
continue;
}
if (min_version <= ext_version &&
- ext_version <= max_version) {
+ ext_version <= max_version &&
+ (!found_version || version < ext_version)) {
version = ext_version;
found_version = 1;
- break;
}
}
@@ -662,7 +662,8 @@
}
static int ssl3_get_client_hello(SSL *ssl) {
- int al = SSL_AD_INTERNAL_ERROR, ret = -1;
+ uint8_t al = SSL_AD_INTERNAL_ERROR;
+ int ret = -1;
SSL_SESSION *session = NULL;
if (ssl->state == SSL3_ST_SR_CLNT_HELLO_A) {
@@ -729,76 +730,6 @@
memcpy(ssl->s3->client_random, client_hello.random,
client_hello.random_len);
- /* Determine whether we are doing session resumption. */
- int send_new_ticket = 0;
- switch (
- ssl_get_prev_session(ssl, &session, &send_new_ticket, &client_hello)) {
- case ssl_session_success:
- break;
- case ssl_session_error:
- goto err;
- case ssl_session_retry:
- ssl->rwstate = SSL_PENDING_SESSION;
- goto err;
- }
- ssl->tlsext_ticket_expected = send_new_ticket;
-
- /* The EMS state is needed when making the resumption decision, but
- * extensions are not normally parsed until later. This detects the EMS
- * extension for the resumption decision and it's checked against the result
- * of the normal parse later in this function. */
- CBS ems;
- int have_extended_master_secret =
- ssl->version != SSL3_VERSION &&
- ssl_early_callback_get_extension(&client_hello, &ems,
- TLSEXT_TYPE_extended_master_secret) &&
- CBS_len(&ems) == 0;
-
- int has_session = 0;
- if (session != NULL) {
- if (session->extended_master_secret &&
- !have_extended_master_secret) {
- /* A ClientHello without EMS that attempts to resume a session with EMS
- * is fatal to the connection. */
- al = SSL_AD_HANDSHAKE_FAILURE;
- OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
- goto f_err;
- }
-
- has_session =
- /* Only resume if the session's version matches the negotiated
- * version: most clients do not accept a mismatch. */
- ssl->version == session->ssl_version &&
- /* If the client offers the EMS extension, but the previous session
- * didn't use it, then negotiate a new session. */
- have_extended_master_secret == session->extended_master_secret;
- }
-
- if (has_session) {
- /* Use the old session. */
- ssl->session = session;
- session = NULL;
- ssl->s3->session_reused = 1;
- } else {
- ssl_set_session(ssl, NULL);
- if (!ssl_get_new_session(ssl, 1 /* server */)) {
- goto err;
- }
-
- /* Clear the session ID if we want the session to be single-use. */
- if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
- ssl->s3->new_session->session_id_length = 0;
- }
- }
-
- if (ssl->ctx->dos_protection_cb != NULL &&
- ssl->ctx->dos_protection_cb(&client_hello) == 0) {
- /* Connection rejected for DOS reasons. */
- al = SSL_AD_INTERNAL_ERROR;
- OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
- goto f_err;
- }
-
/* Only null compression is supported. */
if (memchr(client_hello.compression_methods, 0,
client_hello.compression_methods_len) == NULL) {
@@ -813,30 +744,10 @@
goto err;
}
- if (have_extended_master_secret != ssl->s3->tmp.extended_master_secret) {
- al = SSL_AD_INTERNAL_ERROR;
- OPENSSL_PUT_ERROR(SSL, SSL_R_EMS_STATE_INCONSISTENT);
- goto f_err;
- }
-
ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
}
- /* Determine the remaining connection parameters. This is a separate state so
- * |cert_cb| does not cause earlier logic to run multiple times. */
- assert(ssl->state == SSL3_ST_SR_CLNT_HELLO_D);
-
- if (ssl->session != NULL) {
- /* Check that the cipher is in the list. */
- if (!ssl_client_cipher_list_contains_cipher(
- &client_hello, (uint16_t)ssl->session->cipher->id)) {
- al = SSL_AD_ILLEGAL_PARAMETER;
- OPENSSL_PUT_ERROR(SSL, SSL_R_REQUIRED_CIPHER_MISSING);
- goto f_err;
- }
-
- ssl->s3->tmp.new_cipher = ssl->session->cipher;
- } else {
+ if (ssl->state == SSL3_ST_SR_CLNT_HELLO_D) {
/* 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);
@@ -851,16 +762,92 @@
}
}
- const SSL_CIPHER *c =
+ /* Negotiate the cipher suite. This must be done after |cert_cb| so the
+ * certificate is finalized. */
+ ssl->s3->tmp.new_cipher =
ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
- if (c == NULL) {
+ if (ssl->s3->tmp.new_cipher == NULL) {
al = SSL_AD_HANDSHAKE_FAILURE;
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
goto f_err;
}
- ssl->s3->new_session->cipher = c;
- ssl->s3->tmp.new_cipher = c;
+ ssl->state = SSL3_ST_SR_CLNT_HELLO_E;
+ }
+
+ assert(ssl->state == SSL3_ST_SR_CLNT_HELLO_E);
+
+ /* Determine whether we are doing session resumption. */
+ int tickets_supported = 0, renew_ticket = 0;
+ switch (ssl_get_prev_session(ssl, &session, &tickets_supported, &renew_ticket,
+ &client_hello)) {
+ case ssl_session_success:
+ break;
+ case ssl_session_error:
+ goto err;
+ case ssl_session_retry:
+ ssl->rwstate = SSL_PENDING_SESSION;
+ goto err;
+ }
+
+ if (session != NULL) {
+ if (session->extended_master_secret &&
+ !ssl->s3->tmp.extended_master_secret) {
+ /* A ClientHello without EMS that attempts to resume a session with EMS
+ * is fatal to the connection. */
+ al = SSL_AD_HANDSHAKE_FAILURE;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
+ goto f_err;
+ }
+
+ if (!ssl_session_is_resumable(ssl, session) ||
+ /* If the client offers the EMS extension, but the previous session
+ * didn't use it, then negotiate a new session. */
+ ssl->s3->tmp.extended_master_secret !=
+ session->extended_master_secret) {
+ SSL_SESSION_free(session);
+ session = NULL;
+ }
+ }
+
+ if (session != NULL) {
+ /* Use the old session. */
+ ssl->s3->hs->ticket_expected = renew_ticket;
+ ssl->session = session;
+ session = NULL;
+ ssl->s3->session_reused = 1;
+ } else {
+ ssl->s3->hs->ticket_expected = tickets_supported;
+ ssl_set_session(ssl, NULL);
+ if (!ssl_get_new_session(ssl, 1 /* server */)) {
+ goto err;
+ }
+
+ /* Clear the session ID if we want the session to be single-use. */
+ if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
+ ssl->s3->new_session->session_id_length = 0;
+ }
+ }
+
+ if (ssl->ctx->dos_protection_cb != NULL &&
+ ssl->ctx->dos_protection_cb(&client_hello) == 0) {
+ /* Connection rejected for DOS reasons. */
+ al = SSL_AD_INTERNAL_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+ goto f_err;
+ }
+
+ if (ssl->session == NULL) {
+ ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
+
+ /* On new sessions, stash the SNI value in the session. */
+ if (ssl->s3->hs->hostname != NULL) {
+ ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->s3->hs->hostname);
+ if (ssl->s3->new_session->tlsext_hostname == NULL) {
+ al = SSL_AD_INTERNAL_ERROR;
+ goto f_err;
+ }
+ }
/* Determine whether to request a client certificate. */
ssl->s3->hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
@@ -881,7 +868,13 @@
}
}
- /* Now that the cipher is known, initialize the handshake hash. */
+ /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
+ * deferred. Complete it now. */
+ if (!ssl_negotiate_alpn(ssl, &al, &client_hello)) {
+ goto f_err;
+ }
+
+ /* Now that all parameters are known, initialize the handshake hash. */
if (!ssl3_init_handshake_hash(ssl)) {
goto f_err;
}
@@ -953,7 +946,7 @@
!CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
!CBB_add_u8(&body, 0 /* no compression */) ||
!ssl_add_serverhello_tlsext(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
@@ -992,7 +985,7 @@
!CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
!CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
ssl->ctx->ocsp_response_length) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
@@ -1173,7 +1166,7 @@
}
}
- if (!ssl->method->finish_message(ssl, &cbb)) {
+ if (!ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -1255,7 +1248,7 @@
}
if (!ssl_add_client_CA_list(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -1275,7 +1268,7 @@
CBB cbb, body;
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
@@ -1320,7 +1313,7 @@
CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
uint8_t alert;
STACK_OF(X509) *chain = ssl_parse_cert_chain(
- ssl, &alert, ssl->ctx->retain_only_sha256_of_client_certs
+ ssl, &alert, ssl->retain_only_sha256_of_client_certs
? ssl->s3->new_session->peer_sha256
: NULL,
&certificate_msg);
@@ -1359,7 +1352,7 @@
ssl->s3->new_session->verify_result = X509_V_OK;
} else {
/* The hash would have been filled in. */
- if (ssl->ctx->retain_only_sha256_of_client_certs) {
+ if (ssl->retain_only_sha256_of_client_certs) {
ssl->s3->new_session->peer_sha256_valid = 1;
}
@@ -1369,12 +1362,12 @@
}
}
- X509_free(ssl->s3->new_session->peer);
- ssl->s3->new_session->peer = sk_X509_shift(chain);
+ X509_free(ssl->s3->new_session->x509_peer);
+ ssl->s3->new_session->x509_peer = sk_X509_shift(chain);
- sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
- ssl->s3->new_session->cert_chain = chain;
- /* Inconsistency alert: cert_chain does *not* include the peer's own
+ sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+ ssl->s3->new_session->x509_chain = chain;
+ /* Inconsistency alert: x509_chain does *not* include the peer's own
* certificate, while we do include it in s3_clnt.c */
return 1;
@@ -1656,7 +1649,7 @@
static int ssl3_get_cert_verify(SSL *ssl) {
int al, ret = 0;
CBS certificate_verify, signature;
- X509 *peer = ssl->s3->new_session->peer;
+ X509 *peer = ssl->s3->new_session->x509_peer;
EVP_PKEY *pkey = NULL;
/* Only RSA and ECDSA client certificates are supported, so a
@@ -1750,7 +1743,7 @@
/* The handshake buffer is no longer necessary, and we may hash the current
* message.*/
ssl3_free_handshake_buffer(ssl);
- if (!ssl->method->hash_current_message(ssl)) {
+ if (!ssl_hash_current_message(ssl)) {
goto err;
}
@@ -1796,109 +1789,17 @@
/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
static int ssl3_get_channel_id(SSL *ssl) {
- int ret = -1;
- uint8_t channel_id_hash[EVP_MAX_MD_SIZE];
- size_t channel_id_hash_len;
- const uint8_t *p;
- uint16_t extension_type;
- EC_GROUP *p256 = NULL;
- EC_KEY *key = NULL;
- EC_POINT *point = NULL;
- ECDSA_SIG sig;
- BIGNUM x, y;
- CBS encrypted_extensions, extension;
-
int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID,
ssl_dont_hash_message);
if (msg_ret <= 0) {
return msg_ret;
}
- /* Before incorporating the EncryptedExtensions message to the handshake
- * hash, compute the hash that should have been signed. */
- if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) {
+ if (!tls1_verify_channel_id(ssl) ||
+ !ssl_hash_current_message(ssl)) {
return -1;
}
- assert(channel_id_hash_len == SHA256_DIGEST_LENGTH);
-
- if (!ssl->method->hash_current_message(ssl)) {
- return -1;
- }
-
- CBS_init(&encrypted_extensions, ssl->init_msg, ssl->init_num);
-
- /* EncryptedExtensions could include multiple extensions, but the only
- * extension that could be negotiated is Channel ID, so there can only be one
- * entry. */
- if (!CBS_get_u16(&encrypted_extensions, &extension_type) ||
- !CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) ||
- CBS_len(&encrypted_extensions) != 0 ||
- extension_type != TLSEXT_TYPE_channel_id ||
- CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return -1;
- }
-
- p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
- if (!p256) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
- return -1;
- }
-
- BN_init(&x);
- BN_init(&y);
- sig.r = BN_new();
- sig.s = BN_new();
- if (sig.r == NULL || sig.s == NULL) {
- goto err;
- }
-
- p = CBS_data(&extension);
- if (BN_bin2bn(p + 0, 32, &x) == NULL ||
- BN_bin2bn(p + 32, 32, &y) == NULL ||
- BN_bin2bn(p + 64, 32, sig.r) == NULL ||
- BN_bin2bn(p + 96, 32, sig.s) == NULL) {
- goto err;
- }
-
- point = EC_POINT_new(p256);
- if (!point ||
- !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
- goto err;
- }
-
- key = EC_KEY_new();
- if (!key || !EC_KEY_set_group(key, p256) ||
- !EC_KEY_set_public_key(key, point)) {
- goto err;
- }
-
- /* We stored the handshake hash in |tlsext_channel_id| the first time that we
- * were called. */
- int sig_ok = ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key);
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
- sig_ok = 1;
-#endif
- if (!sig_ok) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
- ssl->s3->tlsext_channel_id_valid = 0;
- goto err;
- }
-
- memcpy(ssl->s3->tlsext_channel_id, p, 64);
- ret = 1;
-
-err:
- BN_free(&x);
- BN_free(&y);
- BN_free(sig.r);
- BN_free(sig.s);
- EC_KEY_free(key);
- EC_POINT_free(point);
- EC_GROUP_free(p256);
- return ret;
+ return 1;
}
static int ssl3_send_new_session_ticket(SSL *ssl) {
@@ -1906,20 +1807,37 @@
return ssl->method->write_message(ssl);
}
+ const SSL_SESSION *session;
+ SSL_SESSION *session_copy = NULL;
+ if (ssl->session == NULL) {
+ /* Fix the timeout to measure from the ticket issuance time. */
+ ssl_session_refresh_time(ssl, ssl->s3->new_session);
+ session = ssl->s3->new_session;
+ } else {
+ /* We are renewing an existing session. Duplicate the session to adjust the
+ * timeout. */
+ session_copy = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
+ if (session_copy == NULL) {
+ return -1;
+ }
+
+ ssl_session_refresh_time(ssl, session_copy);
+ session = session_copy;
+ }
+
CBB cbb, body, ticket;
- if (!ssl->method->init_message(ssl, &cbb, &body,
- SSL3_MT_NEW_SESSION_TICKET) ||
- /* Ticket lifetime hint (advisory only): We leave this unspecified for
- * resumed session (for simplicity), and guess that tickets for new
- * sessions will live as long as their sessions. */
- !CBB_add_u32(&body,
- ssl->session != NULL ? 0 : ssl->s3->new_session->timeout) ||
- !CBB_add_u16_length_prefixed(&body, &ticket) ||
- !ssl_encrypt_ticket(ssl, &ticket, ssl->session != NULL
- ? ssl->session
- : ssl->s3->new_session) ||
- !ssl->method->finish_message(ssl, &cbb)) {
- return 0;
+ int ok =
+ ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) &&
+ CBB_add_u32(&body, session->timeout) &&
+ CBB_add_u16_length_prefixed(&body, &ticket) &&
+ ssl_encrypt_ticket(ssl, &ticket, session) &&
+ ssl_complete_message(ssl, &cbb);
+
+ SSL_SESSION_free(session_copy);
+ CBB_cleanup(&cbb);
+
+ if (!ok) {
+ return -1;
}
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 7e9954d..1d78ec1 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -230,14 +230,12 @@
/* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in
* |ssl_method|. It sets |*out_cipher_list| to a newly-allocated
- * |ssl_cipher_preference_list_st| containing the result.
- * |*out_cipher_list_by_id| is set to a list of selected ciphers sorted by
- * id. It returns |(*out_cipher_list)->ciphers| on success and NULL on
+ * |ssl_cipher_preference_list_st| containing the result. It returns
+ * |(*out_cipher_list)->ciphers| on success and NULL on
* failure. */
STACK_OF(SSL_CIPHER) *
ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
struct ssl_cipher_preference_list_st **out_cipher_list,
- STACK_OF(SSL_CIPHER) **out_cipher_list_by_id,
const char *rule_str);
/* ssl_cipher_get_value returns the cipher suite id of |cipher|. */
@@ -269,7 +267,7 @@
/* SSL_AEAD_CTX contains information about an AEAD that is being used to encrypt
* an SSL connection. */
-struct ssl_aead_ctx_st {
+typedef struct ssl_aead_ctx_st {
const SSL_CIPHER *cipher;
EVP_AEAD_CTX ctx;
/* fixed_nonce contains any bytes of the nonce that are fixed for all
@@ -294,7 +292,7 @@
/* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
* variable nonce rather than prepended. */
char xor_fixed_nonce;
-} /* SSL_AEAD_CTX */;
+} SSL_AEAD_CTX;
/* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied
* key material. It returns NULL on error. Only one of |SSL_AEAD_CTX_open| or
@@ -420,13 +418,6 @@
* buffer-free APIs are available. */
size_t ssl_seal_align_prefix_len(const SSL *ssl);
-/* ssl_max_seal_overhead returns the maximum overhead of sealing a record with
- * |ssl|.
- *
- * TODO(davidben): Expose this as part of public API once the high-level
- * buffer-free APIs are available. */
-size_t ssl_max_seal_overhead(const SSL *ssl);
-
/* tls_seal_record seals a new record of type |type| and body |in| and writes it
* to |out|. At most |max_out| bytes will be written. It returns one on success
* and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1
@@ -434,7 +425,7 @@
*
* For a large record, the bulk of the ciphertext will begin
* |ssl_seal_align_prefix_len| bytes into out. Aligning |out| appropriately may
- * improve performance. It writes at most |in_len| + |ssl_max_seal_overhead|
+ * improve performance. It writes at most |in_len| + |SSL_max_seal_overhead|
* bytes to |out|.
*
* |in| and |out| may not alias. */
@@ -557,9 +548,11 @@
/* ECDH groups. */
+typedef struct ssl_ecdh_ctx_st SSL_ECDH_CTX;
+
/* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for
* TLS. */
-struct ssl_ecdh_method_st {
+typedef struct ssl_ecdh_method_st {
int nid;
uint16_t group_id;
const char name[8];
@@ -599,7 +592,12 @@
* be passed to |offer| or |accept|. It returns one on success and zero on
* error. */
int (*add_key)(CBB *cbb, CBB *out_contents);
-} /* SSL_ECDH_METHOD */;
+} SSL_ECDH_METHOD;
+
+struct ssl_ecdh_ctx_st {
+ const SSL_ECDH_METHOD *method;
+ void *data;
+};
/* ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
* sets |*out_group_id| to the group ID and returns one. Otherwise, it returns
@@ -750,6 +748,10 @@
* configured and zero otherwise. */
int ssl_has_certificate(const SSL *ssl);
+/* ssl_parse_x509 parses a X509 certificate from |cbs|. It returns NULL
+ * on error. */
+X509 *ssl_parse_x509(CBS *cbs);
+
/* ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
* by a TLS Certificate message. On success, it returns a newly-allocated
* |X509| list and advances |cbs|. Otherwise, it returns NULL and sets
@@ -794,21 +796,19 @@
/* TLS 1.3 key derivation. */
/* tls13_init_key_schedule initializes the handshake hash and key derivation
- * state with the given resumption context. The cipher suite and PRF hash must
- * have been selected at this point. It returns one on success and zero on
- * error. */
-int tls13_init_key_schedule(SSL *ssl, const uint8_t *resumption_ctx,
- size_t resumption_ctx_len);
+ * state. The cipher suite and PRF hash must have been selected at this point.
+ * It returns one on success and zero on error. */
+int tls13_init_key_schedule(SSL *ssl);
/* tls13_advance_key_schedule incorporates |in| into the key schedule with
* HKDF-Extract. It returns one on success and zero on error. */
int tls13_advance_key_schedule(SSL *ssl, const uint8_t *in, size_t len);
-/* tls13_get_context_hashes writes Hash(Handshake Context) +
- * Hash(resumption_context) to |out| which much have room for at least 2 *
- * |EVP_MAX_MD_SIZE| bytes. On success, it returns one and sets |*out_len| to
- * the number of bytes written. Otherwise, it returns zero. */
-int tls13_get_context_hashes(SSL *ssl, uint8_t *out, size_t *out_len);
+/* tls13_get_context_hash writes Hash(Handshake Context) to |out| which must
+ * have room for at least |EVP_MAX_MD_SIZE| bytes. On success, it returns one
+ * and sets |*out_len| to the number of bytes written. Otherwise, it returns
+ * zero. */
+int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len);
enum tls_record_type_t {
type_early_handshake,
@@ -817,11 +817,9 @@
type_data,
};
-/* tls13_set_traffic_key sets the read or write traffic keys to |traffic_secret|
- * for the given traffic phase |type|. It returns one on success and zero on
- * error. */
-int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
- enum evp_aead_direction_t direction,
+/* tls13_set_traffic_key sets the read or write traffic keys to
+ * |traffic_secret|. It returns one on success and zero on error. */
+int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
const uint8_t *traffic_secret,
size_t traffic_secret_len);
@@ -834,15 +832,15 @@
* returns one on success and zero on error. */
int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction);
-/* tls13_derive_traffic_secret_0 derives the initial application data traffic
- * secret based on the handshake transcripts and |master_secret|. It returns one
- * on success and zero on error. */
-int tls13_derive_traffic_secret_0(SSL *ssl);
+/* tls13_derive_application_secrets derives the initial application data traffic
+ * and exporter secrets based on the handshake transcripts and |master_secret|.
+ * It returns one on success and zero on error. */
+int tls13_derive_application_secrets(SSL *ssl);
-/* tls13_finalize_keys derives the |exporter_secret| and |resumption_secret|. */
-int tls13_finalize_keys(SSL *ssl);
+/* tls13_derive_resumption_secret derives the |resumption_secret|. */
+int tls13_derive_resumption_secret(SSL *ssl);
-/* tls13_export_keying_material provides and exporter interface to use the
+/* tls13_export_keying_material provides an exporter interface to use the
* |exporter_secret|. */
int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
const char *label, size_t label_len,
@@ -855,17 +853,15 @@
* 0 for the Client Finished. */
int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server);
-/* tls13_resumption_psk calculates the PSK to use for the resumption of
- * |session| and stores the result in |out|. It returns one on success, and
- * zero on failure. */
-int tls13_resumption_psk(SSL *ssl, uint8_t *out, size_t out_len,
- const SSL_SESSION *session);
+/* tls13_write_psk_binder calculates the PSK binder value and replaces the last
+ * bytes of |msg| with the resulting value. It returns 1 on success, and 0 on
+ * failure. */
+int tls13_write_psk_binder(SSL *ssl, uint8_t *msg, size_t len);
-/* tls13_resumption_context derives the context to be used for the handshake
- * transcript on the resumption of |session|. It returns one on success, and
- * zero on failure. */
-int tls13_resumption_context(SSL *ssl, uint8_t *out, size_t out_len,
- const SSL_SESSION *session);
+/* tls13_verify_psk_binder verifies that the handshake transcript, truncated
+ * up to the binders has a valid signature using the value of |session|'s
+ * resumption secret. It returns 1 on success, and 0 on failure. */
+int tls13_verify_psk_binder(SSL *ssl, SSL_SESSION *session, CBS *binders);
/* Handshake functions. */
@@ -878,10 +874,11 @@
ssl_hs_flush,
ssl_hs_flush_and_read_message,
ssl_hs_x509_lookup,
+ ssl_hs_channel_id_lookup,
ssl_hs_private_key_operation,
};
-struct ssl_handshake_st {
+typedef struct ssl_handshake_st {
/* wait contains the operation |do_handshake| is currently blocking on or
* |ssl_hs_ok| if none. */
enum ssl_hs_wait_t wait;
@@ -894,7 +891,6 @@
int state;
size_t hash_len;
- uint8_t resumption_hash[EVP_MAX_MD_SIZE];
uint8_t secret[EVP_MAX_MD_SIZE];
uint8_t client_traffic_secret_0[EVP_MAX_MD_SIZE];
uint8_t server_traffic_secret_0[EVP_MAX_MD_SIZE];
@@ -923,8 +919,19 @@
/* ecdh_ctx is the current ECDH instance. */
SSL_ECDH_CTX ecdh_ctx;
+ /* scts_requested is one if the SCT extension is in the ClientHello. */
+ unsigned scts_requested:1;
+
+ /* needs_psk_binder if the ClientHello has a placeholder PSK binder to be
+ * filled in. */
+ unsigned needs_psk_binder:1;
+
unsigned received_hello_retry_request:1;
+ /* accept_psk_mode stores whether the client's PSK mode is compatible with our
+ * preferences. */
+ unsigned accept_psk_mode:1;
+
/* retry_group is the group ID selected by the server in HelloRetryRequest in
* TLS 1.3. */
uint16_t retry_group;
@@ -992,6 +999,10 @@
/* next_proto_neg_seen is one of NPN was negotiated. */
unsigned next_proto_neg_seen:1;
+ /* ticket_expected is one if a TLS 1.2 NewSessionTicket message is to be sent
+ * or received. */
+ unsigned ticket_expected:1;
+
/* peer_psk_identity_hint, on the client, is the psk_identity_hint sent by the
* server when using a TLS 1.2 PSK key exchange. */
char *peer_psk_identity_hint;
@@ -1004,7 +1015,14 @@
* received in a CertificateRequest message. */
uint8_t *certificate_types;
size_t num_certificate_types;
-} /* SSL_HANDSHAKE */;
+
+ /* key_block is the record-layer key block for TLS 1.2 and earlier. */
+ uint8_t *key_block;
+ uint8_t key_block_len;
+
+ /* hostname, on the server, is the value of the SNI extension. */
+ char *hostname;
+} SSL_HANDSHAKE;
SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl));
@@ -1051,15 +1069,56 @@
CBS *contents);
int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
SSL_SESSION **out_session,
+ CBS *out_binders,
uint8_t *out_alert, CBS *contents);
int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out);
-int ssl_add_client_hello_body(SSL *ssl, CBB *body);
+/* ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and
+ * returns one iff it's valid. */
+int ssl_is_sct_list_valid(const CBS *contents);
+
+int ssl_write_client_hello(SSL *ssl);
/* ssl_clear_tls13_state releases client state only needed for TLS 1.3. It
* should be called once the version is known to be TLS 1.2 or earlier. */
void ssl_clear_tls13_state(SSL *ssl);
+enum ssl_cert_verify_context_t {
+ ssl_cert_verify_server,
+ ssl_cert_verify_client,
+ ssl_cert_verify_channel_id,
+};
+
+/* tls13_get_cert_verify_signature_input generates the message to be signed for
+ * TLS 1.3's CertificateVerify message. |cert_verify_context| determines the
+ * type of signature. It sets |*out| and |*out_len| to a newly allocated buffer
+ * containing the result. The caller must free it with |OPENSSL_free| to release
+ * it. This function returns one on success and zero on failure. */
+int tls13_get_cert_verify_signature_input(
+ SSL *ssl, uint8_t **out, size_t *out_len,
+ enum ssl_cert_verify_context_t cert_verify_context);
+
+/* ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
+ * one on successful negotiation or if nothing was negotiated. It returns zero
+ * and sets |*out_alert| to an alert on error. */
+int ssl_negotiate_alpn(SSL *ssl, uint8_t *out_alert,
+ const struct ssl_early_callback_ctx *client_hello);
+
+typedef struct {
+ uint16_t type;
+ int *out_present;
+ CBS *out_data;
+} SSL_EXTENSION_TYPE;
+
+/* ssl_parse_extensions parses a TLS extensions block out of |cbs| and advances
+ * it. It writes the parsed extensions to pointers denoted by |ext_types|. On
+ * success, it fills in the |out_present| and |out_data| fields and returns one.
+ * Otherwise, it sets |*out_alert| to an alert to send and returns zero. Unknown
+ * extensions are rejected. */
+int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
+ const SSL_EXTENSION_TYPE *ext_types,
+ size_t num_ext_types);
+
/* SSLKEYLOGFILE functions. */
@@ -1147,10 +1206,9 @@
};
typedef struct cert_st {
- X509 *x509;
EVP_PKEY *privatekey;
- /* Chain for this certificate */
- STACK_OF(X509) *chain;
+ X509 *x509_leaf;
+ STACK_OF(X509) *x509_chain;
/* key_method, if non-NULL, is a set of callbacks to call for private key
* operations. */
@@ -1220,10 +1278,9 @@
* Otherwise, it returns <= 0. */
int (*ssl_get_message)(SSL *ssl, int msg_type,
enum ssl_hash_message_t hash_message);
- /* hash_current_message incorporates the current handshake message into the
- * handshake hash. It returns one on success and zero on allocation
- * failure. */
- int (*hash_current_message)(SSL *ssl);
+ /* get_current_message sets |*out| to the current handshake message. This
+ * includes the protocol-specific message header. */
+ void (*get_current_message)(const SSL *ssl, CBS *out);
/* release_current_message is called to release the current handshake message.
* If |free_buffer| is one, buffers will also be released. */
void (*release_current_message)(SSL *ssl, int free_buffer);
@@ -1245,9 +1302,15 @@
* root CBB to be passed into |finish_message|. |*body| is set to a child CBB
* the caller should write to. It returns one on success and zero on error. */
int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
- /* finish_message finishes a handshake message and prepares it to be
- * written. It returns one on success and zero on error. */
- int (*finish_message)(SSL *ssl, CBB *cbb);
+ /* finish_message finishes a handshake message. It sets |*out_msg| to a
+ * newly-allocated buffer with the serialized message. The caller must
+ * release it with |OPENSSL_free| when done. It returns one on success and
+ * zero on error. */
+ int (*finish_message)(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
+ /* queue_message queues a handshake message and prepares it to be written. It
+ * takes ownership of |msg| and releases it with |OPENSSL_free| when done. It
+ * returns one on success and zero on error. */
+ int (*queue_message)(SSL *ssl, uint8_t *msg, size_t len);
/* write_message writes the next message to the transport. It returns one on
* success and <= 0 on error. */
int (*write_message)(SSL *ssl);
@@ -1271,7 +1334,7 @@
/* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
* of a mess of functions, but hell, think of it as an opaque structure. */
-struct ssl3_enc_method {
+typedef struct ssl3_enc_method {
/* prf computes the PRF function for |ssl|. It writes |out_len| bytes to
* |out|, using |secret| as the secret and |label| as the label. |seed1| and
* |seed2| are concatenated to form the seed parameter. It returns one on
@@ -1281,7 +1344,7 @@
size_t label_len, const uint8_t *seed1, size_t seed1_len,
const uint8_t *seed2, size_t seed2_len);
int (*final_finish_mac)(SSL *ssl, int from_server, uint8_t *out);
-};
+} SSL3_ENC_METHOD;
typedef struct ssl3_record_st {
/* type is the record type. */
@@ -1415,9 +1478,6 @@
int reuse_message;
- uint8_t *key_block;
- uint8_t key_block_length;
-
uint8_t new_mac_secret_len;
uint8_t new_key_len;
uint8_t new_fixed_iv_len;
@@ -1569,12 +1629,9 @@
extern const SSL3_ENC_METHOD TLSv1_enc_data;
extern const SSL3_ENC_METHOD SSLv3_enc_data;
-/* From draft-ietf-tls-tls13-16, used in determining PSK modes. */
-#define SSL_PSK_KE 0x0
-#define SSL_PSK_DHE_KE 0x1
-
-#define SSL_PSK_AUTH 0x0
-#define SSL_PSK_SIGN_AUTH 0x1
+/* From draft-ietf-tls-tls13-18, used in determining PSK modes. */
+#define SSL_PSK_KE 0x0
+#define SSL_PSK_DHE_KE 0x1
/* From draft-ietf-tls-tls13-16, used in determining whether to respond with a
* KeyUpdate. */
@@ -1596,6 +1653,10 @@
* it has expired. */
int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session);
+/* ssl_session_is_resumable returns one if |session| is resumable for |ssl| and
+ * zero otherwise. */
+int ssl_session_is_resumable(const SSL *ssl, const SSL_SESSION *session);
+
void ssl_set_session(SSL *ssl, SSL_SESSION *session);
enum ssl_session_result_t {
@@ -1605,14 +1666,13 @@
};
/* ssl_get_prev_session looks up the previous session based on |ctx|. On
- * success, it sets |*out_session| to the session or NULL if none was found. It
- * sets |*out_send_ticket| to whether a ticket should be sent at the end of the
- * handshake. If the session could not be looked up synchronously, it returns
+ * 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|. */
enum ssl_session_result_t ssl_get_prev_session(
- SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
- const struct ssl_early_callback_ctx *ctx);
+ SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
+ int *out_renew_ticket, const struct ssl_early_callback_ctx *ctx);
/* The following flags determine which parts of the session are duplicated. */
#define SSL_SESSION_DUP_AUTH_ONLY 0x0
@@ -1627,9 +1687,17 @@
OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session,
int dup_flags);
+/* ssl_session_refresh_time updates |session|'s start time to the current time,
+ * adjusting the timeout so the expiration time is unchanged. */
+void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session);
+
void ssl_cipher_preference_list_free(
struct ssl_cipher_preference_list_st *cipher_list);
-struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl);
+
+/* ssl_get_cipher_preferences returns the cipher preference list for TLS 1.2 and
+ * below. */
+const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(
+ const SSL *ssl);
int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain);
int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain);
@@ -1649,16 +1717,14 @@
void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
uint32_t *out_mask_a);
-STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl);
int ssl_verify_alarm_type(long type);
int ssl3_get_finished(SSL *ssl);
int ssl3_send_change_cipher_spec(SSL *ssl);
-void ssl3_cleanup_key_block(SSL *ssl);
int ssl3_send_alert(SSL *ssl, int level, int desc);
int ssl3_get_message(SSL *ssl, int msg_type,
enum ssl_hash_message_t hash_message);
-int ssl3_hash_current_message(SSL *ssl);
+void ssl3_get_current_message(const SSL *ssl, CBS *out);
void ssl3_release_current_message(SSL *ssl, int free_buffer);
/* ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify hash into the
@@ -1679,6 +1745,7 @@
int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
int ssl3_output_cert_chain(SSL *ssl);
+
const SSL_CIPHER *ssl3_choose_cipher(
SSL *ssl, const struct ssl_early_callback_ctx *client_hello,
const struct ssl_cipher_preference_list_st *srvr);
@@ -1689,16 +1756,27 @@
int ssl3_connect(SSL *ssl);
int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-int ssl3_finish_message(SSL *ssl, CBB *cbb);
+int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
+int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len);
int ssl3_write_message(SSL *ssl);
void ssl3_expect_flight(SSL *ssl);
void ssl3_received_flight(SSL *ssl);
int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-int dtls1_finish_message(SSL *ssl, CBB *cbb);
+int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
+ size_t *out_len);
+int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len);
int dtls1_write_message(SSL *ssl);
+/* ssl_complete_message calls |finish_message| and |queue_message| on |cbb| to
+ * queue the message for writing. */
+int ssl_complete_message(SSL *ssl, CBB *cbb);
+
+/* ssl_hash_current_message incorporates the current handshake message into the
+ * handshake hash. It returns one on success and zero on allocation failure. */
+int ssl_hash_current_message(SSL *ssl);
+
/* dtls1_get_record reads a new input record. On success, it places it in
* |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
* more data is needed. */
@@ -1740,7 +1818,7 @@
void dtls1_free(SSL *ssl);
int dtls1_get_message(SSL *ssl, int mt, enum ssl_hash_message_t hash_message);
-int dtls1_hash_current_message(SSL *ssl);
+void dtls1_get_current_message(const SSL *ssl, CBS *out);
void dtls1_release_current_message(SSL *ssl, int free_buffer);
int dtls1_dispatch_alert(SSL *ssl);
@@ -1752,7 +1830,6 @@
void ssl_free_wbio_buffer(SSL *ssl);
int tls1_change_cipher_state(SSL *ssl, int which);
-int tls1_setup_key_block(SSL *ssl);
int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
size_t premaster_len);
@@ -1808,6 +1885,16 @@
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
+ * returns one. Otherwise, it returns zero. */
+int tls1_verify_channel_id(SSL *ssl);
+
+/* tls1_write_channel_id generates a Channel ID message and puts the output in
+ * |cbb|. |ssl->tlsext_channel_id_private| must already be set before calling.
+ * This function returns one on success and zero on error. */
+int tls1_write_channel_id(SSL *ssl, CBB *cbb);
+
/* tls1_channel_id_hash computes the hash to be signed by Channel ID and writes
* it to |out|, which must contain at least |EVP_MAX_MD_SIZE| bytes. It returns
* one on success and zero on failure. */
@@ -1815,6 +1902,12 @@
int tls1_record_handshake_hashes_for_channel_id(SSL *ssl);
+/* ssl_do_channel_id_callback checks runs |ssl->ctx->channel_id_cb| if
+ * necessary. It returns one on success and zero on fatal error. Note that, on
+ * success, |ssl->tlsext_channel_id_private| may be unset, in which case the
+ * operation should be retried later. */
+int ssl_do_channel_id_callback(SSL *ssl);
+
/* ssl3_can_false_start returns one if |ssl| is allowed to False Start and zero
* otherwise. */
int ssl3_can_false_start(const SSL *ssl);
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 1e7e4e1..d872020 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -163,6 +163,13 @@
OPENSSL_free(hs->peer_psk_identity_hint);
sk_X509_NAME_pop_free(hs->ca_names, X509_NAME_free);
OPENSSL_free(hs->certificate_types);
+
+ if (hs->key_block != NULL) {
+ OPENSSL_cleanse(hs->key_block, hs->key_block_len);
+ OPENSSL_free(hs->key_block);
+ }
+
+ OPENSSL_free(hs->hostname);
OPENSSL_free(hs);
}
@@ -199,18 +206,21 @@
return 1;
}
-int ssl3_finish_message(SSL *ssl, CBB *cbb) {
- if (ssl->s3->pending_message != NULL) {
+int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
+ size_t *out_len) {
+ if (!CBB_finish(cbb, out_msg, out_len)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
- uint8_t *msg = NULL;
- size_t len;
- if (!CBB_finish(cbb, &msg, &len) ||
+ return 1;
+}
+
+int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
+ if (ssl->s3->pending_message != NULL ||
len > 0xffffffffu) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
OPENSSL_free(msg);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
@@ -221,6 +231,17 @@
return 1;
}
+int ssl_complete_message(SSL *ssl, CBB *cbb) {
+ uint8_t *msg;
+ size_t len;
+ if (!ssl->method->finish_message(ssl, cbb, &msg, &len) ||
+ !ssl->method->queue_message(ssl, msg, len)) {
+ return 0;
+ }
+
+ return 1;
+}
+
int ssl3_write_message(SSL *ssl) {
if (ssl->s3->pending_message == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -278,7 +299,7 @@
CBB cbb, body;
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
!CBB_add_bytes(&body, finished, finished_len) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return -1;
@@ -300,7 +321,7 @@
size_t finished_len =
ssl->s3->enc_method->final_finish_mac(ssl, !ssl->server, finished);
if (finished_len == 0 ||
- !ssl->method->hash_current_message(ssl)) {
+ !ssl_hash_current_message(ssl)) {
return -1;
}
@@ -346,7 +367,7 @@
CBB cbb, body;
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
!ssl_add_cert_chain(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
return 0;
@@ -640,16 +661,21 @@
}
/* Feed this message into MAC computation. */
- if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
+ if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
return -1;
}
return 1;
}
-int ssl3_hash_current_message(SSL *ssl) {
- return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data,
- ssl->init_buf->length);
+void ssl3_get_current_message(const SSL *ssl, CBS *out) {
+ CBS_init(out, (uint8_t *)ssl->init_buf->data, ssl->init_buf->length);
+}
+
+int ssl_hash_current_message(SSL *ssl) {
+ CBS cbs;
+ ssl->method->get_current_message(ssl, &cbs);
+ return ssl3_update_handshake_hash(ssl, CBS_data(&cbs), CBS_len(&cbs));
}
void ssl3_release_current_message(SSL *ssl, int free_buffer) {
@@ -742,3 +768,51 @@
return al;
}
+
+int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
+ const SSL_EXTENSION_TYPE *ext_types,
+ size_t num_ext_types) {
+ /* Reset everything. */
+ for (size_t i = 0; i < num_ext_types; i++) {
+ *ext_types[i].out_present = 0;
+ CBS_init(ext_types[i].out_data, NULL, 0);
+ }
+
+ CBS copy = *cbs;
+ while (CBS_len(©) != 0) {
+ uint16_t type;
+ CBS data;
+ if (!CBS_get_u16(©, &type) ||
+ !CBS_get_u16_length_prefixed(©, &data)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ const SSL_EXTENSION_TYPE *ext_type = NULL;
+ for (size_t i = 0; i < num_ext_types; i++) {
+ if (type == ext_types[i].type) {
+ ext_type = &ext_types[i];
+ break;
+ }
+ }
+
+ if (ext_type == NULL) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+ *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
+ return 0;
+ }
+
+ /* Duplicate ext_types are forbidden. */
+ if (*ext_type->out_present) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return 0;
+ }
+
+ *ext_type->out_present = 1;
+ *ext_type->out_data = data;
+ }
+
+ return 1;
+}
diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c
index 2209e80..7cdc294 100644
--- a/src/ssl/s3_enc.c
+++ b/src/ssl/s3_enc.c
@@ -209,15 +209,6 @@
return 1;
}
-void ssl3_cleanup_key_block(SSL *ssl) {
- if (ssl->s3->tmp.key_block != NULL) {
- OPENSSL_cleanse(ssl->s3->tmp.key_block, ssl->s3->tmp.key_block_length);
- OPENSSL_free(ssl->s3->tmp.key_block);
- ssl->s3->tmp.key_block = NULL;
- }
- ssl->s3->tmp.key_block_length = 0;
-}
-
int ssl3_init_handshake_buffer(SSL *ssl) {
ssl3_free_handshake_buffer(ssl);
ssl3_free_handshake_hash(ssl);
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 69d3a9d..901b8af 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -200,7 +200,6 @@
return;
}
- ssl3_cleanup_key_block(ssl);
ssl_read_buffer_clear(ssl);
ssl_write_buffer_clear(ssl);
@@ -220,7 +219,8 @@
ssl->s3 = NULL;
}
-struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl) {
+const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(
+ const SSL *ssl) {
if (ssl->cipher_list != NULL) {
return ssl->cipher_list;
}
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index fda9a25..e4116fb 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -245,8 +245,8 @@
static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf,
unsigned int len) {
if (ssl->s3->wpend_tot > (int)len ||
- (ssl->s3->wpend_buf != buf &&
- !(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) ||
+ (!(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) &&
+ ssl->s3->wpend_buf != buf) ||
ssl->s3->wpend_type != type) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
return -1;
@@ -284,7 +284,7 @@
return 0;
}
- size_t max_out = len + ssl_max_seal_overhead(ssl);
+ size_t max_out = len + SSL_max_seal_overhead(ssl);
if (max_out < len) {
OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
return -1;
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index 0f6f64f..b05df0b 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -34,8 +34,12 @@
const uint8_t *mac_key, size_t mac_key_len,
const uint8_t *fixed_iv, size_t fixed_iv_len) {
const EVP_AEAD *aead;
- size_t discard;
- if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, cipher, version)) {
+ size_t expected_mac_key_len, expected_fixed_iv_len;
+ if (!ssl_cipher_get_evp_aead(&aead, &expected_mac_key_len,
+ &expected_fixed_iv_len, cipher, version) ||
+ /* Ensure the caller returned correct key sizes. */
+ expected_fixed_iv_len != fixed_iv_len ||
+ expected_mac_key_len != mac_key_len) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index eeea826..2a7b7d4 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -97,13 +97,13 @@
/* An SSL_SESSION is serialized as the following ASN.1 structure:
*
* SSLSession ::= SEQUENCE {
- * version INTEGER (1), -- ignored
+ * version INTEGER (1), -- session structure version
* sslVersion INTEGER, -- protocol version number
* cipher OCTET STRING, -- two bytes long
* sessionID OCTET STRING,
* masterKey OCTET STRING,
- * time [1] INTEGER OPTIONAL, -- seconds since UNIX epoch
- * timeout [2] INTEGER OPTIONAL, -- in seconds
+ * time [1] INTEGER, -- seconds since UNIX epoch
+ * timeout [2] INTEGER, -- in seconds
* peer [3] Certificate OPTIONAL,
* sessionIDContext [4] OCTET STRING OPTIONAL,
* verifyResult [5] INTEGER OPTIONAL, -- one of X509_V_* codes
@@ -196,30 +196,36 @@
goto err;
}
- if (in->time != 0) {
- if (!CBB_add_asn1(&session, &child, kTimeTag) ||
- !CBB_add_asn1_uint64(&child, in->time)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- goto err;
- }
+ if (in->time < 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+ goto err;
}
- if (in->timeout != 0) {
- if (!CBB_add_asn1(&session, &child, kTimeoutTag) ||
- !CBB_add_asn1_uint64(&child, in->timeout)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- goto err;
- }
+ if (!CBB_add_asn1(&session, &child, kTimeTag) ||
+ !CBB_add_asn1_uint64(&child, in->time)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (in->timeout < 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+ goto err;
+ }
+
+ if (!CBB_add_asn1(&session, &child, kTimeoutTag) ||
+ !CBB_add_asn1_uint64(&child, in->timeout)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ goto err;
}
/* The peer certificate is only serialized if the SHA-256 isn't
* serialized instead. */
- if (in->peer && !in->peer_sha256_valid) {
+ if (in->x509_peer && !in->peer_sha256_valid) {
if (!CBB_add_asn1(&session, &child, kPeerTag)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
- if (!ssl_add_cert_to_cbb(&child, in->peer)) {
+ if (!ssl_add_cert_to_cbb(&child, in->x509_peer)) {
goto err;
}
}
@@ -334,13 +340,13 @@
/* The certificate chain is only serialized if the leaf's SHA-256 isn't
* serialized instead. */
- if (in->cert_chain != NULL && !in->peer_sha256_valid) {
+ if (in->x509_chain != NULL && !in->peer_sha256_valid) {
if (!CBB_add_asn1(&session, &child, kCertChainTag)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
- for (size_t i = 0; i < sk_X509_num(in->cert_chain); i++) {
- if (!ssl_add_cert_to_cbb(&child, sk_X509_value(in->cert_chain, i))) {
+ for (size_t i = 0; i < sk_X509_num(in->x509_chain); i++) {
+ if (!ssl_add_cert_to_cbb(&child, sk_X509_value(in->x509_chain, i))) {
goto err;
}
}
@@ -505,20 +511,6 @@
return 1;
}
-static X509 *parse_x509(CBS *cbs) {
- if (CBS_len(cbs) > LONG_MAX) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
- return NULL;
- }
- const uint8_t *ptr = CBS_data(cbs);
- X509 *ret = d2i_X509(NULL, &ptr, (long)CBS_len(cbs));
- if (ret == NULL) {
- return NULL;
- }
- CBS_skip(cbs, ptr - CBS_data(cbs));
- return ret;
-}
-
static SSL_SESSION *SSL_SESSION_parse(CBS *cbs) {
SSL_SESSION *ret = SSL_SESSION_new();
if (ret == NULL) {
@@ -563,23 +555,32 @@
memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key));
ret->master_key_length = CBS_len(&master_key);
- if (!SSL_SESSION_parse_long(&session, &ret->time, kTimeTag, time(NULL)) ||
- !SSL_SESSION_parse_long(&session, &ret->timeout, kTimeoutTag, 3)) {
+ CBS child;
+ uint64_t time, timeout;
+ if (!CBS_get_asn1(&session, &child, kTimeTag) ||
+ !CBS_get_asn1_uint64(&child, &time) ||
+ time > LONG_MAX ||
+ !CBS_get_asn1(&session, &child, kTimeoutTag) ||
+ !CBS_get_asn1_uint64(&child, &timeout) ||
+ timeout > LONG_MAX) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
}
+ ret->time = (long)time;
+ ret->timeout = (long)timeout;
+
CBS peer;
int has_peer;
if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
}
- X509_free(ret->peer);
- ret->peer = NULL;
+ X509_free(ret->x509_peer);
+ ret->x509_peer = NULL;
if (has_peer) {
- ret->peer = parse_x509(&peer);
- if (ret->peer == NULL) {
+ ret->x509_peer = ssl_parse_x509(&peer);
+ if (ret->x509_peer == NULL) {
goto err;
}
if (CBS_len(&peer) != 0) {
@@ -605,7 +606,7 @@
}
if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) {
- CBS child, peer_sha256;
+ CBS peer_sha256;
if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) ||
!CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) ||
CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) ||
@@ -655,20 +656,20 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
}
- sk_X509_pop_free(ret->cert_chain, X509_free);
- ret->cert_chain = NULL;
+ sk_X509_pop_free(ret->x509_chain, X509_free);
+ ret->x509_chain = NULL;
if (has_cert_chain) {
- ret->cert_chain = sk_X509_new_null();
- if (ret->cert_chain == NULL) {
+ ret->x509_chain = sk_X509_new_null();
+ if (ret->x509_chain == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
while (CBS_len(&cert_chain) > 0) {
- X509 *x509 = parse_x509(&cert_chain);
+ X509 *x509 = ssl_parse_x509(&cert_chain);
if (x509 == NULL) {
goto err;
}
- if (!sk_X509_push(ret->cert_chain, x509)) {
+ if (!sk_X509_push(ret->x509_chain, x509)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
X509_free(x509);
goto err;
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 55b464f..7f6cf63 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -115,6 +115,7 @@
#include <openssl/ssl.h>
#include <assert.h>
+#include <limits.h>
#include <string.h>
#include <openssl/bn.h>
@@ -158,9 +159,9 @@
}
memset(ret, 0, sizeof(CERT));
- if (cert->x509 != NULL) {
- X509_up_ref(cert->x509);
- ret->x509 = cert->x509;
+ if (cert->x509_leaf != NULL) {
+ X509_up_ref(cert->x509_leaf);
+ ret->x509_leaf = cert->x509_leaf;
}
if (cert->privatekey != NULL) {
@@ -168,9 +169,9 @@
ret->privatekey = cert->privatekey;
}
- if (cert->chain) {
- ret->chain = X509_chain_up_ref(cert->chain);
- if (!ret->chain) {
+ if (cert->x509_chain) {
+ ret->x509_chain = X509_chain_up_ref(cert->x509_chain);
+ if (!ret->x509_chain) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
@@ -220,12 +221,12 @@
return;
}
- X509_free(cert->x509);
- cert->x509 = NULL;
+ X509_free(cert->x509_leaf);
+ cert->x509_leaf = NULL;
EVP_PKEY_free(cert->privatekey);
cert->privatekey = NULL;
- sk_X509_pop_free(cert->chain, X509_free);
- cert->chain = NULL;
+ sk_X509_pop_free(cert->x509_chain, X509_free);
+ cert->x509_chain = NULL;
cert->key_method = NULL;
}
@@ -244,8 +245,8 @@
}
int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
- sk_X509_pop_free(cert->chain, X509_free);
- cert->chain = chain;
+ sk_X509_pop_free(cert->x509_chain, X509_free);
+ cert->x509_chain = chain;
return 1;
}
@@ -269,10 +270,10 @@
}
int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
- if (cert->chain == NULL) {
- cert->chain = sk_X509_new_null();
+ if (cert->x509_chain == NULL) {
+ cert->x509_chain = sk_X509_new_null();
}
- if (cert->chain == NULL || !sk_X509_push(cert->chain, x509)) {
+ if (cert->x509_chain == NULL || !sk_X509_push(cert->x509_chain, x509)) {
return 0;
}
@@ -443,7 +444,21 @@
}
int ssl_has_certificate(const SSL *ssl) {
- return ssl->cert->x509 != NULL && ssl_has_private_key(ssl);
+ return ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl);
+}
+
+X509 *ssl_parse_x509(CBS *cbs) {
+ if (CBS_len(cbs) > LONG_MAX) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ return NULL;
+ }
+ const uint8_t *ptr = CBS_data(cbs);
+ X509 *ret = d2i_X509(NULL, &ptr, (long)CBS_len(cbs));
+ if (ret == NULL) {
+ return NULL;
+ }
+ CBS_skip(cbs, ptr - CBS_data(cbs));
+ return ret;
}
STACK_OF(X509) *ssl_parse_cert_chain(SSL *ssl, uint8_t *out_alert,
@@ -476,10 +491,8 @@
SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256);
}
- /* A u24 length cannot overflow a long. */
- const uint8_t *data = CBS_data(&certificate);
- x = d2i_X509(NULL, &data, (long)CBS_len(&certificate));
- if (x == NULL || data != CBS_data(&certificate) + CBS_len(&certificate)) {
+ x = ssl_parse_x509(&certificate);
+ if (x == NULL || CBS_len(&certificate) != 0) {
*out_alert = SSL_AD_DECODE_ERROR;
goto err;
}
@@ -528,7 +541,7 @@
}
CERT *cert = ssl->cert;
- X509 *x = cert->x509;
+ X509 *x = cert->x509_leaf;
CBB child;
if (!CBB_add_u24_length_prefixed(cbb, &child)) {
@@ -537,7 +550,7 @@
}
int no_chain = 0;
- STACK_OF(X509) *chain = cert->chain;
+ STACK_OF(X509) *chain = cert->x509_chain;
if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || chain != NULL) {
no_chain = 1;
}
@@ -763,7 +776,7 @@
}
int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
- *out_chain = ctx->cert->chain;
+ *out_chain = ctx->cert->x509_chain;
return 1;
}
@@ -773,7 +786,7 @@
}
int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
- *out_chain = ssl->cert->chain;
+ *out_chain = ssl->cert->x509_chain;
return 1;
}
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 6d48c89..99aba72 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -738,9 +738,6 @@
{"TLSv1", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
{"TLSv1.2", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION},
- /* AEAD-only ciphers for TLS 1.3. */
- {"GENERIC", SSL_kGENERIC, SSL_aGENERIC, ~0u, ~0u, 0},
-
/* Legacy strength classes. */
{"HIGH", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, 0},
{"FIPS", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, 0},
@@ -761,10 +758,6 @@
}
}
-static int ssl_cipher_ptr_id_cmp(const SSL_CIPHER **a, const SSL_CIPHER **b) {
- return ssl_cipher_id_cmp(*a, *b);
-}
-
const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) {
SSL_CIPHER c;
@@ -781,125 +774,93 @@
*out_mac_secret_len = 0;
*out_fixed_iv_len = 0;
- switch (cipher->algorithm_enc) {
- case SSL_AES128GCM:
+ if (cipher->algorithm_mac == SSL_AEAD) {
+ if (cipher->algorithm_enc == SSL_AES128GCM) {
*out_aead = EVP_aead_aes_128_gcm();
*out_fixed_iv_len = 4;
- break;
-
- case SSL_AES256GCM:
+ } else if (cipher->algorithm_enc == SSL_AES256GCM) {
*out_aead = EVP_aead_aes_256_gcm();
*out_fixed_iv_len = 4;
- break;
-
#if !defined(BORINGSSL_ANDROID_SYSTEM)
- case SSL_CHACHA20POLY1305_OLD:
+ } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305_OLD) {
*out_aead = EVP_aead_chacha20_poly1305_old();
*out_fixed_iv_len = 0;
- break;
#endif
-
- case SSL_CHACHA20POLY1305:
+ } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305) {
*out_aead = EVP_aead_chacha20_poly1305();
*out_fixed_iv_len = 12;
- break;
-
- case SSL_AES128:
- switch (cipher->algorithm_mac) {
- case SSL_SHA1:
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_aes_128_cbc_sha1_ssl3();
- *out_fixed_iv_len = 16;
- } else if (version == TLS1_VERSION) {
- *out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv();
- *out_fixed_iv_len = 16;
- } else {
- *out_aead = EVP_aead_aes_128_cbc_sha1_tls();
- }
- *out_mac_secret_len = SHA_DIGEST_LENGTH;
- break;
- case SSL_SHA256:
- *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
- *out_mac_secret_len = SHA256_DIGEST_LENGTH;
- break;
- default:
- return 0;
- }
- break;
-
- case SSL_AES256:
- switch (cipher->algorithm_mac) {
- case SSL_SHA1:
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_aes_256_cbc_sha1_ssl3();
- *out_fixed_iv_len = 16;
- } else if (version == TLS1_VERSION) {
- *out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv();
- *out_fixed_iv_len = 16;
- } else {
- *out_aead = EVP_aead_aes_256_cbc_sha1_tls();
- }
- *out_mac_secret_len = SHA_DIGEST_LENGTH;
- break;
- case SSL_SHA256:
- *out_aead = EVP_aead_aes_256_cbc_sha256_tls();
- *out_mac_secret_len = SHA256_DIGEST_LENGTH;
- break;
- case SSL_SHA384:
- *out_aead = EVP_aead_aes_256_cbc_sha384_tls();
- *out_mac_secret_len = SHA384_DIGEST_LENGTH;
- break;
- default:
- return 0;
- }
- break;
-
- case SSL_3DES:
- switch (cipher->algorithm_mac) {
- case SSL_SHA1:
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_des_ede3_cbc_sha1_ssl3();
- *out_fixed_iv_len = 8;
- } else if (version == TLS1_VERSION) {
- *out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv();
- *out_fixed_iv_len = 8;
- } else {
- *out_aead = EVP_aead_des_ede3_cbc_sha1_tls();
- }
- *out_mac_secret_len = SHA_DIGEST_LENGTH;
- break;
- default:
- return 0;
- }
- break;
-
- case SSL_eNULL:
- switch (cipher->algorithm_mac) {
- case SSL_SHA1:
- if (version == SSL3_VERSION) {
- *out_aead = EVP_aead_null_sha1_ssl3();
- } else {
- *out_aead = EVP_aead_null_sha1_tls();
- }
- *out_mac_secret_len = SHA_DIGEST_LENGTH;
- break;
- default:
- return 0;
- }
- break;
-
- default:
+ } else {
return 0;
+ }
+
+ /* In TLS 1.3, the iv_len is equal to the AEAD nonce length whereas the code
+ * above computes the TLS 1.2 construction. */
+ if (version >= TLS1_3_VERSION) {
+ *out_fixed_iv_len = EVP_AEAD_nonce_length(*out_aead);
+ }
+ } else if (cipher->algorithm_mac == SSL_SHA1) {
+ if (cipher->algorithm_enc == SSL_eNULL) {
+ if (version == SSL3_VERSION) {
+ *out_aead = EVP_aead_null_sha1_ssl3();
+ } else {
+ *out_aead = EVP_aead_null_sha1_tls();
+ }
+ } else if (cipher->algorithm_enc == SSL_3DES) {
+ if (version == SSL3_VERSION) {
+ *out_aead = EVP_aead_des_ede3_cbc_sha1_ssl3();
+ *out_fixed_iv_len = 8;
+ } else if (version == TLS1_VERSION) {
+ *out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv();
+ *out_fixed_iv_len = 8;
+ } else {
+ *out_aead = EVP_aead_des_ede3_cbc_sha1_tls();
+ }
+ } else if (cipher->algorithm_enc == SSL_AES128) {
+ if (version == SSL3_VERSION) {
+ *out_aead = EVP_aead_aes_128_cbc_sha1_ssl3();
+ *out_fixed_iv_len = 16;
+ } else if (version == TLS1_VERSION) {
+ *out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv();
+ *out_fixed_iv_len = 16;
+ } else {
+ *out_aead = EVP_aead_aes_128_cbc_sha1_tls();
+ }
+ } else if (cipher->algorithm_enc == SSL_AES256) {
+ if (version == SSL3_VERSION) {
+ *out_aead = EVP_aead_aes_256_cbc_sha1_ssl3();
+ *out_fixed_iv_len = 16;
+ } else if (version == TLS1_VERSION) {
+ *out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv();
+ *out_fixed_iv_len = 16;
+ } else {
+ *out_aead = EVP_aead_aes_256_cbc_sha1_tls();
+ }
+ } else {
+ return 0;
+ }
+
+ *out_mac_secret_len = SHA_DIGEST_LENGTH;
+ } else if (cipher->algorithm_mac == SSL_SHA256) {
+ if (cipher->algorithm_enc == SSL_AES128) {
+ *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
+ } else if (cipher->algorithm_enc == SSL_AES256) {
+ *out_aead = EVP_aead_aes_256_cbc_sha256_tls();
+ } else {
+ return 0;
+ }
+
+ *out_mac_secret_len = SHA256_DIGEST_LENGTH;
+ } else if (cipher->algorithm_mac == SSL_SHA384) {
+ if (cipher->algorithm_enc != SSL_AES256) {
+ return 0;
+ }
+
+ *out_aead = EVP_aead_aes_256_cbc_sha384_tls();
+ *out_mac_secret_len = SHA384_DIGEST_LENGTH;
+ } else {
+ return 0;
}
- /* In TLS 1.3, the iv_len is equal to the AEAD nonce length whereas the code
- * above computes the TLS 1.2 construction.
- *
- * TODO(davidben,svaldez): Avoid computing the wrong value and fixing it. */
- if (version >= TLS1_3_VERSION) {
- *out_fixed_iv_len = EVP_AEAD_nonce_length(*out_aead);
- assert(*out_fixed_iv_len >= 8);
- }
return 1;
}
@@ -975,7 +936,9 @@
size_t co_list_num = 0;
for (size_t i = 0; i < kCiphersLen; i++) {
const SSL_CIPHER *cipher = &kCiphers[i];
- if (ssl_method->supports_cipher(cipher)) {
+ if (ssl_method->supports_cipher(cipher) &&
+ /* TLS 1.3 ciphers do not participate in this mechanism. */
+ cipher->algorithm_mkey != SSL_kGENERIC) {
co_list[co_list_num].cipher = cipher;
co_list[co_list_num].next = NULL;
co_list[co_list_num].prev = NULL;
@@ -1389,11 +1352,8 @@
STACK_OF(SSL_CIPHER) *
ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
struct ssl_cipher_preference_list_st **out_cipher_list,
- STACK_OF(SSL_CIPHER) **out_cipher_list_by_id,
const char *rule_str) {
- int ok;
- STACK_OF(SSL_CIPHER) *cipherstack = NULL, *tmp_cipher_list = NULL;
- const char *rule_p;
+ STACK_OF(SSL_CIPHER) *cipherstack = NULL;
CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr;
uint8_t *in_group_flags = NULL;
unsigned int num_in_group_flags = 0;
@@ -1418,11 +1378,8 @@
/* Now arrange all ciphers by preference:
* TODO(davidben): Compute this order once and copy it. */
- /* Everything else being equal, prefer TLS 1.3 ciphers then ECDHE_ECDSA then
- * ECDHE_RSA over other key exchange mechanisms */
-
- ssl_cipher_apply_rule(0, SSL_kGENERIC, SSL_aGENERIC, ~0u, ~0u, 0, CIPHER_ADD,
- -1, 0, &head, &tail);
+ /* Everything else being equal, prefer ECDHE_ECDSA and ECDHE_RSA over other
+ * key exchange mechanisms */
ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, 0, CIPHER_ADD, -1,
0, &head, &tail);
ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0,
@@ -1477,22 +1434,20 @@
/* If the rule_string begins with DEFAULT, apply the default rule before
* using the (possibly available) additional rules. */
- ok = 1;
- rule_p = rule_str;
+ const char *rule_p = rule_str;
if (strncmp(rule_str, "DEFAULT", 7) == 0) {
- ok = ssl_cipher_process_rulestr(ssl_method, SSL_DEFAULT_CIPHER_LIST, &head,
- &tail);
+ if (!ssl_cipher_process_rulestr(ssl_method, SSL_DEFAULT_CIPHER_LIST, &head,
+ &tail)) {
+ goto err;
+ }
rule_p += 7;
if (*rule_p == ':') {
rule_p++;
}
}
- if (ok && strlen(rule_p) > 0) {
- ok = ssl_cipher_process_rulestr(ssl_method, rule_p, &head, &tail);
- }
-
- if (!ok) {
+ if (*rule_p != '\0' &&
+ !ssl_cipher_process_rulestr(ssl_method, rule_p, &head, &tail)) {
goto err;
}
@@ -1521,10 +1476,6 @@
OPENSSL_free(co_list); /* Not needed any longer */
co_list = NULL;
- tmp_cipher_list = sk_SSL_CIPHER_dup(cipherstack);
- if (tmp_cipher_list == NULL) {
- goto err;
- }
pref_list = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));
if (!pref_list) {
goto err;
@@ -1543,26 +1494,12 @@
*out_cipher_list = pref_list;
pref_list = NULL;
- if (out_cipher_list_by_id != NULL) {
- sk_SSL_CIPHER_free(*out_cipher_list_by_id);
- *out_cipher_list_by_id = tmp_cipher_list;
- tmp_cipher_list = NULL;
- (void) sk_SSL_CIPHER_set_cmp_func(*out_cipher_list_by_id,
- ssl_cipher_ptr_id_cmp);
-
- sk_SSL_CIPHER_sort(*out_cipher_list_by_id);
- } else {
- sk_SSL_CIPHER_free(tmp_cipher_list);
- tmp_cipher_list = NULL;
- }
-
return cipherstack;
err:
OPENSSL_free(co_list);
OPENSSL_free(in_group_flags);
sk_SSL_CIPHER_free(cipherstack);
- sk_SSL_CIPHER_free(tmp_cipher_list);
if (pref_list) {
OPENSSL_free(pref_list->in_group_flags);
}
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 491c408..70a39ea 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -276,7 +276,7 @@
}
ssl_create_cipher_list(ret->method, &ret->cipher_list,
- &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST);
+ SSL_DEFAULT_CIPHER_LIST);
if (ret->cipher_list == NULL ||
sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS);
@@ -304,6 +304,10 @@
ret->options |= SSL_OP_NO_TICKET;
}
+ /* Disable the auto-chaining feature by default. Once this has stuck without
+ * problems, the feature will be removed entirely. */
+ ret->mode = SSL_MODE_NO_AUTO_CHAIN;
+
/* Lock the SSL_CTX to the specified version, for compatibility with legacy
* uses of SSL_METHOD. */
if (!SSL_CTX_set_max_proto_version(ret, method->version) ||
@@ -348,7 +352,6 @@
lh_SSL_SESSION_free(ctx->sessions);
X509_STORE_free(ctx->cert_store);
ssl_cipher_preference_list_free(ctx->cipher_list);
- sk_SSL_CIPHER_free(ctx->cipher_list_by_id);
ssl_cipher_preference_list_free(ctx->cipher_list_tls10);
ssl_cipher_preference_list_free(ctx->cipher_list_tls11);
ssl_cert_free(ctx->cert);
@@ -409,6 +412,8 @@
assert(ssl->sid_ctx_length <= sizeof ssl->sid_ctx);
memcpy(&ssl->sid_ctx, &ctx->sid_ctx, sizeof(ssl->sid_ctx));
ssl->verify_callback = ctx->default_verify_callback;
+ ssl->retain_only_sha256_of_client_certs =
+ ctx->retain_only_sha256_of_client_certs;
ssl->param = X509_VERIFY_PARAM_new();
if (!ssl->param) {
@@ -500,7 +505,6 @@
/* add extra stuff */
ssl_cipher_preference_list_free(ssl->cipher_list);
- sk_SSL_CIPHER_free(ssl->cipher_list_by_id);
SSL_SESSION_free(ssl->session);
@@ -1046,11 +1050,11 @@
return NULL;
}
SSL_SESSION *session = SSL_get_session(ssl);
- if (session == NULL || session->peer == NULL) {
+ if (session == NULL || session->x509_peer == NULL) {
return NULL;
}
- X509_up_ref(session->peer);
- return session->peer;
+ X509_up_ref(session->x509_peer);
+ return session->x509_peer;
}
STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl) {
@@ -1061,7 +1065,7 @@
if (session == NULL) {
return NULL;
}
- return session->cert_chain;
+ return session->x509_chain;
}
int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len,
@@ -1334,32 +1338,34 @@
/* Fix this so it checks all the valid key/cert options */
int SSL_CTX_check_private_key(const SSL_CTX *ctx) {
- if (ctx->cert->x509 == NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
- return 0;
- }
-
if (ctx->cert->privatekey == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
return 0;
}
- return X509_check_private_key(ctx->cert->x509, ctx->cert->privatekey);
-}
-
-/* Fix this function so that it takes an optional type parameter */
-int SSL_check_private_key(const SSL *ssl) {
- if (ssl->cert->x509 == NULL) {
+ X509 *x509 = ctx->cert->x509_leaf;
+ if (x509 == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
return 0;
}
+ return X509_check_private_key(x509, ctx->cert->privatekey);
+}
+
+/* Fix this function so that it takes an optional type parameter */
+int SSL_check_private_key(const SSL *ssl) {
if (ssl->cert->privatekey == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
return 0;
}
- return X509_check_private_key(ssl->cert->x509, ssl->cert->privatekey);
+ X509 *x509 = ssl->cert->x509_leaf;
+ if (x509 == NULL) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
+ return 0;
+ }
+
+ return X509_check_private_key(x509, ssl->cert->privatekey);
}
long SSL_get_default_timeout(const SSL *ssl) {
@@ -1564,41 +1570,13 @@
return NULL;
}
- if (ssl->cipher_list != NULL) {
- return ssl->cipher_list->ciphers;
- }
-
- if (ssl->version >= TLS1_1_VERSION && ssl->ctx->cipher_list_tls11 != NULL) {
- return ssl->ctx->cipher_list_tls11->ciphers;
- }
-
- if (ssl->version >= TLS1_VERSION && ssl->ctx->cipher_list_tls10 != NULL) {
- return ssl->ctx->cipher_list_tls10->ciphers;
- }
-
- if (ssl->ctx->cipher_list != NULL) {
- return ssl->ctx->cipher_list->ciphers;
- }
-
- return NULL;
-}
-
-/* return a STACK of the ciphers available for the SSL and in order of
- * algorithm id */
-STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl) {
- if (ssl == NULL) {
+ const struct ssl_cipher_preference_list_st *prefs =
+ ssl_get_cipher_preferences(ssl);
+ if (prefs == NULL) {
return NULL;
}
- if (ssl->cipher_list_by_id != NULL) {
- return ssl->cipher_list_by_id;
- }
-
- if (ssl->ctx->cipher_list_by_id != NULL) {
- return ssl->ctx->cipher_list_by_id;
- }
-
- return NULL;
+ return prefs->ciphers;
}
const char *SSL_get_cipher_list(const SSL *ssl, int n) {
@@ -1623,8 +1601,8 @@
}
int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
- ctx->method, &ctx->cipher_list, &ctx->cipher_list_by_id, str);
+ STACK_OF(SSL_CIPHER) *cipher_list =
+ ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str);
if (cipher_list == NULL) {
return 0;
}
@@ -1639,8 +1617,8 @@
}
int SSL_CTX_set_cipher_list_tls10(SSL_CTX *ctx, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
- ctx->method, &ctx->cipher_list_tls10, NULL, str);
+ STACK_OF(SSL_CIPHER) *cipher_list =
+ ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls10, str);
if (cipher_list == NULL) {
return 0;
}
@@ -1655,8 +1633,8 @@
}
int SSL_CTX_set_cipher_list_tls11(SSL_CTX *ctx, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
- ctx->method, &ctx->cipher_list_tls11, NULL, str);
+ STACK_OF(SSL_CIPHER) *cipher_list =
+ ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls11, str);
if (cipher_list == NULL) {
return 0;
}
@@ -1671,8 +1649,8 @@
}
int SSL_set_cipher_list(SSL *ssl, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list = ssl_create_cipher_list(
- ssl->ctx->method, &ssl->cipher_list, &ssl->cipher_list_by_id, str);
+ STACK_OF(SSL_CIPHER) *cipher_list =
+ ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str);
if (cipher_list == NULL) {
return 0;
}
@@ -1730,6 +1708,15 @@
return ssl->tlsext_hostname;
}
+ /* During the handshake, report the handshake value. */
+ if (ssl->s3->hs != NULL) {
+ return ssl->s3->hs->hostname;
+ }
+
+ /* SSL_get_servername may also be called after the handshake to look up the
+ * SNI value.
+ *
+ * TODO(davidben): This is almost unused. Can we remove it? */
SSL_SESSION *session = SSL_get_session(ssl);
if (session == NULL) {
return NULL;
@@ -1792,16 +1779,15 @@
int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list,
size_t list_len) {
- OPENSSL_free(ctx->signed_cert_timestamp_list);
- ctx->signed_cert_timestamp_list_length = 0;
-
- ctx->signed_cert_timestamp_list = BUF_memdup(list, list_len);
- if (ctx->signed_cert_timestamp_list == NULL) {
+ CBS sct_list;
+ CBS_init(&sct_list, list, list_len);
+ if (!ssl_is_sct_list_valid(&sct_list)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SCT_LIST);
return 0;
}
- ctx->signed_cert_timestamp_list_length = list_len;
- return 1;
+ return CBS_stow(&sct_list, &ctx->signed_cert_timestamp_list,
+ &ctx->signed_cert_timestamp_list_length);
}
int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
@@ -2056,7 +2042,7 @@
uint32_t mask_k = 0;
uint32_t mask_a = 0;
- if (ssl->cert->x509 != NULL && ssl_has_private_key(ssl)) {
+ if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
int type = ssl_private_key_type(ssl);
if (type == NID_rsaEncryption) {
mask_k |= SSL_kRSA;
@@ -2105,7 +2091,7 @@
* decides to renew the ticket. Once the handshake is completed, it should be
* inserted into the cache. */
if (ssl->s3->established_session != ssl->session ||
- (!ssl->server && ssl->tlsext_ticket_expected)) {
+ (!ssl->server && ssl->s3->hs->ticket_expected)) {
if (use_internal_cache) {
SSL_CTX_add_session(ctx, ssl->s3->established_session);
}
@@ -2177,7 +2163,7 @@
X509 *SSL_get_certificate(const SSL *ssl) {
if (ssl->cert != NULL) {
- return ssl->cert->x509;
+ return ssl->cert->x509_leaf;
}
return NULL;
@@ -2193,7 +2179,7 @@
X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
if (ctx->cert != NULL) {
- return ctx->cert->x509;
+ return ctx->cert->x509_leaf;
}
return NULL;
@@ -2923,6 +2909,10 @@
return ssl->s3->tmp.new_cipher;
}
+void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl, int enabled) {
+ ssl->retain_only_sha256_of_client_certs = !!enabled;
+}
+
void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
ctx->retain_only_sha256_of_client_certs = !!enabled;
}
@@ -3060,7 +3050,7 @@
return;
}
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
out_clock->tv_sec = 1234;
out_clock->tv_usec = 1234;
#elif defined(OPENSSL_WINDOWS)
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 29e5f19..e984012 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -134,13 +134,14 @@
return 0;
}
- if (c->x509 != NULL) {
+ X509 *x509_leaf = c->x509_leaf;
+ if (x509_leaf != NULL) {
/* Sanity-check that the private key and the certificate match, unless the
* key is opaque (in case of, say, a smartcard). */
if (!EVP_PKEY_is_opaque(pkey) &&
- !X509_check_private_key(c->x509, pkey)) {
- X509_free(c->x509);
- c->x509 = NULL;
+ !X509_check_private_key(x509_leaf, pkey)) {
+ X509_free(c->x509_leaf);
+ c->x509_leaf = NULL;
return 0;
}
}
@@ -248,9 +249,9 @@
EVP_PKEY_free(pkey);
- X509_free(c->x509);
+ X509_free(c->x509_leaf);
X509_up_ref(x);
- c->x509 = x;
+ c->x509_leaf = x;
return 1;
}
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index c2396b1..a6b669d 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -199,13 +199,13 @@
goto err;
}
}
- if (session->peer != NULL) {
- X509_up_ref(session->peer);
- new_session->peer = session->peer;
+ if (session->x509_peer != NULL) {
+ X509_up_ref(session->x509_peer);
+ new_session->x509_peer = session->x509_peer;
}
- if (session->cert_chain != NULL) {
- new_session->cert_chain = X509_chain_up_ref(session->cert_chain);
- if (new_session->cert_chain == NULL) {
+ if (session->x509_chain != NULL) {
+ new_session->x509_chain = X509_chain_up_ref(session->x509_chain);
+ if (new_session->x509_chain == NULL) {
goto err;
}
}
@@ -234,6 +234,13 @@
memcpy(new_session->peer_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH);
new_session->peer_sha256_valid = session->peer_sha256_valid;
+ if (session->tlsext_hostname != NULL) {
+ new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
+ if (new_session->tlsext_hostname == NULL) {
+ goto err;
+ }
+ }
+
new_session->timeout = session->timeout;
new_session->time = session->time;
@@ -245,13 +252,6 @@
new_session->key_exchange_info = session->key_exchange_info;
- if (session->tlsext_hostname != NULL) {
- new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
- if (new_session->tlsext_hostname == NULL) {
- goto err;
- }
- }
-
memcpy(new_session->original_handshake_hash,
session->original_handshake_hash,
session->original_handshake_hash_len);
@@ -285,6 +285,31 @@
return 0;
}
+void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session) {
+ struct timeval now;
+ ssl_get_current_time(ssl, &now);
+
+ /* To avoid overflows and underflows, if we've gone back in time or any value
+ * is negative, update the time, but mark the session expired. */
+ if (session->time > now.tv_sec ||
+ session->time < 0 ||
+ now.tv_sec < 0) {
+ session->time = now.tv_sec;
+ session->timeout = 0;
+ return;
+ }
+
+ /* Adjust the session time and timeout. If the session has already expired,
+ * clamp the timeout at zero. */
+ long delta = now.tv_sec - session->time;
+ session->time = now.tv_sec;
+ if (session->timeout < delta) {
+ session->timeout = 0;
+ } else {
+ session->timeout -= delta;
+ }
+}
+
int SSL_SESSION_up_ref(SSL_SESSION *session) {
CRYPTO_refcount_inc(&session->references);
return 1;
@@ -300,8 +325,8 @@
OPENSSL_cleanse(session->master_key, sizeof(session->master_key));
OPENSSL_cleanse(session->session_id, sizeof(session->session_id));
- X509_free(session->peer);
- sk_X509_pop_free(session->cert_chain, X509_free);
+ X509_free(session->x509_peer);
+ sk_X509_pop_free(session->x509_chain, X509_free);
OPENSSL_free(session->tlsext_hostname);
OPENSSL_free(session->tlsext_tick);
OPENSSL_free(session->tlsext_signed_cert_timestamp_list);
@@ -332,7 +357,7 @@
}
X509 *SSL_SESSION_get0_peer(const SSL_SESSION *session) {
- return session->peer;
+ return session->x509_peer;
}
size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, uint8_t *out,
@@ -448,7 +473,7 @@
session->ssl_version = ssl->version;
if (is_server) {
- if (ssl->tlsext_ticket_expected) {
+ if (ssl->s3->hs->ticket_expected) {
/* Don't set session IDs for sessions resumed with tickets. This will keep
* them out of the session cache. */
session->session_id_length = 0;
@@ -599,7 +624,30 @@
struct timeval now;
ssl_get_current_time(ssl, &now);
- return session->timeout >= (long)now.tv_sec - session->time;
+
+ /* Reject tickets from the future to avoid underflow. */
+ if ((long)now.tv_sec < session->time) {
+ return 0;
+ }
+
+ return session->timeout > (long)now.tv_sec - session->time;
+}
+
+int ssl_session_is_resumable(const SSL *ssl, const SSL_SESSION *session) {
+ return ssl_session_is_context_valid(ssl, session) &&
+ /* The session must not be expired. */
+ ssl_session_is_time_valid(ssl, session) &&
+ /* Only resume if the session's version matches the negotiated
+ * version. */
+ ssl->version == session->ssl_version &&
+ /* Only resume if the session's cipher matches the negotiated one. */
+ ssl->s3->tmp.new_cipher == session->cipher &&
+ /* If the session contains a client certificate (either the full
+ * certificate or just the hash) then require that the form of the
+ * certificate matches the current configuration. */
+ ((session->x509_peer == NULL && !session->peer_sha256_valid) ||
+ session->peer_sha256_valid ==
+ ssl->retain_only_sha256_of_client_certs);
}
/* ssl_lookup_session looks up |session_id| in the session cache and sets
@@ -662,15 +710,8 @@
}
}
- if (session == NULL) {
- return ssl_session_success;
- }
-
- if (!ssl_session_is_context_valid(ssl, session)) {
- /* The client did not offer a suitable ticket or session ID. */
- SSL_SESSION_free(session);
- session = NULL;
- } else if (!ssl_session_is_time_valid(ssl, session)) {
+ if (session != NULL &&
+ !ssl_session_is_time_valid(ssl, session)) {
/* The session was from the cache, so remove it. */
SSL_CTX_remove_session(ssl->initial_ctx, session);
SSL_SESSION_free(session);
@@ -682,8 +723,8 @@
}
enum ssl_session_result_t ssl_get_prev_session(
- SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
- const struct ssl_early_callback_ctx *ctx) {
+ SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
+ int *out_renew_ticket, const struct ssl_early_callback_ctx *ctx) {
/* This is used only by servers. */
assert(ssl->server);
SSL_SESSION *session = NULL;
@@ -712,11 +753,8 @@
}
*out_session = session;
- if (session != NULL) {
- *out_send_ticket = renew_ticket;
- } else {
- *out_send_ticket = tickets_supported;
- }
+ *out_tickets_supported = tickets_supported;
+ *out_renew_ticket = renew_ticket;
return ssl_session_success;
}
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index cfce2d0..ae9d5ca 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -23,11 +23,14 @@
#include <openssl/base64.h>
#include <openssl/bio.h>
+#include <openssl/cipher.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
+#include <openssl/hmac.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <openssl/ssl.h>
+#include <openssl/rand.h>
#include <openssl/x509.h>
#include "internal.h"
@@ -269,7 +272,6 @@
"SSLv3",
"TLSv1",
"TLSv1.2",
- "GENERIC",
};
static const char *kMustNotIncludeCECPQ1[] = {
@@ -294,7 +296,6 @@
"AES256",
"AESGCM",
"CHACHA20",
- "GENERIC",
};
static const CurveTest kCurveTests[] = {
@@ -864,18 +865,22 @@
return true;
}
-// CreateSessionWithTicket returns a sample |SSL_SESSION| with the ticket
-// replaced for one of length |ticket_len| or nullptr on failure.
-static bssl::UniquePtr<SSL_SESSION> CreateSessionWithTicket(size_t ticket_len) {
+// CreateSessionWithTicket returns a sample |SSL_SESSION| with the specified
+// version and ticket length or nullptr on failure.
+static bssl::UniquePtr<SSL_SESSION> CreateSessionWithTicket(uint16_t version,
+ size_t ticket_len) {
std::vector<uint8_t> der;
if (!DecodeBase64(&der, kOpenSSLSession)) {
return nullptr;
}
- bssl::UniquePtr<SSL_SESSION> session(SSL_SESSION_from_bytes(der.data(), der.size()));
+ bssl::UniquePtr<SSL_SESSION> session(
+ SSL_SESSION_from_bytes(der.data(), der.size()));
if (!session) {
return nullptr;
}
+ session->ssl_version = version;
+
// Swap out the ticket for a garbage one.
OPENSSL_free(session->tlsext_tick);
session->tlsext_tick = reinterpret_cast<uint8_t*>(OPENSSL_malloc(ticket_len));
@@ -886,7 +891,11 @@
session->tlsext_ticklen = ticket_len;
// Fix up the timeout.
+#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+ session->time = 1234;
+#else
session->time = time(NULL);
+#endif
return session;
}
@@ -914,27 +923,32 @@
return true;
}
-// GetClientHelloLen creates a client SSL connection with a ticket of length
-// |ticket_len| and records the ClientHello. It returns the length of the
-// ClientHello, not including the record header, on success and zero on error.
-static size_t GetClientHelloLen(size_t ticket_len) {
+// GetClientHelloLen creates a client SSL connection with the specified version
+// and ticket length. It returns the length of the ClientHello, not including
+// the record header, on success and zero on error.
+static size_t GetClientHelloLen(uint16_t max_version, uint16_t session_version,
+ size_t ticket_len) {
bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
- bssl::UniquePtr<SSL_SESSION> session = CreateSessionWithTicket(ticket_len);
+ bssl::UniquePtr<SSL_SESSION> session =
+ CreateSessionWithTicket(session_version, ticket_len);
if (!ctx || !session) {
return 0;
}
+
+ // Set a one-element cipher list so the baseline ClientHello is unpadded.
bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
- // Test at TLS 1.2. TLS 1.3 adds enough extensions that the ClientHello is
- // longer than our test vectors.
if (!ssl || !SSL_set_session(ssl.get(), session.get()) ||
- !SSL_set_max_proto_version(ssl.get(), TLS1_2_VERSION)) {
+ !SSL_set_cipher_list(ssl.get(), "ECDHE-RSA-AES128-GCM-SHA256") ||
+ !SSL_set_max_proto_version(ssl.get(), max_version)) {
return 0;
}
+
std::vector<uint8_t> client_hello;
if (!GetClientHello(ssl.get(), &client_hello) ||
client_hello.size() <= SSL3_RT_HEADER_LENGTH) {
return 0;
}
+
return client_hello.size() - SSL3_RT_HEADER_LENGTH;
}
@@ -963,28 +977,37 @@
{0x201, 0x201},
};
-static bool TestPaddingExtension() {
+static bool TestPaddingExtension(uint16_t max_version,
+ uint16_t session_version) {
// Sample a baseline length.
- size_t base_len = GetClientHelloLen(1);
+ size_t base_len = GetClientHelloLen(max_version, session_version, 1);
if (base_len == 0) {
return false;
}
for (const PaddingTest &test : kPaddingTests) {
if (base_len > test.input_len) {
- fprintf(stderr, "Baseline ClientHello too long.\n");
+ fprintf(stderr,
+ "Baseline ClientHello too long (max_version = %04x, "
+ "session_version = %04x).\n",
+ max_version, session_version);
return false;
}
- size_t padded_len = GetClientHelloLen(1 + test.input_len - base_len);
+ size_t padded_len = GetClientHelloLen(max_version, session_version,
+ 1 + test.input_len - base_len);
if (padded_len != test.padded_len) {
- fprintf(stderr, "%u-byte ClientHello padded to %u bytes, not %u.\n",
+ fprintf(stderr,
+ "%u-byte ClientHello padded to %u bytes, not %u (max_version = "
+ "%04x, session_version = %04x).\n",
static_cast<unsigned>(test.input_len),
static_cast<unsigned>(padded_len),
- static_cast<unsigned>(test.padded_len));
+ static_cast<unsigned>(test.padded_len), max_version,
+ session_version);
return false;
}
}
+
return true;
}
@@ -1273,115 +1296,104 @@
return true;
}
-static uint16_t kTLSVersions[] = {
- SSL3_VERSION, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION, TLS1_3_VERSION,
-};
-
-static uint16_t kDTLSVersions[] = {
- DTLS1_VERSION, DTLS1_2_VERSION,
-};
-
-static bool TestSequenceNumber() {
- for (bool is_dtls : std::vector<bool>{false, true}) {
- const SSL_METHOD *method = is_dtls ? DTLS_method() : TLS_method();
- const uint16_t *versions = is_dtls ? kDTLSVersions : kTLSVersions;
- size_t num_versions = is_dtls ? OPENSSL_ARRAY_SIZE(kDTLSVersions)
- : OPENSSL_ARRAY_SIZE(kTLSVersions);
- for (size_t i = 0; i < num_versions; i++) {
- uint16_t version = versions[i];
- bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
- bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
- if (!server_ctx || !client_ctx ||
- !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
- return false;
- }
-
- bssl::UniquePtr<X509> cert = GetTestCertificate();
- bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
- if (!cert || !key ||
- !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
- return false;
- }
-
- bssl::UniquePtr<SSL> client, server;
- if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
- server_ctx.get(), nullptr /* no session */)) {
- return false;
- }
-
- // Drain any post-handshake messages to ensure there are no unread records
- // on either end.
- uint8_t byte = 0;
- if (SSL_read(client.get(), &byte, 1) > 0 ||
- SSL_read(server.get(), &byte, 1) > 0) {
- fprintf(stderr, "Received unexpected data.\n");
- return false;
- }
-
- uint64_t client_read_seq = SSL_get_read_sequence(client.get());
- uint64_t client_write_seq = SSL_get_write_sequence(client.get());
- uint64_t server_read_seq = SSL_get_read_sequence(server.get());
- uint64_t server_write_seq = SSL_get_write_sequence(server.get());
-
- if (is_dtls) {
- // Both client and server must be at epoch 1.
- if (EpochFromSequence(client_read_seq) != 1 ||
- EpochFromSequence(client_write_seq) != 1 ||
- EpochFromSequence(server_read_seq) != 1 ||
- EpochFromSequence(server_write_seq) != 1) {
- fprintf(stderr, "Bad epochs.\n");
- return false;
- }
-
- // The next record to be written should exceed the largest received.
- if (client_write_seq <= server_read_seq ||
- server_write_seq <= client_read_seq) {
- fprintf(stderr, "Inconsistent sequence numbers.\n");
- return false;
- }
- } else {
- // The next record to be written should equal the next to be received.
- if (client_write_seq != server_read_seq ||
- server_write_seq != client_read_seq) {
- fprintf(stderr, "Inconsistent sequence numbers.\n");
- return false;
- }
- }
-
- // Send a record from client to server.
- if (SSL_write(client.get(), &byte, 1) != 1 ||
- SSL_read(server.get(), &byte, 1) != 1) {
- fprintf(stderr, "Could not send byte.\n");
- return false;
- }
-
- // The client write and server read sequence numbers should have
- // incremented.
- if (client_write_seq + 1 != SSL_get_write_sequence(client.get()) ||
- server_read_seq + 1 != SSL_get_read_sequence(server.get())) {
- fprintf(stderr, "Sequence numbers did not increment.\n");
- return false;
- }
- }
- }
-
- return true;
-}
-
-static bool TestOneSidedShutdown() {
- bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
- bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
- if (!client_ctx || !server_ctx) {
+static bool TestSequenceNumber(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+ if (!server_ctx || !client_ctx ||
+ !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
return false;
}
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
- if (!cert || !key ||
+ if (!cert || !key || !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+ return false;
+ }
+
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get(), nullptr /* no session */)) {
+ return false;
+ }
+
+ // Drain any post-handshake messages to ensure there are no unread records
+ // on either end.
+ uint8_t byte = 0;
+ if (SSL_read(client.get(), &byte, 1) > 0 ||
+ SSL_read(server.get(), &byte, 1) > 0) {
+ fprintf(stderr, "Received unexpected data.\n");
+ return false;
+ }
+
+ uint64_t client_read_seq = SSL_get_read_sequence(client.get());
+ uint64_t client_write_seq = SSL_get_write_sequence(client.get());
+ uint64_t server_read_seq = SSL_get_read_sequence(server.get());
+ uint64_t server_write_seq = SSL_get_write_sequence(server.get());
+
+ if (is_dtls) {
+ // Both client and server must be at epoch 1.
+ if (EpochFromSequence(client_read_seq) != 1 ||
+ EpochFromSequence(client_write_seq) != 1 ||
+ EpochFromSequence(server_read_seq) != 1 ||
+ EpochFromSequence(server_write_seq) != 1) {
+ fprintf(stderr, "Bad epochs.\n");
+ return false;
+ }
+
+ // The next record to be written should exceed the largest received.
+ if (client_write_seq <= server_read_seq ||
+ server_write_seq <= client_read_seq) {
+ fprintf(stderr, "Inconsistent sequence numbers.\n");
+ return false;
+ }
+ } else {
+ // The next record to be written should equal the next to be received.
+ if (client_write_seq != server_read_seq ||
+ server_write_seq != client_read_seq) {
+ fprintf(stderr, "Inconsistent sequence numbers.\n");
+ return false;
+ }
+ }
+
+ // Send a record from client to server.
+ if (SSL_write(client.get(), &byte, 1) != 1 ||
+ SSL_read(server.get(), &byte, 1) != 1) {
+ fprintf(stderr, "Could not send byte.\n");
+ return false;
+ }
+
+ // The client write and server read sequence numbers should have
+ // incremented.
+ if (client_write_seq + 1 != SSL_get_write_sequence(client.get()) ||
+ server_read_seq + 1 != SSL_get_read_sequence(server.get())) {
+ fprintf(stderr, "Sequence numbers did not increment.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool TestOneSidedShutdown(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
+ // SSL_shutdown is a no-op in DTLS.
+ if (is_dtls) {
+ return true;
+ }
+
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<X509> cert = GetTestCertificate();
+ bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+ if (!client_ctx || !server_ctx || !cert || !key ||
+ !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
!SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
!SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
return false;
@@ -1427,6 +1439,7 @@
return true;
}
+
static bool TestSessionDuplication() {
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
@@ -1631,63 +1644,63 @@
static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { return 1; }
-static bool TestGetPeerCertificate() {
+static bool TestGetPeerCertificate(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
if (!cert || !key) {
return false;
}
- for (uint16_t version : kTLSVersions) {
- // Configure both client and server to accept any certificate.
- bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
- if (!ctx ||
- !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
- !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
- return false;
- }
- SSL_CTX_set_verify(
- ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
- SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
+ // Configure both client and server to accept any certificate.
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+ if (!ctx ||
+ !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+ !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
+ return false;
+ }
+ SSL_CTX_set_verify(
+ ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+ SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
- bssl::UniquePtr<SSL> client, server;
- if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
- nullptr /* no session */)) {
- return false;
- }
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+ nullptr /* no session */)) {
+ return false;
+ }
- // Client and server should both see the leaf certificate.
- bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
- if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
- fprintf(stderr, "%x: Server peer certificate did not match.\n", version);
- return false;
- }
+ // Client and server should both see the leaf certificate.
+ bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
+ if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
+ fprintf(stderr, "Server peer certificate did not match.\n");
+ return false;
+ }
- peer.reset(SSL_get_peer_certificate(client.get()));
- if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
- fprintf(stderr, "%x: Client peer certificate did not match.\n", version);
- return false;
- }
+ peer.reset(SSL_get_peer_certificate(client.get()));
+ if (!peer || X509_cmp(cert.get(), peer.get()) != 0) {
+ fprintf(stderr, "Client peer certificate did not match.\n");
+ return false;
+ }
- // However, for historical reasons, the chain includes the leaf on the
- // client, but does not on the server.
- if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1) {
- fprintf(stderr, "%x: Client peer chain was incorrect.\n", version);
- return false;
- }
+ // However, for historical reasons, the chain includes the leaf on the
+ // client, but does not on the server.
+ if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1) {
+ fprintf(stderr, "Client peer chain was incorrect.\n");
+ return false;
+ }
- if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0) {
- fprintf(stderr, "%x: Server peer chain was incorrect.\n", version);
- return false;
- }
+ if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0) {
+ fprintf(stderr, "Server peer chain was incorrect.\n");
+ return false;
}
return true;
}
-static bool TestRetainOnlySHA256OfCerts() {
+static bool TestRetainOnlySHA256OfCerts(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
if (!cert || !key) {
@@ -1704,45 +1717,43 @@
uint8_t cert_sha256[SHA256_DIGEST_LENGTH];
SHA256(cert_der, cert_der_len, cert_sha256);
- for (uint16_t version : kTLSVersions) {
- // Configure both client and server to accept any certificate, but the
- // server must retain only the SHA-256 of the peer.
- bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
- if (!ctx ||
- !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
- !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
- return false;
- }
- SSL_CTX_set_verify(
- ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
- SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
- SSL_CTX_set_retain_only_sha256_of_client_certs(ctx.get(), 1);
+ // Configure both client and server to accept any certificate, but the
+ // server must retain only the SHA-256 of the peer.
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+ if (!ctx ||
+ !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+ !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
+ return false;
+ }
+ SSL_CTX_set_verify(
+ ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
+ SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL);
+ SSL_CTX_set_retain_only_sha256_of_client_certs(ctx.get(), 1);
- bssl::UniquePtr<SSL> client, server;
- if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
- nullptr /* no session */)) {
- return false;
- }
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+ nullptr /* no session */)) {
+ return false;
+ }
- // The peer certificate has been dropped.
- bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
- if (peer) {
- fprintf(stderr, "%x: Peer certificate was retained.\n", version);
- return false;
- }
+ // The peer certificate has been dropped.
+ bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
+ if (peer) {
+ fprintf(stderr, "Peer certificate was retained.\n");
+ return false;
+ }
- SSL_SESSION *session = SSL_get_session(server.get());
- if (!session->peer_sha256_valid) {
- fprintf(stderr, "%x: peer_sha256_valid was not set.\n", version);
- return false;
- }
+ SSL_SESSION *session = SSL_get_session(server.get());
+ if (!session->peer_sha256_valid) {
+ fprintf(stderr, "peer_sha256_valid was not set.\n");
+ return false;
+ }
- if (memcmp(cert_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH) != 0) {
- fprintf(stderr, "%x: peer_sha256 did not match.\n", version);
- return false;
- }
+ if (memcmp(cert_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH) != 0) {
+ fprintf(stderr, "peer_sha256 did not match.\n");
+ return false;
}
return true;
@@ -1900,8 +1911,8 @@
0xc0, 0x28, 0x00, 0x39, 0x00, 0x6b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f,
0x00, 0x3c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37,
0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00,
- 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x08, 0x06, 0x06, 0x01, 0x08,
- 0x05, 0x05, 0x01, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01, 0x04, 0x03, 0x02,
+ 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
+ 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02,
0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00,
0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
};
@@ -1974,7 +1985,64 @@
return true;
}
-static bool TestSessionIDContext() {
+static bssl::UniquePtr<SSL_SESSION> ExpectSessionRenewed(SSL_CTX *client_ctx,
+ SSL_CTX *server_ctx,
+ SSL_SESSION *session) {
+ g_last_session = nullptr;
+ SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession);
+
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx,
+ server_ctx, session)) {
+ fprintf(stderr, "Failed to connect client and server.\n");
+ return nullptr;
+ }
+
+ if (SSL_session_reused(client.get()) != SSL_session_reused(server.get())) {
+ fprintf(stderr, "Client and server were inconsistent.\n");
+ return nullptr;
+ }
+
+ if (!SSL_session_reused(client.get())) {
+ fprintf(stderr, "Session was not reused.\n");
+ return nullptr;
+ }
+
+ // Run the read loop to account for post-handshake tickets in TLS 1.3.
+ SSL_read(client.get(), nullptr, 0);
+
+ SSL_CTX_sess_set_new_cb(client_ctx, nullptr);
+
+ if (!g_last_session) {
+ fprintf(stderr, "Client did not receive a renewed session.\n");
+ return nullptr;
+ }
+ return std::move(g_last_session);
+}
+
+static int SwitchSessionIDContextSNI(SSL *ssl, int *out_alert, void *arg) {
+ static const uint8_t kContext[] = {3};
+
+ if (!SSL_set_session_id_context(ssl, kContext, sizeof(kContext))) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static int SwitchSessionIDContextEarly(
+ const struct ssl_early_callback_ctx *ctx) {
+ static const uint8_t kContext[] = {3};
+
+ if (!SSL_set_session_id_context(ctx->ssl, kContext, sizeof(kContext))) {
+ return -1;
+ }
+
+ return 1;
+}
+
+static bool TestSessionIDContext(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
if (!cert || !key) {
@@ -1984,50 +2052,74 @@
static const uint8_t kContext1[] = {1};
static const uint8_t kContext2[] = {2};
- for (uint16_t version : kTLSVersions) {
- bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
- bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
- if (!server_ctx || !client_ctx ||
- !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
- !SSL_CTX_set_session_id_context(server_ctx.get(), kContext1,
- sizeof(kContext1)) ||
- !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
- return false;
- }
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+ if (!server_ctx || !client_ctx ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+ !SSL_CTX_set_session_id_context(server_ctx.get(), kContext1,
+ sizeof(kContext1)) ||
+ !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) {
+ return false;
+ }
- 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_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
- bssl::UniquePtr<SSL_SESSION> session =
- CreateClientSession(client_ctx.get(), server_ctx.get());
- if (!session) {
- fprintf(stderr, "Error getting session (version = %04x).\n", version);
- return false;
- }
+ bssl::UniquePtr<SSL_SESSION> session =
+ CreateClientSession(client_ctx.get(), server_ctx.get());
+ if (!session) {
+ fprintf(stderr, "Error getting session.\n");
+ return false;
+ }
- if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
- true /* expect session reused */)) {
- fprintf(stderr, "Error resuming session (version = %04x).\n", version);
- return false;
- }
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+ true /* expect session reused */)) {
+ fprintf(stderr, "Error resuming session.\n");
+ return false;
+ }
- // Change the session ID context.
- if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext2,
- sizeof(kContext2))) {
- return false;
- }
+ // Change the session ID context.
+ if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext2,
+ sizeof(kContext2))) {
+ return false;
+ }
- if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
- false /* expect session not reused */)) {
- fprintf(stderr,
- "Error connection with different context (version = %04x).\n",
- version);
- return false;
- }
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+ false /* expect session not reused */)) {
+ fprintf(stderr, "Error connecting with a different context.\n");
+ return false;
+ }
+
+ // Change the session ID context back and install an SNI callback to switch
+ // it.
+ if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext1,
+ sizeof(kContext1))) {
+ return false;
+ }
+
+ SSL_CTX_set_tlsext_servername_callback(server_ctx.get(),
+ SwitchSessionIDContextSNI);
+
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+ false /* expect session not reused */)) {
+ fprintf(stderr, "Error connecting with a context switch on SNI callback.\n");
+ return false;
+ }
+
+ // Switch the session ID context with the early callback instead.
+ SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), nullptr);
+ SSL_CTX_set_select_certificate_cb(server_ctx.get(),
+ SwitchSessionIDContextEarly);
+
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+ false /* expect session not reused */)) {
+ fprintf(stderr,
+ "Error connecting with a context switch on early callback.\n");
+ return false;
}
return true;
@@ -2039,16 +2131,78 @@
*out_clock = g_current_time;
}
-static bool TestSessionTimeout() {
+static int RenewTicketCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
+ EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
+ int encrypt) {
+ static const uint8_t kZeros[16] = {0};
+
+ if (encrypt) {
+ memcpy(key_name, kZeros, sizeof(kZeros));
+ RAND_bytes(iv, 16);
+ } else if (memcmp(key_name, kZeros, 16) != 0) {
+ return 0;
+ }
+
+ if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) ||
+ !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) {
+ return -1;
+ }
+
+ // Returning two from the callback in decrypt mode renews the
+ // session in TLS 1.2 and below.
+ return encrypt ? 1 : 2;
+}
+
+static bool GetServerTicketTime(long *out, const SSL_SESSION *session) {
+ if (session->tlsext_ticklen < 16 + 16 + SHA256_DIGEST_LENGTH) {
+ return false;
+ }
+
+ const uint8_t *ciphertext = session->tlsext_tick + 16 + 16;
+ size_t len = session->tlsext_ticklen - 16 - 16 - SHA256_DIGEST_LENGTH;
+ std::unique_ptr<uint8_t[]> plaintext(new uint8_t[len]);
+
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ // Fuzzer-mode tickets are unencrypted.
+ memcpy(plaintext.get(), ciphertext, len);
+#else
+ static const uint8_t kZeros[16] = {0};
+ const uint8_t *iv = session->tlsext_tick + 16;
+ bssl::ScopedEVP_CIPHER_CTX ctx;
+ int len1, len2;
+ if (!EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, kZeros, iv) ||
+ !EVP_DecryptUpdate(ctx.get(), plaintext.get(), &len1, ciphertext, len) ||
+ !EVP_DecryptFinal_ex(ctx.get(), plaintext.get() + len1, &len2)) {
+ return false;
+ }
+
+ len = static_cast<size_t>(len1 + len2);
+#endif
+
+ bssl::UniquePtr<SSL_SESSION> server_session(
+ SSL_SESSION_from_bytes(plaintext.get(), len));
+ if (!server_session) {
+ return false;
+ }
+
+ *out = server_session->time;
+ return true;
+}
+
+static bool TestSessionTimeout(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
if (!cert || !key) {
return false;
}
- for (uint16_t version : kTLSVersions) {
- bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
- bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+ for (bool server_test : std::vector<bool>{false, true}) {
+ static const int kStartTime = 1000;
+ g_current_time.tv_sec = kStartTime;
+
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
if (!server_ctx || !client_ctx ||
!SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
!SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
@@ -2060,23 +2214,31 @@
}
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(server_ctx.get(), CurrentTimeCallback);
+
+ // Both client and server must enforce session timeouts.
+ if (server_test) {
+ SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
+ } else {
+ SSL_CTX_set_current_time_cb(client_ctx.get(), CurrentTimeCallback);
+ }
+
+ // Configure a ticket callback which renews tickets.
+ SSL_CTX_set_tlsext_ticket_key_cb(server_ctx.get(), RenewTicketCallback);
bssl::UniquePtr<SSL_SESSION> session =
CreateClientSession(client_ctx.get(), server_ctx.get());
if (!session) {
- fprintf(stderr, "Error getting session (version = %04x).\n", version);
+ fprintf(stderr, "Error getting session.\n");
return false;
}
// Advance the clock just behind the timeout.
- g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT;
+ g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT - 1;
if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
true /* expect session reused */)) {
- fprintf(stderr, "Error resuming session (version = %04x).\n", version);
+ fprintf(stderr, "Error resuming session.\n");
return false;
}
@@ -2085,7 +2247,71 @@
if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
false /* expect session not reused */)) {
- fprintf(stderr, "Error resuming session (version = %04x).\n", version);
+ fprintf(stderr, "Error resuming session.\n");
+ return false;
+ }
+
+ // Rewind the clock to before the session was minted.
+ g_current_time.tv_sec = kStartTime - 1;
+
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
+ false /* expect session not reused */)) {
+ fprintf(stderr, "Error resuming session.\n");
+ return false;
+ }
+
+ // SSL 3.0 cannot renew sessions.
+ if (version == SSL3_VERSION) {
+ continue;
+ }
+
+ // Renew the session 10 seconds before expiration.
+ g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 10;
+ bssl::UniquePtr<SSL_SESSION> new_session =
+ ExpectSessionRenewed(client_ctx.get(), server_ctx.get(), session.get());
+ if (!new_session) {
+ fprintf(stderr, "Error renewing session.\n");
+ return false;
+ }
+
+ // This new session is not the same object as before.
+ if (session.get() == new_session.get()) {
+ fprintf(stderr, "New and old sessions alias.\n");
+ return false;
+ }
+
+ // Check the sessions have timestamps measured from issuance.
+ long session_time = 0;
+ if (server_test) {
+ if (!GetServerTicketTime(&session_time, new_session.get())) {
+ fprintf(stderr, "Failed to decode session ticket.\n");
+ return false;
+ }
+ } else {
+ session_time = new_session->time;
+ }
+
+ if (session_time != g_current_time.tv_sec) {
+ fprintf(stderr, "New session is not measured from issuance.\n");
+ return false;
+ }
+
+ // The new session is usable just before the old expiration.
+ g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 1;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ true /* expect session reused */)) {
+ fprintf(stderr, "Error resuming renewed session.\n");
+ return false;
+ }
+
+ // Renewal does not extend the lifetime, so it is not usable beyond the
+ // old expiration.
+ g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT + 1;
+ if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+ new_session.get(),
+ false /* expect session not reused */)) {
+ fprintf(stderr, "Renewed session's lifetime is too long.\n");
return false;
}
}
@@ -2099,7 +2325,13 @@
return SSL_TLSEXT_ERR_OK;
}
-static bool TestSNICallback() {
+static bool TestSNICallback(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
+ // SSL 3.0 lacks extensions.
+ if (version == SSL3_VERSION) {
+ return true;
+ }
+
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
bssl::UniquePtr<X509> cert2 = GetECDSATestCertificate();
@@ -2108,56 +2340,47 @@
return false;
}
- // At each version, test that switching the |SSL_CTX| at the SNI callback
- // behaves correctly.
- for (uint16_t version : kTLSVersions) {
- if (version == SSL3_VERSION) {
- continue;
- }
+ // Test that switching the |SSL_CTX| at the SNI callback behaves correctly.
+ static const uint16_t kECDSAWithSHA256 = SSL_SIGN_ECDSA_SECP256R1_SHA256;
- static const uint16_t kECDSAWithSHA256 = SSL_SIGN_ECDSA_SECP256R1_SHA256;
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> server_ctx2(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+ if (!server_ctx || !server_ctx2 || !client_ctx ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+ !SSL_CTX_use_certificate(server_ctx2.get(), cert2.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx2.get(), key2.get()) ||
+ // Historically signing preferences would be lost in some cases with the
+ // SNI callback, which triggers the TLS 1.2 SHA-1 default. To ensure
+ // this doesn't happen when |version| is TLS 1.2, configure the private
+ // key to only sign SHA-256.
+ !SSL_CTX_set_signing_algorithm_prefs(server_ctx2.get(), &kECDSAWithSHA256,
+ 1) ||
+ !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(server_ctx2.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx2.get(), version)) {
+ return false;
+ }
- bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
- bssl::UniquePtr<SSL_CTX> server_ctx2(SSL_CTX_new(TLS_method()));
- bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
- if (!server_ctx || !server_ctx2 || !client_ctx ||
- !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
- !SSL_CTX_use_certificate(server_ctx2.get(), cert2.get()) ||
- !SSL_CTX_use_PrivateKey(server_ctx2.get(), key2.get()) ||
- // Historically signing preferences would be lost in some cases with the
- // SNI callback, which triggers the TLS 1.2 SHA-1 default. To ensure
- // this doesn't happen when |version| is TLS 1.2, configure the private
- // key to only sign SHA-256.
- !SSL_CTX_set_signing_algorithm_prefs(server_ctx2.get(),
- &kECDSAWithSHA256, 1) ||
- !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
- !SSL_CTX_set_min_proto_version(server_ctx2.get(), version) ||
- !SSL_CTX_set_max_proto_version(server_ctx2.get(), version)) {
- return false;
- }
+ SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), SwitchContext);
+ SSL_CTX_set_tlsext_servername_arg(server_ctx.get(), server_ctx2.get());
- SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), SwitchContext);
- SSL_CTX_set_tlsext_servername_arg(server_ctx.get(), server_ctx2.get());
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get(), nullptr)) {
+ fprintf(stderr, "Handshake failed.\n");
+ return false;
+ }
- bssl::UniquePtr<SSL> client, server;
- if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
- server_ctx.get(), nullptr)) {
- fprintf(stderr, "Handshake failed at version %04x.\n", version);
- return false;
- }
-
- // The client should have received |cert2|.
- bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(client.get()));
- if (!peer ||
- X509_cmp(peer.get(), cert2.get()) != 0) {
- fprintf(stderr, "Incorrect certificate received at version %04x.\n",
- version);
- return false;
- }
+ // The client should have received |cert2|.
+ bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(client.get()));
+ if (!peer || X509_cmp(peer.get(), cert2.get()) != 0) {
+ fprintf(stderr, "Incorrect certificate received.\n");
+ return false;
}
return true;
@@ -2280,45 +2503,120 @@
return true;
}
-static bool TestVersions() {
+static bool TestVersion(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
bssl::UniquePtr<X509> cert = GetTestCertificate();
bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
if (!cert || !key) {
return false;
}
- for (bool is_dtls : std::vector<bool>{false, true}) {
- const SSL_METHOD *method = is_dtls ? DTLS_method() : TLS_method();
- const char *name = is_dtls ? "DTLS" : "TLS";
- const uint16_t *versions = is_dtls ? kDTLSVersions : kTLSVersions;
- size_t num_versions = is_dtls ? OPENSSL_ARRAY_SIZE(kDTLSVersions)
- : OPENSSL_ARRAY_SIZE(kTLSVersions);
- for (size_t i = 0; i < num_versions; i++) {
- uint16_t version = versions[i];
- bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
- bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
- bssl::UniquePtr<SSL> client, server;
- if (!server_ctx || !client_ctx ||
- !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
- !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
- !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
- !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
- !ConnectClientAndServer(&client, &server, client_ctx.get(),
- server_ctx.get(), nullptr /* no session */)) {
- fprintf(stderr, "Failed to connect %s at version %04x.\n", name,
- version);
- return false;
- }
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
+ bssl::UniquePtr<SSL> client, server;
+ if (!server_ctx || !client_ctx ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+ !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+ !ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get(), nullptr /* no session */)) {
+ fprintf(stderr, "Failed to connect.\n");
+ return false;
+ }
- if (SSL_version(client.get()) != version ||
- SSL_version(server.get()) != version) {
- fprintf(stderr,
- "%s version mismatch. Got %04x and %04x, wanted %04x.\n", name,
- SSL_version(client.get()), SSL_version(server.get()), version);
- return false;
- }
+ if (SSL_version(client.get()) != version ||
+ SSL_version(server.get()) != version) {
+ fprintf(stderr, "Version mismatch. Got %04x and %04x, wanted %04x.\n",
+ SSL_version(client.get()), SSL_version(server.get()), version);
+ return false;
+ }
+
+ return true;
+}
+
+// Tests that that |SSL_get_pending_cipher| is available during the ALPN
+// selection callback.
+static bool TestALPNCipherAvailable(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
+ // SSL 3.0 lacks extensions.
+ if (version == SSL3_VERSION) {
+ return true;
+ }
+
+ static const uint8_t kALPNProtos[] = {0x03, 'f', 'o', 'o'};
+
+ bssl::UniquePtr<X509> cert = GetTestCertificate();
+ bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+ if (!cert || !key) {
+ return false;
+ }
+
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+ if (!ctx || !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+ !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(ctx.get(), version) ||
+ SSL_CTX_set_alpn_protos(ctx.get(), kALPNProtos, sizeof(kALPNProtos)) !=
+ 0) {
+ return false;
+ }
+
+ // The ALPN callback does not fail the handshake on error, so have the
+ // callback write a boolean.
+ std::pair<uint16_t, bool> callback_state(version, false);
+ SSL_CTX_set_alpn_select_cb(
+ ctx.get(),
+ [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in,
+ unsigned in_len, void *arg) -> int {
+ auto state = reinterpret_cast<std::pair<uint16_t, bool> *>(arg);
+ if (SSL_get_pending_cipher(ssl) != nullptr &&
+ SSL_version(ssl) == state->first) {
+ state->second = true;
+ }
+ return SSL_TLSEXT_ERR_NOACK;
+ },
+ &callback_state);
+
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+ nullptr /* no session */)) {
+ return false;
+ }
+
+ if (!callback_state.second) {
+ fprintf(stderr, "The pending cipher was not known in the ALPN callback.\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool ForEachVersion(bool (*test_func)(bool is_dtls,
+ const SSL_METHOD *method,
+ uint16_t version)) {
+ static uint16_t kTLSVersions[] = {
+ SSL3_VERSION, TLS1_VERSION, TLS1_1_VERSION,
+ TLS1_2_VERSION, TLS1_3_VERSION,
+ };
+
+ static uint16_t kDTLSVersions[] = {
+ DTLS1_VERSION, DTLS1_2_VERSION,
+ };
+
+ for (uint16_t version : kTLSVersions) {
+ if (!test_func(false, TLS_method(), version)) {
+ fprintf(stderr, "Test failed at TLS version %04x.\n", version);
+ return false;
+ }
+ }
+
+ for (uint16_t version : kDTLSVersions) {
+ if (!test_func(true, DTLS_method(), version)) {
+ fprintf(stderr, "Test failed at DTLS version %04x.\n", version);
+ return false;
}
}
@@ -2346,23 +2644,31 @@
!TestDefaultVersion(TLS1_1_VERSION, TLS1_1_VERSION, &DTLSv1_method) ||
!TestDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &DTLSv1_2_method) ||
!TestCipherGetRFCName() ||
- !TestPaddingExtension() ||
+ // Test the padding extension at TLS 1.2.
+ !TestPaddingExtension(TLS1_2_VERSION, TLS1_2_VERSION) ||
+ // Test the padding extension at TLS 1.3 with a TLS 1.2 session, so there
+ // will be no PSK binder after the padding extension.
+ !TestPaddingExtension(TLS1_3_VERSION, TLS1_2_VERSION) ||
+ // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
+ // will be a PSK binder after the padding extension.
+ !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT_VERSION) ||
!TestClientCAList() ||
!TestInternalSessionCache() ||
- !TestSequenceNumber() ||
- !TestOneSidedShutdown() ||
+ !ForEachVersion(TestSequenceNumber) ||
+ !ForEachVersion(TestOneSidedShutdown) ||
!TestSessionDuplication() ||
!TestSetFD() ||
!TestSetBIO() ||
- !TestGetPeerCertificate() ||
- !TestRetainOnlySHA256OfCerts() ||
+ !ForEachVersion(TestGetPeerCertificate) ||
+ !ForEachVersion(TestRetainOnlySHA256OfCerts) ||
!TestClientHello() ||
- !TestSessionIDContext() ||
- !TestSessionTimeout() ||
- !TestSNICallback() ||
+ !ForEachVersion(TestSessionIDContext) ||
+ !ForEachVersion(TestSessionTimeout) ||
+ !ForEachVersion(TestSNICallback) ||
!TestEarlyCallbackVersionSwitch() ||
!TestSetVersion() ||
- !TestVersions()) {
+ !ForEachVersion(TestVersion) ||
+ !ForEachVersion(TestALPNCipherAvailable)) {
ERR_print_errors_fp(stderr);
return 1;
}
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 4b8fe45..4c7d3ee 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -258,6 +258,63 @@
return 1;
}
+static int tls1_setup_key_block(SSL *ssl) {
+ if (ssl->s3->hs->key_block_len != 0) {
+ return 1;
+ }
+
+ SSL_SESSION *session = ssl->session;
+ if (ssl->s3->new_session != NULL) {
+ session = ssl->s3->new_session;
+ }
+
+ const EVP_AEAD *aead = NULL;
+ size_t mac_secret_len, fixed_iv_len;
+ if (session->cipher == NULL ||
+ !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+ session->cipher, ssl3_protocol_version(ssl))) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
+ return 0;
+ }
+ size_t key_len = EVP_AEAD_key_length(aead);
+ if (mac_secret_len > 0) {
+ /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
+ * key length reported by |EVP_AEAD_key_length| will include the MAC key
+ * bytes and initial implicit IV. */
+ if (key_len < mac_secret_len + fixed_iv_len) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ key_len -= mac_secret_len + fixed_iv_len;
+ }
+
+ assert(mac_secret_len < 256);
+ assert(key_len < 256);
+ assert(fixed_iv_len < 256);
+
+ ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
+ ssl->s3->tmp.new_key_len = (uint8_t)key_len;
+ ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
+
+ size_t key_block_len = SSL_get_key_block_len(ssl);
+
+ uint8_t *keyblock = OPENSSL_malloc(key_block_len);
+ if (keyblock == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ if (!SSL_generate_key_block(ssl, keyblock, key_block_len)) {
+ OPENSSL_free(keyblock);
+ return 0;
+ }
+
+ assert(key_block_len < 256);
+ ssl->s3->hs->key_block_len = (uint8_t)key_block_len;
+ ssl->s3->hs->key_block = keyblock;
+ return 1;
+}
+
int tls1_change_cipher_state(SSL *ssl, int which) {
/* Ensure the key block is set up. */
if (!tls1_setup_key_block(ssl)) {
@@ -276,10 +333,9 @@
size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
size_t key_len = ssl->s3->tmp.new_key_len;
size_t iv_len = ssl->s3->tmp.new_fixed_iv_len;
- assert((mac_secret_len + key_len + iv_len) * 2 ==
- ssl->s3->tmp.key_block_length);
+ assert((mac_secret_len + key_len + iv_len) * 2 == ssl->s3->hs->key_block_len);
- const uint8_t *key_data = ssl->s3->tmp.key_block;
+ const uint8_t *key_data = ssl->s3->hs->key_block;
const uint8_t *client_write_mac_secret = key_data;
key_data += mac_secret_len;
const uint8_t *server_write_mac_secret = key_data;
@@ -333,65 +389,6 @@
ssl->s3->client_random, SSL3_RANDOM_SIZE);
}
-int tls1_setup_key_block(SSL *ssl) {
- if (ssl->s3->tmp.key_block_length != 0) {
- return 1;
- }
-
- SSL_SESSION *session = ssl->session;
- if (ssl->s3->new_session != NULL) {
- session = ssl->s3->new_session;
- }
-
- const EVP_AEAD *aead = NULL;
- size_t mac_secret_len, fixed_iv_len;
- if (session->cipher == NULL ||
- !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
- session->cipher, ssl3_protocol_version(ssl))) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
- return 0;
- }
- size_t key_len = EVP_AEAD_key_length(aead);
- if (mac_secret_len > 0) {
- /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the
- * key length reported by |EVP_AEAD_key_length| will include the MAC key
- * bytes and initial implicit IV. */
- if (key_len < mac_secret_len + fixed_iv_len) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
- key_len -= mac_secret_len + fixed_iv_len;
- }
-
- assert(mac_secret_len < 256);
- assert(key_len < 256);
- assert(fixed_iv_len < 256);
-
- ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
- ssl->s3->tmp.new_key_len = (uint8_t)key_len;
- ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
-
- size_t key_block_len = SSL_get_key_block_len(ssl);
-
- ssl3_cleanup_key_block(ssl);
-
- uint8_t *keyblock = OPENSSL_malloc(key_block_len);
- if (keyblock == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
- }
-
- if (!SSL_generate_key_block(ssl, keyblock, key_block_len)) {
- OPENSSL_free(keyblock);
- return 0;
- }
-
- assert(key_block_len < 256);
- ssl->s3->tmp.key_block_length = (uint8_t)key_block_len;
- ssl->s3->tmp.key_block = keyblock;
- return 1;
-}
-
static int append_digest(const EVP_MD_CTX *ctx, uint8_t *out, size_t *out_len,
size_t max_out) {
int ret = 0;
@@ -505,6 +502,11 @@
return 0;
}
+ /* Exporters may not be used in the middle of a renegotiation. */
+ if (SSL_in_init(ssl) && !SSL_in_false_start(ssl)) {
+ return 0;
+ }
+
if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
return tls13_export_keying_material(ssl, out, out_len, label, label_len,
context, context_len, use_context);
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 9655b83..421232f 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -442,62 +442,73 @@
}
/* kVerifySignatureAlgorithms is the default list of accepted signature
- * algorithms for verifying. */
+ * algorithms for verifying.
+ *
+ * For now, RSA-PSS signature algorithms are not enabled on Android's system
+ * BoringSSL. Once the change in Chrome has stuck and the values are finalized,
+ * restore them. */
static const uint16_t kVerifySignatureAlgorithms[] = {
- /* For now, do not enable RSA-PSS signature algorithms on Android's system
- * BoringSSL. Once TLS 1.3 is finalized and the change in Chrome has stuck,
- * restore them. */
+ /* Prefer SHA-256 algorithms. */
+ SSL_SIGN_ECDSA_SECP256R1_SHA256,
#if !defined(BORINGSSL_ANDROID_SYSTEM)
- SSL_SIGN_RSA_PSS_SHA512,
+ SSL_SIGN_RSA_PSS_SHA256,
#endif
- SSL_SIGN_RSA_PKCS1_SHA512,
+ SSL_SIGN_RSA_PKCS1_SHA256,
+
+ /* Larger hashes are acceptable. */
+ SSL_SIGN_ECDSA_SECP384R1_SHA384,
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+ SSL_SIGN_RSA_PSS_SHA384,
+#endif
+ SSL_SIGN_RSA_PKCS1_SHA384,
+
/* TODO(davidben): Remove this entry and SSL_CURVE_SECP521R1 from
* kDefaultGroups. */
#if defined(BORINGSSL_ANDROID_SYSTEM)
SSL_SIGN_ECDSA_SECP521R1_SHA512,
#endif
-
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
- SSL_SIGN_RSA_PSS_SHA384,
-#endif
- SSL_SIGN_RSA_PKCS1_SHA384,
- SSL_SIGN_ECDSA_SECP384R1_SHA384,
-
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
- SSL_SIGN_RSA_PSS_SHA256,
-#endif
- SSL_SIGN_RSA_PKCS1_SHA256,
- SSL_SIGN_ECDSA_SECP256R1_SHA256,
-
- SSL_SIGN_RSA_PKCS1_SHA1,
-};
-
-/* kSignSignatureAlgorithms is the default list of supported signature
- * algorithms for signing. */
-static const uint16_t kSignSignatureAlgorithms[] = {
- /* For now, do not enable RSA-PSS signature algorithms on Android's system
- * BoringSSL. Once TLS 1.3 is finalized and the change in Chrome has stuck,
- * restore them. */
#if !defined(BORINGSSL_ANDROID_SYSTEM)
SSL_SIGN_RSA_PSS_SHA512,
#endif
SSL_SIGN_RSA_PKCS1_SHA512,
- SSL_SIGN_ECDSA_SECP521R1_SHA512,
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
- SSL_SIGN_RSA_PSS_SHA384,
-#endif
- SSL_SIGN_RSA_PKCS1_SHA384,
- SSL_SIGN_ECDSA_SECP384R1_SHA384,
+ /* For now, SHA-1 is still accepted but least preferable. */
+ SSL_SIGN_RSA_PKCS1_SHA1,
+};
+
+/* kSignSignatureAlgorithms is the default list of supported signature
+ * algorithms for signing.
+ *
+ * For now, RSA-PSS signature algorithms are not enabled on Android's system
+ * BoringSSL. Once the change in Chrome has stuck and the values are finalized,
+ * restore them. */
+static const uint16_t kSignSignatureAlgorithms[] = {
+ /* Prefer SHA-256 algorithms. */
+ SSL_SIGN_ECDSA_SECP256R1_SHA256,
#if !defined(BORINGSSL_ANDROID_SYSTEM)
SSL_SIGN_RSA_PSS_SHA256,
#endif
SSL_SIGN_RSA_PKCS1_SHA256,
- SSL_SIGN_ECDSA_SECP256R1_SHA256,
- SSL_SIGN_RSA_PKCS1_SHA1,
+ /* If needed, sign larger hashes.
+ *
+ * TODO(davidben): Determine which of these may be pruned. */
+ SSL_SIGN_ECDSA_SECP384R1_SHA384,
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+ SSL_SIGN_RSA_PSS_SHA384,
+#endif
+ SSL_SIGN_RSA_PKCS1_SHA384,
+
+ SSL_SIGN_ECDSA_SECP521R1_SHA512,
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+ SSL_SIGN_RSA_PSS_SHA512,
+#endif
+ SSL_SIGN_RSA_PKCS1_SHA512,
+
+ /* If the peer supports nothing else, sign with SHA-1. */
SSL_SIGN_ECDSA_SHA1,
+ SSL_SIGN_RSA_PKCS1_SHA1,
};
size_t tls12_get_verify_sigalgs(const SSL *ssl, const uint16_t **out) {
@@ -694,28 +705,19 @@
return 0;
}
- /* TODO(davidben): SNI should be resolved before resumption. We have the
- * early callback as a replacement, but we should fix the current callback
- * and avoid the need for |SSL_CTX_set_session_id_context|. */
- if (ssl->session == NULL) {
- assert(ssl->s3->new_session->tlsext_hostname == NULL);
-
- /* Copy the hostname as a string. */
- if (!CBS_strdup(&host_name, &ssl->s3->new_session->tlsext_hostname)) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
- return 0;
- }
-
- ssl->s3->hs->should_ack_sni = 1;
+ /* Copy the hostname as a string. */
+ if (!CBS_strdup(&host_name, &ssl->s3->hs->hostname)) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ return 0;
}
+ ssl->s3->hs->should_ack_sni = 1;
return 1;
}
static int ext_sni_add_serverhello(SSL *ssl, CBB *out) {
- if (ssl->session != NULL ||
- !ssl->s3->hs->should_ack_sni ||
- ssl->s3->new_session->tlsext_hostname == NULL) {
+ if (ssl->s3->session_reused ||
+ !ssl->s3->hs->should_ack_sni) {
return 1;
}
@@ -1019,8 +1021,6 @@
static int ext_ticket_parse_serverhello(SSL *ssl, uint8_t *out_alert,
CBS *contents) {
- ssl->tlsext_ticket_expected = 0;
-
if (contents == NULL) {
return 1;
}
@@ -1038,17 +1038,16 @@
return 0;
}
- ssl->tlsext_ticket_expected = 1;
+ ssl->s3->hs->ticket_expected = 1;
return 1;
}
static int ext_ticket_add_serverhello(SSL *ssl, CBB *out) {
- if (!ssl->tlsext_ticket_expected) {
+ if (!ssl->s3->hs->ticket_expected) {
return 1;
}
- /* If |SSL_OP_NO_TICKET| is set, |tlsext_ticket_expected| should never be
- * true. */
+ /* If |SSL_OP_NO_TICKET| is set, |ticket_expected| should never be true. */
assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0);
if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) ||
@@ -1152,42 +1151,22 @@
return 1;
}
- if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
- /* OCSP stapling is forbidden on non-certificate ciphers. */
- if (CBS_len(contents) != 0 ||
- !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
- return 0;
- }
-
- /* Note this does not check for resumption in TLS 1.2. Sending
- * status_request here does not make sense, but OpenSSL does so and the
- * specification does not say anything. Tolerate it but ignore it. */
-
- ssl->s3->hs->certificate_status_expected = 1;
- return 1;
- }
-
- /* In TLS 1.3, OCSP stapling is forbidden on resumption. */
- if (ssl->s3->session_reused) {
+ /* TLS 1.3 OCSP responses are included in the Certificate extensions. */
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
return 0;
}
- uint8_t status_type;
- CBS ocsp_response;
- if (!CBS_get_u8(contents, &status_type) ||
- status_type != TLSEXT_STATUSTYPE_ocsp ||
- !CBS_get_u24_length_prefixed(contents, &ocsp_response) ||
- CBS_len(&ocsp_response) == 0 ||
- CBS_len(contents) != 0) {
+ /* OCSP stapling is forbidden on non-certificate ciphers. */
+ if (CBS_len(contents) != 0 ||
+ !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
return 0;
}
- if (!CBS_stow(&ocsp_response, &ssl->s3->new_session->ocsp_response,
- &ssl->s3->new_session->ocsp_response_length)) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
- return 0;
- }
+ /* Note this does not check for resumption in TLS 1.2. Sending
+ * status_request here does not make sense, but OpenSSL does so and the
+ * specification does not say anything. Tolerate it but ignore it. */
+ ssl->s3->hs->certificate_status_expected = 1;
return 1;
}
@@ -1210,34 +1189,18 @@
}
static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) {
- if (!ssl->s3->hs->ocsp_stapling_requested ||
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+ !ssl->s3->hs->ocsp_stapling_requested ||
ssl->ctx->ocsp_response_length == 0 ||
ssl->s3->session_reused ||
- (ssl3_protocol_version(ssl) < TLS1_3_VERSION &&
- !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher))) {
+ !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
return 1;
}
- if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
- /* The extension shouldn't be sent when resuming sessions. */
- if (ssl->session != NULL) {
- return 1;
- }
+ ssl->s3->hs->certificate_status_expected = 1;
- ssl->s3->hs->certificate_status_expected = 1;
-
- return CBB_add_u16(out, TLSEXT_TYPE_status_request) &&
- CBB_add_u16(out, 0 /* length */);
- }
-
- CBB body, ocsp_response;
return CBB_add_u16(out, TLSEXT_TYPE_status_request) &&
- CBB_add_u16_length_prefixed(out, &body) &&
- CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) &&
- CBB_add_u24_length_prefixed(&body, &ocsp_response) &&
- CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
- ssl->ctx->ocsp_response_length) &&
- CBB_flush(out);
+ CBB_add_u16(out, 0 /* length */);
}
@@ -1331,10 +1294,6 @@
if (contents == NULL ||
ssl->s3->initial_handshake_complete ||
- /* If the ALPN extension is seen before NPN, ignore it. (If ALPN is seen
- * afterwards, parsing the ALPN extension will clear
- * |next_proto_neg_seen|. */
- ssl->s3->alpn_selected != NULL ||
ssl->ctx->next_protos_advertised_cb == NULL ||
SSL_is_dtls(ssl)) {
return 1;
@@ -1396,11 +1355,17 @@
return 1;
}
+ /* TLS 1.3 SCTs are included in the Certificate extensions. */
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
/* If this is false then we should never have sent the SCT extension in the
* ClientHello and thus this function should never have been called. */
assert(ssl->signed_cert_timestamps_enabled);
- if (CBS_len(contents) == 0) {
+ if (!ssl_is_sct_list_valid(contents)) {
*out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
@@ -1424,12 +1389,22 @@
static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert,
CBS *contents) {
- return contents == NULL || CBS_len(contents) == 0;
+ if (contents == NULL) {
+ return 1;
+ }
+
+ if (CBS_len(contents) != 0) {
+ return 0;
+ }
+
+ ssl->s3->hs->scts_requested = 1;
+ return 1;
}
static int ext_sct_add_serverhello(SSL *ssl, CBB *out) {
/* The extension shouldn't be sent when resuming sessions. */
- if (ssl->s3->session_reused ||
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+ ssl->s3->session_reused ||
ssl->ctx->signed_cert_timestamp_list_length == 0) {
return 1;
}
@@ -1447,11 +1422,6 @@
*
* https://tools.ietf.org/html/rfc7301 */
-static void ext_alpn_init(SSL *ssl) {
- OPENSSL_free(ssl->s3->alpn_selected);
- ssl->s3->alpn_selected = NULL;
-}
-
static int ext_alpn_add_clienthello(SSL *ssl, CBB *out) {
if (ssl->alpn_client_proto_list == NULL ||
ssl->s3->initial_handshake_complete) {
@@ -1534,14 +1504,14 @@
return 1;
}
-static int ext_alpn_parse_clienthello(SSL *ssl, uint8_t *out_alert,
- CBS *contents) {
- if (contents == NULL) {
- return 1;
- }
-
+int ssl_negotiate_alpn(SSL *ssl, uint8_t *out_alert,
+ const struct ssl_early_callback_ctx *client_hello) {
+ CBS contents;
if (ssl->ctx->alpn_select_cb == NULL ||
- ssl->s3->initial_handshake_complete) {
+ !ssl_early_callback_get_extension(
+ client_hello, &contents,
+ TLSEXT_TYPE_application_layer_protocol_negotiation)) {
+ /* Ignore ALPN if not configured or no extension was supplied. */
return 1;
}
@@ -1549,9 +1519,11 @@
ssl->s3->hs->next_proto_neg_seen = 0;
CBS protocol_name_list;
- if (!CBS_get_u16_length_prefixed(contents, &protocol_name_list) ||
- CBS_len(contents) != 0 ||
+ if (!CBS_get_u16_length_prefixed(&contents, &protocol_name_list) ||
+ CBS_len(&contents) != 0 ||
CBS_len(&protocol_name_list) < 2) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+ *out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
@@ -1563,6 +1535,8 @@
if (!CBS_get_u8_length_prefixed(&protocol_name_list_copy, &protocol_name) ||
/* Empty protocol names are forbidden. */
CBS_len(&protocol_name) == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+ *out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
}
@@ -1633,10 +1607,6 @@
return 1;
}
- if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
- return 0;
- }
-
assert(!SSL_is_dtls(ssl));
assert(ssl->tlsext_channel_id_enabled);
@@ -1665,10 +1635,6 @@
}
static int ext_channel_id_add_serverhello(SSL *ssl, CBB *out) {
- if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
- return 1;
- }
-
if (!ssl->s3->tlsext_channel_id_valid) {
return 1;
}
@@ -1836,26 +1802,6 @@
*
* https://tools.ietf.org/html/rfc4492#section-5.1.2 */
-static int ssl_any_ec_cipher_suites_enabled(const SSL *ssl) {
- if (ssl->version < TLS1_VERSION && !SSL_is_dtls(ssl)) {
- return 0;
- }
-
- const STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl);
-
- for (size_t i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) {
- const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(cipher_stack, i);
-
- const uint32_t alg_k = cipher->algorithm_mkey;
- const uint32_t alg_a = cipher->algorithm_auth;
- if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) {
- return 1;
- }
- }
-
- return 0;
-}
-
static int ext_ec_point_add_extension(SSL *ssl, CBB *out) {
CBB contents, formats;
if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) ||
@@ -1870,7 +1816,13 @@
}
static int ext_ec_point_add_clienthello(SSL *ssl, CBB *out) {
- if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
+ uint16_t min_version, max_version;
+ if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+ return 0;
+ }
+
+ /* The point format extension is unneccessary in TLS 1.3. */
+ if (min_version >= TLS1_3_VERSION) {
return 1;
}
@@ -1929,9 +1881,30 @@
return ext_ec_point_add_extension(ssl, out);
}
+
/* Pre Shared Key
*
- * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.6 */
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.6 */
+
+static size_t ext_pre_shared_key_clienthello_length(SSL *ssl) {
+ uint16_t min_version, max_version;
+ if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+ return 0;
+ }
+
+ uint16_t session_version;
+ if (max_version < TLS1_3_VERSION || ssl->session == NULL ||
+ !ssl->method->version_from_wire(&session_version,
+ ssl->session->ssl_version) ||
+ session_version < TLS1_3_VERSION) {
+ return 0;
+ }
+
+ const EVP_MD *digest =
+ ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+ size_t binder_len = EVP_MD_size(digest);
+ return 15 + ssl->session->tlsext_ticklen + binder_len;
+}
static int ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
uint16_t min_version, max_version;
@@ -1947,20 +1920,33 @@
return 1;
}
- CBB contents, identity, ke_modes, auth_modes, ticket;
+ struct timeval now;
+ ssl_get_current_time(ssl, &now);
+ uint32_t ticket_age = 1000 * (now.tv_sec - ssl->session->time);
+ uint32_t obfuscated_ticket_age = ticket_age + ssl->session->ticket_age_add;
+
+ /* Fill in a placeholder zero binder of the appropriate length. It will be
+ * computed and filled in later after length prefixes are computed. */
+ uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
+ const EVP_MD *digest =
+ ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+ size_t binder_len = EVP_MD_size(digest);
+
+ CBB contents, identity, ticket, binders, binder;
if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
!CBB_add_u16_length_prefixed(&contents, &identity) ||
- !CBB_add_u8_length_prefixed(&identity, &ke_modes) ||
- !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE) ||
- !CBB_add_u8_length_prefixed(&identity, &auth_modes) ||
- !CBB_add_u8(&auth_modes, SSL_PSK_AUTH) ||
!CBB_add_u16_length_prefixed(&identity, &ticket) ||
!CBB_add_bytes(&ticket, ssl->session->tlsext_tick,
- ssl->session->tlsext_ticklen)) {
+ ssl->session->tlsext_ticklen) ||
+ !CBB_add_u32(&identity, obfuscated_ticket_age) ||
+ !CBB_add_u16_length_prefixed(&contents, &binders) ||
+ !CBB_add_u8_length_prefixed(&binders, &binder) ||
+ !CBB_add_bytes(&binder, zero_binder, binder_len)) {
return 0;
}
+ ssl->s3->hs->needs_psk_binder = 1;
return CBB_flush(out);
}
@@ -1974,6 +1960,7 @@
return 0;
}
+ /* We only advertise one PSK identity, so the only legal index is zero. */
if (psk_id != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
*out_alert = SSL_AD_UNKNOWN_PSK_IDENTITY;
@@ -1985,32 +1972,45 @@
int ssl_ext_pre_shared_key_parse_clienthello(SSL *ssl,
SSL_SESSION **out_session,
+ CBS *out_binders,
uint8_t *out_alert,
CBS *contents) {
/* We only process the first PSK identity since we don't support pure PSK. */
- CBS identity, ke_modes, auth_modes, ticket;
+ uint32_t obfuscated_ticket_age;
+ CBS identity, ticket, binders;
if (!CBS_get_u16_length_prefixed(contents, &identity) ||
- !CBS_get_u8_length_prefixed(&identity, &ke_modes) ||
- !CBS_get_u8_length_prefixed(&identity, &auth_modes) ||
!CBS_get_u16_length_prefixed(&identity, &ticket) ||
- CBS_len(&identity) != 0) {
+ !CBS_get_u32(&identity, &obfuscated_ticket_age) ||
+ !CBS_get_u16_length_prefixed(contents, &binders) ||
+ CBS_len(contents) != 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
*out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
- /* We only support tickets with PSK_DHE_KE and PSK_AUTH. */
- if (memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) == NULL ||
- memchr(CBS_data(&auth_modes), SSL_PSK_AUTH, CBS_len(&auth_modes)) ==
- NULL) {
- *out_session = NULL;
- return 1;
+ *out_binders = binders;
+
+ /* The PSK identity must have a corresponding binder. */
+ CBS binder;
+ if (!CBS_get_u8_length_prefixed(&binders, &binder)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
}
+ /* TODO(svaldez): Check that the ticket_age is valid when attempting to use
+ * the PSK for 0-RTT. http://crbug.com/boringssl/113 */
+
/* TLS 1.3 session tickets are renewed separately as part of the
* NewSessionTicket. */
- int renew;
- return tls_process_ticket(ssl, out_session, &renew, CBS_data(&ticket),
- CBS_len(&ticket), NULL, 0);
+ 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;
+ }
+
+ return 1;
}
int ssl_ext_pre_shared_key_add_serverhello(SSL *ssl, CBB *out) {
@@ -2031,6 +2031,53 @@
}
+/* Pre-Shared Key Exchange Modes
+ *
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.7 */
+static int ext_psk_key_exchange_modes_add_clienthello(SSL *ssl, CBB *out) {
+ uint16_t min_version, max_version;
+ if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+ return 0;
+ }
+
+ if (max_version < TLS1_3_VERSION) {
+ return 1;
+ }
+
+ CBB contents, ke_modes;
+ if (!CBB_add_u16(out, TLSEXT_TYPE_psk_key_exchange_modes) ||
+ !CBB_add_u16_length_prefixed(out, &contents) ||
+ !CBB_add_u8_length_prefixed(&contents, &ke_modes) ||
+ !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE)) {
+ return 0;
+ }
+
+ return CBB_flush(out);
+}
+
+static int ext_psk_key_exchange_modes_parse_clienthello(SSL *ssl,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == NULL) {
+ return 1;
+ }
+
+ CBS ke_modes;
+ if (!CBS_get_u8_length_prefixed(contents, &ke_modes) ||
+ CBS_len(&ke_modes) == 0 ||
+ CBS_len(contents) != 0) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ /* We only support tickets with PSK_DHE_KE. */
+ ssl->s3->hs->accept_psk_mode =
+ memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) != NULL;
+
+ return 1;
+}
+
+
/* Key Share
*
* https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.5 */
@@ -2052,21 +2099,18 @@
return 0;
}
- uint16_t group_id;
+ uint16_t group_id = ssl->s3->hs->retry_group;
if (ssl->s3->hs->received_hello_retry_request) {
- /* Replay the old key shares. */
- if (!CBB_add_bytes(&kse_bytes, ssl->s3->hs->key_share_bytes,
+ /* We received a HelloRetryRequest without a new curve, so there is no new
+ * share to append. Leave |ecdh_ctx| as-is. */
+ if (group_id == 0 &&
+ !CBB_add_bytes(&kse_bytes, ssl->s3->hs->key_share_bytes,
ssl->s3->hs->key_share_bytes_len)) {
return 0;
}
OPENSSL_free(ssl->s3->hs->key_share_bytes);
ssl->s3->hs->key_share_bytes = NULL;
ssl->s3->hs->key_share_bytes_len = 0;
-
- group_id = ssl->s3->hs->retry_group;
-
- /* We received a HelloRetryRequest without a new curve, so there is no new
- * share to append. Leave |ecdh_ctx| as-is. */
if (group_id == 0) {
return CBB_flush(out);
}
@@ -2319,10 +2363,6 @@
* https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.4 */
static int ext_supported_groups_add_clienthello(SSL *ssl, CBB *out) {
- if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
- return 1;
- }
-
CBB contents, groups_bytes;
if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
@@ -2473,10 +2513,11 @@
},
{
TLSEXT_TYPE_application_layer_protocol_negotiation,
- ext_alpn_init,
+ NULL,
ext_alpn_add_clienthello,
ext_alpn_parse_serverhello,
- ext_alpn_parse_clienthello,
+ /* ALPN is negotiated late in |ssl_negotiate_alpn|. */
+ ignore_parse_clienthello,
ext_alpn_add_serverhello,
},
{
@@ -2512,11 +2553,11 @@
dont_add_serverhello,
},
{
- TLSEXT_TYPE_pre_shared_key,
+ TLSEXT_TYPE_psk_key_exchange_modes,
NULL,
- ext_pre_shared_key_add_clienthello,
+ ext_psk_key_exchange_modes_add_clienthello,
forbid_parse_serverhello,
- ignore_parse_clienthello,
+ ext_psk_key_exchange_modes_parse_clienthello,
dont_add_serverhello,
},
{
@@ -2611,7 +2652,7 @@
const size_t len_before = CBB_len(&extensions);
if (!kExtensions[i].add_clienthello(ssl, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+ ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
goto err;
}
@@ -2643,7 +2684,8 @@
}
if (!SSL_is_dtls(ssl)) {
- header_len += 2 + CBB_len(&extensions);
+ size_t psk_extension_len = ext_pre_shared_key_clienthello_length(ssl);
+ header_len += 2 + CBB_len(&extensions) + psk_extension_len;
if (header_len > 0xff && header_len < 0x200) {
/* Add padding to workaround bugs in F5 terminators. See RFC 7685.
*
@@ -2671,6 +2713,11 @@
}
}
+ /* The PSK extension must be last, including after the padding. */
+ if (!ext_pre_shared_key_add_clienthello(ssl, &extensions)) {
+ goto err;
+ }
+
/* Discard empty extensions blocks. */
if (CBB_len(&extensions) == 0) {
CBB_discard_child(out);
@@ -2698,7 +2745,7 @@
if (!kExtensions[i].add_serverhello(ssl, &extensions)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+ ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
goto err;
}
}
@@ -2768,7 +2815,7 @@
if (!ext->parse_clienthello(ssl, &alert, &extension)) {
*out_alert = alert;
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)type);
+ ERR_add_error_dataf("extension %u", (unsigned)type);
return 0;
}
}
@@ -2796,7 +2843,7 @@
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!kExtensions[i].parse_clienthello(ssl, &alert, contents)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+ ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
*out_alert = alert;
return 0;
}
@@ -2875,7 +2922,7 @@
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!ext->parse_serverhello(ssl, &alert, &extension)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)type);
+ ERR_add_error_dataf("extension %u", (unsigned)type);
*out_alert = alert;
return 0;
}
@@ -2888,7 +2935,7 @@
uint8_t alert = SSL_AD_DECODE_ERROR;
if (!kExtensions[i].parse_serverhello(ssl, &alert, NULL)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
- ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
+ ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
*out_alert = alert;
return 0;
}
@@ -3090,12 +3137,6 @@
memcpy(session->session_id, session_id, session_id_len);
session->session_id_length = session_id_len;
- if (!ssl_session_is_context_valid(ssl, session) ||
- !ssl_session_is_time_valid(ssl, session)) {
- SSL_SESSION_free(session);
- session = NULL;
- }
-
*out_session = session;
done:
@@ -3207,45 +3248,184 @@
return 0;
}
-int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
+int tls1_verify_channel_id(SSL *ssl) {
int ret = 0;
- EVP_MD_CTX ctx;
+ uint16_t extension_type;
+ CBS extension, channel_id;
- EVP_MD_CTX_init(&ctx);
- if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL)) {
+ /* A Channel ID handshake message is structured to contain multiple
+ * extensions, but the only one that can be present is Channel ID. */
+ CBS_init(&channel_id, ssl->init_msg, ssl->init_num);
+ if (!CBS_get_u16(&channel_id, &extension_type) ||
+ !CBS_get_u16_length_prefixed(&channel_id, &extension) ||
+ CBS_len(&channel_id) != 0 ||
+ extension_type != TLSEXT_TYPE_channel_id ||
+ CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return 0;
+ }
+
+ EC_GROUP *p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ if (!p256) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
+ return 0;
+ }
+
+ EC_KEY *key = NULL;
+ EC_POINT *point = NULL;
+ BIGNUM x, y;
+ ECDSA_SIG sig;
+ BN_init(&x);
+ BN_init(&y);
+ sig.r = BN_new();
+ sig.s = BN_new();
+ if (sig.r == NULL || sig.s == NULL) {
goto err;
}
+ const uint8_t *p = CBS_data(&extension);
+ if (BN_bin2bn(p + 0, 32, &x) == NULL ||
+ BN_bin2bn(p + 32, 32, &y) == NULL ||
+ BN_bin2bn(p + 64, 32, sig.r) == NULL ||
+ BN_bin2bn(p + 96, 32, sig.s) == NULL) {
+ goto err;
+ }
+
+ point = EC_POINT_new(p256);
+ if (point == NULL ||
+ !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
+ goto err;
+ }
+
+ key = EC_KEY_new();
+ if (key == NULL ||
+ !EC_KEY_set_group(key, p256) ||
+ !EC_KEY_set_public_key(key, point)) {
+ goto err;
+ }
+
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+ goto err;
+ }
+
+ int sig_ok = ECDSA_do_verify(digest, digest_len, &sig, key);
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ sig_ok = 1;
+#endif
+ if (!sig_ok) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+ ssl->s3->tlsext_channel_id_valid = 0;
+ goto err;
+ }
+
+ memcpy(ssl->s3->tlsext_channel_id, p, 64);
+ ret = 1;
+
+err:
+ BN_free(&x);
+ BN_free(&y);
+ BN_free(sig.r);
+ BN_free(sig.s);
+ EC_KEY_free(key);
+ EC_POINT_free(point);
+ EC_GROUP_free(p256);
+ return ret;
+}
+
+int tls1_write_channel_id(SSL *ssl, CBB *cbb) {
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+ return 0;
+ }
+
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
+ if (ec_key == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ int ret = 0;
+ BIGNUM *x = BN_new();
+ BIGNUM *y = BN_new();
+ ECDSA_SIG *sig = NULL;
+ if (x == NULL || y == NULL ||
+ !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key),
+ EC_KEY_get0_public_key(ec_key),
+ x, y, NULL)) {
+ goto err;
+ }
+
+ sig = ECDSA_do_sign(digest, digest_len, ec_key);
+ if (sig == NULL) {
+ goto err;
+ }
+
+ CBB child;
+ if (!CBB_add_u16(cbb, TLSEXT_TYPE_channel_id) ||
+ !CBB_add_u16_length_prefixed(cbb, &child) ||
+ !BN_bn2cbb_padded(&child, 32, x) ||
+ !BN_bn2cbb_padded(&child, 32, y) ||
+ !BN_bn2cbb_padded(&child, 32, sig->r) ||
+ !BN_bn2cbb_padded(&child, 32, sig->s) ||
+ !CBB_flush(cbb)) {
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ BN_free(x);
+ BN_free(y);
+ ECDSA_SIG_free(sig);
+ return ret;
+}
+
+int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+ uint8_t *msg;
+ size_t msg_len;
+ if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
+ ssl_cert_verify_channel_id)) {
+ return 0;
+ }
+ SHA256(msg, msg_len, out);
+ *out_len = SHA256_DIGEST_LENGTH;
+ OPENSSL_free(msg);
+ return 1;
+ }
+
+ SHA256_CTX ctx;
+
+ SHA256_Init(&ctx);
static const char kClientIDMagic[] = "TLS Channel ID signature";
- EVP_DigestUpdate(&ctx, kClientIDMagic, sizeof(kClientIDMagic));
+ SHA256_Update(&ctx, kClientIDMagic, sizeof(kClientIDMagic));
if (ssl->session != NULL) {
static const char kResumptionMagic[] = "Resumption";
- EVP_DigestUpdate(&ctx, kResumptionMagic, sizeof(kResumptionMagic));
+ SHA256_Update(&ctx, kResumptionMagic, sizeof(kResumptionMagic));
if (ssl->session->original_handshake_hash_len == 0) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- goto err;
+ return 0;
}
- EVP_DigestUpdate(&ctx, ssl->session->original_handshake_hash,
- ssl->session->original_handshake_hash_len);
+ SHA256_Update(&ctx, ssl->session->original_handshake_hash,
+ ssl->session->original_handshake_hash_len);
}
uint8_t handshake_hash[EVP_MAX_MD_SIZE];
int handshake_hash_len = tls1_handshake_digest(ssl, handshake_hash,
sizeof(handshake_hash));
if (handshake_hash_len < 0) {
- goto err;
+ return 0;
}
- EVP_DigestUpdate(&ctx, handshake_hash, (size_t)handshake_hash_len);
- unsigned len_u;
- EVP_DigestFinal_ex(&ctx, out, &len_u);
- *out_len = len_u;
-
- ret = 1;
-
-err:
- EVP_MD_CTX_cleanup(&ctx);
- return ret;
+ SHA256_Update(&ctx, handshake_hash, (size_t)handshake_hash_len);
+ SHA256_Final(out, &ctx);
+ *out_len = SHA256_DIGEST_LENGTH;
+ return 1;
}
/* tls1_record_handshake_hashes_for_channel_id records the current handshake
@@ -3272,3 +3452,44 @@
return 1;
}
+
+int ssl_do_channel_id_callback(SSL *ssl) {
+ if (ssl->tlsext_channel_id_private != NULL ||
+ ssl->ctx->channel_id_cb == NULL) {
+ return 1;
+ }
+
+ EVP_PKEY *key = NULL;
+ ssl->ctx->channel_id_cb(ssl, &key);
+ if (key == NULL) {
+ /* The caller should try again later. */
+ return 1;
+ }
+
+ int ret = SSL_set1_tls_channel_id(ssl, key);
+ EVP_PKEY_free(key);
+ return ret;
+}
+
+int ssl_is_sct_list_valid(const CBS *contents) {
+ /* Shallow parse the SCT list for sanity. By the RFC
+ * (https://tools.ietf.org/html/rfc6962#section-3.3) neither the list nor any
+ * of the SCTs may be empty. */
+ CBS copy = *contents;
+ CBS sct_list;
+ if (!CBS_get_u16_length_prefixed(©, &sct_list) ||
+ CBS_len(©) != 0 ||
+ CBS_len(&sct_list) == 0) {
+ return 0;
+ }
+
+ while (CBS_len(&sct_list) > 0) {
+ CBS sct;
+ if (!CBS_get_u16_length_prefixed(&sct_list, &sct) ||
+ CBS_len(&sct) == 0) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 76caa4e..97860c4 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -40,6 +40,7 @@
#include <inttypes.h>
#include <string.h>
+#include <openssl/aead.h>
#include <openssl/bio.h>
#include <openssl/buf.h>
#include <openssl/bytestring.h>
@@ -137,12 +138,46 @@
return (TestState *)SSL_get_ex_data(ssl, g_state_index);
}
-static bssl::UniquePtr<X509> LoadCertificate(const std::string &file) {
+static bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
+ const std::string &file) {
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
- return nullptr;
+ return false;
}
- return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL));
+
+ out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ if (!*out_x509) {
+ return false;
+ }
+
+ out_chain->reset(sk_X509_new_null());
+ if (!*out_chain) {
+ return false;
+ }
+
+ // Keep reading the certificate chain.
+ for (;;) {
+ bssl::UniquePtr<X509> cert(
+ PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ if (!cert) {
+ break;
+ }
+
+ if (!sk_X509_push(out_chain->get(), cert.get())) {
+ return false;
+ }
+ cert.release(); // sk_X509_push takes ownership.
+ }
+
+ uint32_t err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
+ ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
+ return false;
+}
+
+ ERR_clear_error();
+ return true;
}
static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
@@ -320,6 +355,7 @@
};
static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509,
+ bssl::UniquePtr<STACK_OF(X509)> *out_chain,
bssl::UniquePtr<EVP_PKEY> *out_pkey) {
const TestConfig *config = GetTestConfig(ssl);
@@ -358,14 +394,12 @@
return false;
}
}
- if (!config->cert_file.empty()) {
- *out_x509 = LoadCertificate(config->cert_file.c_str());
- if (!*out_x509) {
- return false;
- }
+ if (!config->cert_file.empty() &&
+ !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) {
+ return false;
}
if (!config->ocsp_response.empty() &&
- !SSL_CTX_set_ocsp_response(ssl->ctx,
+ !SSL_CTX_set_ocsp_response(SSL_get_SSL_CTX(ssl),
(const uint8_t *)config->ocsp_response.data(),
config->ocsp_response.size())) {
return false;
@@ -375,8 +409,9 @@
static bool InstallCertificate(SSL *ssl) {
bssl::UniquePtr<X509> x509;
+ bssl::UniquePtr<STACK_OF(X509)> chain;
bssl::UniquePtr<EVP_PKEY> pkey;
- if (!GetCertificate(ssl, &x509, &pkey)) {
+ if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
return false;
}
@@ -395,6 +430,11 @@
return false;
}
+ if (sk_X509_num(chain.get()) > 0 &&
+ !SSL_set1_chain(ssl, chain.get())) {
+ return false;
+ }
+
return true;
}
@@ -456,8 +496,9 @@
}
bssl::UniquePtr<X509> x509;
+ bssl::UniquePtr<STACK_OF(X509)> chain;
bssl::UniquePtr<EVP_PKEY> pkey;
- if (!GetCertificate(ssl, &x509, &pkey)) {
+ if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
return -1;
}
@@ -466,7 +507,7 @@
return 0;
}
- // Asynchronous private keys are not supported with client_cert_cb.
+ // Chains and asynchronous private keys are not supported with client_cert_cb.
*out_x509 = x509.release();
*out_pkey = pkey.release();
return 1;
@@ -594,8 +635,10 @@
return config->psk.size();
}
+static timeval g_clock;
+
static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
- *out_clock = PacketedBioGetClock(GetTestState(ssl)->packeted_bio);
+ *out_clock = g_clock;
}
static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
@@ -622,6 +665,10 @@
}
}
+ if (config->fail_cert_callback) {
+ return 0;
+ }
+
// The certificate will be installed via other means.
if (!config->async || config->use_early_callback ||
config->use_old_client_cert_callback) {
@@ -774,6 +821,20 @@
return 1;
}
+static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) {
+ // SNI must be accessible from the SNI callback.
+ const TestConfig *config = GetTestConfig(ssl);
+ const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (server_name == nullptr ||
+ std::string(server_name) != config->expected_server_name) {
+ fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name,
+ config->expected_server_name.c_str());
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
// Connect returns a new socket connected to localhost on |port| or -1 on
// error.
static int Connect(uint16_t port) {
@@ -923,9 +984,7 @@
SSL_CTX_enable_tls_channel_id(ssl_ctx.get());
SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
- if (config->is_dtls) {
- SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
- }
+ SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
@@ -971,6 +1030,16 @@
SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
}
+ if (!config->expected_server_name.empty()) {
+ SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
+ }
+
+ if (!config->ticket_key.empty() &&
+ !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), config->ticket_key.data(),
+ config->ticket_key.size())) {
+ return nullptr;
+ }
+
return ssl_ctx;
}
@@ -1054,6 +1123,16 @@
if (config->async) {
AsyncBioEnforceWriteQuota(test_state->async_bio, true);
}
+
+ // Run the exporter after each read. This is to test that the exporter fails
+ // during a renegotiation.
+ if (config->use_exporter_between_reads) {
+ uint8_t buf;
+ if (!SSL_export_keying_material(ssl, &buf, 1, NULL, 0, NULL, 0, 0)) {
+ fprintf(stderr, "failed to export keying material\n");
+ return -1;
+ }
+ }
} while (config->async && RetryAsync(ssl, ret));
if (config->peek_then_read && ret > 0) {
@@ -1171,7 +1250,8 @@
if (!config->expected_server_name.empty()) {
const char *server_name =
SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
- if (server_name != config->expected_server_name) {
+ if (server_name == nullptr ||
+ server_name != config->expected_server_name) {
fprintf(stderr, "servername mismatch (got %s; want %s)\n",
server_name, config->expected_server_name.c_str());
return false;
@@ -1285,6 +1365,25 @@
}
}
+ uint16_t cipher_id =
+ static_cast<uint16_t>(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl)));
+ if (config->expect_cipher_aes != 0 &&
+ EVP_has_aes_hardware() &&
+ static_cast<uint16_t>(config->expect_cipher_aes) != cipher_id) {
+ fprintf(stderr, "Cipher ID was %04x, wanted %04x (has AES hardware)\n",
+ cipher_id, static_cast<uint16_t>(config->expect_cipher_aes));
+ return false;
+ }
+
+ if (config->expect_cipher_no_aes != 0 &&
+ !EVP_has_aes_hardware() &&
+ static_cast<uint16_t>(config->expect_cipher_no_aes) != cipher_id) {
+ fprintf(stderr, "Cipher ID was %04x, wanted %04x (no AES hardware)\n",
+ cipher_id, static_cast<uint16_t>(config->expect_cipher_no_aes));
+ return false;
+ }
+
+
if (!config->psk.empty()) {
if (SSL_get_peer_cert_chain(ssl) != nullptr) {
fprintf(stderr, "Received peer certificate on a PSK cipher.\n");
@@ -1297,6 +1396,65 @@
}
}
+ if (!config->expect_peer_cert_file.empty()) {
+ bssl::UniquePtr<X509> expect_leaf;
+ bssl::UniquePtr<STACK_OF(X509)> expect_chain;
+ if (!LoadCertificate(&expect_leaf, &expect_chain,
+ config->expect_peer_cert_file)) {
+ return false;
+ }
+
+ // For historical reasons, clients report a chain with a leaf and servers
+ // without.
+ if (!config->is_server) {
+ if (!sk_X509_insert(expect_chain.get(), expect_leaf.get(), 0)) {
+ return false;
+ }
+ X509_up_ref(expect_leaf.get()); // sk_X509_push takes ownership.
+ }
+
+ bssl::UniquePtr<X509> leaf(SSL_get_peer_certificate(ssl));
+ STACK_OF(X509) *chain = SSL_get_peer_cert_chain(ssl);
+ if (X509_cmp(leaf.get(), expect_leaf.get()) != 0) {
+ fprintf(stderr, "Received a different leaf certificate than expected.\n");
+ return false;
+ }
+
+ if (sk_X509_num(chain) != sk_X509_num(expect_chain.get())) {
+ fprintf(stderr, "Received a chain of length %zu instead of %zu.\n",
+ sk_X509_num(chain), sk_X509_num(expect_chain.get()));
+ return false;
+ }
+
+ for (size_t i = 0; i < sk_X509_num(chain); i++) {
+ if (X509_cmp(sk_X509_value(chain, i),
+ sk_X509_value(expect_chain.get(), i)) != 0) {
+ fprintf(stderr, "Chain certificate %zu did not match.\n",
+ i + 1);
+ return false;
+ }
+ }
+ }
+
+ bool expected_sha256_client_cert = config->expect_sha256_client_cert_initial;
+ if (is_resume) {
+ expected_sha256_client_cert = config->expect_sha256_client_cert_resume;
+ }
+
+ if (SSL_get_session(ssl)->peer_sha256_valid != expected_sha256_client_cert) {
+ fprintf(stderr,
+ "Unexpected SHA-256 client cert state: expected:%d is_resume:%d.\n",
+ expected_sha256_client_cert, is_resume);
+ return false;
+ }
+
+ if (expected_sha256_client_cert &&
+ SSL_get_session(ssl)->x509_peer != nullptr) {
+ fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n",
+ is_resume);
+ return false;
+ }
+
return true;
}
@@ -1456,6 +1614,12 @@
if (config->max_cert_list > 0) {
SSL_set_max_cert_list(ssl.get(), config->max_cert_list);
}
+ if (!is_resume && config->retain_only_sha256_client_cert_initial) {
+ SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
+ }
+ if (is_resume && config->retain_only_sha256_client_cert_resume) {
+ SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
+ }
int sock = Connect(config->port);
if (sock == -1) {
@@ -1468,7 +1632,7 @@
return false;
}
if (config->is_dtls) {
- bssl::UniquePtr<BIO> packeted = PacketedBioCreate(!config->async);
+ bssl::UniquePtr<BIO> packeted = PacketedBioCreate(&g_clock, !config->async);
if (!packeted) {
return false;
}
@@ -1743,6 +1907,11 @@
return Usage(argv[0]);
}
+ // Some code treats the zero time special, so initialize the clock to a
+ // non-zero time.
+ g_clock.tv_sec = 1234;
+ g_clock.tv_usec = 1234;
+
bssl::UniquePtr<SSL_CTX> ssl_ctx = SetupCtx(&config);
if (!ssl_ctx) {
ERR_print_errors_fp(stderr);
@@ -1764,6 +1933,10 @@
ERR_print_errors_fp(stderr);
return 1;
}
+
+ if (config.resumption_delay != 0) {
+ g_clock.tv_sec += config.resumption_delay;
+ }
}
return 0;
diff --git a/src/ssl/test/packeted_bio.cc b/src/ssl/test/packeted_bio.cc
index f7267fc..8331b4b 100644
--- a/src/ssl/test/packeted_bio.cc
+++ b/src/ssl/test/packeted_bio.cc
@@ -31,10 +31,9 @@
const uint8_t kOpcodeTimeoutAck = 't';
struct PacketedBio {
- explicit PacketedBio(bool advance_clock_arg)
- : advance_clock(advance_clock_arg) {
+ PacketedBio(timeval *clock_arg, bool advance_clock_arg)
+ : clock(clock_arg), advance_clock(advance_clock_arg) {
memset(&timeout, 0, sizeof(timeout));
- memset(&clock, 0, sizeof(clock));
memset(&read_deadline, 0, sizeof(read_deadline));
}
@@ -47,14 +46,14 @@
return true;
}
- if (clock.tv_sec == read_deadline.tv_sec) {
- return clock.tv_usec < read_deadline.tv_usec;
+ if (clock->tv_sec == read_deadline.tv_sec) {
+ return clock->tv_usec < read_deadline.tv_usec;
}
- return clock.tv_sec < read_deadline.tv_sec;
+ return clock->tv_sec < read_deadline.tv_sec;
}
timeval timeout;
- timeval clock;
+ timeval *clock;
timeval read_deadline;
bool advance_clock;
};
@@ -66,10 +65,6 @@
return (PacketedBio *)bio->ptr;
}
-const PacketedBio *GetData(const BIO *bio) {
- return GetData(const_cast<BIO*>(bio));
-}
-
// ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
// 0 or -1 on error.
static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
@@ -272,19 +267,15 @@
} // namespace
-bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock) {
+bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock, bool advance_clock) {
bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method));
if (!bio) {
return nullptr;
}
- bio->ptr = new PacketedBio(advance_clock);
+ bio->ptr = new PacketedBio(clock, advance_clock);
return bio;
}
-timeval PacketedBioGetClock(const BIO *bio) {
- return GetData(bio)->clock;
-}
-
bool PacketedBioAdvanceClock(BIO *bio) {
PacketedBio *data = GetData(bio);
if (data == nullptr) {
@@ -295,10 +286,10 @@
return false;
}
- data->clock.tv_usec += data->timeout.tv_usec;
- data->clock.tv_sec += data->clock.tv_usec / 1000000;
- data->clock.tv_usec %= 1000000;
- data->clock.tv_sec += data->timeout.tv_sec;
+ data->clock->tv_usec += data->timeout.tv_usec;
+ data->clock->tv_sec += data->clock->tv_usec / 1000000;
+ data->clock->tv_usec %= 1000000;
+ data->clock->tv_sec += data->timeout.tv_sec;
memset(&data->timeout, 0, sizeof(data->timeout));
return true;
}
diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h
index 07930d4..9d4cdcb 100644
--- a/src/ssl/test/packeted_bio.h
+++ b/src/ssl/test/packeted_bio.h
@@ -28,21 +28,18 @@
// PacketedBioCreate creates a filter BIO which implements a reliable in-order
-// blocking datagram socket. It internally maintains a clock and honors
-// |BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT| based on it.
+// blocking datagram socket. It uses the value of |*clock| as the clock and
+// honors |BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT| based on it.
//
// During a |BIO_read|, the peer may signal the filter BIO to simulate a
// timeout. If |advance_clock| is true, it automatically advances the clock and
// continues reading, subject to the read deadline. Otherwise, it fails
// immediately. The caller must then call |PacketedBioAdvanceClock| before
// retrying |BIO_read|.
-bssl::UniquePtr<BIO> PacketedBioCreate(bool advance_clock);
+bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock, bool advance_clock);
-// PacketedBioGetClock returns the current time for |bio|.
-timeval PacketedBioGetClock(const BIO *bio);
-
-// PacketedBioAdvanceClock advances |bio|'s internal clock and returns true if
-// there is a pending timeout. Otherwise, it returns false.
+// PacketedBioAdvanceClock advances |bio|'s clock and returns true if there is a
+// pending timeout. Otherwise, it returns false.
bool PacketedBioAdvanceClock(BIO *bio);
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index 656a3d0..a997016 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -480,12 +480,16 @@
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
for _, id := range have {
if id == want {
- for _, suite := range cipherSuites {
- if suite.id == want {
- return suite
- }
- }
- return nil
+ return cipherSuiteFromID(id)
+ }
+ }
+ return nil
+}
+
+func cipherSuiteFromID(id uint16) *cipherSuite {
+ for _, suite := range cipherSuites {
+ if suite.id == id {
+ return suite
}
}
return nil
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 62c8dd3..a6496fd 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -27,7 +27,7 @@
)
// A draft version of TLS 1.3 that is sent over the wire for the current draft.
-const tls13DraftVersion = 0x7f10
+const tls13DraftVersion = 0x7f12
const (
maxPlaintext = 16384 // maximum plaintext payload length
@@ -94,6 +94,7 @@
extensionEarlyData uint16 = 42 // draft-ietf-tls-tls13-16
extensionSupportedVersions uint16 = 43 // draft-ietf-tls-tls13-16
extensionCookie uint16 = 44 // draft-ietf-tls-tls13-16
+ extensionPSKKeyExchangeModes uint16 = 45 // draft-ietf-tls-tls13-18
extensionCustom uint16 = 1234 // not IANA assigned
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
@@ -200,12 +201,6 @@
pskDHEKEMode = 1
)
-// PskAuthenticationMode values (see draft-ietf-tls-tls13-16)
-const (
- pskAuthMode = 0
- pskSignAuthMode = 1
-)
-
// KeyUpdateRequest values (see draft-ietf-tls-tls13-16, section 4.5.3)
const (
keyUpdateNotRequested = 0
@@ -259,6 +254,7 @@
ocspResponse []byte
ticketCreationTime time.Time
ticketExpiration time.Time
+ ticketAgeAdd uint32
}
// ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -661,9 +657,9 @@
// TLS 1.2 and 1.3 extensions.
SendBothTickets bool
- // CorruptTicket causes a client to corrupt a session ticket before
- // sending it in a resume handshake.
- CorruptTicket bool
+ // FilterTicket, if not nil, causes the client to modify a session
+ // ticket before sending it in a resume handshake.
+ FilterTicket func([]byte) ([]byte, error)
// OversizedSessionId causes the session id that is sent with a ticket
// resumption attempt to be too large (33 bytes).
@@ -753,9 +749,17 @@
RequireSameRenegoClientVersion bool
// ExpectInitialRecordVersion, if non-zero, is the expected value of
- // record-layer version field before the version is determined.
+ // record-layer version field before the protocol version is determined.
ExpectInitialRecordVersion uint16
+ // SendRecordVersion, if non-zero, is the value to send as the
+ // record-layer version.
+ SendRecordVersion uint16
+
+ // SendInitialRecordVersion, if non-zero, is the value to send as the
+ // record-layer version before the protocol version is determined.
+ SendInitialRecordVersion uint16
+
// MaxPacketLength, if non-zero, is the maximum acceptable size for a
// packet.
MaxPacketLength int
@@ -765,6 +769,11 @@
// the server believes it has actually negotiated.
SendCipherSuite uint16
+ // SendCipherSuites, if not nil, is the cipher suite list that the
+ // client will send in the ClientHello. This does not affect the cipher
+ // the client believes it has actually offered.
+ SendCipherSuites []uint16
+
// AppDataBeforeHandshake, if not nil, causes application data to be
// sent immediately before the first handshake message.
AppDataBeforeHandshake []byte
@@ -939,12 +948,17 @@
// session ticket.
SendEmptySessionTicket bool
- // SnedPSKKeyExchangeModes, if present, determines the PSK key exchange modes
+ // SendPSKKeyExchangeModes, if present, determines the PSK key exchange modes
// to send.
SendPSKKeyExchangeModes []byte
- // SendPSKAuthModes, if present, determines the PSK auth modes to send.
- SendPSKAuthModes []byte
+ // ExpectNoNewSessionTicket, if present, means that the client will fail upon
+ // receipt of a NewSessionTicket message.
+ ExpectNoNewSessionTicket bool
+
+ // ExpectTicketAge, if non-zero, is the expected age of the ticket that the
+ // server receives from the client.
+ ExpectTicketAge time.Duration
// FailIfSessionOffered, if true, causes the server to fail any
// connections where the client offers a non-empty session ID or session
@@ -991,6 +1005,26 @@
// supplied stapled response.
SendOCSPResponseOnResume []byte
+ // SendExtensionOnCertificate, if not nil, causes the runner to send the
+ // supplied bytes in the extensions on the Certificate message.
+ SendExtensionOnCertificate []byte
+
+ // SendOCSPOnIntermediates, if not nil, causes the server to send the
+ // supplied OCSP on intermediate certificates in the Certificate message.
+ SendOCSPOnIntermediates []byte
+
+ // SendSCTOnIntermediates, if not nil, causes the server to send the
+ // supplied SCT on intermediate certificates in the Certificate message.
+ SendSCTOnIntermediates []byte
+
+ // SendDuplicateCertExtensions, if true, causes the server to send an extra
+ // copy of the OCSP/SCT extensions in the Certificate message.
+ SendDuplicateCertExtensions bool
+
+ // ExpectNoExtensionsOnIntermediate, if true, causes the client to
+ // reject extensions on intermediate certificates.
+ ExpectNoExtensionsOnIntermediate bool
+
// CECPQ1BadX25519Part corrupts the X25519 part of a CECPQ1 key exchange, as
// a trivial proof that it is actually used.
CECPQ1BadX25519Part bool
@@ -1028,10 +1062,6 @@
// Renegotiation Info to be negotiated at all versions.
NegotiateRenegotiationInfoAtAllVersions bool
- // NegotiateChannelIDAtAllVersions, if true, causes Channel ID to be
- // negotiated at all versions.
- NegotiateChannelIDAtAllVersions bool
-
// NegotiateNPNAtAllVersions, if true, causes NPN to be negotiated at
// all versions.
NegotiateNPNAtAllVersions bool
@@ -1056,13 +1086,9 @@
// the specified PSK identity index rather than the actual value.
SelectPSKIdentityOnResume uint16
- // OmitServerHelloSignatureAlgorithms, if true, causes the server to omit the
- // signature_algorithms extension in the ServerHello.
- OmitServerHelloSignatureAlgorithms bool
-
- // IncludeServerHelloSignatureAlgorithms, if true, causes the server to
- // include the signature_algorithms extension in all ServerHellos.
- IncludeServerHelloSignatureAlgorithms bool
+ // ExtraPSKIdentity, if true, causes the client to send an extra PSK
+ // identity.
+ ExtraPSKIdentity bool
// MissingKeyShare, if true, causes the TLS 1.3 implementation to skip
// sending a key_share extension and use the zero ECDHE secret
@@ -1160,6 +1186,29 @@
// ExpectGREASE, if true, causes messages without GREASE values to be
// rejected. See draft-davidben-tls-grease-01.
ExpectGREASE bool
+
+ // SendShortPSKBinder, if true, causes the client to send a PSK binder
+ // that is one byte shorter than it should be.
+ SendShortPSKBinder bool
+
+ // SendInvalidPSKBinder, if true, causes the client to send an invalid
+ // PSK binder.
+ SendInvalidPSKBinder bool
+
+ // SendNoPSKBinder, if true, causes the client to send no PSK binders.
+ SendNoPSKBinder bool
+
+ // PSKBinderFirst, if true, causes the client to send the PSK Binder
+ // extension as the first extension instead of the last extension.
+ PSKBinderFirst bool
+
+ // NoOCSPStapling, if true, causes the client to not request OCSP
+ // stapling.
+ NoOCSPStapling bool
+
+ // NoSignedCertificateTimestamps, if true, causes the client to not
+ // request signed certificate timestamps.
+ NoSignedCertificateTimestamps bool
}
func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index f5014d4..39c2785 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -208,9 +208,9 @@
}
// useTrafficSecret sets the current cipher state for TLS 1.3.
-func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret, phase []byte, side trafficDirection) {
+func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) {
hc.version = version
- hc.cipher = deriveTrafficAEAD(version, suite, secret, phase, side)
+ hc.cipher = deriveTrafficAEAD(version, suite, secret, side)
if hc.config.Bugs.NullAllCiphers {
hc.cipher = nullCipher{}
}
@@ -223,7 +223,7 @@
if c.isClient == isOutgoing {
side = clientWrite
}
- hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), applicationPhase, side)
+ hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side)
}
// incSeq increments the sequence number.
@@ -753,16 +753,18 @@
// record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer
// version is irrelevant.)
if typ != recordTypeAlert {
+ var expect uint16
if c.haveVers {
- if vers != c.vers && c.vers < VersionTLS13 {
- c.sendAlert(alertProtocolVersion)
- return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers))
+ expect = c.vers
+ if c.vers >= VersionTLS13 {
+ expect = VersionTLS10
}
} else {
- if expect := c.config.Bugs.ExpectInitialRecordVersion; expect != 0 && vers != expect {
- c.sendAlert(alertProtocolVersion)
- return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, expect))
- }
+ expect = c.config.Bugs.ExpectInitialRecordVersion
+ }
+ if expect != 0 && vers != expect {
+ c.sendAlert(alertProtocolVersion)
+ return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, expect))
}
}
if n > maxCiphertext {
@@ -1063,6 +1065,12 @@
// layer to {3, 1}.
vers = VersionTLS10
}
+ if c.config.Bugs.SendRecordVersion != 0 {
+ vers = c.config.Bugs.SendRecordVersion
+ }
+ if c.vers == 0 && c.config.Bugs.SendInitialRecordVersion != 0 {
+ vers = c.config.Bugs.SendInitialRecordVersion
+ }
b.data[1] = byte(vers >> 8)
b.data[2] = byte(vers)
b.data[3] = byte(m >> 8)
@@ -1396,27 +1404,14 @@
return errors.New("tls: no GREASE ticket extension found")
}
+ if c.config.Bugs.ExpectNoNewSessionTicket {
+ return errors.New("tls: received unexpected NewSessionTicket")
+ }
+
if c.config.ClientSessionCache == nil || newSessionTicket.ticketLifetime == 0 {
return nil
}
- var foundKE, foundAuth bool
- for _, mode := range newSessionTicket.keModes {
- if mode == pskDHEKEMode {
- foundKE = true
- }
- }
- for _, mode := range newSessionTicket.authModes {
- if mode == pskAuthMode {
- foundAuth = true
- }
- }
-
- // Ignore the ticket if the server preferences do not match a mode we implement.
- if !foundKE || !foundAuth {
- return nil
- }
-
session := &ClientSessionState{
sessionTicket: newSessionTicket.ticket,
vers: c.vers,
@@ -1427,6 +1422,7 @@
ocspResponse: c.ocspResponse,
ticketCreationTime: c.config.time(),
ticketExpiration: c.config.time().Add(time.Duration(newSessionTicket.ticketLifetime) * time.Second),
+ ticketAgeAdd: newSessionTicket.ticketAgeAdd,
}
cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
@@ -1709,20 +1705,20 @@
peerCertificatesRaw = append(peerCertificatesRaw, cert.Raw)
}
+ addBuffer := make([]byte, 4)
+ _, err := io.ReadFull(c.config.rand(), addBuffer)
+ if err != nil {
+ c.sendAlert(alertInternalError)
+ return errors.New("tls: short read from Rand: " + err.Error())
+ }
+ ticketAgeAdd := uint32(addBuffer[3])<<24 | uint32(addBuffer[2])<<16 | uint32(addBuffer[1])<<8 | uint32(addBuffer[0])
+
// TODO(davidben): Allow configuring these values.
m := &newSessionTicketMsg{
version: c.vers,
ticketLifetime: uint32(24 * time.Hour / time.Second),
- keModes: []byte{pskDHEKEMode},
- authModes: []byte{pskAuthMode},
customExtension: c.config.Bugs.CustomTicketExtension,
- }
-
- if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
- m.keModes = c.config.Bugs.SendPSKKeyExchangeModes
- }
- if len(c.config.Bugs.SendPSKAuthModes) != 0 {
- m.authModes = c.config.Bugs.SendPSKAuthModes
+ ticketAgeAdd: ticketAgeAdd,
}
state := sessionState{
@@ -1732,6 +1728,7 @@
certificates: peerCertificatesRaw,
ticketCreationTime: c.config.time(),
ticketExpiration: c.config.time().Add(time.Duration(m.ticketLifetime) * time.Second),
+ ticketAgeAdd: uint32(addBuffer[3])<<24 | uint32(addBuffer[2])<<16 | uint32(addBuffer[1])<<8 | uint32(addBuffer[0]),
}
if !c.config.Bugs.SendEmptySessionTicket {
@@ -1744,7 +1741,7 @@
c.out.Lock()
defer c.out.Unlock()
- _, err := c.writeRecord(recordTypeHandshake, m.marshal())
+ _, err = c.writeRecord(recordTypeHandshake, m.marshal())
return err
}
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index 8fc1a56..24f3bd6 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -1,14 +1,14 @@
{
"DisabledTests": {
"BadCBCPadding*": "Fuzzer mode has no CBC padding.",
- "BadECDSA-*": "Fuzzer mode ignores invalid signatures.",
- "*-InvalidSignature-*": "Fuzzer mode ignores invalid signatures.",
+
"BadFinished-*": "Fuzzer mode ignores Finished checks.",
"FalseStart-BadFinished": "Fuzzer mode ignores Finished checks.",
"TrailingMessageData-*Finished*": "Fuzzer mode ignores Finished checks.",
"DTLSIgnoreBadPackets*": "Fuzzer mode has no bad packets.",
"TLSFatalBadPackets": "Fuzzer mode has no bad packets.",
+ "*-BadRecord": "Fuzzer mode has no bad packets.",
"BadRSAClientKeyExchange*": "Fuzzer mode does not notice a bad premaster secret.",
"CECPQ1-*-BadNewhopePart": "Fuzzer mode does not notice a bad premaster secret.",
@@ -19,10 +19,20 @@
"UnknownUnencryptedExtension-Client-TLS13": "Fuzzer mode will not read the peer's alert as a MAC error",
"WrongMessageType-TLS13-ServerHello": "Fuzzer mode will not read the peer's alert as a MAC error",
+ "BadECDSA-*": "Fuzzer mode always accepts a signature.",
+ "*-InvalidSignature-*": "Fuzzer mode always accepts a signature.",
"*Auth-Verify-RSA-PKCS1-*-TLS13": "Fuzzer mode always accepts a signature.",
"*Auth-Verify-ECDSA-SHA1-TLS13": "Fuzzer mode always accepts a signature.",
"Verify-*Auth-SignatureType*": "Fuzzer mode always accepts a signature.",
"ECDSACurveMismatch-Verify-TLS13": "Fuzzer mode always accepts a signature.",
- "InvalidChannelIDSignature": "Fuzzer mode always accepts a signature."
+ "InvalidChannelIDSignature-*": "Fuzzer mode always accepts a signature.",
+
+ "Resume-Server-DeclineBadCipher*": "Fuzzer mode does not encrypt tickets.",
+ "Resume-Server-DeclineCrossVersion*": "Fuzzer mode does not encrypt tickets.",
+ "TicketCallback-SingleCall-*": "Fuzzer mode does not encrypt tickets.",
+ "CorruptTicket-*": "Fuzzer mode does not encrypt tickets.",
+ "ShimTicketRewritable": "Fuzzer mode does not encrypt tickets.",
+
+ "Resume-Server-*Binder*": "Fuzzer mode does not check binders."
}
}
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index ae3228a..d074778 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -64,21 +64,23 @@
vers: versionToWire(maxVersion, c.isDTLS),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
- ocspStapling: true,
- sctListSupported: true,
+ ocspStapling: !c.config.Bugs.NoOCSPStapling,
+ sctListSupported: !c.config.Bugs.NoSignedCertificateTimestamps,
serverName: c.config.ServerName,
supportedCurves: c.config.curvePreferences(),
+ pskKEModes: []byte{pskDHEKEMode},
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
secureRenegotiation: []byte{},
alpnProtocols: c.config.NextProtos,
duplicateExtension: c.config.Bugs.DuplicateExtension,
channelIDSupported: c.config.ChannelID != nil,
- npnLast: c.config.Bugs.SwapNPNAndALPN,
+ npnAfterAlpn: c.config.Bugs.SwapNPNAndALPN,
extendedMasterSecret: maxVersion >= VersionTLS10,
srtpProtectionProfiles: c.config.SRTPProtectionProfiles,
srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
customExtension: c.config.Bugs.CustomExtension,
+ pskBinderFirst: c.config.Bugs.PSKBinderFirst,
}
disableEMS := c.config.Bugs.NoExtendedMasterSecret
@@ -94,6 +96,10 @@
hello.supportedCurves = nil
}
+ if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
+ hello.pskKEModes = c.config.Bugs.SendPSKKeyExchangeModes
+ }
+
if c.config.Bugs.SendCompressionMethods != nil {
hello.compressionMethods = c.config.Bugs.SendCompressionMethods
}
@@ -217,11 +223,17 @@
// Check that the ciphersuite/version used for the
// previous session are still valid.
cipherSuiteOk := false
- for _, id := range hello.cipherSuites {
- if id == candidateSession.cipherSuite {
- cipherSuiteOk = true
- break
+ if candidateSession.vers <= VersionTLS12 {
+ for _, id := range hello.cipherSuites {
+ if id == candidateSession.cipherSuite {
+ cipherSuiteOk = true
+ break
+ }
}
+ } else {
+ // TLS 1.3 allows the cipher to change on
+ // resumption.
+ cipherSuiteOk = true
}
versOk := candidateSession.vers >= minVersion &&
@@ -232,34 +244,37 @@
}
}
+ var pskCipherSuite *cipherSuite
if session != nil && c.config.time().Before(session.ticketExpiration) {
ticket := session.sessionTicket
- if c.config.Bugs.CorruptTicket && len(ticket) > 0 {
+ if c.config.Bugs.FilterTicket != nil && len(ticket) > 0 {
+ // Copy the ticket so FilterTicket may act in-place.
ticket = make([]byte, len(session.sessionTicket))
copy(ticket, session.sessionTicket)
- offset := 40
- if offset >= len(ticket) {
- offset = len(ticket) - 1
+
+ ticket, err = c.config.Bugs.FilterTicket(ticket)
+ if err != nil {
+ return err
}
- ticket[offset] ^= 0x40
}
if session.vers >= VersionTLS13 || c.config.Bugs.SendBothTickets {
+ pskCipherSuite = cipherSuiteFromID(session.cipherSuite)
+ if pskCipherSuite == nil {
+ return errors.New("tls: client session cache has invalid cipher suite")
+ }
// TODO(nharper): Support sending more
// than one PSK identity.
+ ticketAge := uint32(c.config.time().Sub(session.ticketCreationTime) / time.Millisecond)
psk := pskIdentity{
- keModes: []byte{pskDHEKEMode},
- authModes: []byte{pskAuthMode},
- ticket: ticket,
+ ticket: ticket,
+ obfuscatedTicketAge: session.ticketAgeAdd + ticketAge,
}
- if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 {
- psk.keModes = c.config.Bugs.SendPSKKeyExchangeModes
- }
- if len(c.config.Bugs.SendPSKAuthModes) != 0 {
- psk.authModes = c.config.Bugs.SendPSKAuthModes
- }
-
hello.pskIdentities = []pskIdentity{psk}
+
+ if c.config.Bugs.ExtraPSKIdentity {
+ hello.pskIdentities = append(hello.pskIdentities, psk)
+ }
}
if session.vers < VersionTLS13 || c.config.Bugs.SendBothTickets {
@@ -300,6 +315,10 @@
hello.vers = c.config.Bugs.SendClientVersion
}
+ if c.config.Bugs.SendCipherSuites != nil {
+ hello.cipherSuites = c.config.Bugs.SendCipherSuites
+ }
+
var helloBytes []byte
if c.config.Bugs.SendV2ClientHello {
// Test that the peer left-pads random.
@@ -314,7 +333,11 @@
helloBytes = v2Hello.marshal()
c.writeV2Record(helloBytes)
} else {
+ if len(hello.pskIdentities) > 0 {
+ generatePSKBinders(hello, pskCipherSuite, session.masterSecret, []byte{}, c.config)
+ }
helloBytes = hello.marshal()
+
if c.config.Bugs.PartialClientFinishedWithClientHello {
// Include one byte of Finished. We can compute it
// without completing the handshake. This assumes we
@@ -341,7 +364,7 @@
if c.isDTLS {
helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg)
if ok {
- if helloVerifyRequest.vers != VersionTLS10 {
+ if helloVerifyRequest.vers != versionToWire(VersionTLS10, c.isDTLS) {
// Per RFC 6347, the version field in
// HelloVerifyRequest SHOULD be always DTLS
// 1.0. Enforce this for testing purposes.
@@ -419,10 +442,10 @@
return err
}
keyShares[group] = curve
- hello.keyShares = append(hello.keyShares, keyShareEntry{
+ hello.keyShares = []keyShareEntry{{
group: group,
keyExchange: publicKey,
- })
+ }}
}
if c.config.Bugs.SecondClientHelloMissingKeyShare {
@@ -430,9 +453,11 @@
}
hello.hasEarlyData = false
- hello.earlyDataContext = nil
hello.raw = nil
+ if len(hello.pskIdentities) > 0 {
+ generatePSKBinders(hello, pskCipherSuite, session.masterSecret, append(helloBytes, helloRetryRequest.marshal()...), c.config)
+ }
secondHelloBytes = hello.marshal()
c.writeRecord(recordTypeHandshake, secondHelloBytes)
c.flushHandshake()
@@ -599,35 +624,29 @@
// 0-RTT is implemented.
var psk []byte
if hs.serverHello.hasPSKIdentity {
- if hs.serverHello.useCertAuth || !hs.serverHello.hasKeyShare {
- c.sendAlert(alertUnsupportedExtension)
- return errors.New("tls: server omitted KeyShare or included SignatureAlgorithms on resumption.")
- }
-
// We send at most one PSK identity.
if hs.session == nil || hs.serverHello.pskIdentity != 0 {
c.sendAlert(alertUnknownPSKIdentity)
return errors.New("tls: server sent unknown PSK identity")
}
- if hs.session.cipherSuite != hs.suite.id {
+ sessionCipher := cipherSuiteFromID(hs.session.cipherSuite)
+ if sessionCipher == nil || sessionCipher.hash() != hs.suite.hash() {
c.sendAlert(alertHandshakeFailure)
- return errors.New("tls: server sent invalid cipher suite")
+ return errors.New("tls: server resumed an invalid session for the cipher suite")
}
- psk = deriveResumptionPSK(hs.suite, hs.session.masterSecret)
- hs.finishedHash.setResumptionContext(deriveResumptionContext(hs.suite, hs.session.masterSecret))
+ psk = hs.session.masterSecret
c.didResume = true
} else {
- if !hs.serverHello.useCertAuth || !hs.serverHello.hasKeyShare {
- c.sendAlert(alertUnsupportedExtension)
- return errors.New("tls: server omitted KeyShare and SignatureAlgorithms on non-resumption.")
- }
-
psk = zeroSecret
- hs.finishedHash.setResumptionContext(zeroSecret)
}
earlySecret := hs.finishedHash.extractKey(zeroSecret, psk)
+ if !hs.serverHello.hasKeyShare {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server omitted KeyShare on resumption.")
+ }
+
// Resolve ECDHE and compute the handshake secret.
var ecdheSecret []byte
if !c.config.Bugs.MissingKeyShare && !c.config.Bugs.SecondClientHelloMissingKeyShare {
@@ -652,9 +671,9 @@
// Switch to handshake traffic keys.
clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
- c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
+ c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
- c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
+ c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
msg, err := c.readHandshake()
if err != nil {
@@ -675,24 +694,12 @@
var chainToSend *Certificate
var certReq *certificateRequestMsg
- if !hs.serverHello.useCertAuth {
- if encryptedExtensions.extensions.ocspResponse != nil {
- c.sendAlert(alertUnsupportedExtension)
- return errors.New("tls: server sent OCSP response without a certificate")
- }
- if encryptedExtensions.extensions.sctList != nil {
- c.sendAlert(alertUnsupportedExtension)
- return errors.New("tls: server sent SCT list without a certificate")
- }
-
+ if c.didResume {
// Copy over authentication from the session.
c.peerCertificates = hs.session.serverCertificates
c.sctList = hs.session.sctList
c.ocspResponse = hs.session.ocspResponse
} else {
- c.ocspResponse = encryptedExtensions.extensions.ocspResponse
- c.sctList = encryptedExtensions.extensions.sctList
-
msg, err := c.readHandshake()
if err != nil {
return err
@@ -729,10 +736,28 @@
}
hs.writeServerHash(certMsg.marshal())
+ // Check for unsolicited extensions.
+ for i, cert := range certMsg.certificates {
+ if c.config.Bugs.NoOCSPStapling && cert.ocspResponse != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: unexpected OCSP response in the server certificate")
+ }
+ if c.config.Bugs.NoSignedCertificateTimestamps && cert.sctList != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: unexpected SCT list in the server certificate")
+ }
+ if i > 0 && c.config.Bugs.ExpectNoExtensionsOnIntermediate && (cert.ocspResponse != nil || cert.sctList != nil) {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: unexpected extensions in the server certificate")
+ }
+ }
+
if err := hs.verifyCertificates(certMsg); err != nil {
return err
}
leaf := c.peerCertificates[0]
+ c.ocspResponse = certMsg.certificates[0].ocspResponse
+ c.sctList = certMsg.certificates[0].sctList
msg, err = c.readHandshake()
if err != nil {
@@ -785,7 +810,12 @@
requestContext: certReq.requestContext,
}
if chainToSend != nil {
- certMsg.certificates = chainToSend.Certificate
+ for _, certData := range chainToSend.Certificate {
+ certMsg.certificates = append(certMsg.certificates, certificateEntry{
+ data: certData,
+ extraExtension: c.config.Bugs.SendExtensionOnCertificate,
+ })
+ }
}
hs.writeClientHash(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
@@ -820,6 +850,17 @@
}
}
+ if encryptedExtensions.extensions.channelIDRequested {
+ channelIDHash := crypto.SHA256.New()
+ channelIDHash.Write(hs.finishedHash.certificateVerifyInput(channelIDContextTLS13))
+ channelIDMsgBytes, err := hs.writeChannelIDMessage(channelIDHash.Sum(nil))
+ if err != nil {
+ return err
+ }
+ hs.writeClientHash(channelIDMsgBytes)
+ c.writeRecord(recordTypeHandshake, channelIDMsgBytes)
+ }
+
// Send a client Finished message.
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.clientSum(clientHandshakeTrafficSecret)
@@ -839,8 +880,8 @@
c.flushHandshake()
// Switch to application data keys.
- c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
- c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
+ c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
+ c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
@@ -953,7 +994,11 @@
} else if !c.config.Bugs.SkipClientCertificate {
certMsg := new(certificateMsg)
if chainToSend != nil {
- certMsg.certificates = chainToSend.Certificate
+ for _, certData := range chainToSend.Certificate {
+ certMsg.certificates = append(certMsg.certificates, certificateEntry{
+ data: certData,
+ })
+ }
}
hs.writeClientHash(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
@@ -1041,8 +1086,8 @@
}
certs := make([]*x509.Certificate, len(certMsg.certificates))
- for i, asn1Data := range certMsg.certificates {
- cert, err := x509.ParseCertificate(asn1Data)
+ for i, certEntry := range certMsg.certificates {
+ cert, err := x509.ParseCertificate(certEntry.data)
if err != nil {
c.sendAlert(alertBadCertificate)
return errors.New("tls: failed to parse certificate from server: " + err.Error())
@@ -1169,11 +1214,6 @@
return errors.New("server advertised unrequested Channel ID extension")
}
- if serverExtensions.channelIDRequested && c.vers >= VersionTLS13 {
- c.sendAlert(alertHandshakeFailure)
- return errors.New("server advertised Channel ID over TLS 1.3")
- }
-
if serverExtensions.extendedMasterSecret && c.vers >= VersionTLS13 {
return errors.New("tls: server advertised extended master secret over TLS 1.3")
}
@@ -1182,6 +1222,22 @@
return errors.New("tls: server advertised ticket extension over TLS 1.3")
}
+ if serverExtensions.ocspStapling && c.vers >= VersionTLS13 {
+ return errors.New("tls: server advertised OCSP in ServerHello over TLS 1.3")
+ }
+
+ if serverExtensions.ocspStapling && c.config.Bugs.NoOCSPStapling {
+ return errors.New("tls: server advertised unrequested OCSP extension")
+ }
+
+ if len(serverExtensions.sctList) > 0 && c.vers >= VersionTLS13 {
+ return errors.New("tls: server advertised SCTs in ServerHello over TLS 1.3")
+ }
+
+ if len(serverExtensions.sctList) > 0 && c.config.Bugs.NoSignedCertificateTimestamps {
+ return errors.New("tls: server advertised unrequested SCTs")
+ }
+
if serverExtensions.srtpProtectionProfile != 0 {
if serverExtensions.srtpMasterKeyIdentifier != "" {
return errors.New("tls: server selected SRTP MKI value")
@@ -1287,7 +1343,7 @@
vers: c.vers,
cipherSuite: hs.suite.id,
masterSecret: hs.masterSecret,
- handshakeHash: hs.finishedHash.server.Sum(nil),
+ handshakeHash: hs.finishedHash.Sum(),
serverCertificates: c.peerCertificates,
sctList: c.sctList,
ocspResponse: c.ocspResponse,
@@ -1346,31 +1402,14 @@
}
if hs.serverHello.extensions.channelIDRequested {
- channelIDMsg := new(channelIDMsg)
- if c.config.ChannelID.Curve != elliptic.P256() {
- return fmt.Errorf("tls: Channel ID is not on P-256.")
- }
var resumeHash []byte
if isResume {
resumeHash = hs.session.handshakeHash
}
- r, s, err := ecdsa.Sign(c.config.rand(), c.config.ChannelID, hs.finishedHash.hashForChannelID(resumeHash))
+ channelIDMsgBytes, err := hs.writeChannelIDMessage(hs.finishedHash.hashForChannelID(resumeHash))
if err != nil {
return err
}
- channelID := make([]byte, 128)
- writeIntPadded(channelID[0:32], c.config.ChannelID.X)
- writeIntPadded(channelID[32:64], c.config.ChannelID.Y)
- writeIntPadded(channelID[64:96], r)
- writeIntPadded(channelID[96:128], s)
- if c.config.Bugs.InvalidChannelIDSignature {
- channelID[64] ^= 1
- }
- channelIDMsg.channelID = channelID
-
- c.channelID = &c.config.ChannelID.PublicKey
-
- channelIDMsgBytes := channelIDMsg.marshal()
hs.writeHash(channelIDMsgBytes, seqno)
seqno++
postCCSMsgs = append(postCCSMsgs, channelIDMsgBytes)
@@ -1431,6 +1470,31 @@
return nil
}
+func (hs *clientHandshakeState) writeChannelIDMessage(channelIDHash []byte) ([]byte, error) {
+ c := hs.c
+ channelIDMsg := new(channelIDMsg)
+ if c.config.ChannelID.Curve != elliptic.P256() {
+ return nil, fmt.Errorf("tls: Channel ID is not on P-256.")
+ }
+ r, s, err := ecdsa.Sign(c.config.rand(), c.config.ChannelID, channelIDHash)
+ if err != nil {
+ return nil, err
+ }
+ channelID := make([]byte, 128)
+ writeIntPadded(channelID[0:32], c.config.ChannelID.X)
+ writeIntPadded(channelID[32:64], c.config.ChannelID.Y)
+ writeIntPadded(channelID[64:96], r)
+ writeIntPadded(channelID[96:128], s)
+ if c.config.Bugs.InvalidChannelIDSignature {
+ channelID[64] ^= 1
+ }
+ channelIDMsg.channelID = channelID
+
+ c.channelID = &c.config.ChannelID.PublicKey
+
+ return channelIDMsg.marshal(), nil
+}
+
func (hs *clientHandshakeState) writeClientHash(msg []byte) {
// writeClientHash is called before writeRecord.
hs.writeHash(msg, hs.c.sendHandshakeSeq)
@@ -1573,3 +1637,39 @@
xb := x.Bytes()
copy(b[len(b)-len(xb):], xb)
}
+
+func generatePSKBinders(hello *clientHelloMsg, pskCipherSuite *cipherSuite, psk, transcript []byte, config *Config) {
+ if config.Bugs.SendNoPSKBinder {
+ return
+ }
+
+ binderLen := pskCipherSuite.hash().Size()
+ if config.Bugs.SendShortPSKBinder {
+ binderLen--
+ }
+
+ // Fill hello.pskBinders with appropriate length arrays of zeros so the
+ // length prefixes are correct when computing the binder over the truncated
+ // ClientHello message.
+ hello.pskBinders = make([][]byte, len(hello.pskIdentities))
+ for i := range hello.pskIdentities {
+ hello.pskBinders[i] = make([]byte, binderLen)
+ }
+
+ helloBytes := hello.marshal()
+ binderSize := len(hello.pskBinders)*(binderLen+1) + 2
+ truncatedHello := helloBytes[:len(helloBytes)-binderSize]
+ binder := computePSKBinder(psk, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello)
+ if config.Bugs.SendShortPSKBinder {
+ binder = binder[:binderLen]
+ }
+ if config.Bugs.SendInvalidPSKBinder {
+ binder[0] ^= 1
+ }
+
+ for i := range hello.pskBinders {
+ hello.pskBinders[i] = binder
+ }
+
+ hello.raw = nil
+}
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 285587e..8a338f0 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -125,9 +125,8 @@
}
type pskIdentity struct {
- keModes []byte
- authModes []byte
- ticket []uint8
+ ticket []uint8
+ obfuscatedTicketAge uint32
}
type clientHelloMsg struct {
@@ -148,8 +147,9 @@
keyShares []keyShareEntry
trailingKeyShareData bool
pskIdentities []pskIdentity
+ pskKEModes []byte
+ pskBinders [][]uint8
hasEarlyData bool
- earlyDataContext []byte
tls13Cookie []byte
ticketSupported bool
sessionTicket []uint8
@@ -159,13 +159,14 @@
alpnProtocols []string
duplicateExtension bool
channelIDSupported bool
- npnLast bool
+ npnAfterAlpn bool
extendedMasterSecret bool
srtpProtectionProfiles []uint16
srtpMasterKeyIdentifier string
sctListSupported bool
customExtension string
hasGREASEExtension bool
+ pskBinderFirst bool
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -191,8 +192,9 @@
eqKeyShareEntryLists(m.keyShares, m1.keyShares) &&
m.trailingKeyShareData == m1.trailingKeyShareData &&
eqPSKIdentityLists(m.pskIdentities, m1.pskIdentities) &&
+ bytes.Equal(m.pskKEModes, m1.pskKEModes) &&
+ eqByteSlices(m.pskBinders, m1.pskBinders) &&
m.hasEarlyData == m1.hasEarlyData &&
- bytes.Equal(m.earlyDataContext, m1.earlyDataContext) &&
bytes.Equal(m.tls13Cookie, m1.tls13Cookie) &&
m.ticketSupported == m1.ticketSupported &&
bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
@@ -203,13 +205,14 @@
eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
m.duplicateExtension == m1.duplicateExtension &&
m.channelIDSupported == m1.channelIDSupported &&
- m.npnLast == m1.npnLast &&
+ m.npnAfterAlpn == m1.npnAfterAlpn &&
m.extendedMasterSecret == m1.extendedMasterSecret &&
eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier &&
m.sctListSupported == m1.sctListSupported &&
m.customExtension == m1.customExtension &&
- m.hasGREASEExtension == m1.hasGREASEExtension
+ m.hasGREASEExtension == m1.hasGREASEExtension &&
+ m.pskBinderFirst == m1.pskBinderFirst
}
func (m *clientHelloMsg) marshal() []byte {
@@ -236,12 +239,26 @@
compressionMethods.addBytes(m.compressionMethods)
extensions := hello.addU16LengthPrefixed()
+ if len(m.pskIdentities) > 0 && m.pskBinderFirst {
+ extensions.addU16(extensionPreSharedKey)
+ pskExtension := extensions.addU16LengthPrefixed()
+
+ pskIdentities := pskExtension.addU16LengthPrefixed()
+ for _, psk := range m.pskIdentities {
+ pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
+ pskIdentities.addU32(psk.obfuscatedTicketAge)
+ }
+ pskBinders := pskExtension.addU16LengthPrefixed()
+ for _, binder := range m.pskBinders {
+ pskBinders.addU8LengthPrefixed().addBytes(binder)
+ }
+ }
if m.duplicateExtension {
// Add a duplicate bogus extension at the beginning and end.
extensions.addU16(0xffff)
extensions.addU16(0) // 0-length for empty extension
}
- if m.nextProtoNeg && !m.npnLast {
+ if m.nextProtoNeg && !m.npnAfterAlpn {
extensions.addU16(extensionNextProtoNeg)
extensions.addU16(0) // The length is always 0
}
@@ -316,23 +333,14 @@
keyShares.addU8(0)
}
}
- if len(m.pskIdentities) > 0 {
- extensions.addU16(extensionPreSharedKey)
- pskExtension := extensions.addU16LengthPrefixed()
-
- pskIdentities := pskExtension.addU16LengthPrefixed()
- for _, psk := range m.pskIdentities {
- pskIdentities.addU8LengthPrefixed().addBytes(psk.keModes)
- pskIdentities.addU8LengthPrefixed().addBytes(psk.authModes)
- pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
- }
+ if len(m.pskKEModes) > 0 {
+ extensions.addU16(extensionPSKKeyExchangeModes)
+ pskModesExtension := extensions.addU16LengthPrefixed()
+ pskModesExtension.addU8LengthPrefixed().addBytes(m.pskKEModes)
}
if m.hasEarlyData {
extensions.addU16(extensionEarlyData)
- earlyDataIndication := extensions.addU16LengthPrefixed()
-
- context := earlyDataIndication.addU8LengthPrefixed()
- context.addBytes(m.earlyDataContext)
+ extensions.addU16(0) // The length is zero.
}
if len(m.tls13Cookie) > 0 {
extensions.addU16(extensionCookie)
@@ -383,7 +391,7 @@
extensions.addU16(extensionChannelID)
extensions.addU16(0) // Length is always 0
}
- if m.nextProtoNeg && m.npnLast {
+ if m.nextProtoNeg && m.npnAfterAlpn {
extensions.addU16(extensionNextProtoNeg)
extensions.addU16(0) // Length is always 0
}
@@ -422,6 +430,21 @@
customExt := extensions.addU16LengthPrefixed()
customExt.addBytes([]byte(m.customExtension))
}
+ // The PSK extension must be last (draft-ietf-tls-tls13-18 section 4.2.6).
+ if len(m.pskIdentities) > 0 && !m.pskBinderFirst {
+ extensions.addU16(extensionPreSharedKey)
+ pskExtension := extensions.addU16LengthPrefixed()
+
+ pskIdentities := pskExtension.addU16LengthPrefixed()
+ for _, psk := range m.pskIdentities {
+ pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket)
+ pskIdentities.addU32(psk.obfuscatedTicketAge)
+ }
+ pskBinders := pskExtension.addU16LengthPrefixed()
+ for _, binder := range m.pskBinders {
+ pskBinders.addU8LengthPrefixed().addBytes(binder)
+ }
+ }
if extensions.len() == 0 {
hello.discardChild()
@@ -490,7 +513,6 @@
m.keyShares = nil
m.pskIdentities = nil
m.hasEarlyData = false
- m.earlyDataContext = nil
m.ticketSupported = false
m.sessionTicket = nil
m.signatureAlgorithms = nil
@@ -614,63 +636,75 @@
m.keyShares = append(m.keyShares, entry)
}
case extensionPreSharedKey:
- // draft-ietf-tls-tls13 section 6.3.2.4
+ // draft-ietf-tls-tls13-18 section 4.2.6
if length < 2 {
return false
}
l := int(data[0])<<8 | int(data[1])
- if l != length-2 {
- return false
- }
- d := data[2:length]
+ d := data[2 : l+2]
+ // Parse PSK identities.
for len(d) > 0 {
- var psk pskIdentity
-
- if len(d) < 1 {
- return false
- }
- keModesLen := int(d[0])
- d = d[1:]
- if len(d) < keModesLen {
- return false
- }
- psk.keModes = d[:keModesLen]
- d = d[keModesLen:]
-
- if len(d) < 1 {
- return false
- }
- authModesLen := int(d[0])
- d = d[1:]
- if len(d) < authModesLen {
- return false
- }
- psk.authModes = d[:authModesLen]
- d = d[authModesLen:]
if len(d) < 2 {
return false
}
pskLen := int(d[0])<<8 | int(d[1])
d = d[2:]
- if len(d) < pskLen {
+ if len(d) < pskLen+4 {
return false
}
- psk.ticket = d[:pskLen]
+ ticket := d[:pskLen]
+ obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3])
+ psk := pskIdentity{
+ ticket: ticket,
+ obfuscatedTicketAge: obfuscatedTicketAge,
+ }
m.pskIdentities = append(m.pskIdentities, psk)
- d = d[pskLen:]
+ d = d[pskLen+4:]
}
- case extensionEarlyData:
- // draft-ietf-tls-tls13 section 6.3.2.5
+ d = data[l+2:]
+ if len(d) < 2 {
+ return false
+ }
+ l = int(d[0])<<8 | int(d[1])
+ d = d[2:]
+ if l != len(d) {
+ return false
+ }
+ // Parse PSK binders.
+ for len(d) > 0 {
+ if len(d) < 1 {
+ return false
+ }
+ binderLen := int(d[0])
+ d = d[1:]
+ if binderLen > len(d) {
+ return false
+ }
+ m.pskBinders = append(m.pskBinders, d[:binderLen])
+ d = d[binderLen:]
+ }
+
+ // There must be the same number of identities as binders.
+ if len(m.pskIdentities) != len(m.pskBinders) {
+ return false
+ }
+ case extensionPSKKeyExchangeModes:
+ // draft-ietf-tls-tls13-18 section 4.2.7
if length < 1 {
return false
}
l := int(data[0])
- if length != l+1 {
+ if l != length-1 {
+ return false
+ }
+ m.pskKEModes = data[1:length]
+ case extensionEarlyData:
+ // draft-ietf-tls-tls13 section 6.3.2.5
+ if length != 0 {
return false
}
m.hasEarlyData = true
- m.earlyDataContext = data[1:length]
case extensionCookie:
if length < 2 {
return false
@@ -793,7 +827,6 @@
keyShare keyShareEntry
hasPSKIdentity bool
pskIdentity uint16
- useCertAuth bool
earlyDataIndication bool
compressionMethod uint8
customExtension string
@@ -848,10 +881,6 @@
extensions.addU16(2) // Length
extensions.addU16(m.pskIdentity)
}
- if m.useCertAuth {
- extensions.addU16(extensionSignatureAlgorithms)
- extensions.addU16(0) // Length
- }
if m.earlyDataIndication {
extensions.addU16(extensionEarlyData)
extensions.addU16(0) // Length
@@ -870,7 +899,7 @@
protocolName.addBytes([]byte(m.unencryptedALPN))
}
} else {
- m.extensions.marshal(extensions, vers)
+ m.extensions.marshal(extensions)
if extensions.len() == 0 {
hello.discardChild()
}
@@ -962,11 +991,6 @@
}
m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
m.hasPSKIdentity = true
- case extensionSignatureAlgorithms:
- if len(d) != 0 {
- return false
- }
- m.useCertAuth = true
case extensionEarlyData:
if len(d) != 0 {
return false
@@ -1001,7 +1025,7 @@
encryptedExtensions := encryptedExtensionsMsg.addU24LengthPrefixed()
if !m.empty {
extensions := encryptedExtensions.addU16LengthPrefixed()
- m.extensions.marshal(extensions, VersionTLS13)
+ m.extensions.marshal(extensions)
}
m.raw = encryptedExtensionsMsg.finish()
@@ -1033,7 +1057,6 @@
nextProtoNeg bool
nextProtos []string
ocspStapling bool
- ocspResponse []byte
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
@@ -1045,18 +1068,18 @@
srtpMasterKeyIdentifier string
sctList []byte
customExtension string
- npnLast bool
+ npnAfterAlpn bool
hasKeyShare bool
keyShare keyShareEntry
}
-func (m *serverExtensions) marshal(extensions *byteBuilder, version uint16) {
+func (m *serverExtensions) marshal(extensions *byteBuilder) {
if m.duplicateExtension {
// Add a duplicate bogus extension at the beginning and end.
extensions.addU16(0xffff)
extensions.addU16(0) // length = 0 for empty extension
}
- if m.nextProtoNeg && !m.npnLast {
+ if m.nextProtoNeg && !m.npnAfterAlpn {
extensions.addU16(extensionNextProtoNeg)
extension := extensions.addU16LengthPrefixed()
@@ -1068,19 +1091,9 @@
npn.addBytes([]byte(v))
}
}
- if version >= VersionTLS13 {
- if m.ocspResponse != nil {
- extensions.addU16(extensionStatusRequest)
- body := extensions.addU16LengthPrefixed()
- body.addU8(statusTypeOCSP)
- response := body.addU24LengthPrefixed()
- response.addBytes(m.ocspResponse)
- }
- } else {
- if m.ocspStapling {
- extensions.addU16(extensionStatusRequest)
- extensions.addU16(0)
- }
+ if m.ocspStapling {
+ extensions.addU16(extensionStatusRequest)
+ extensions.addU16(0)
}
if m.ticketSupported {
extensions.addU16(extensionSessionTicket)
@@ -1133,7 +1146,7 @@
customExt := extensions.addU16LengthPrefixed()
customExt.addBytes([]byte(m.customExtension))
}
- if m.nextProtoNeg && m.npnLast {
+ if m.nextProtoNeg && m.npnAfterAlpn {
extensions.addU16(extensionNextProtoNeg)
extension := extensions.addU16LengthPrefixed()
@@ -1183,25 +1196,10 @@
d = d[l:]
}
case extensionStatusRequest:
- if version >= VersionTLS13 {
- if length < 4 {
- return false
- }
- d := data[:length]
- if d[0] != statusTypeOCSP {
- return false
- }
- respLen := int(d[1])<<16 | int(d[2])<<8 | int(d[3])
- if respLen+4 != len(d) || respLen == 0 {
- return false
- }
- m.ocspResponse = d[4:]
- } else {
- if length > 0 {
- return false
- }
- m.ocspStapling = true
+ if length > 0 {
+ return false
}
+ m.ocspStapling = true
case extensionSessionTicket:
if length > 0 {
return false
@@ -1377,11 +1375,19 @@
return true
}
+type certificateEntry struct {
+ data []byte
+ ocspResponse []byte
+ sctList []byte
+ duplicateExtensions bool
+ extraExtension []byte
+}
+
type certificateMsg struct {
raw []byte
hasRequestContext bool
requestContext []byte
- certificates [][]byte
+ certificates []certificateEntry
}
func (m *certificateMsg) marshal() (x []byte) {
@@ -1399,7 +1405,33 @@
certificateList := certificate.addU24LengthPrefixed()
for _, cert := range m.certificates {
certEntry := certificateList.addU24LengthPrefixed()
- certEntry.addBytes(cert)
+ certEntry.addBytes(cert.data)
+ if m.hasRequestContext {
+ extensions := certificateList.addU16LengthPrefixed()
+ count := 1
+ if cert.duplicateExtensions {
+ count = 2
+ }
+
+ for i := 0; i < count; i++ {
+ if cert.ocspResponse != nil {
+ extensions.addU16(extensionStatusRequest)
+ body := extensions.addU16LengthPrefixed()
+ body.addU8(statusTypeOCSP)
+ response := body.addU24LengthPrefixed()
+ response.addBytes(cert.ocspResponse)
+ }
+
+ if cert.sctList != nil {
+ extensions.addU16(extensionSignedCertificateTimestamp)
+ extension := extensions.addU16LengthPrefixed()
+ extension.addBytes(cert.sctList)
+ }
+ }
+ if cert.extraExtension != nil {
+ extensions.addBytes(cert.extraExtension)
+ }
+ }
}
m.raw = certMsg.finish()
@@ -1436,27 +1468,62 @@
return false
}
- numCerts := 0
- d := data
- for certsLen > 0 {
- if len(d) < 4 {
+ m.certificates = nil
+ for len(data) != 0 {
+ if len(data) < 3 {
return false
}
- certLen := int(d[0])<<16 | int(d[1])<<8 | int(d[2])
- if len(d) < 3+certLen {
+ certLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
+ if len(data) < 3+certLen {
return false
}
- d = d[3+certLen:]
- certsLen -= 3 + certLen
- numCerts++
- }
+ cert := certificateEntry{
+ data: data[3 : 3+certLen],
+ }
+ data = data[3+certLen:]
+ if m.hasRequestContext {
+ if len(data) < 2 {
+ return false
+ }
+ extensionsLen := int(data[0])<<8 | int(data[1])
+ if len(data) < 2+extensionsLen {
+ return false
+ }
+ extensions := data[2 : 2+extensionsLen]
+ data = data[2+extensionsLen:]
+ for len(extensions) != 0 {
+ if len(extensions) < 4 {
+ return false
+ }
+ extension := uint16(extensions[0])<<8 | uint16(extensions[1])
+ length := int(extensions[2])<<8 | int(extensions[3])
+ if len(extensions) < 4+length {
+ return false
+ }
+ contents := extensions[4 : 4+length]
+ extensions = extensions[4+length:]
- m.certificates = make([][]byte, numCerts)
- d = data
- for i := 0; i < numCerts; i++ {
- certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
- m.certificates[i] = d[3 : 3+certLen]
- d = d[3+certLen:]
+ switch extension {
+ case extensionStatusRequest:
+ if length < 4 {
+ return false
+ }
+ if contents[0] != statusTypeOCSP {
+ return false
+ }
+ respLen := int(contents[1])<<16 | int(contents[2])<<8 | int(contents[3])
+ if respLen+4 != len(contents) || respLen == 0 {
+ return false
+ }
+ cert.ocspResponse = contents[4:]
+ case extensionSignedCertificateTimestamp:
+ cert.sctList = contents
+ default:
+ return false
+ }
+ }
+ }
+ m.certificates = append(m.certificates, cert)
}
return true
@@ -1904,8 +1971,7 @@
raw []byte
version uint16
ticketLifetime uint32
- keModes []byte
- authModes []byte
+ ticketAgeAdd uint32
ticket []byte
customExtension string
hasGREASEExtension bool
@@ -1922,8 +1988,7 @@
body := ticketMsg.addU24LengthPrefixed()
body.addU32(m.ticketLifetime)
if m.version >= VersionTLS13 {
- body.addU8LengthPrefixed().addBytes(m.keModes)
- body.addU8LengthPrefixed().addBytes(m.authModes)
+ body.addU32(m.ticketAgeAdd)
}
ticket := body.addU16LengthPrefixed()
@@ -1951,25 +2016,11 @@
data = data[8:]
if m.version >= VersionTLS13 {
- if len(data) < 1 {
+ if len(data) < 4 {
return false
}
- keModesLength := int(data[0])
- if len(data)-1 < keModesLength {
- return false
- }
- m.keModes = data[1 : 1+keModesLength]
- data = data[1+keModesLength:]
-
- if len(data) < 1 {
- return false
- }
- authModesLength := int(data[0])
- if len(data)-1 < authModesLength {
- return false
- }
- m.authModes = data[1 : 1+authModesLength]
- data = data[1+authModesLength:]
+ m.ticketAgeAdd = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
+ data = data[4:]
}
if len(data) < 2 {
@@ -2268,7 +2319,7 @@
return false
}
for i, v := range x {
- if !bytes.Equal(y[i].keModes, v.keModes) || !bytes.Equal(y[i].authModes, v.authModes) || !bytes.Equal(y[i].ticket, v.ticket) {
+ if !bytes.Equal(y[i].ticket, v.ticket) || y[i].obfuscatedTicketAge != v.obfuscatedTicketAge {
return false
}
}
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 9147134..67950ba 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -16,6 +16,7 @@
"fmt"
"io"
"math/big"
+ "time"
)
// serverHandshakeState contains details of a server handshake in progress.
@@ -158,7 +159,7 @@
// Per RFC 6347, the version field in HelloVerifyRequest SHOULD
// be always DTLS 1.0
helloVerifyRequest := &helloVerifyRequestMsg{
- vers: VersionTLS10,
+ vers: versionToWire(VersionTLS10, c.isDTLS),
cookie: make([]byte, 32),
}
if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil {
@@ -384,6 +385,36 @@
return err
}
+ // Select the cipher suite.
+ var preferenceList, supportedList []uint16
+ if config.PreferServerCipherSuites {
+ preferenceList = config.cipherSuites()
+ supportedList = hs.clientHello.cipherSuites
+ } else {
+ preferenceList = hs.clientHello.cipherSuites
+ supportedList = config.cipherSuites()
+ }
+
+ for _, id := range preferenceList {
+ if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, true, true); hs.suite != nil {
+ break
+ }
+ }
+
+ if hs.suite == nil {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: no cipher suite supported by both client and server")
+ }
+
+ hs.hello.cipherSuite = hs.suite.id
+ if c.config.Bugs.SendCipherSuite != 0 {
+ hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite
+ }
+
+ hs.finishedHash = newFinishedHash(c.vers, hs.suite)
+ hs.finishedHash.discardHandshakeBuffer()
+ hs.writeClientHash(hs.clientHello.marshal())
+
supportedCurve := false
var selectedCurve CurveID
preferredCurves := config.curvePreferences()
@@ -404,78 +435,55 @@
}
pskIdentities := hs.clientHello.pskIdentities
+ pskKEModes := hs.clientHello.pskKEModes
+
if len(pskIdentities) == 0 && len(hs.clientHello.sessionTicket) > 0 && c.config.Bugs.AcceptAnySession {
psk := pskIdentity{
- keModes: []byte{pskDHEKEMode},
- authModes: []byte{pskAuthMode},
- ticket: hs.clientHello.sessionTicket,
+ ticket: hs.clientHello.sessionTicket,
}
pskIdentities = []pskIdentity{psk}
+ pskKEModes = []byte{pskDHEKEMode}
}
- for i, pskIdentity := range pskIdentities {
- foundKE := false
- foundAuth := false
- for _, keMode := range pskIdentity.keModes {
- if keMode == pskDHEKEMode {
- foundKE = true
- }
- }
-
- for _, authMode := range pskIdentity.authModes {
- if authMode == pskAuthMode {
- foundAuth = true
- }
- }
-
- if !foundKE || !foundAuth {
- continue
- }
-
- sessionState, ok := c.decryptTicket(pskIdentity.ticket)
- if !ok {
- continue
- }
- if config.Bugs.AcceptAnySession {
- // Replace the cipher suite with one known to work, to
- // test cross-version resumption attempts.
- sessionState.cipherSuite = TLS_AES_128_GCM_SHA256
- } else {
- if sessionState.vers != c.vers && c.config.Bugs.AcceptAnySession {
- continue
- }
- if sessionState.ticketExpiration.Before(c.config.time()) {
+ var pskIndex int
+ foundKEMode := bytes.IndexByte(pskKEModes, pskDHEKEMode) >= 0
+ if foundKEMode {
+ for i, pskIdentity := range pskIdentities {
+ // TODO(svaldez): Check the obfuscatedTicketAge before accepting 0-RTT.
+ sessionState, ok := c.decryptTicket(pskIdentity.ticket)
+ if !ok {
continue
}
- cipherSuiteOk := false
- // Check that the client is still offering the ciphersuite in the session.
- for _, id := range hs.clientHello.cipherSuites {
- if id == sessionState.cipherSuite {
- cipherSuiteOk = true
- break
+ if !config.Bugs.AcceptAnySession {
+ if sessionState.vers != c.vers {
+ continue
+ }
+ if sessionState.ticketExpiration.Before(c.config.time()) {
+ continue
+ }
+ sessionCipher := cipherSuiteFromID(sessionState.cipherSuite)
+ if sessionCipher == nil || sessionCipher.hash() != hs.suite.hash() {
+ continue
}
}
- if !cipherSuiteOk {
- continue
+
+ clientTicketAge := time.Duration(uint32(pskIdentity.obfuscatedTicketAge-sessionState.ticketAgeAdd)) * time.Millisecond
+ if config.Bugs.ExpectTicketAge != 0 && clientTicketAge != config.Bugs.ExpectTicketAge {
+ c.sendAlert(alertHandshakeFailure)
+ return errors.New("tls: invalid ticket age")
}
- }
- // Check that we also support the ciphersuite from the session.
- suite := c.tryCipherSuite(sessionState.cipherSuite, c.config.cipherSuites(), c.vers, true, true)
- if suite == nil {
- continue
+ hs.sessionState = sessionState
+ hs.hello.hasPSKIdentity = true
+ hs.hello.pskIdentity = uint16(i)
+ pskIndex = i
+ if config.Bugs.SelectPSKIdentityOnResume != 0 {
+ hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume
+ }
+ c.didResume = true
+ break
}
-
- hs.sessionState = sessionState
- hs.suite = suite
- hs.hello.hasPSKIdentity = true
- hs.hello.pskIdentity = uint16(i)
- if config.Bugs.SelectPSKIdentityOnResume != 0 {
- hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume
- }
- c.didResume = true
- break
}
if config.Bugs.AlwaysSelectPSKIdentity {
@@ -483,58 +491,25 @@
hs.hello.pskIdentity = 0
}
- // If not resuming, select the cipher suite.
- if hs.suite == nil {
- var preferenceList, supportedList []uint16
- if config.PreferServerCipherSuites {
- preferenceList = config.cipherSuites()
- supportedList = hs.clientHello.cipherSuites
- } else {
- preferenceList = hs.clientHello.cipherSuites
- supportedList = config.cipherSuites()
- }
-
- for _, id := range preferenceList {
- if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, true, true); hs.suite != nil {
- break
- }
+ // Verify the PSK binder. Note there may not be a PSK binder if
+ // AcceptAnyBinder is set. See https://crbug.com/boringssl/115.
+ if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
+ binderToVerify := hs.clientHello.pskBinders[pskIndex]
+ if err := verifyPSKBinder(hs.clientHello, hs.sessionState, binderToVerify, []byte{}); err != nil {
+ return err
}
}
- if hs.suite == nil {
- c.sendAlert(alertHandshakeFailure)
- return errors.New("tls: no cipher suite supported by both client and server")
- }
-
- hs.hello.cipherSuite = hs.suite.id
- if c.config.Bugs.SendCipherSuite != 0 {
- hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite
- }
-
- hs.finishedHash = newFinishedHash(c.vers, hs.suite)
- hs.finishedHash.discardHandshakeBuffer()
- hs.writeClientHash(hs.clientHello.marshal())
-
- hs.hello.useCertAuth = hs.sessionState == nil
-
// Resolve PSK and compute the early secret.
var psk []byte
if hs.sessionState != nil {
- psk = deriveResumptionPSK(hs.suite, hs.sessionState.masterSecret)
- hs.finishedHash.setResumptionContext(deriveResumptionContext(hs.suite, hs.sessionState.masterSecret))
+ psk = hs.sessionState.masterSecret
} else {
psk = hs.finishedHash.zeroSecret()
- hs.finishedHash.setResumptionContext(hs.finishedHash.zeroSecret())
}
earlySecret := hs.finishedHash.extractKey(hs.finishedHash.zeroSecret(), psk)
- if config.Bugs.OmitServerHelloSignatureAlgorithms {
- hs.hello.useCertAuth = false
- } else if config.Bugs.IncludeServerHelloSignatureAlgorithms {
- hs.hello.useCertAuth = true
- }
-
hs.hello.hasKeyShare = true
if hs.sessionState != nil && config.Bugs.NegotiatePSKResumption {
hs.hello.hasKeyShare = false
@@ -598,6 +573,7 @@
}
if sendHelloRetryRequest {
+ oldClientHelloBytes := hs.clientHello.marshal()
hs.writeServerHash(helloRetryRequest.marshal())
c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
c.flushHandshake()
@@ -621,17 +597,16 @@
oldClientHelloCopy := *hs.clientHello
oldClientHelloCopy.raw = nil
oldClientHelloCopy.hasEarlyData = false
- oldClientHelloCopy.earlyDataContext = nil
newClientHelloCopy := *newClientHello
newClientHelloCopy.raw = nil
if helloRetryRequest.hasSelectedGroup {
newKeyShares := newClientHelloCopy.keyShares
- if len(newKeyShares) == 0 || newKeyShares[len(newKeyShares)-1].group != helloRetryRequest.selectedGroup {
- return errors.New("tls: KeyShare from HelloRetryRequest not present in new ClientHello")
+ if len(newKeyShares) != 1 || newKeyShares[0].group != helloRetryRequest.selectedGroup {
+ return errors.New("tls: KeyShare from HelloRetryRequest not in new ClientHello")
}
- selectedKeyShare = &newKeyShares[len(newKeyShares)-1]
- newClientHelloCopy.keyShares = newKeyShares[:len(newKeyShares)-1]
+ selectedKeyShare = &newKeyShares[0]
+ newClientHelloCopy.keyShares = oldClientHelloCopy.keyShares
}
if len(helloRetryRequest.cookie) > 0 {
@@ -641,6 +616,16 @@
newClientHelloCopy.tls13Cookie = nil
}
+ // PSK binders and obfuscated ticket age are both updated in the
+ // second ClientHello.
+ if len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) {
+ return errors.New("tls: PSK identity count from old and new ClientHello do not match")
+ }
+ for i, identity := range oldClientHelloCopy.pskIdentities {
+ newClientHelloCopy.pskIdentities[i].obfuscatedTicketAge = identity.obfuscatedTicketAge
+ }
+ newClientHelloCopy.pskBinders = oldClientHelloCopy.pskBinders
+
if !oldClientHelloCopy.equal(&newClientHelloCopy) {
return errors.New("tls: new ClientHello does not match")
}
@@ -649,6 +634,16 @@
firstHelloRetryRequest = false
goto ResendHelloRetryRequest
}
+
+ // Verify the PSK binder. Note there may not be a PSK binder if
+ // AcceptAnyBinder is set. See https://crbug.com/115.
+ if hs.sessionState != nil && !config.Bugs.AcceptAnySession {
+ binderToVerify := newClientHello.pskBinders[pskIndex]
+ err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...))
+ if err != nil {
+ return err
+ }
+ }
}
// Resolve ECDHE and compute the handshake secret.
@@ -728,25 +723,9 @@
// Switch to handshake traffic keys.
serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
- c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
+ c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite)
clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
- c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
-
- if hs.hello.useCertAuth {
- if hs.clientHello.ocspStapling {
- encryptedExtensions.extensions.ocspResponse = hs.cert.OCSPStaple
- }
- if hs.clientHello.sctListSupported {
- encryptedExtensions.extensions.sctList = hs.cert.SignedCertificateTimestampList
- }
- } else {
- if config.Bugs.SendOCSPResponseOnResume != nil {
- encryptedExtensions.extensions.ocspResponse = config.Bugs.SendOCSPResponseOnResume
- }
- if config.Bugs.SendSCTListOnResume != nil {
- encryptedExtensions.extensions.sctList = config.Bugs.SendSCTListOnResume
- }
- }
+ c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite)
// Send EncryptedExtensions.
hs.writeServerHash(encryptedExtensions.marshal())
@@ -757,7 +736,7 @@
c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal())
}
- if hs.hello.useCertAuth {
+ if hs.sessionState == nil {
if config.ClientAuth >= RequestClientCert {
// Request a client certificate
certReq := &certificateRequestMsg{
@@ -785,7 +764,29 @@
hasRequestContext: true,
}
if !config.Bugs.EmptyCertificateList {
- certMsg.certificates = hs.cert.Certificate
+ for i, certData := range hs.cert.Certificate {
+ cert := certificateEntry{
+ data: certData,
+ }
+ if i == 0 {
+ if hs.clientHello.ocspStapling {
+ cert.ocspResponse = hs.cert.OCSPStaple
+ }
+ if hs.clientHello.sctListSupported {
+ cert.sctList = hs.cert.SignedCertificateTimestampList
+ }
+ cert.duplicateExtensions = config.Bugs.SendDuplicateCertExtensions
+ cert.extraExtension = config.Bugs.SendExtensionOnCertificate
+ } else {
+ if config.Bugs.SendOCSPOnIntermediates != nil {
+ cert.ocspResponse = config.Bugs.SendOCSPOnIntermediates
+ }
+ if config.Bugs.SendSCTOnIntermediates != nil {
+ cert.sctList = config.Bugs.SendSCTOnIntermediates
+ }
+ }
+ certMsg.certificates = append(certMsg.certificates, cert)
+ }
}
certMsgBytes := certMsg.marshal()
hs.writeServerHash(certMsgBytes)
@@ -844,10 +845,11 @@
masterSecret := hs.finishedHash.extractKey(handshakeSecret, hs.finishedHash.zeroSecret())
clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
+ c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
// Switch to application data keys on write. In particular, any alerts
// from the client certificate are sent over these keys.
- c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
+ c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
// If we requested a client certificate, then the client must send a
// certificate message, even if it's empty.
@@ -873,7 +875,17 @@
}
}
- pub, err := hs.processCertsFromClient(certMsg.certificates)
+ var certs [][]byte
+ for _, cert := range certMsg.certificates {
+ certs = append(certs, cert.data)
+ // OCSP responses and SCT lists are not negotiated in
+ // client certificates.
+ if cert.ocspResponse != nil || cert.sctList != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: unexpected extensions in the client certificate")
+ }
+ }
+ pub, err := hs.processCertsFromClient(certs)
if err != nil {
return err
}
@@ -900,6 +912,27 @@
}
}
+ if encryptedExtensions.extensions.channelIDRequested {
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ channelIDMsg, ok := msg.(*channelIDMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return unexpectedMessageError(channelIDMsg, msg)
+ }
+ channelIDHash := crypto.SHA256.New()
+ channelIDHash.Write(hs.finishedHash.certificateVerifyInput(channelIDContextTLS13))
+ channelID, err := verifyChannelIDMessage(channelIDMsg, channelIDHash.Sum(nil))
+ if err != nil {
+ return err
+ }
+ c.channelID = channelID
+
+ hs.writeClientHash(channelIDMsg.marshal())
+ }
+
// Read the client Finished message.
msg, err := c.readHandshake()
if err != nil {
@@ -920,15 +953,14 @@
hs.writeClientHash(clientFinished.marshal())
// Switch to application data keys on read.
- c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
+ c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
c.cipherSuite = hs.suite
- c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
// TODO(davidben): Allow configuring the number of tickets sent for
// testing.
- if !c.config.SessionTicketsDisabled {
+ if !c.config.SessionTicketsDisabled && foundKEMode {
ticketCount := 2
for i := 0; i < ticketCount; i++ {
c.SendNewSessionTicket()
@@ -1113,7 +1145,7 @@
if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
serverExtensions.nextProtoNeg = true
serverExtensions.nextProtos = config.NextProtos
- serverExtensions.npnLast = config.Bugs.SwapNPNAndALPN
+ serverExtensions.npnAfterAlpn = config.Bugs.SwapNPNAndALPN
}
}
}
@@ -1126,10 +1158,8 @@
serverExtensions.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !disableEMS
}
- if c.vers < VersionTLS13 || config.Bugs.NegotiateChannelIDAtAllVersions {
- if hs.clientHello.channelIDSupported && config.RequestChannelID {
- serverExtensions.channelIDRequested = true
- }
+ if hs.clientHello.channelIDSupported && config.RequestChannelID {
+ serverExtensions.channelIDRequested = true
}
if hs.clientHello.srtpProtectionProfiles != nil {
@@ -1321,7 +1351,11 @@
if !isPSK {
certMsg := new(certificateMsg)
if !config.Bugs.EmptyCertificateList {
- certMsg.certificates = hs.cert.Certificate
+ for _, certData := range hs.cert.Certificate {
+ certMsg.certificates = append(certMsg.certificates, certificateEntry{
+ data: certData,
+ })
+ }
}
if !config.Bugs.UnauthenticatedECDH {
certMsgBytes := certMsg.marshal()
@@ -1409,7 +1443,9 @@
}
hs.writeClientHash(certMsg.marshal())
- certificates = certMsg.certificates
+ for _, cert := range certMsg.certificates {
+ certificates = append(certificates, cert.data)
+ }
} else if c.vers != VersionSSL30 {
// In TLS, the Certificate message is required. In SSL
// 3.0, the peer skips it when sending no certificates.
@@ -1568,20 +1604,13 @@
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(channelIDMsg, msg)
}
- x := new(big.Int).SetBytes(channelIDMsg.channelID[0:32])
- y := new(big.Int).SetBytes(channelIDMsg.channelID[32:64])
- r := new(big.Int).SetBytes(channelIDMsg.channelID[64:96])
- s := new(big.Int).SetBytes(channelIDMsg.channelID[96:128])
- if !elliptic.P256().IsOnCurve(x, y) {
- return errors.New("tls: invalid channel ID public key")
- }
- channelID := &ecdsa.PublicKey{elliptic.P256(), x, y}
var resumeHash []byte
if isResume {
resumeHash = hs.sessionState.handshakeHash
}
- if !ecdsa.Verify(channelID, hs.finishedHash.hashForChannelID(resumeHash), r, s) {
- return errors.New("tls: invalid channel ID signature")
+ channelID, err := verifyChannelIDMessage(channelIDMsg, hs.finishedHash.hashForChannelID(resumeHash))
+ if err != nil {
+ return err
}
c.channelID = channelID
@@ -1618,7 +1647,7 @@
cipherSuite: hs.suite.id,
masterSecret: hs.masterSecret,
certificates: hs.certsFromClient,
- handshakeHash: hs.finishedHash.server.Sum(nil),
+ handshakeHash: hs.finishedHash.Sum(),
}
if !hs.hello.extensions.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket {
@@ -1765,6 +1794,21 @@
return nil, nil
}
+func verifyChannelIDMessage(channelIDMsg *channelIDMsg, channelIDHash []byte) (*ecdsa.PublicKey, error) {
+ x := new(big.Int).SetBytes(channelIDMsg.channelID[0:32])
+ y := new(big.Int).SetBytes(channelIDMsg.channelID[32:64])
+ r := new(big.Int).SetBytes(channelIDMsg.channelID[64:96])
+ s := new(big.Int).SetBytes(channelIDMsg.channelID[96:128])
+ if !elliptic.P256().IsOnCurve(x, y) {
+ return nil, errors.New("tls: invalid channel ID public key")
+ }
+ channelID := &ecdsa.PublicKey{elliptic.P256(), x, y}
+ if !ecdsa.Verify(channelID, channelIDHash, r, s) {
+ return nil, errors.New("tls: invalid channel ID signature")
+ }
+ return channelID, nil
+}
+
func (hs *serverHandshakeState) writeServerHash(msg []byte) {
// writeServerHash is called before writeRecord.
hs.writeHash(msg, hs.c.sendHandshakeSeq)
@@ -1849,3 +1893,24 @@
func isGREASEValue(val uint16) bool {
return val&0x0f0f == 0x0a0a && val&0xff == val>>8
}
+
+func verifyPSKBinder(clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, transcript []byte) error {
+ binderLen := 2
+ for _, binder := range clientHello.pskBinders {
+ binderLen += 1 + len(binder)
+ }
+
+ truncatedHello := clientHello.marshal()
+ truncatedHello = truncatedHello[:len(truncatedHello)-binderLen]
+ pskCipherSuite := cipherSuiteFromID(sessionState.cipherSuite)
+ if pskCipherSuite == nil {
+ return errors.New("tls: Unknown cipher suite for PSK in session")
+ }
+
+ binder := computePSKBinder(sessionState.masterSecret, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello)
+ if !bytes.Equal(binder, binderToVerify) {
+ return errors.New("tls: PSK binder does not verify")
+ }
+
+ return nil
+}
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index ffa68e9..c311e99 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -230,10 +230,6 @@
// full buffer is required.
buffer []byte
- // TLS 1.3 has a resumption context which is carried over on PSK
- // resumption.
- resumptionContextHash []byte
-
version uint16
prf func(result, secret, label, seed []byte)
}
@@ -357,7 +353,7 @@
hash.Write(channelIDResumeLabel)
hash.Write(resumeHash)
}
- hash.Write(h.server.Sum(nil))
+ hash.Write(h.Sum())
return hash.Sum(nil)
}
@@ -374,13 +370,6 @@
return make([]byte, h.hash.Size())
}
-// setResumptionContext sets the TLS 1.3 resumption context.
-func (h *finishedHash) setResumptionContext(resumptionContext []byte) {
- hash := h.hash.New()
- hash.Write(resumptionContext)
- h.resumptionContextHash = hash.Sum(nil)
-}
-
// extractKey combines two secrets together with HKDF-Expand in the TLS 1.3 key
// derivation schedule.
func (h *finishedHash) extractKey(salt, ikm []byte) []byte {
@@ -412,12 +401,13 @@
// resumption context hash, as used in TLS 1.3.
func (h *finishedHash) appendContextHashes(b []byte) []byte {
b = h.client.Sum(b)
- b = append(b, h.resumptionContextHash...)
return b
}
// The following are labels for traffic secret derivation in TLS 1.3.
var (
+ externalPSKBinderLabel = []byte("external psk binder key")
+ resumptionPSKBinderLabel = []byte("resumption psk binder key")
earlyTrafficLabel = []byte("client early traffic secret")
clientHandshakeTrafficLabel = []byte("client handshake traffic secret")
serverHandshakeTrafficLabel = []byte("server handshake traffic secret")
@@ -431,10 +421,6 @@
// deriveSecret implements TLS 1.3's Derive-Secret function, as defined in
// section 7.1 of draft ietf-tls-tls13-16.
func (h *finishedHash) deriveSecret(secret, label []byte) []byte {
- if h.resumptionContextHash == nil {
- panic("Resumption context not set.")
- }
-
return hkdfExpandLabel(h.hash, secret, label, h.appendContextHashes(nil), h.hash.Size())
}
@@ -442,6 +428,7 @@
var (
clientCertificateVerifyContextTLS13 = []byte("TLS 1.3, client CertificateVerify")
serverCertificateVerifyContextTLS13 = []byte("TLS 1.3, server CertificateVerify")
+ channelIDContextTLS13 = []byte("TLS 1.3, Channel ID")
)
// certificateVerifyMessage returns the input to be signed for CertificateVerify
@@ -458,14 +445,6 @@
return b
}
-// The following are phase values for traffic key derivation in TLS 1.3.
-var (
- earlyHandshakePhase = []byte("early handshake key expansion")
- earlyApplicationPhase = []byte("early application data key expansion")
- handshakePhase = []byte("handshake key expansion")
- applicationPhase = []byte("application data key expansion")
-)
-
type trafficDirection int
const (
@@ -473,17 +452,16 @@
serverWrite
)
+var (
+ keyTLS13 = []byte("key")
+ ivTLS13 = []byte("iv")
+)
+
// deriveTrafficAEAD derives traffic keys and constructs an AEAD given a traffic
// secret.
-func deriveTrafficAEAD(version uint16, suite *cipherSuite, secret, phase []byte, side trafficDirection) interface{} {
- label := make([]byte, 0, len(phase)+2+16)
- label = append(label, phase...)
- label = append(label, []byte(", key")...)
- key := hkdfExpandLabel(suite.hash(), secret, label, nil, suite.keyLen)
-
- label = label[:len(label)-3] // Remove "key" from the end.
- label = append(label, []byte("iv")...)
- iv := hkdfExpandLabel(suite.hash(), secret, label, nil, suite.ivLen(version))
+func deriveTrafficAEAD(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) interface{} {
+ key := hkdfExpandLabel(suite.hash(), secret, keyTLS13, nil, suite.keyLen)
+ iv := hkdfExpandLabel(suite.hash(), secret, ivTLS13, nil, suite.ivLen(version))
return suite.aead(version, key, iv)
}
@@ -492,10 +470,11 @@
return hkdfExpandLabel(hash, secret, applicationTrafficLabel, nil, hash.Size())
}
-func deriveResumptionPSK(suite *cipherSuite, resumptionSecret []byte) []byte {
- return hkdfExpandLabel(suite.hash(), resumptionSecret, []byte("resumption psk"), nil, suite.hash().Size())
-}
-
-func deriveResumptionContext(suite *cipherSuite, resumptionSecret []byte) []byte {
- return hkdfExpandLabel(suite.hash(), resumptionSecret, []byte("resumption context"), nil, suite.hash().Size())
+func computePSKBinder(psk, label []byte, cipherSuite *cipherSuite, transcript, truncatedHello []byte) []byte {
+ finishedHash := newFinishedHash(VersionTLS13, cipherSuite)
+ earlySecret := finishedHash.extractKey(finishedHash.zeroSecret(), psk)
+ binderKey := finishedHash.deriveSecret(earlySecret, label)
+ finishedHash.Write(transcript)
+ finishedHash.Write(truncatedHello)
+ return finishedHash.clientSum(binderKey)
}
diff --git a/src/ssl/test/runner/rsa_chain_cert.pem b/src/ssl/test/runner/rsa_chain_cert.pem
new file mode 100644
index 0000000..8eb6e60
--- /dev/null
+++ b/src/ssl/test/runner/rsa_chain_cert.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD
+QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs
+aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8
+CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/
+kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3
+tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c
+IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1
+z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V
+iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI
+KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw
+rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7
+AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w
+giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW
+ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK
+MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC
+1ngWZ7Ih
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS
+b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE
+AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D
+GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ
+3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l
+HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF
+Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7
+6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM
+cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ
+BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1
+QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye
+NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b
+WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv
+XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=
+-----END CERTIFICATE-----
diff --git a/src/ssl/test/runner/rsa_chain_key.pem b/src/ssl/test/runner/rsa_chain_key.pem
new file mode 100644
index 0000000..d94d704
--- /dev/null
+++ b/src/ssl/test/runner/rsa_chain_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC
+afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M
+/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P
+Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l
+P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50
+Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ
+i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0
+YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk
+wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe
+iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ
+HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9
+042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn
+1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f
+CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE
+NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f
+AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z
+YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt
+Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA
+UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP
+2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS
+fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy
+xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN
+FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ
+2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk
+buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi
+SxpiPQ8d/hmSGwn4ksrWUsJD
+-----END PRIVATE KEY-----
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 367fef1..683f07c 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -62,6 +62,7 @@
looseErrors = flag.Bool("loose-errors", false, "If true, allow shims to report an untranslated error code.")
shimConfigFile = flag.String("shim-config", "", "A config file to use to configure the tests for this shim.")
includeDisabled = flag.Bool("include-disabled", false, "If true, also runs disabled tests.")
+ repeatUntilFailure = flag.Bool("repeat-until-failure", false, "If true, the first selected test will be run repeatedly until failure.")
)
// ShimConfigurations is used with the “json” package and represents a shim
@@ -87,6 +88,7 @@
const (
testCertRSA testCert = iota
testCertRSA1024
+ testCertRSAChain
testCertECDSAP256
testCertECDSAP384
testCertECDSAP521
@@ -95,6 +97,7 @@
const (
rsaCertificateFile = "cert.pem"
rsa1024CertificateFile = "rsa_1024_cert.pem"
+ rsaChainCertificateFile = "rsa_chain_cert.pem"
ecdsaP256CertificateFile = "ecdsa_p256_cert.pem"
ecdsaP384CertificateFile = "ecdsa_p384_cert.pem"
ecdsaP521CertificateFile = "ecdsa_p521_cert.pem"
@@ -103,6 +106,7 @@
const (
rsaKeyFile = "key.pem"
rsa1024KeyFile = "rsa_1024_key.pem"
+ rsaChainKeyFile = "rsa_chain_key.pem"
ecdsaP256KeyFile = "ecdsa_p256_key.pem"
ecdsaP384KeyFile = "ecdsa_p384_key.pem"
ecdsaP521KeyFile = "ecdsa_p521_key.pem"
@@ -112,6 +116,7 @@
var (
rsaCertificate Certificate
rsa1024Certificate Certificate
+ rsaChainCertificate Certificate
ecdsaP256Certificate Certificate
ecdsaP384Certificate Certificate
ecdsaP521Certificate Certificate
@@ -135,6 +140,12 @@
cert: &rsa1024Certificate,
},
{
+ id: testCertRSAChain,
+ certFile: rsaChainCertificateFile,
+ keyFile: rsaChainKeyFile,
+ cert: &rsaChainCertificate,
+ },
+ {
id: testCertECDSAP256,
certFile: ecdsaP256CertificateFile,
keyFile: ecdsaP256KeyFile,
@@ -158,7 +169,10 @@
var channelIDBytes []byte
var testOCSPResponse = []byte{1, 2, 3, 4}
-var testSCTList = []byte{5, 6, 7, 8}
+var testSCTList = []byte{0, 6, 0, 4, 5, 6, 7, 8}
+
+var testOCSPExtension = append([]byte{byte(extensionStatusRequest) >> 8, byte(extensionStatusRequest), 0, 8, statusTypeOCSP, 0, 0, 4}, testOCSPResponse...)
+var testSCTExtension = append([]byte{byte(extensionSignedCertificateTimestamp) >> 8, byte(extensionSignedCertificateTimestamp), 0, byte(len(testSCTList))}, testSCTList...)
func initCertificates() {
for i := range testCerts {
@@ -364,6 +378,9 @@
// expectMessageDropped, if true, means the test message is expected to
// be dropped by the client rather than echoed back.
expectMessageDropped bool
+ // expectPeerCertificate, if not nil, is the certificate chain the peer
+ // is expected to send.
+ expectPeerCertificate *Certificate
}
var testCases []testCase
@@ -581,6 +598,17 @@
return fmt.Errorf("expected peer to use curve %04x, but got %04x", expected, connState.CurveID)
}
+ if test.expectPeerCertificate != nil {
+ if len(connState.PeerCertificates) != len(test.expectPeerCertificate.Certificate) {
+ return fmt.Errorf("expected peer to send %d certificates, but got %d", len(connState.PeerCertificates), len(test.expectPeerCertificate.Certificate))
+ }
+ for i, cert := range connState.PeerCertificates {
+ if !bytes.Equal(cert.Raw, test.expectPeerCertificate.Certificate[i]) {
+ return fmt.Errorf("peer certificate %d did not match", i+1)
+ }
+ }
+ }
+
if test.exportKeyingMaterial > 0 {
actual := make([]byte, test.exportKeyingMaterial)
if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -1682,6 +1710,50 @@
expectedLocalError: "remote error: handshake failure",
},
{
+ name: "FailCertCallback-Client-TLS12",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ ClientAuth: RequestClientCert,
+ },
+ flags: []string{"-fail-cert-callback"},
+ shouldFail: true,
+ expectedError: ":CERT_CB_ERROR:",
+ expectedLocalError: "remote error: internal error",
+ },
+ {
+ testType: serverTest,
+ name: "FailCertCallback-Server-TLS12",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ },
+ flags: []string{"-fail-cert-callback"},
+ shouldFail: true,
+ expectedError: ":CERT_CB_ERROR:",
+ expectedLocalError: "remote error: internal error",
+ },
+ {
+ name: "FailCertCallback-Client-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ ClientAuth: RequestClientCert,
+ },
+ flags: []string{"-fail-cert-callback"},
+ shouldFail: true,
+ expectedError: ":CERT_CB_ERROR:",
+ expectedLocalError: "remote error: internal error",
+ },
+ {
+ testType: serverTest,
+ name: "FailCertCallback-Server-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ flags: []string{"-fail-cert-callback"},
+ shouldFail: true,
+ expectedError: ":CERT_CB_ERROR:",
+ expectedLocalError: "remote error: internal error",
+ },
+ {
protocol: dtls,
name: "FragmentMessageTypeMismatch-DTLS",
config: Config{
@@ -2299,10 +2371,13 @@
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
+ // TLS 1.3 servers are expected to
+ // always enable GREASE. TLS 1.3 is new,
+ // so there is no existing ecosystem to
+ // worry about.
ExpectGREASE: true,
},
},
- flags: []string{"-enable-grease"},
},
}
testCases = append(testCases, basicTests...)
@@ -2485,7 +2560,8 @@
if !shouldClientFail {
// Ensure the maximum record size is accepted.
testCases = append(testCases, testCase{
- name: prefix + ver.name + "-" + suite.name + "-LargeRecord",
+ protocol: protocol,
+ name: prefix + ver.name + "-" + suite.name + "-LargeRecord",
config: Config{
MinVersion: ver.version,
MaxVersion: ver.version,
@@ -2497,6 +2573,33 @@
flags: flags,
messageLen: maxPlaintext,
})
+
+ // Test bad records for all ciphers. Bad records are fatal in TLS
+ // and ignored in DTLS.
+ var shouldFail bool
+ var expectedError string
+ if protocol == tls {
+ shouldFail = true
+ expectedError = ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"
+ }
+
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: prefix + ver.name + "-" + suite.name + "-BadRecord",
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ CipherSuites: []uint16{suite.id},
+ Certificates: []Certificate{cert},
+ PreSharedKey: []byte(psk),
+ PreSharedKeyIdentity: pskIdentity,
+ },
+ flags: flags,
+ damageFirstWrite: true,
+ messageLen: maxPlaintext,
+ shouldFail: shouldFail,
+ expectedError: expectedError,
+ })
}
}
}
@@ -3940,33 +4043,54 @@
},
})
- // Client sends a Channel ID.
- tests = append(tests, testCase{
- name: "ChannelID-Client",
- config: Config{
- MaxVersion: VersionTLS12,
- RequestChannelID: true,
- },
- flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
- resumeSession: true,
- expectChannelID: true,
- })
+ // Test Channel ID
+ for _, ver := range tlsVersions {
+ if ver.version < VersionTLS10 {
+ continue
+ }
+ // Client sends a Channel ID.
+ tests = append(tests, testCase{
+ name: "ChannelID-Client-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ RequestChannelID: true,
+ },
+ flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
+ resumeSession: true,
+ expectChannelID: true,
+ })
- // Server accepts a Channel ID.
- tests = append(tests, testCase{
- testType: serverTest,
- name: "ChannelID-Server",
- config: Config{
- MaxVersion: VersionTLS12,
- ChannelID: channelIDKey,
- },
- flags: []string{
- "-expect-channel-id",
- base64.StdEncoding.EncodeToString(channelIDBytes),
- },
- resumeSession: true,
- expectChannelID: true,
- })
+ // Server accepts a Channel ID.
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "ChannelID-Server-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ ChannelID: channelIDKey,
+ },
+ flags: []string{
+ "-expect-channel-id",
+ base64.StdEncoding.EncodeToString(channelIDBytes),
+ },
+ resumeSession: true,
+ expectChannelID: true,
+ })
+
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "InvalidChannelIDSignature-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ ChannelID: channelIDKey,
+ Bugs: ProtocolBugs{
+ InvalidChannelIDSignature: true,
+ },
+ },
+ flags: []string{"-enable-channel-id"},
+ shouldFail: true,
+ expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:",
+ })
+ }
// Channel ID and NPN at the same time, to ensure their relative
// ordering is correct.
@@ -4333,6 +4457,19 @@
expectedVersion: VersionTLS12,
})
+ // Test that the maximum version is selected regardless of the
+ // client-sent order.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "IgnoreClientVersionOrder",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSupportedVersions: []uint16{VersionTLS12, tls13DraftVersion},
+ },
+ },
+ expectedVersion: VersionTLS13,
+ })
+
// Test for version tolerance.
testCases = append(testCases, testCase{
testType: serverTest,
@@ -4715,6 +4852,8 @@
config: Config{
MaxVersion: ver.version,
NextProtos: []string{"foo", "bar", "baz"},
+ // Prior to TLS 1.3, exercise the asynchronous session callback.
+ SessionTicketsDisabled: ver.version < VersionTLS13,
},
flags: []string{
"-expect-advertised-alpn", "\x03foo\x03bar\x03baz",
@@ -4859,7 +4998,10 @@
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
- CorruptTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ in[len(in)-1] ^= 1
+ return in, nil
+ },
},
},
resumeSession: true,
@@ -4897,7 +5039,10 @@
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
- CorruptTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ in[len(in)-1] ^= 1
+ return in, nil
+ },
},
},
resumeSession: true,
@@ -5024,6 +5169,10 @@
resumeSession: true,
})
+ var differentSCTList []byte
+ differentSCTList = append(differentSCTList, testSCTList...)
+ differentSCTList[len(differentSCTList)-1] ^= 1
+
// The SCT extension did not specify that it must only be sent on resumption as it
// should have, so test that we tolerate but ignore it.
testCases = append(testCases, testCase{
@@ -5031,7 +5180,7 @@
config: Config{
MaxVersion: ver.version,
Bugs: ProtocolBugs{
- SendSCTListOnResume: []byte("bogus"),
+ SendSCTListOnResume: differentSCTList,
},
},
flags: []string{
@@ -5055,6 +5204,61 @@
expectedSCTList: testSCTList,
resumeSession: true,
})
+
+ emptySCTListCert := *testCerts[0].cert
+ emptySCTListCert.SignedCertificateTimestampList = []byte{0, 0}
+
+ // Test empty SCT list.
+ testCases = append(testCases, testCase{
+ name: "SignedCertificateTimestampListEmpty-Client-" + ver.name,
+ testType: clientTest,
+ config: Config{
+ MaxVersion: ver.version,
+ Certificates: []Certificate{emptySCTListCert},
+ },
+ flags: []string{
+ "-enable-signed-cert-timestamps",
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+
+ emptySCTCert := *testCerts[0].cert
+ emptySCTCert.SignedCertificateTimestampList = []byte{0, 6, 0, 2, 1, 2, 0, 0}
+
+ // Test empty SCT in non-empty list.
+ testCases = append(testCases, testCase{
+ name: "SignedCertificateTimestampListEmptySCT-Client-" + ver.name,
+ testType: clientTest,
+ config: Config{
+ MaxVersion: ver.version,
+ Certificates: []Certificate{emptySCTCert},
+ },
+ flags: []string{
+ "-enable-signed-cert-timestamps",
+ },
+ shouldFail: true,
+ expectedError: ":ERROR_PARSING_EXTENSION:",
+ })
+
+ // Test that certificate-related extensions are not sent unsolicited.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "UnsolicitedCertificateExtensions-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ NoOCSPStapling: true,
+ NoSignedCertificateTimestamps: true,
+ },
+ },
+ flags: []string{
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
+ })
}
testCases = append(testCases, testCase{
@@ -5171,19 +5375,6 @@
expectedError: ":ERROR_PARSING_EXTENSION:",
})
testCases = append(testCases, testCase{
- name: "ChannelID-Forbidden-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- RequestChannelID: true,
- Bugs: ProtocolBugs{
- NegotiateChannelIDAtAllVersions: true,
- },
- },
- flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
- shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
- })
- testCases = append(testCases, testCase{
name: "Ticket-Forbidden-TLS13",
config: Config{
MaxVersion: VersionTLS12,
@@ -5205,15 +5396,6 @@
// implicit in every test.)
testCases = append(testCases, testCase{
testType: serverTest,
- name: "ChannelID-Declined-TLS13",
- config: Config{
- MaxVersion: VersionTLS13,
- ChannelID: channelIDKey,
- },
- flags: []string{"-enable-channel-id"},
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
name: "NPN-Declined-TLS13",
config: Config{
MaxVersion: VersionTLS13,
@@ -5222,22 +5404,6 @@
flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"},
})
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "InvalidChannelIDSignature",
- config: Config{
- MaxVersion: VersionTLS12,
- ChannelID: channelIDKey,
- Bugs: ProtocolBugs{
- InvalidChannelIDSignature: true,
- },
- },
- flags: []string{"-enable-channel-id"},
- shouldFail: true,
- expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:",
- expectedLocalError: "remote error: error decrypting message",
- })
-
// OpenSSL sends the status_request extension on resumption in TLS 1.2. Test that this is
// tolerated.
testCases = append(testCases, testCase{
@@ -5256,23 +5422,154 @@
resumeSession: true,
})
- // Beginning TLS 1.3, enforce this does not happen.
testCases = append(testCases, testCase{
- name: "SendOCSPResponseOnResume-TLS13",
+ name: "SendUnsolicitedOCSPOnCertificate-TLS13",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
- SendOCSPResponseOnResume: []byte("bogus"),
+ SendExtensionOnCertificate: testOCSPExtension,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SendUnsolicitedSCTOnCertificate-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendExtensionOnCertificate: testSCTExtension,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ // Test that extensions on client certificates are never accepted.
+ testCases = append(testCases, testCase{
+ name: "SendExtensionOnClientCertificate-TLS13",
+ testType: serverTest,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Certificates: []Certificate{rsaCertificate},
+ Bugs: ProtocolBugs{
+ SendExtensionOnCertificate: testOCSPExtension,
+ },
+ },
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-require-any-client-certificate",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SendUnknownExtensionOnCertificate-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendExtensionOnCertificate: []byte{0x00, 0x7f, 0, 0},
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_EXTENSION:",
+ })
+
+ var differentSCTList []byte
+ differentSCTList = append(differentSCTList, testSCTList...)
+ differentSCTList[len(differentSCTList)-1] ^= 1
+
+ // Test that extensions on intermediates are allowed but ignored.
+ testCases = append(testCases, testCase{
+ name: "IgnoreExtensionsOnIntermediates-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Certificates: []Certificate{rsaChainCertificate},
+ Bugs: ProtocolBugs{
+ // Send different values on the intermediate. This tests
+ // the intermediate's extensions do not override the
+ // leaf's.
+ SendOCSPOnIntermediates: []byte{1, 3, 3, 7},
+ SendSCTOnIntermediates: differentSCTList,
},
},
flags: []string{
"-enable-ocsp-stapling",
"-expect-ocsp-response",
base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-enable-signed-cert-timestamps",
+ "-expect-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
+ resumeSession: true,
+ })
+
+ // Test that extensions are not sent on intermediates when configured
+ // only for a leaf.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SendNoExtensionsOnIntermediate-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectNoExtensionsOnIntermediate: true,
+ },
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
+ })
+
+ // Test that extensions are not sent on client certificates.
+ testCases = append(testCases, testCase{
+ name: "SendNoClientCertificateExtensions-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ ClientAuth: RequireAnyClientCert,
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-ocsp-response",
+ base64.StdEncoding.EncodeToString(testOCSPResponse),
+ "-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SendDuplicateExtensionsOnCerts-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendDuplicateCertExtensions: true,
+ },
+ },
+ flags: []string{
+ "-enable-ocsp-stapling",
+ "-enable-signed-cert-timestamps",
},
resumeSession: true,
shouldFail: true,
- expectedError: ":ERROR_PARSING_EXTENSION:",
+ expectedError: ":DUPLICATE_EXTENSION:",
+ })
+
+ testCases = append(testCases, testCase{
+ name: "SignedCertificateTimestampListInvalid-Server",
+ testType: serverTest,
+ flags: []string{
+ "-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString([]byte{0, 0}),
+ },
+ shouldFail: true,
+ expectedError: ":INVALID_SCT_LIST:",
})
}
@@ -5381,6 +5678,223 @@
}
}
+ // Make sure shim ticket mutations are functional.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "ShimTicketRewritable",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ FilterTicket: func(in []byte) ([]byte, error) {
+ in, err := SetShimTicketVersion(in, VersionTLS12)
+ if err != nil {
+ return nil, err
+ }
+ return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ })
+
+ // Resumptions are declined if the version does not match.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-DeclineCrossVersion",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketVersion(in, VersionTLS13)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ expectResumeRejected: true,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-DeclineCrossVersion-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketVersion(in, VersionTLS12)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ expectResumeRejected: true,
+ })
+
+ // Resumptions are declined if the cipher is invalid or disabled.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-DeclineBadCipher",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ expectResumeRejected: true,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-DeclineBadCipher-2",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
+ },
+ },
+ },
+ flags: []string{
+ "-cipher", "AES128",
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ expectResumeRejected: true,
+ })
+
+ // Sessions are not resumed if they do not use the preferred cipher.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-CipherNotPreferred",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ ExpectNewTicket: true,
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ shouldFail: false,
+ expectResumeRejected: true,
+ })
+
+ // TLS 1.3 allows sessions to be resumed at a different cipher if their
+ // PRF hashes match, but BoringSSL will always decline such resumptions.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-CipherNotPreferred-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256},
+ Bugs: ProtocolBugs{
+ FilterTicket: func(in []byte) ([]byte, error) {
+ // If the client (runner) offers ChaCha20-Poly1305 first, the
+ // server (shim) always prefers it. Switch it to AES-GCM.
+ return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ shouldFail: false,
+ expectResumeRejected: true,
+ })
+
+ // Sessions may not be resumed if they contain another version's cipher.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-DeclineBadCipher-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ FilterTicket: func(in []byte) ([]byte, error) {
+ return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
+ },
+ },
+ },
+ flags: []string{
+ "-ticket-key",
+ base64.StdEncoding.EncodeToString(TestShimTicketKey),
+ },
+ expectResumeRejected: true,
+ })
+
+ // If the client does not offer the cipher from the session, decline to
+ // resume. Clients are forbidden from doing this, but BoringSSL selects
+ // the cipher first, so we only decline.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-UnofferedCipher",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS12,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+ Bugs: ProtocolBugs{
+ SendCipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ },
+ },
+ expectResumeRejected: true,
+ })
+
+ // In TLS 1.3, clients may advertise a cipher list which does not
+ // include the selected cipher. Test that we tolerate this. Servers may
+ // resume at another cipher if the PRF matches, but BoringSSL will
+ // always decline.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-UnofferedCipher-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256},
+ Bugs: ProtocolBugs{
+ SendCipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
+ },
+ },
+ expectResumeRejected: true,
+ })
+
+ // Sessions may not be resumed at a different cipher.
testCases = append(testCases, testCase{
name: "Resume-Client-CipherMismatch",
resumeSession: true,
@@ -5399,6 +5913,8 @@
expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
})
+ // Session resumption in TLS 1.3 may change the cipher suite if the PRF
+ // matches.
testCases = append(testCases, testCase{
name: "Resume-Client-CipherMismatch-TLS13",
resumeSession: true,
@@ -5408,13 +5924,87 @@
},
resumeConfig: &Config{
MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256},
+ },
+ })
+
+ // Session resumption in TLS 1.3 is forbidden if the PRF does not match.
+ testCases = append(testCases, testCase{
+ name: "Resume-Client-PRFMismatch-TLS13",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
CipherSuites: []uint16{TLS_AES_128_GCM_SHA256},
Bugs: ProtocolBugs{
SendCipherSuite: TLS_AES_256_GCM_SHA384,
},
},
shouldFail: true,
- expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
+ expectedError: ":OLD_SESSION_PRF_HASH_MISMATCH:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-BinderWrongLength",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendShortPSKBinder: true,
+ },
+ },
+ shouldFail: true,
+ expectedLocalError: "remote error: error decrypting message",
+ expectedError: ":DIGEST_CHECK_FAILED:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-NoPSKBinder",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendNoPSKBinder: true,
+ },
+ },
+ shouldFail: true,
+ expectedLocalError: "remote error: error decoding message",
+ expectedError: ":DECODE_ERROR:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-InvalidPSKBinder",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendInvalidPSKBinder: true,
+ },
+ },
+ shouldFail: true,
+ expectedLocalError: "remote error: error decrypting message",
+ expectedError: ":DIGEST_CHECK_FAILED:",
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "Resume-Server-PSKBinderFirstExtension",
+ resumeSession: true,
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ PSKBinderFirst: true,
+ },
+ },
+ shouldFail: true,
+ expectedLocalError: "remote error: illegal parameter",
+ expectedError: ":PRE_SHARED_KEY_MUST_BE_LAST:",
})
}
@@ -5579,6 +6169,8 @@
CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
Bugs: ProtocolBugs{
NegotiateVersionOnRenego: VersionTLS11,
+ // Avoid failing early at the record layer.
+ SendRecordVersion: VersionTLS12,
},
},
renegotiate: 1,
@@ -6874,6 +7466,7 @@
useExportContext: true,
})
}
+
testCases = append(testCases, testCase{
name: "ExportKeyingMaterial-SSL3",
config: Config{
@@ -6886,6 +7479,47 @@
shouldFail: true,
expectedError: "failed to export keying material",
})
+
+ // Exporters work during a False Start.
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-FalseStart",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ NextProtos: []string{"foo"},
+ Bugs: ProtocolBugs{
+ ExpectFalseStart: true,
+ },
+ },
+ flags: []string{
+ "-false-start",
+ "-advertise-alpn", "\x03foo",
+ },
+ shimWritesFirst: true,
+ exportKeyingMaterial: 1024,
+ exportLabel: "label",
+ exportContext: "context",
+ useExportContext: true,
+ })
+
+ // Exporters do not work in the middle of a renegotiation. Test this by
+ // triggering the exporter after every SSL_read call and configuring the
+ // shim to run asynchronously.
+ testCases = append(testCases, testCase{
+ name: "ExportKeyingMaterial-Renegotiate",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ },
+ renegotiate: 1,
+ flags: []string{
+ "-async",
+ "-use-exporter-between-reads",
+ "-renegotiate-freely",
+ "-expect-total-renegotiations", "1",
+ },
+ shouldFail: true,
+ expectedError: "failed to export keying material",
+ })
}
func addTLSUniqueTests() {
@@ -7636,19 +8270,34 @@
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a},
- SendPSKAuthModes: []byte{0x1a, pskAuthMode, 0x2a},
},
},
resumeSession: true,
expectedResumeVersion: VersionTLS13,
})
- // Test that the server declines sessions with no matching key exchange mode.
+ // Test that the server does not send session tickets with no matching key exchange mode.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-ExpectNoSessionTicketOnBadKEMode-Server",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendPSKKeyExchangeModes: []byte{0x1a},
+ ExpectNoNewSessionTicket: true,
+ },
+ },
+ })
+
+ // Test that the server does not accept a session with no matching key exchange mode.
testCases = append(testCases, testCase{
testType: serverTest,
name: "TLS13-SendBadKEModeSessionTicket-Server",
config: Config{
MaxVersion: VersionTLS13,
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
SendPSKKeyExchangeModes: []byte{0x1a},
},
@@ -7657,60 +8306,37 @@
expectResumeRejected: true,
})
- // Test that the server declines sessions with no matching auth mode.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "TLS13-SendBadAuthModeSessionTicket-Server",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendPSKAuthModes: []byte{0x1a},
- },
- },
- resumeSession: true,
- expectResumeRejected: true,
- })
-
- // Test that the client ignores unknown PSK modes.
+ // Test that the client ticket age is sent correctly.
testCases = append(testCases, testCase{
testType: clientTest,
- name: "TLS13-SendUnknownModeSessionTicket-Client",
+ name: "TLS13-TestValidTicketAge-Client",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
- SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a},
- SendPSKAuthModes: []byte{0x1a, pskAuthMode, 0x2a},
+ ExpectTicketAge: 10 * time.Second,
},
},
- resumeSession: true,
- expectedResumeVersion: VersionTLS13,
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "10",
+ },
})
- // Test that the client ignores tickets with no matching key exchange mode.
+ // Test that the client ticket age is enforced.
testCases = append(testCases, testCase{
testType: clientTest,
- name: "TLS13-SendBadKEModeSessionTicket-Client",
+ name: "TLS13-TestBadTicketAge-Client",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
- SendPSKKeyExchangeModes: []byte{0x1a},
+ ExpectTicketAge: 1000 * time.Second,
},
},
- flags: []string{"-expect-no-session"},
+ resumeSession: true,
+ shouldFail: true,
+ expectedLocalError: "tls: invalid ticket age",
})
- // Test that the client ignores tickets with no matching auth mode.
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "TLS13-SendBadAuthModeSessionTicket-Client",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- SendPSKAuthModes: []byte{0x1a},
- },
- },
- flags: []string{"-expect-no-session"},
- })
}
func addChangeCipherSpecTests() {
@@ -8369,33 +8995,6 @@
testCases = append(testCases, testCase{
testType: clientTest,
- name: "OmitServerHelloSignatureAlgorithms",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- OmitServerHelloSignatureAlgorithms: true,
- },
- },
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "IncludeServerHelloSignatureAlgorithms",
- config: Config{
- MaxVersion: VersionTLS13,
- Bugs: ProtocolBugs{
- IncludeServerHelloSignatureAlgorithms: true,
- },
- },
- resumeSession: true,
- shouldFail: true,
- expectedError: ":UNEXPECTED_EXTENSION:",
- })
-
- testCases = append(testCases, testCase{
- testType: clientTest,
name: "MissingKeyShare-Client",
config: Config{
MaxVersion: VersionTLS13,
@@ -8759,6 +9358,18 @@
expectedError: ":PSK_IDENTITY_NOT_FOUND:",
})
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-ExtraPSKIdentity",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExtraPSKIdentity: true,
+ },
+ },
+ resumeSession: true,
+ })
+
// Test that unknown NewSessionTicket extensions are tolerated.
testCases = append(testCases, testCase{
name: "TLS13-CustomTicketExtension",
@@ -8771,6 +9382,58 @@
})
}
+func addTLS13CipherPreferenceTests() {
+ // Test that client preference is honored if the shim has AES hardware
+ // and ChaCha20-Poly1305 is preferred otherwise.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-Server-ChaCha20-AES",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ },
+ },
+ flags: []string{
+ "-expect-cipher-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-Server-AES-ChaCha20",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
+ },
+ flags: []string{
+ "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)),
+ "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ },
+ })
+
+ // Test that the client orders ChaCha20-Poly1305 and AES-GCM based on
+ // whether it has AES hardware.
+ testCases = append(testCases, testCase{
+ name: "TLS13-CipherPreference-Client",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ // Use the client cipher order. (This is the default but
+ // is listed to be explicit.)
+ PreferServerCipherSuites: false,
+ },
+ flags: []string{
+ "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)),
+ "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ },
+ })
+}
+
func addPeekTests() {
// Test SSL_peek works, including on empty records.
testCases = append(testCases, testCase{
@@ -8841,16 +9504,177 @@
})
}
+func addRecordVersionTests() {
+ for _, ver := range tlsVersions {
+ // Test that the record version is enforced.
+ testCases = append(testCases, testCase{
+ name: "CheckRecordVersion-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ SendRecordVersion: 0x03ff,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_VERSION_NUMBER:",
+ })
+
+ // Test that the ClientHello may use any record version, for
+ // compatibility reasons.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "LooseInitialRecordVersion-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ SendInitialRecordVersion: 0x03ff,
+ },
+ },
+ })
+
+ // Test that garbage ClientHello record versions are rejected.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "GarbageInitialRecordVersion-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Bugs: ProtocolBugs{
+ SendInitialRecordVersion: 0xffff,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_VERSION_NUMBER:",
+ })
+ }
+}
+
+func addCertificateTests() {
+ // Test that a certificate chain with intermediate may be sent and
+ // received as both client and server.
+ for _, ver := range tlsVersions {
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "SendReceiveIntermediate-Client-" + ver.name,
+ config: Config{
+ Certificates: []Certificate{rsaChainCertificate},
+ ClientAuth: RequireAnyClientCert,
+ },
+ expectPeerCertificate: &rsaChainCertificate,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ "-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "SendReceiveIntermediate-Server-" + ver.name,
+ config: Config{
+ Certificates: []Certificate{rsaChainCertificate},
+ },
+ expectPeerCertificate: &rsaChainCertificate,
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+ "-require-any-client-certificate",
+ "-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+ },
+ })
+ }
+}
+
+func addRetainOnlySHA256ClientCertTests() {
+ for _, ver := range tlsVersions {
+ // Test that enabling
+ // SSL_CTX_set_retain_only_sha256_of_client_certs without
+ // actually requesting a client certificate is a no-op.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "RetainOnlySHA256-NoCert-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-retain-only-sha256-client-cert-initial",
+ "-retain-only-sha256-client-cert-resume",
+ },
+ resumeSession: true,
+ })
+
+ // Test that when retaining only a SHA-256 certificate is
+ // enabled, the hash appears as expected.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "RetainOnlySHA256-Cert-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ flags: []string{
+ "-verify-peer",
+ "-retain-only-sha256-client-cert-initial",
+ "-retain-only-sha256-client-cert-resume",
+ "-expect-sha256-client-cert-initial",
+ "-expect-sha256-client-cert-resume",
+ },
+ resumeSession: true,
+ })
+
+ // Test that when the config changes from on to off, a
+ // resumption is rejected because the server now wants the full
+ // certificate chain.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "RetainOnlySHA256-OnOff-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ flags: []string{
+ "-verify-peer",
+ "-retain-only-sha256-client-cert-initial",
+ "-expect-sha256-client-cert-initial",
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+
+ // Test that when the config changes from off to on, a
+ // resumption is rejected because the server now wants just the
+ // hash.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "RetainOnlySHA256-OffOn-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ flags: []string{
+ "-verify-peer",
+ "-retain-only-sha256-client-cert-resume",
+ "-expect-sha256-client-cert-resume",
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ })
+ }
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
for test := range c {
var err error
- if *mallocTest < 0 {
- statusChan <- statusMsg{test: test, started: true}
- err = runTest(test, shimPath, -1)
- } else {
+ if *mallocTest >= 0 {
for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ {
statusChan <- statusMsg{test: test, started: true}
if err = runTest(test, shimPath, mallocNumToFail); err != errMoreMallocs {
@@ -8860,6 +9684,14 @@
break
}
}
+ } else if *repeatUntilFailure {
+ for err == nil {
+ statusChan <- statusMsg{test: test, started: true}
+ err = runTest(test, shimPath, -1)
+ }
+ } else {
+ statusChan <- statusMsg{test: test, started: true}
+ err = runTest(test, shimPath, -1)
}
statusChan <- statusMsg{test: test, err: err}
}
@@ -8958,7 +9790,11 @@
addWrongMessageTypeTests()
addTrailingMessageDataTests()
addTLS13HandshakeTests()
+ addTLS13CipherPreferenceTests()
addPeekTests()
+ addRecordVersionTests()
+ addCertificateTests()
+ addRetainOnlySHA256ClientCertTests()
var wg sync.WaitGroup
@@ -9016,6 +9852,11 @@
if matched {
foundTest = true
testChan <- &testCases[i]
+
+ // Only run one test if repeating until failure.
+ if *repeatUntilFailure {
+ break
+ }
}
}
diff --git a/src/ssl/test/runner/shim_ticket.go b/src/ssl/test/runner/shim_ticket.go
new file mode 100644
index 0000000..9e57d48
--- /dev/null
+++ b/src/ssl/test/runner/shim_ticket.go
@@ -0,0 +1,249 @@
+// 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.
+
+package runner
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/asn1"
+ "errors"
+)
+
+// TestShimTicketKey is the testing key assumed for the shim.
+var TestShimTicketKey = make([]byte, 48)
+
+func DecryptShimTicket(in []byte) ([]byte, error) {
+ name := TestShimTicketKey[:16]
+ macKey := TestShimTicketKey[16:32]
+ encKey := TestShimTicketKey[32:48]
+
+ h := hmac.New(sha256.New, macKey)
+
+ block, err := aes.NewCipher(encKey)
+ if err != nil {
+ panic(err)
+ }
+
+ if len(in) < len(name)+block.BlockSize()+1+h.Size() {
+ return nil, errors.New("tls: shim ticket too short")
+ }
+
+ // Check the key name.
+ if !bytes.Equal(name, in[:len(name)]) {
+ return nil, errors.New("tls: shim ticket name mismatch")
+ }
+
+ // Check the MAC at the end of the ticket.
+ mac := in[len(in)-h.Size():]
+ in = in[:len(in)-h.Size()]
+ h.Write(in)
+ if !hmac.Equal(mac, h.Sum(nil)) {
+ return nil, errors.New("tls: shim ticket MAC mismatch")
+ }
+
+ // The MAC covers the key name, but the encryption does not.
+ in = in[len(name):]
+
+ // Decrypt in-place.
+ iv := in[:block.BlockSize()]
+ in = in[block.BlockSize():]
+ if l := len(in); l == 0 || l%block.BlockSize() != 0 {
+ return nil, errors.New("tls: ticket ciphertext not a multiple of the block size")
+ }
+ out := make([]byte, len(in))
+ cbc := cipher.NewCBCDecrypter(block, iv)
+ cbc.CryptBlocks(out, in)
+
+ // Remove the padding.
+ pad := int(out[len(out)-1])
+ if pad == 0 || pad > block.BlockSize() || pad > len(in) {
+ return nil, errors.New("tls: bad shim ticket CBC pad")
+ }
+
+ for i := 0; i < pad; i++ {
+ if out[len(out)-1-i] != byte(pad) {
+ return nil, errors.New("tls: bad shim ticket CBC pad")
+ }
+ }
+
+ return out[:len(out)-pad], nil
+}
+
+func EncryptShimTicket(in []byte) []byte {
+ name := TestShimTicketKey[:16]
+ macKey := TestShimTicketKey[16:32]
+ encKey := TestShimTicketKey[32:48]
+
+ h := hmac.New(sha256.New, macKey)
+
+ block, err := aes.NewCipher(encKey)
+ if err != nil {
+ panic(err)
+ }
+
+ // Use the zero IV for rewritten tickets.
+ iv := make([]byte, block.BlockSize())
+ cbc := cipher.NewCBCEncrypter(block, iv)
+ pad := block.BlockSize() - (len(in) % block.BlockSize())
+
+ out := make([]byte, 0, len(name)+len(iv)+len(in)+pad+h.Size())
+ out = append(out, name...)
+ out = append(out, iv...)
+ out = append(out, in...)
+ for i := 0; i < pad; i++ {
+ out = append(out, byte(pad))
+ }
+
+ ciphertext := out[len(name)+len(iv):]
+ cbc.CryptBlocks(ciphertext, ciphertext)
+
+ h.Write(out)
+ return h.Sum(out)
+}
+
+const asn1Constructed = 0x20
+
+func parseDERElement(in []byte) (tag byte, body, rest []byte, ok bool) {
+ rest = in
+ if len(rest) < 1 {
+ return
+ }
+
+ tag = rest[0]
+ rest = rest[1:]
+
+ if tag&0x1f == 0x1f {
+ // Long-form tags not supported.
+ return
+ }
+
+ if len(rest) < 1 {
+ return
+ }
+
+ length := int(rest[0])
+ rest = rest[1:]
+ if length > 0x7f {
+ lengthLength := length & 0x7f
+ length = 0
+ if lengthLength == 0 {
+ // No indefinite-length encoding.
+ return
+ }
+
+ // Decode long-form lengths.
+ for lengthLength > 0 {
+ if len(rest) < 1 || (length<<8)>>8 != length {
+ return
+ }
+ if length == 0 && rest[0] == 0 {
+ // Length not minimally-encoded.
+ return
+ }
+ length <<= 8
+ length |= int(rest[0])
+ rest = rest[1:]
+ lengthLength--
+ }
+
+ if length < 0x80 {
+ // Length not minimally-encoded.
+ return
+ }
+ }
+
+ if len(rest) < length {
+ return
+ }
+
+ body = rest[:length]
+ rest = rest[length:]
+ ok = true
+ return
+}
+
+func SetShimTicketVersion(in []byte, vers uint16) ([]byte, error) {
+ plaintext, err := DecryptShimTicket(in)
+ if err != nil {
+ return nil, err
+ }
+
+ tag, session, _, ok := parseDERElement(plaintext)
+ if !ok || tag != asn1.TagSequence|asn1Constructed {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ // Skip the session version.
+ tag, _, session, ok = parseDERElement(session)
+ if !ok || tag != asn1.TagInteger {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ // Next field is the protocol version.
+ tag, version, _, ok := parseDERElement(session)
+ if !ok || tag != asn1.TagInteger {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ // This code assumes both old and new versions are encoded in two
+ // bytes. This isn't quite right as INTEGERs are minimally-encoded, but
+ // we do not need to support other caess for now.
+ if len(version) != 2 || vers < 0x80 || vers >= 0x8000 {
+ return nil, errors.New("tls: unsupported version in shim session")
+ }
+
+ version[0] = byte(vers >> 8)
+ version[1] = byte(vers)
+
+ return EncryptShimTicket(plaintext), nil
+}
+
+func SetShimTicketCipherSuite(in []byte, id uint16) ([]byte, error) {
+ plaintext, err := DecryptShimTicket(in)
+ if err != nil {
+ return nil, err
+ }
+
+ tag, session, _, ok := parseDERElement(plaintext)
+ if !ok || tag != asn1.TagSequence|asn1Constructed {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ // Skip the session version.
+ tag, _, session, ok = parseDERElement(session)
+ if !ok || tag != asn1.TagInteger {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ // Skip the protocol version.
+ tag, _, session, ok = parseDERElement(session)
+ if !ok || tag != asn1.TagInteger {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ // Next field is the cipher suite.
+ tag, cipherSuite, _, ok := parseDERElement(session)
+ if !ok || tag != asn1.TagOctetString || len(cipherSuite) != 2 {
+ return nil, errors.New("tls: could not decode shim session")
+ }
+
+ cipherSuite[0] = byte(id >> 8)
+ cipherSuite[1] = byte(id)
+
+ return EncryptShimTicket(plaintext), nil
+}
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 425664d..940e676 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -75,6 +75,7 @@
{ "-install-ddos-callback", &TestConfig::install_ddos_callback },
{ "-fail-ddos-callback", &TestConfig::fail_ddos_callback },
{ "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback },
+ { "-fail-cert-callback", &TestConfig::fail_cert_callback },
{ "-handshake-never-done", &TestConfig::handshake_never_done },
{ "-use-export-context", &TestConfig::use_export_context },
{ "-tls-unique", &TestConfig::tls_unique },
@@ -106,6 +107,15 @@
{ "-send-alert", &TestConfig::send_alert },
{ "-peek-then-read", &TestConfig::peek_then_read },
{ "-enable-grease", &TestConfig::enable_grease },
+ { "-use-exporter-between-reads", &TestConfig::use_exporter_between_reads },
+ { "-retain-only-sha256-client-cert-initial",
+ &TestConfig::retain_only_sha256_client_cert_initial },
+ { "-retain-only-sha256-client-cert-resume",
+ &TestConfig::retain_only_sha256_client_cert_resume },
+ { "-expect-sha256-client-cert-initial",
+ &TestConfig::expect_sha256_client_cert_initial },
+ { "-expect-sha256-client-cert-resume",
+ &TestConfig::expect_sha256_client_cert_resume },
};
const Flag<std::string> kStringFlags[] = {
@@ -130,6 +140,7 @@
{ "-cipher-tls11", &TestConfig::cipher_tls11 },
{ "-export-label", &TestConfig::export_label },
{ "-export-context", &TestConfig::export_context },
+ { "-expect-peer-cert-file", &TestConfig::expect_peer_cert_file },
};
const Flag<std::string> kBase64Flags[] = {
@@ -140,6 +151,7 @@
&TestConfig::expected_signed_cert_timestamps },
{ "-ocsp-response", &TestConfig::ocsp_response },
{ "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps },
+ { "-ticket-key", &TestConfig::ticket_key },
};
const Flag<int> kIntFlags[] = {
@@ -156,6 +168,9 @@
{ "-expect-dhe-group-size", &TestConfig::expect_dhe_group_size },
{ "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
{ "-max-cert-list", &TestConfig::max_cert_list },
+ { "-expect-cipher-aes", &TestConfig::expect_cipher_aes },
+ { "-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes },
+ { "-resumption-delay", &TestConfig::resumption_delay },
};
const Flag<std::vector<int>> kIntVectorFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 9f74297..ed1a47b 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -73,6 +73,7 @@
bool install_ddos_callback = false;
bool fail_ddos_callback = false;
bool fail_second_ddos_callback = false;
+ bool fail_cert_callback = false;
std::string cipher;
std::string cipher_tls10;
std::string cipher_tls11;
@@ -115,6 +116,16 @@
bool peek_then_read = false;
bool enable_grease = false;
int max_cert_list = 0;
+ std::string ticket_key;
+ bool use_exporter_between_reads = false;
+ int expect_cipher_aes = 0;
+ int expect_cipher_no_aes = 0;
+ std::string expect_peer_cert_file;
+ int resumption_delay = 0;
+ bool retain_only_sha256_client_cert_initial = false;
+ bool retain_only_sha256_client_cert_resume = false;
+ bool expect_sha256_client_cert_initial = false;
+ bool expect_sha256_client_cert_resume = false;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index e32464d..17f7161 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -25,6 +25,7 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
+#include "../crypto/internal.h"
#include "internal.h"
@@ -79,6 +80,11 @@
hs->wait = ssl_hs_ok;
return -1;
+ case ssl_hs_channel_id_lookup:
+ ssl->rwstate = SSL_CHANNEL_ID_LOOKUP;
+ hs->wait = ssl_hs_ok;
+ return -1;
+
case ssl_hs_private_key_operation:
ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
hs->wait = ssl_hs_ok;
@@ -105,8 +111,9 @@
}
}
-static int tls13_get_cert_verify_signature_input(SSL *ssl, uint8_t **out,
- size_t *out_len, int server) {
+int tls13_get_cert_verify_signature_input(
+ SSL *ssl, uint8_t **out, size_t *out_len,
+ enum ssl_cert_verify_context_t cert_verify_context) {
CBB cbb;
if (!CBB_init(&cbb, 64 + 33 + 1 + 2 * EVP_MAX_MD_SIZE)) {
goto err;
@@ -118,23 +125,33 @@
}
}
- if (server) {
+ const uint8_t *context;
+ size_t context_len;
+ if (cert_verify_context == ssl_cert_verify_server) {
/* Include the NUL byte. */
static const char kContext[] = "TLS 1.3, server CertificateVerify";
- if (!CBB_add_bytes(&cbb, (const uint8_t *)kContext, sizeof(kContext))) {
- goto err;
- }
- } else {
+ context = (const uint8_t *)kContext;
+ context_len = sizeof(kContext);
+ } else if (cert_verify_context == ssl_cert_verify_client) {
static const char kContext[] = "TLS 1.3, client CertificateVerify";
- if (!CBB_add_bytes(&cbb, (const uint8_t *)kContext, sizeof(kContext))) {
- goto err;
- }
+ context = (const uint8_t *)kContext;
+ context_len = sizeof(kContext);
+ } else if (cert_verify_context == ssl_cert_verify_channel_id) {
+ static const char kContext[] = "TLS 1.3, Channel ID";
+ context = (const uint8_t *)kContext;
+ context_len = sizeof(kContext);
+ } else {
+ goto err;
}
- uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
- size_t context_hashes_len;
- if (!tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len) ||
- !CBB_add_bytes(&cbb, context_hashes, context_hashes_len) ||
+ if (!CBB_add_bytes(&cbb, context, context_len)) {
+ goto err;
+ }
+
+ uint8_t context_hash[EVP_MAX_MD_SIZE];
+ size_t context_hash_len;
+ if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len) ||
+ !CBB_add_bytes(&cbb, context_hash, context_hash_len) ||
!CBB_finish(&cbb, out, out_len)) {
goto err;
}
@@ -148,7 +165,7 @@
}
int tls13_process_certificate(SSL *ssl, int allow_anonymous) {
- CBS cbs, context;
+ CBS cbs, context, certificate_list;
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
CBS_len(&context) != 0) {
@@ -158,17 +175,117 @@
}
const int retain_sha256 =
- ssl->server && ssl->ctx->retain_only_sha256_of_client_certs;
+ ssl->server && ssl->retain_only_sha256_of_client_certs;
int ret = 0;
- uint8_t alert;
- STACK_OF(X509) *chain = ssl_parse_cert_chain(
- ssl, &alert, retain_sha256 ? ssl->s3->new_session->peer_sha256 : NULL,
- &cbs);
+
+ STACK_OF(X509) *chain = sk_X509_new_null();
if (chain == NULL) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
+ if (!CBS_get_u24_length_prefixed(&cbs, &certificate_list)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ goto err;
+ }
+
+ while (CBS_len(&certificate_list) > 0) {
+ CBS certificate, extensions;
+ if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate) ||
+ !CBS_get_u16_length_prefixed(&certificate_list, &extensions)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
+ goto err;
+ }
+
+ /* Retain the hash of the leaf certificate if requested. */
+ if (sk_X509_num(chain) == 0 && retain_sha256) {
+ SHA256(CBS_data(&certificate), CBS_len(&certificate),
+ ssl->s3->new_session->peer_sha256);
+ }
+
+ X509 *x = ssl_parse_x509(&certificate);
+ if (x == NULL || CBS_len(&certificate) != 0) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ X509_free(x);
+ goto err;
+ }
+ if (!sk_X509_push(chain, x)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ X509_free(x);
+ goto err;
+ }
+
+ /* Parse out the extensions. */
+ int have_status_request = 0, have_sct = 0;
+ CBS status_request, sct;
+ const SSL_EXTENSION_TYPE ext_types[] = {
+ {TLSEXT_TYPE_status_request, &have_status_request, &status_request},
+ {TLSEXT_TYPE_certificate_timestamp, &have_sct, &sct},
+ };
+
+ uint8_t alert;
+ if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+ OPENSSL_ARRAY_SIZE(ext_types))) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ goto err;
+ }
+
+ /* All Certificate extensions are parsed, but only the leaf extensions are
+ * stored. */
+ if (have_status_request) {
+ if (ssl->server || !ssl->ocsp_stapling_enabled) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+ goto err;
+ }
+
+ uint8_t status_type;
+ CBS ocsp_response;
+ if (!CBS_get_u8(&status_request, &status_type) ||
+ status_type != TLSEXT_STATUSTYPE_ocsp ||
+ !CBS_get_u24_length_prefixed(&status_request, &ocsp_response) ||
+ CBS_len(&ocsp_response) == 0 ||
+ CBS_len(&status_request) != 0) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ goto err;
+ }
+
+ if (sk_X509_num(chain) == 1 &&
+ !CBS_stow(&ocsp_response, &ssl->s3->new_session->ocsp_response,
+ &ssl->s3->new_session->ocsp_response_length)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ if (have_sct) {
+ if (ssl->server || !ssl->signed_cert_timestamps_enabled) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+ goto err;
+ }
+
+ if (!ssl_is_sct_list_valid(&sct)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ goto err;
+ }
+
+ if (sk_X509_num(chain) == 1 &&
+ !CBS_stow(&sct,
+ &ssl->s3->new_session->tlsext_signed_cert_timestamp_list,
+ &ssl->s3->new_session
+ ->tlsext_signed_cert_timestamp_list_length)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+ }
+
if (CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -198,13 +315,13 @@
goto err;
}
- X509_free(ssl->s3->new_session->peer);
+ X509_free(ssl->s3->new_session->x509_peer);
X509 *leaf = sk_X509_value(chain, 0);
X509_up_ref(leaf);
- ssl->s3->new_session->peer = leaf;
+ ssl->s3->new_session->x509_peer = leaf;
- sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
- ssl->s3->new_session->cert_chain = chain;
+ sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
+ ssl->s3->new_session->x509_chain = chain;
chain = NULL;
ret = 1;
@@ -216,7 +333,7 @@
int tls13_process_certificate_verify(SSL *ssl) {
int ret = 0;
- X509 *peer = ssl->s3->new_session->peer;
+ X509 *peer = ssl->s3->new_session->x509_peer;
EVP_PKEY *pkey = NULL;
uint8_t *msg = NULL;
size_t msg_len;
@@ -245,8 +362,9 @@
}
ssl->s3->tmp.peer_signature_algorithm = signature_algorithm;
- if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
- !ssl->server)) {
+ if (!tls13_get_cert_verify_signature_input(
+ ssl, &msg, &msg_len,
+ ssl->server ? ssl_cert_verify_client : ssl_cert_verify_server)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
goto err;
}
@@ -307,17 +425,79 @@
}
int tls13_prepare_certificate(SSL *ssl) {
- CBB cbb, body;
+ CBB cbb, body, certificate_list;
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
/* The request context is always empty in the handshake. */
!CBB_add_u8(&body, 0) ||
- !ssl_add_cert_chain(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
- CBB_cleanup(&cbb);
- return 0;
+ !CBB_add_u24_length_prefixed(&body, &certificate_list)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (!ssl_has_certificate(ssl)) {
+ if (!ssl_complete_message(ssl, &cbb)) {
+ goto err;
+ }
+
+ return 1;
+ }
+
+ CERT *cert = ssl->cert;
+ CBB leaf, extensions;
+ if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
+ !ssl_add_cert_to_cbb(&leaf, cert->x509_leaf) ||
+ !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
+ if (ssl->s3->hs->scts_requested &&
+ ssl->ctx->signed_cert_timestamp_list_length != 0) {
+ CBB contents;
+ if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
+ !CBB_add_u16_length_prefixed(&extensions, &contents) ||
+ !CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
+ ssl->ctx->signed_cert_timestamp_list_length) ||
+ !CBB_flush(&extensions)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ if (ssl->s3->hs->ocsp_stapling_requested &&
+ ssl->ctx->ocsp_response_length != 0) {
+ CBB contents, ocsp_response;
+ if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) ||
+ !CBB_add_u16_length_prefixed(&extensions, &contents) ||
+ !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) ||
+ !CBB_add_u24_length_prefixed(&contents, &ocsp_response) ||
+ !CBB_add_bytes(&ocsp_response, ssl->ctx->ocsp_response,
+ ssl->ctx->ocsp_response_length) ||
+ !CBB_flush(&extensions)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ for (size_t i = 0; i < sk_X509_num(cert->x509_chain); i++) {
+ CBB child;
+ if (!CBB_add_u24_length_prefixed(&certificate_list, &child) ||
+ !ssl_add_cert_to_cbb(&child, sk_X509_value(cert->x509_chain, i)) ||
+ !CBB_add_u16(&certificate_list, 0 /* no extensions */)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ }
+
+ if (!ssl_complete_message(ssl, &cbb)) {
+ goto err;
}
return 1;
+
+err:
+ CBB_cleanup(&cbb);
+ return 0;
}
enum ssl_private_key_result_t tls13_prepare_certificate_verify(
@@ -352,8 +532,9 @@
enum ssl_private_key_result_t sign_result;
if (is_first_run) {
- if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
- ssl->server)) {
+ if (!tls13_get_cert_verify_signature_input(
+ ssl, &msg, &msg_len,
+ ssl->server ? ssl_cert_verify_server : ssl_cert_verify_client)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
goto err;
}
@@ -369,7 +550,7 @@
}
if (!CBB_did_write(&child, sig_len) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -394,7 +575,7 @@
CBB cbb, body;
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
!CBB_add_bytes(&body, verify_data, verify_data_len) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
CBB_cleanup(&cbb);
return 0;
}
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 87ccdc4..4a30ce3 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -24,6 +24,7 @@
#include <openssl/stack.h>
#include <openssl/x509.h>
+#include "../crypto/internal.h"
#include "internal.h"
@@ -41,6 +42,7 @@
state_send_client_certificate,
state_send_client_certificate_verify,
state_complete_client_certificate_verify,
+ state_send_channel_id,
state_send_client_finished,
state_flush,
state_done,
@@ -66,97 +68,73 @@
return ssl_hs_error;
}
- while (CBS_len(&extensions) != 0) {
- uint16_t type;
- CBS extension;
- if (!CBS_get_u16(&extensions, &type) ||
- !CBS_get_u16_length_prefixed(&extensions, &extension)) {
+ int have_cookie, have_key_share;
+ CBS cookie, key_share;
+ const SSL_EXTENSION_TYPE ext_types[] = {
+ {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
+ {TLSEXT_TYPE_cookie, &have_cookie, &cookie},
+ };
+
+ uint8_t alert;
+ if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+ OPENSSL_ARRAY_SIZE(ext_types))) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+ }
+
+ if (have_cookie) {
+ CBS cookie_value;
+ if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) ||
+ CBS_len(&cookie_value) == 0 ||
+ CBS_len(&cookie) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return ssl_hs_error;
}
- switch (type) {
- case TLSEXT_TYPE_cookie: {
- if (hs->cookie != NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
- }
-
- /* Cookies may be requested whether or not advertised, so no need to
- * check. */
-
- CBS cookie;
- if (!CBS_get_u16_length_prefixed(&extension, &cookie) ||
- CBS_len(&cookie) == 0 ||
- CBS_len(&extension) != 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return ssl_hs_error;
- }
-
- if (!CBS_stow(&cookie, &hs->cookie, &hs->cookie_len)) {
- return ssl_hs_error;
- }
- break;
- }
-
- case TLSEXT_TYPE_key_share: {
- if (hs->retry_group != 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
- }
-
- /* key_share is always advertised, so no need to check. */
-
- uint16_t group_id;
- if (!CBS_get_u16(&extension, &group_id) ||
- CBS_len(&extension) != 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return ssl_hs_error;
- }
-
- /* The group must be supported. */
- const uint16_t *groups;
- size_t groups_len;
- tls1_get_grouplist(ssl, &groups, &groups_len);
- int found = 0;
- for (size_t i = 0; i < groups_len; i++) {
- if (groups[i] == group_id) {
- found = 1;
- break;
- }
- }
-
- if (!found) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
- return ssl_hs_error;
- }
-
- /* Check that the HelloRetryRequest does not request the key share that
- * was provided in the initial ClientHello. */
- if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) == group_id) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
- return ssl_hs_error;
- }
-
- SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
- hs->retry_group = group_id;
- break;
- }
-
- default:
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
- return ssl_hs_error;
+ if (!CBS_stow(&cookie_value, &hs->cookie, &hs->cookie_len)) {
+ return ssl_hs_error;
}
}
+ if (have_key_share) {
+ uint16_t group_id;
+ if (!CBS_get_u16(&key_share, &group_id) || CBS_len(&key_share) != 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return ssl_hs_error;
+ }
+
+ /* The group must be supported. */
+ const uint16_t *groups;
+ size_t groups_len;
+ tls1_get_grouplist(ssl, &groups, &groups_len);
+ int found = 0;
+ for (size_t i = 0; i < groups_len; i++) {
+ if (groups[i] == group_id) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+ return ssl_hs_error;
+ }
+
+ /* Check that the HelloRetryRequest does not request the key share that
+ * was provided in the initial ClientHello. */
+ if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) == group_id) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+ return ssl_hs_error;
+ }
+
+ SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
+ hs->retry_group = group_id;
+ }
+
hs->received_hello_retry_request = 1;
hs->state = state_send_second_client_hello;
return ssl_hs_ok;
@@ -164,11 +142,7 @@
static enum ssl_hs_wait_t do_send_second_client_hello(SSL *ssl,
SSL_HANDSHAKE *hs) {
- CBB cbb, body;
- if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO) ||
- !ssl_add_client_hello_body(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
- CBB_cleanup(&cbb);
+ if (!ssl_write_client_hello(ssl)) {
return ssl_hs_error;
}
@@ -217,73 +191,37 @@
return ssl_hs_error;
}
- /* Check if the cipher is disabled. */
- if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
- (cipher->algorithm_auth & ssl->cert->mask_a) ||
- SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
- SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
- !sk_SSL_CIPHER_find(ssl_get_ciphers_by_id(ssl), NULL, cipher)) {
+ /* Check if the cipher is a TLS 1.3 cipher. */
+ if (SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
+ SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
/* Parse out the extensions. */
- int have_key_share = 0, have_pre_shared_key = 0, have_sigalgs = 0;
- CBS key_share, pre_shared_key, sigalgs;
- while (CBS_len(&extensions) != 0) {
- uint16_t type;
- CBS extension;
- if (!CBS_get_u16(&extensions, &type) ||
- !CBS_get_u16_length_prefixed(&extensions, &extension)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return ssl_hs_error;
- }
+ int have_key_share = 0, have_pre_shared_key = 0;
+ CBS key_share, pre_shared_key;
+ const SSL_EXTENSION_TYPE ext_types[] = {
+ {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
+ {TLSEXT_TYPE_pre_shared_key, &have_pre_shared_key, &pre_shared_key},
+ };
- switch (type) {
- case TLSEXT_TYPE_key_share:
- if (have_key_share) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
- }
- key_share = extension;
- have_key_share = 1;
- break;
- case TLSEXT_TYPE_pre_shared_key:
- if (have_pre_shared_key) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
- }
- pre_shared_key = extension;
- have_pre_shared_key = 1;
- break;
- case TLSEXT_TYPE_signature_algorithms:
- if (have_sigalgs) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
- }
- sigalgs = extension;
- have_sigalgs = 1;
- break;
- default:
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
- return ssl_hs_error;
- }
+ uint8_t alert;
+ if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+ OPENSSL_ARRAY_SIZE(ext_types))) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
}
- /* We only support PSK_AUTH and PSK_DHE_KE. */
- if (!have_key_share || have_sigalgs == have_pre_shared_key) {
+ /* We only support PSK_DHE_KE. */
+ if (!have_key_share) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
- uint8_t alert = SSL_AD_DECODE_ERROR;
+ alert = SSL_AD_DECODE_ERROR;
if (have_pre_shared_key) {
if (ssl->session == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
@@ -303,8 +241,8 @@
return ssl_hs_error;
}
- if (ssl->session->cipher != cipher) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
+ if (ssl->session->cipher->algorithm_prf != cipher->algorithm_prf) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_PRF_HASH_MISMATCH);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
}
@@ -339,20 +277,17 @@
EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
/* Derive resumption material. */
- uint8_t resumption_ctx[EVP_MAX_MD_SIZE] = {0};
uint8_t psk_secret[EVP_MAX_MD_SIZE] = {0};
if (ssl->s3->session_reused) {
- if (!tls13_resumption_context(ssl, resumption_ctx, hash_len,
- ssl->s3->new_session) ||
- !tls13_resumption_psk(ssl, psk_secret, hash_len,
- ssl->s3->new_session)) {
+ if (hash_len != (size_t) ssl->s3->new_session->master_key_length) {
return ssl_hs_error;
}
+ memcpy(psk_secret, ssl->s3->new_session->master_key, hash_len);
}
/* Set up the key schedule, hash in the ClientHello, and incorporate the PSK
* into the running secret. */
- if (!tls13_init_key_schedule(ssl, resumption_ctx, hash_len) ||
+ if (!tls13_init_key_schedule(ssl) ||
!tls13_advance_key_schedule(ssl, psk_secret, hash_len)) {
return ssl_hs_error;
}
@@ -372,16 +307,10 @@
}
OPENSSL_free(dhe_secret);
- if (have_sigalgs &&
- CBS_len(&sigalgs) != 0) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return ssl_hs_error;
- }
-
/* If there was no HelloRetryRequest, the version negotiation logic has
* already hashed the message. */
if (hs->received_hello_retry_request &&
- !ssl->method->hash_current_message(ssl)) {
+ !ssl_hash_current_message(ssl)) {
return ssl_hs_error;
}
@@ -411,7 +340,7 @@
return ssl_hs_error;
}
- if (!ssl->method->hash_current_message(ssl)) {
+ if (!ssl_hash_current_message(ssl)) {
return ssl_hs_error;
}
@@ -467,7 +396,7 @@
sk_X509_NAME_pop_free(ssl->s3->hs->ca_names, X509_NAME_free);
ssl->s3->hs->ca_names = ca_sk;
- if (!ssl->method->hash_current_message(ssl)) {
+ if (!ssl_hash_current_message(ssl)) {
return ssl_hs_error;
}
@@ -479,7 +408,7 @@
SSL_HANDSHAKE *hs) {
if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
!tls13_process_certificate(ssl, 0 /* certificate required */) ||
- !ssl->method->hash_current_message(ssl)) {
+ !ssl_hash_current_message(ssl)) {
return ssl_hs_error;
}
@@ -491,8 +420,8 @@
SSL *ssl, SSL_HANDSHAKE *hs) {
if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
!tls13_process_certificate_verify(ssl) ||
- !ssl->method->hash_current_message(ssl)) {
- return 0;
+ !ssl_hash_current_message(ssl)) {
+ return ssl_hs_error;
}
hs->state = state_process_server_finished;
@@ -504,10 +433,10 @@
static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
!tls13_process_finished(ssl) ||
- !ssl->method->hash_current_message(ssl) ||
+ !ssl_hash_current_message(ssl) ||
/* Update the secret to the master secret and derive traffic keys. */
!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
- !tls13_derive_traffic_secret_0(ssl)) {
+ !tls13_derive_application_secrets(ssl)) {
return ssl_hs_error;
}
@@ -519,7 +448,7 @@
static enum ssl_hs_wait_t do_certificate_callback(SSL *ssl, SSL_HANDSHAKE *hs) {
/* The peer didn't request a certificate. */
if (!ssl->s3->hs->cert_request) {
- hs->state = state_send_client_finished;
+ hs->state = state_send_channel_id;
return ssl_hs_ok;
}
@@ -566,13 +495,13 @@
int is_first_run) {
/* Don't send CertificateVerify if there is no certificate. */
if (!ssl_has_certificate(ssl)) {
- hs->state = state_send_client_finished;
+ hs->state = state_send_channel_id;
return ssl_hs_ok;
}
switch (tls13_prepare_certificate_verify(ssl, is_first_run)) {
case ssl_private_key_success:
- hs->state = state_send_client_finished;
+ hs->state = state_send_channel_id;
return ssl_hs_write_message;
case ssl_private_key_retry:
@@ -587,6 +516,32 @@
return ssl_hs_error;
}
+static enum ssl_hs_wait_t do_send_channel_id(SSL *ssl, SSL_HANDSHAKE *hs) {
+ if (!ssl->s3->tlsext_channel_id_valid) {
+ hs->state = state_send_client_finished;
+ return ssl_hs_ok;
+ }
+
+ if (!ssl_do_channel_id_callback(ssl)) {
+ return ssl_hs_error;
+ }
+
+ if (ssl->tlsext_channel_id_private == NULL) {
+ return ssl_hs_channel_id_lookup;
+ }
+
+ CBB cbb, body;
+ if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
+ !tls1_write_channel_id(ssl, &body) ||
+ !ssl_complete_message(ssl, &cbb)) {
+ CBB_cleanup(&cbb);
+ return ssl_hs_error;
+ }
+
+ hs->state = state_send_client_finished;
+ return ssl_hs_write_message;
+}
+
static enum ssl_hs_wait_t do_send_client_finished(SSL *ssl, SSL_HANDSHAKE *hs) {
if (!tls13_prepare_finished(ssl)) {
return ssl_hs_error;
@@ -597,11 +552,11 @@
}
static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
- if (!tls13_set_traffic_key(ssl, type_data, evp_aead_open,
- hs->server_traffic_secret_0, hs->hash_len) ||
- !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
- hs->client_traffic_secret_0, hs->hash_len) ||
- !tls13_finalize_keys(ssl)) {
+ if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_traffic_secret_0,
+ hs->hash_len) ||
+ !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_traffic_secret_0,
+ hs->hash_len) ||
+ !tls13_derive_resumption_secret(ssl)) {
return ssl_hs_error;
}
@@ -651,10 +606,13 @@
break;
case state_send_client_certificate_verify:
ret = do_send_client_certificate_verify(ssl, hs, 1 /* first run */);
- break;
+ break;
case state_complete_client_certificate_verify:
ret = do_send_client_certificate_verify(ssl, hs, 0 /* complete */);
- break;
+ break;
+ case state_send_channel_id:
+ ret = do_send_channel_id(ssl, hs);
+ break;
case state_send_client_finished:
ret = do_send_client_finished(ssl, hs);
break;
@@ -682,13 +640,12 @@
return 0;
}
- CBS cbs, ke_modes, auth_modes, ticket, extensions;
+ ssl_session_refresh_time(ssl, session);
+
+ CBS cbs, ticket, extensions;
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
if (!CBS_get_u32(&cbs, &session->tlsext_tick_lifetime_hint) ||
- !CBS_get_u8_length_prefixed(&cbs, &ke_modes) ||
- CBS_len(&ke_modes) == 0 ||
- !CBS_get_u8_length_prefixed(&cbs, &auth_modes) ||
- CBS_len(&auth_modes) == 0 ||
+ !CBS_get_u32(&cbs, &session->ticket_age_add) ||
!CBS_get_u16_length_prefixed(&cbs, &ticket) ||
!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
!CBS_get_u16_length_prefixed(&cbs, &extensions) ||
@@ -699,13 +656,10 @@
return 0;
}
+ session->ticket_age_add_valid = 1;
session->not_resumable = 0;
- /* Ignore the ticket unless the server preferences are compatible with us. */
- if (memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, CBS_len(&ke_modes)) != NULL &&
- memchr(CBS_data(&auth_modes), SSL_PSK_AUTH, CBS_len(&auth_modes)) !=
- NULL &&
- ssl->ctx->new_session_cb != NULL &&
+ if (ssl->ctx->new_session_cb != NULL &&
ssl->ctx->new_session_cb(ssl, session)) {
/* |new_session_cb|'s return value signals that it took ownership. */
return 1;
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.c
index 1fcde51..d87d8a6 100644
--- a/src/ssl/tls13_enc.c
+++ b/src/ssl/tls13_enc.c
@@ -20,27 +20,19 @@
#include <openssl/aead.h>
#include <openssl/bytestring.h>
#include <openssl/digest.h>
-#include <openssl/hmac.h>
#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
#include <openssl/mem.h>
#include "internal.h"
-int tls13_init_key_schedule(SSL *ssl, const uint8_t *resumption_ctx,
- size_t resumption_ctx_len) {
+int tls13_init_key_schedule(SSL *ssl) {
SSL_HANDSHAKE *hs = ssl->s3->hs;
const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
hs->hash_len = EVP_MD_size(digest);
- /* Save the hash of the resumption context. */
- unsigned resumption_hash_len;
- if (!EVP_Digest(resumption_ctx, resumption_ctx_len, hs->resumption_hash,
- &resumption_hash_len, digest, NULL)) {
- return 0;
- }
-
/* Initialize the secret to the zero key. */
memset(hs->secret, 0, hs->hash_len);
@@ -89,22 +81,17 @@
return ret;
}
-int tls13_get_context_hashes(SSL *ssl, uint8_t *out, size_t *out_len) {
- SSL_HANDSHAKE *hs = ssl->s3->hs;
-
+int tls13_get_context_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
EVP_MD_CTX ctx;
EVP_MD_CTX_init(&ctx);
unsigned handshake_len = 0;
int ok = EVP_MD_CTX_copy_ex(&ctx, &ssl->s3->handshake_hash) &&
EVP_DigestFinal_ex(&ctx, out, &handshake_len);
EVP_MD_CTX_cleanup(&ctx);
- if (!ok) {
- return 0;
+ if (ok) {
+ *out_len = handshake_len;
}
-
- memcpy(out + handshake_len, hs->resumption_hash, hs->hash_len);
- *out_len = handshake_len + hs->hash_len;
- return 1;
+ return ok;
}
/* derive_secret derives a secret of length |len| and writes the result in |out|
@@ -115,18 +102,17 @@
SSL_HANDSHAKE *hs = ssl->s3->hs;
const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
- uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
- size_t context_hashes_len;
- if (!tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len)) {
+ uint8_t context_hash[EVP_MAX_MD_SIZE];
+ size_t context_hash_len;
+ if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len)) {
return 0;
}
return hkdf_expand_label(out, digest, hs->secret, hs->hash_len, label,
- label_len, context_hashes, context_hashes_len, len);
+ label_len, context_hash, context_hash_len, len);
}
-int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
- enum evp_aead_direction_t direction,
+int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
const uint8_t *traffic_secret,
size_t traffic_secret_len) {
if (traffic_secret_len > 0xff) {
@@ -134,33 +120,11 @@
return 0;
}
- const char *key_label, *iv_label;
- switch (type) {
- case type_early_handshake:
- key_label = "early handshake key expansion, key";
- iv_label = "early handshake key expansion, iv";
- break;
- case type_early_data:
- key_label = "early application data key expansion, key";
- iv_label = "early application data key expansion, iv";
- break;
- case type_handshake:
- key_label = "handshake key expansion, key";
- iv_label = "handshake key expansion, iv";
- break;
- case type_data:
- key_label = "application data key expansion, key";
- iv_label = "application data key expansion, iv";
- break;
- default:
- return 0;
- }
-
/* Look up cipher suite properties. */
const EVP_AEAD *aead;
const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
- size_t mac_secret_len, fixed_iv_len;
- if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+ size_t discard;
+ if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard,
SSL_get_session(ssl)->cipher,
ssl3_protocol_version(ssl))) {
return 0;
@@ -170,8 +134,7 @@
size_t key_len = EVP_AEAD_key_length(aead);
uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len,
- (const uint8_t *)key_label, strlen(key_label), NULL, 0,
- key_len)) {
+ (const uint8_t *)"key", 3, NULL, 0, key_len)) {
return 0;
}
@@ -179,8 +142,7 @@
size_t iv_len = EVP_AEAD_nonce_length(aead);
uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len,
- (const uint8_t *)iv_label, strlen(iv_label), NULL, 0,
- iv_len)) {
+ (const uint8_t *)"iv", 2, NULL, 0, iv_len)) {
return 0;
}
@@ -241,26 +203,29 @@
}
if (ssl->server) {
- if (!tls13_set_traffic_key(ssl, type_handshake, evp_aead_open,
- client_traffic_secret, hs->hash_len) ||
- !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal,
- server_traffic_secret, hs->hash_len)) {
+ if (!tls13_set_traffic_key(ssl, evp_aead_open, client_traffic_secret,
+ hs->hash_len) ||
+ !tls13_set_traffic_key(ssl, evp_aead_seal, server_traffic_secret,
+ hs->hash_len)) {
return 0;
}
} else {
- if (!tls13_set_traffic_key(ssl, type_handshake, evp_aead_open,
- server_traffic_secret, hs->hash_len) ||
- !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal,
- client_traffic_secret, hs->hash_len)) {
+ if (!tls13_set_traffic_key(ssl, evp_aead_open, server_traffic_secret,
+ hs->hash_len) ||
+ !tls13_set_traffic_key(ssl, evp_aead_seal, client_traffic_secret,
+ hs->hash_len)) {
return 0;
}
}
return 1;
}
-int tls13_derive_traffic_secret_0(SSL *ssl) {
+static const char kTLS13LabelExporter[] = "exporter master secret";
+
+int tls13_derive_application_secrets(SSL *ssl) {
SSL_HANDSHAKE *hs = ssl->s3->hs;
+ ssl->s3->exporter_secret_len = hs->hash_len;
return derive_secret(ssl, hs->client_traffic_secret_0, hs->hash_len,
(const uint8_t *)kTLS13LabelClientApplicationTraffic,
strlen(kTLS13LabelClientApplicationTraffic)) &&
@@ -270,7 +235,10 @@
(const uint8_t *)kTLS13LabelServerApplicationTraffic,
strlen(kTLS13LabelServerApplicationTraffic)) &&
ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
- hs->server_traffic_secret_0, hs->hash_len);
+ hs->server_traffic_secret_0, hs->hash_len) &&
+ derive_secret(ssl, ssl->s3->exporter_secret, hs->hash_len,
+ (const uint8_t *)kTLS13LabelExporter,
+ strlen(kTLS13LabelExporter));
}
static const char kTLS13LabelApplicationTraffic[] =
@@ -296,27 +264,36 @@
return 0;
}
- return tls13_set_traffic_key(ssl, type_data, direction, secret, secret_len);
+ return tls13_set_traffic_key(ssl, direction, secret, secret_len);
}
-static const char kTLS13LabelExporter[] = "exporter master secret";
static const char kTLS13LabelResumption[] = "resumption master secret";
-int tls13_finalize_keys(SSL *ssl) {
- SSL_HANDSHAKE *hs = ssl->s3->hs;
+int tls13_derive_resumption_secret(SSL *ssl) {
+ ssl->s3->new_session->master_key_length = ssl->s3->hs->hash_len;
+ return derive_secret(ssl, ssl->s3->new_session->master_key,
+ ssl->s3->new_session->master_key_length,
+ (const uint8_t *)kTLS13LabelResumption,
+ strlen(kTLS13LabelResumption));
+}
- ssl->s3->exporter_secret_len = hs->hash_len;
- ssl->s3->new_session->master_key_length = hs->hash_len;
- if (!derive_secret(
- ssl, ssl->s3->exporter_secret, ssl->s3->exporter_secret_len,
- (const uint8_t *)kTLS13LabelExporter, strlen(kTLS13LabelExporter)) ||
- !derive_secret(ssl, ssl->s3->new_session->master_key,
- ssl->s3->new_session->master_key_length,
- (const uint8_t *)kTLS13LabelResumption,
- strlen(kTLS13LabelResumption))) {
+static const char kTLS13LabelFinished[] = "finished";
+
+/* tls13_verify_data sets |out| to be the HMAC of |context| using a derived
+ * Finished key for both Finished messages and the PSK binder. */
+static int tls13_verify_data(const EVP_MD *digest, uint8_t *out,
+ size_t *out_len, const uint8_t *secret,
+ size_t hash_len, uint8_t *context,
+ size_t context_len) {
+ uint8_t key[EVP_MAX_MD_SIZE];
+ unsigned len;
+ if (!hkdf_expand_label(key, digest, secret, hash_len,
+ (const uint8_t *)kTLS13LabelFinished,
+ strlen(kTLS13LabelFinished), NULL, 0, hash_len) ||
+ HMAC(digest, key, hash_len, context, context_len, out, &len) == NULL) {
return 0;
}
-
+ *out_len = len;
return 1;
}
@@ -324,54 +301,23 @@
SSL_HANDSHAKE *hs = ssl->s3->hs;
const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
- uint8_t key[EVP_MAX_MD_SIZE];
- size_t key_len = EVP_MD_size(digest);
-
const uint8_t *traffic_secret;
- const char *label = "finished";
if (is_server == ssl->server) {
traffic_secret = ssl->s3->write_traffic_secret;
} else {
traffic_secret = ssl->s3->read_traffic_secret;
}
- uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
- size_t context_hashes_len;
- unsigned len;
- if (!hkdf_expand_label(key, digest, traffic_secret, hs->hash_len,
- (const uint8_t *)label, strlen(label), NULL, 0,
- hs->hash_len) ||
- !tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len) ||
- HMAC(digest, key, key_len, context_hashes, context_hashes_len, out,
- &len) == NULL) {
+ uint8_t context_hash[EVP_MAX_MD_SIZE];
+ size_t context_hash_len;
+ if (!tls13_get_context_hash(ssl, context_hash, &context_hash_len) ||
+ !tls13_verify_data(digest, out, out_len, traffic_secret, hs->hash_len,
+ context_hash, context_hash_len)) {
return 0;
}
- *out_len = len;
return 1;
}
-static const char kTLS13LabelResumptionPSK[] = "resumption psk";
-static const char kTLS13LabelResumptionContext[] = "resumption context";
-
-int tls13_resumption_psk(SSL *ssl, uint8_t *out, size_t out_len,
- const SSL_SESSION *session) {
- const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
- return hkdf_expand_label(out, digest, session->master_key,
- session->master_key_length,
- (const uint8_t *)kTLS13LabelResumptionPSK,
- strlen(kTLS13LabelResumptionPSK), NULL, 0, out_len);
-}
-
-int tls13_resumption_context(SSL *ssl, uint8_t *out, size_t out_len,
- const SSL_SESSION *session) {
- const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
- return hkdf_expand_label(out, digest, session->master_key,
- session->master_key_length,
- (const uint8_t *)kTLS13LabelResumptionContext,
- strlen(kTLS13LabelResumptionContext), NULL, 0,
- out_len);
-}
-
int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
const char *label, size_t label_len,
const uint8_t *context, size_t context_len,
@@ -388,3 +334,119 @@
ssl->s3->exporter_secret_len, (const uint8_t *)label,
label_len, hash, hash_len, out_len);
}
+
+static const char kTLS13LabelPSKBinder[] = "resumption psk binder key";
+
+static int tls13_psk_binder(SSL *ssl, uint8_t *out, const EVP_MD *digest,
+ uint8_t *psk, size_t psk_len, uint8_t *context,
+ size_t context_len, size_t hash_len) {
+ uint8_t binder_context[EVP_MAX_MD_SIZE];
+ unsigned binder_context_len;
+ if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) {
+ return 0;
+ }
+
+ uint8_t early_secret[EVP_MAX_MD_SIZE] = {0};
+ size_t early_secret_len;
+ if (!HKDF_extract(early_secret, &early_secret_len, digest, psk, hash_len,
+ NULL, 0)) {
+ return 0;
+ }
+
+ uint8_t binder_key[EVP_MAX_MD_SIZE] = {0};
+ size_t len;
+ if (!hkdf_expand_label(binder_key, digest, early_secret, hash_len,
+ (const uint8_t *)kTLS13LabelPSKBinder,
+ strlen(kTLS13LabelPSKBinder), binder_context,
+ binder_context_len, hash_len) ||
+ !tls13_verify_data(digest, out, &len, binder_key, hash_len, context,
+ context_len)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int tls13_write_psk_binder(SSL *ssl, uint8_t *msg, size_t len) {
+ const EVP_MD *digest =
+ ssl_get_handshake_digest(ssl->session->cipher->algorithm_prf);
+ size_t hash_len = EVP_MD_size(digest);
+
+ if (len < hash_len + 3) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ EVP_MD_CTX ctx;
+ EVP_MD_CTX_init(&ctx);
+ uint8_t context[EVP_MAX_MD_SIZE];
+ unsigned context_len;
+ if (!EVP_DigestInit_ex(&ctx, digest, NULL) ||
+ !EVP_DigestUpdate(&ctx, ssl->s3->handshake_buffer->data,
+ ssl->s3->handshake_buffer->length) ||
+ !EVP_DigestUpdate(&ctx, msg, len - hash_len - 3) ||
+ !EVP_DigestFinal_ex(&ctx, context, &context_len)) {
+ EVP_MD_CTX_cleanup(&ctx);
+ return 0;
+ }
+
+ EVP_MD_CTX_cleanup(&ctx);
+
+ uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
+ if (!tls13_psk_binder(ssl, verify_data, digest, ssl->session->master_key,
+ ssl->session->master_key_length, context,
+ context_len, hash_len)) {
+ return 0;
+ }
+
+ memcpy(msg + len - hash_len, verify_data, hash_len);
+ return 1;
+}
+
+int tls13_verify_psk_binder(SSL *ssl, SSL_SESSION *session,
+ CBS *binders) {
+ const EVP_MD *digest =
+ ssl_get_handshake_digest(session->cipher->algorithm_prf);
+ size_t hash_len = EVP_MD_size(digest);
+
+ /* Get the full ClientHello, including message header. It must be large enough
+ * to exclude the binders. */
+ CBS message;
+ ssl->method->get_current_message(ssl, &message);
+ if (CBS_len(&message) < CBS_len(binders) + 2) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ /* Hash a ClientHello prefix up to the binders. For now, this assumes we only
+ * ever verify PSK binders on initial ClientHellos. */
+ uint8_t context[EVP_MAX_MD_SIZE];
+ unsigned context_len;
+ if (!EVP_Digest(CBS_data(&message), CBS_len(&message) - CBS_len(binders) - 2,
+ context, &context_len, digest, NULL)) {
+ return 0;
+ }
+
+ uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
+ CBS binder;
+ if (!tls13_psk_binder(ssl, verify_data, digest, session->master_key,
+ session->master_key_length, context, context_len,
+ hash_len) ||
+ /* We only consider the first PSK, so compare against the first binder. */
+ !CBS_get_u8_length_prefixed(binders, &binder)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ int binder_ok = CBS_len(&binder) == hash_len &&
+ CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ binder_ok = 1;
+#endif
+ if (!binder_ok) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 2570069..83ef679 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -17,6 +17,7 @@
#include <assert.h>
#include <string.h>
+#include <openssl/aead.h>
#include <openssl/bytestring.h>
#include <openssl/digest.h>
#include <openssl/err.h>
@@ -43,9 +44,10 @@
state_flush,
state_process_client_certificate,
state_process_client_certificate_verify,
+ state_process_channel_id,
state_process_client_finished,
state_send_new_session_ticket,
- state_flush_new_session_ticket,
+ state_flush_new_session_tickets,
state_done,
};
@@ -61,7 +63,7 @@
TLSEXT_TYPE_key_share)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
- return ssl_hs_error;
+ return 0;
}
int found_key_share;
@@ -103,53 +105,9 @@
/* Load the client random. */
if (client_hello.random_len != SSL3_RANDOM_SIZE) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return -1;
- }
- memcpy(ssl->s3->client_random, client_hello.random, client_hello.random_len);
-
- uint8_t alert = SSL_AD_DECODE_ERROR;
- SSL_SESSION *session = NULL;
- CBS pre_shared_key;
- if (ssl_early_callback_get_extension(&client_hello, &pre_shared_key,
- TLSEXT_TYPE_pre_shared_key) &&
- !ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &alert,
- &pre_shared_key)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
- return 0;
- }
-
- if (session != NULL &&
- /* Only resume if the session's version matches. */
- (session->ssl_version != ssl->version ||
- !ssl_client_cipher_list_contains_cipher(
- &client_hello, (uint16_t)SSL_CIPHER_get_id(session->cipher)))) {
- SSL_SESSION_free(session);
- session = NULL;
- }
-
- 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;
- }
- } else {
- /* 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);
- }
-
- if (ssl->ctx->dos_protection_cb != NULL &&
- ssl->ctx->dos_protection_cb(&client_hello) == 0) {
- /* Connection rejected for DOS reasons. */
- OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
+ memcpy(ssl->s3->client_random, client_hello.random, client_hello.random_len);
/* TLS 1.3 requires the peer only advertise the null compression. */
if (client_hello.compression_methods_len != 1 ||
@@ -169,20 +127,64 @@
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();
+ const uint16_t version = ssl3_protocol_version(ssl);
+
+ 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;
+ }
+
+ /* Limit to TLS 1.3 ciphers we know about. */
+ const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite);
+ if (candidate == NULL ||
+ SSL_CIPHER_get_min_version(candidate) > version ||
+ SSL_CIPHER_get_max_version(candidate) < version) {
+ 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;
+}
+
static enum ssl_hs_wait_t do_select_parameters(SSL *ssl, SSL_HANDSHAKE *hs) {
- if (!ssl->s3->session_reused) {
- /* 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;
- }
+ /* 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;
}
}
@@ -194,45 +196,116 @@
return ssl_hs_error;
}
- if (!ssl->s3->session_reused) {
- const SSL_CIPHER *cipher =
- ssl3_choose_cipher(ssl, &client_hello, ssl_get_cipher_preferences(ssl));
- if (cipher == NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+ /* 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. */
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ SSL_SESSION *session = NULL;
+ 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;
}
- ssl->s3->new_session->cipher = cipher;
+ if (!ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &binders,
+ &alert, &pre_shared_key)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+ }
}
- ssl->s3->tmp.new_cipher = ssl->s3->new_session->cipher;
- ssl->method->received_flight(ssl);
+ if (session != NULL &&
+ !ssl_session_is_resumable(ssl, session)) {
+ SSL_SESSION_free(session);
+ session = NULL;
+ }
+ /* Set up the new session, either using the original one as a template or
+ * creating a fresh one. */
+ 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;
+ }
+
+ ssl->s3->new_session->cipher = ssl->s3->tmp.new_cipher;
+
+ /* On new sessions, stash the SNI value in the session. */
+ if (ssl->s3->hs->hostname != NULL) {
+ ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->s3->hs->hostname);
+ if (ssl->s3->new_session->tlsext_hostname == NULL) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+ }
+ } else {
+ /* 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;
+ }
+
+ /* 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);
+ }
+
+ if (ssl->ctx->dos_protection_cb != NULL &&
+ ssl->ctx->dos_protection_cb(&client_hello) == 0) {
+ /* Connection rejected for DOS reasons. */
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+
+ /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
+ * deferred. Complete it now. */
+ if (!ssl_negotiate_alpn(ssl, &alert, &client_hello)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+ }
/* The PRF hash is now known. */
size_t hash_len =
EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
/* Derive resumption material. */
- uint8_t resumption_ctx[EVP_MAX_MD_SIZE] = {0};
uint8_t psk_secret[EVP_MAX_MD_SIZE] = {0};
if (ssl->s3->session_reused) {
- if (!tls13_resumption_context(ssl, resumption_ctx, hash_len,
- ssl->s3->new_session) ||
- !tls13_resumption_psk(ssl, psk_secret, hash_len,
- ssl->s3->new_session)) {
+ if (hash_len != (size_t) ssl->s3->new_session->master_key_length) {
return ssl_hs_error;
}
+ memcpy(psk_secret, ssl->s3->new_session->master_key, hash_len);
}
/* Set up the key schedule, hash in the ClientHello, and incorporate the PSK
* into the running secret. */
- if (!tls13_init_key_schedule(ssl, resumption_ctx, hash_len) ||
+ if (!tls13_init_key_schedule(ssl) ||
!tls13_advance_key_schedule(ssl, psk_secret, hash_len)) {
return ssl_hs_error;
}
+ ssl->method->received_flight(ssl);
+
/* Resolve ECDHE and incorporate it into the secret. */
int need_retry;
if (!resolve_ecdhe_secret(ssl, &need_retry, &client_hello)) {
@@ -259,7 +332,7 @@
!CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
!CBB_add_u16(&extensions, 2 /* length */) ||
!CBB_add_u16(&extensions, group_id) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
CBB_cleanup(&cbb);
return ssl_hs_error;
}
@@ -298,7 +371,7 @@
return ssl_hs_error;
}
- if (!ssl->method->hash_current_message(ssl)) {
+ if (!ssl_hash_current_message(ssl)) {
return ssl_hs_error;
}
@@ -316,18 +389,8 @@
!CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
!CBB_add_u16_length_prefixed(&body, &extensions) ||
!ssl_ext_pre_shared_key_add_serverhello(ssl, &extensions) ||
- !ssl_ext_key_share_add_serverhello(ssl, &extensions)) {
- goto err;
- }
-
- if (!ssl->s3->session_reused) {
- if (!CBB_add_u16(&extensions, TLSEXT_TYPE_signature_algorithms) ||
- !CBB_add_u16(&extensions, 0)) {
- goto err;
- }
- }
-
- if (!ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_ext_key_share_add_serverhello(ssl, &extensions) ||
+ !ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -349,7 +412,7 @@
if (!ssl->method->init_message(ssl, &cbb, &body,
SSL3_MT_ENCRYPTED_EXTENSIONS) ||
!ssl_add_serverhello_tlsext(ssl, &body) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
CBB_cleanup(&cbb);
return ssl_hs_error;
}
@@ -394,7 +457,7 @@
if (!ssl_add_client_CA_list(ssl, &body) ||
!CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
- !ssl->method->finish_message(ssl, &cbb)) {
+ !ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -458,9 +521,9 @@
static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
/* Update the secret to the master secret and derive traffic keys. */
if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
- !tls13_derive_traffic_secret_0(ssl) ||
- !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
- hs->server_traffic_secret_0, hs->hash_len)) {
+ !tls13_derive_application_secrets(ssl) ||
+ !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
+ hs->hash_len)) {
return ssl_hs_error;
}
@@ -476,7 +539,7 @@
ssl->s3->new_session->verify_result = X509_V_OK;
/* Skip this state. */
- hs->state = state_process_client_finished;
+ hs->state = state_process_channel_id;
return ssl_hs_ok;
}
@@ -485,14 +548,14 @@
if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
!tls13_process_certificate(ssl, allow_anonymous) ||
- !ssl->method->hash_current_message(ssl)) {
+ !ssl_hash_current_message(ssl)) {
return ssl_hs_error;
}
/* For historical reasons, the server's copy of the chain does not include the
* leaf while the client's does. */
- if (sk_X509_num(ssl->s3->new_session->cert_chain) > 0) {
- X509_free(sk_X509_shift(ssl->s3->new_session->cert_chain));
+ if (sk_X509_num(ssl->s3->new_session->x509_chain) > 0) {
+ X509_free(sk_X509_shift(ssl->s3->new_session->x509_chain));
}
hs->state = state_process_client_certificate_verify;
@@ -501,16 +564,32 @@
static enum ssl_hs_wait_t do_process_client_certificate_verify(
SSL *ssl, SSL_HANDSHAKE *hs) {
- if (ssl->s3->new_session->peer == NULL) {
+ if (ssl->s3->new_session->x509_peer == NULL) {
/* Skip this state. */
- hs->state = state_process_client_finished;
+ hs->state = state_process_channel_id;
return ssl_hs_ok;
}
if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
!tls13_process_certificate_verify(ssl) ||
- !ssl->method->hash_current_message(ssl)) {
- return 0;
+ !ssl_hash_current_message(ssl)) {
+ return ssl_hs_error;
+ }
+
+ hs->state = state_process_channel_id;
+ return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_channel_id(SSL *ssl, SSL_HANDSHAKE *hs) {
+ 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) ||
+ !ssl_hash_current_message(ssl)) {
+ return ssl_hs_error;
}
hs->state = state_process_client_finished;
@@ -521,35 +600,49 @@
SSL_HANDSHAKE *hs) {
if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
!tls13_process_finished(ssl) ||
- !ssl->method->hash_current_message(ssl) ||
+ !ssl_hash_current_message(ssl) ||
/* evp_aead_seal keys have already been switched. */
- !tls13_set_traffic_key(ssl, type_data, evp_aead_open,
- hs->client_traffic_secret_0, hs->hash_len) ||
- !tls13_finalize_keys(ssl)) {
+ !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
+ hs->hash_len) ||
+ !tls13_derive_resumption_secret(ssl)) {
return ssl_hs_error;
}
ssl->method->received_flight(ssl);
+
+ /* Refresh the session timestamp so that it is measured from ticket
+ * issuance. */
+ ssl_session_refresh_time(ssl, ssl->s3->new_session);
hs->state = state_send_new_session_ticket;
return ssl_hs_ok;
}
+/* 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;
+
static enum ssl_hs_wait_t do_send_new_session_ticket(SSL *ssl,
SSL_HANDSHAKE *hs) {
+ /* 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;
+ }
+
SSL_SESSION *session = ssl->s3->new_session;
- session->tlsext_tick_lifetime_hint = session->timeout;
+ if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
+ goto err;
+ }
/* TODO(svaldez): Add support for sending 0RTT through TicketEarlyDataInfo
* extension. */
- CBB cbb, body, ke_modes, auth_modes, ticket, extensions;
+ CBB cbb, body, ticket, extensions;
if (!ssl->method->init_message(ssl, &cbb, &body,
SSL3_MT_NEW_SESSION_TICKET) ||
- !CBB_add_u32(&body, session->tlsext_tick_lifetime_hint) ||
- !CBB_add_u8_length_prefixed(&body, &ke_modes) ||
- !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE) ||
- !CBB_add_u8_length_prefixed(&body, &auth_modes) ||
- !CBB_add_u8(&auth_modes, SSL_PSK_AUTH) ||
+ !CBB_add_u32(&body, session->timeout) ||
+ !CBB_add_u32(&body, session->ticket_age_add) ||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
!ssl_encrypt_ticket(ssl, &ticket, session) ||
!CBB_add_u16_length_prefixed(&body, &extensions)) {
@@ -557,21 +650,23 @@
}
/* Add a fake extension. See draft-davidben-tls-grease-01. */
- if (ssl->ctx->grease_enabled) {
- if (!CBB_add_u16(&extensions,
- ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
- !CBB_add_u16(&extensions, 0 /* empty */)) {
- goto err;
- }
+ if (!CBB_add_u16(&extensions,
+ ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
+ !CBB_add_u16(&extensions, 0 /* empty */)) {
+ goto err;
}
- if (!ssl->method->finish_message(ssl, &cbb)) {
+ if (!ssl_complete_message(ssl, &cbb)) {
goto err;
}
hs->session_tickets_sent++;
+ if (hs->session_tickets_sent >= kNumTickets) {
+ hs->state = state_flush_new_session_tickets;
+ } else {
+ hs->state = state_send_new_session_ticket;
+ }
- hs->state = state_flush_new_session_ticket;
return ssl_hs_write_message;
err:
@@ -579,17 +674,9 @@
return ssl_hs_error;
}
-/* 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;
-
-static enum ssl_hs_wait_t do_flush_new_session_ticket(SSL *ssl,
- SSL_HANDSHAKE *hs) {
- if (hs->session_tickets_sent >= kNumTickets) {
- hs->state = state_done;
- } else {
- hs->state = state_send_new_session_ticket;
- }
+static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL *ssl,
+ SSL_HANDSHAKE *hs) {
+ hs->state = state_done;
return ssl_hs_flush;
}
@@ -645,14 +732,17 @@
case state_process_client_certificate_verify:
ret = do_process_client_certificate_verify(ssl, hs);
break;
+ case state_process_channel_id:
+ ret = do_process_channel_id(ssl, hs);
+ break;
case state_process_client_finished:
ret = do_process_client_finished(ssl, hs);
break;
case state_send_new_session_ticket:
ret = do_send_new_session_ticket(ssl, hs);
break;
- case state_flush_new_session_ticket:
- ret = do_flush_new_session_ticket(ssl, hs);
+ case state_flush_new_session_tickets:
+ ret = do_flush_new_session_tickets(ssl, hs);
break;
case state_done:
ret = ssl_hs_ok;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index 8bcdf8f..ce42904 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -130,7 +130,7 @@
ssl3_new,
ssl3_free,
ssl3_get_message,
- ssl3_hash_current_message,
+ ssl3_get_current_message,
ssl3_release_current_message,
ssl3_read_app_data,
ssl3_read_change_cipher_spec,
@@ -140,6 +140,7 @@
ssl3_supports_cipher,
ssl3_init_message,
ssl3_finish_message,
+ ssl3_queue_message,
ssl3_write_message,
ssl3_send_change_cipher_spec,
ssl3_expect_flight,
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index 7041ce3..5931922 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -174,7 +174,7 @@
}
}
-size_t ssl_max_seal_overhead(const SSL *ssl) {
+size_t SSL_max_seal_overhead(const SSL *ssl) {
size_t ret = SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
if (SSL_is_dtls(ssl)) {
ret += DTLS1_RT_HEADER_LENGTH;
@@ -210,9 +210,20 @@
return ssl_open_record_partial;
}
- /* Check that the major version in the record matches. As of TLS 1.3, the
- * minor version is no longer checked. */
- if ((version >> 8) != SSL3_VERSION_MAJOR) {
+ int version_ok;
+ if (ssl->s3->aead_read_ctx == NULL) {
+ /* Only check the first byte. Enforcing beyond that can prevent decoding
+ * version negotiation failure alerts. */
+ version_ok = (version >> 8) == SSL3_VERSION_MAJOR;
+ } else if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
+ /* Earlier versions of TLS switch the record version. */
+ version_ok = version == ssl->version;
+ } else {
+ /* Starting TLS 1.3, the version field is frozen at {3, 1}. */
+ version_ok = version == TLS1_VERSION;
+ }
+
+ if (!version_ok) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
*out_alert = SSL_AD_PROTOCOL_VERSION;
return ssl_open_record_error;