Update to TLS 1.3 draft 18.
This is the squash of the following CLs:
https://boringssl-review.googlesource.com/c/12021/9
https://boringssl-review.googlesource.com/c/12022/9
https://boringssl-review.googlesource.com/c/12107/19
https://boringssl-review.googlesource.com/c/12141/22
https://boringssl-review.googlesource.com/c/12181/33
The Go portions were written by Nick Harper
BUG=112
Change-Id: I375a1fcead493ec3e0282e231ccc8d7c4dde5063
Reviewed-on: https://boringssl-review.googlesource.com/12300
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index c25683e..3fb4d8f 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -105,6 +105,7 @@
SSL,191,PATH_TOO_LONG
SSL,192,PEER_DID_NOT_RETURN_A_CERTIFICATE
SSL,193,PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE
+SSL,267,PRE_SHARED_KEY_MUST_BE_LAST
SSL,194,PROTOCOL_IS_SHUTDOWN
SSL,195,PSK_IDENTITY_NOT_FOUND
SSL,196,PSK_NO_CLIENT_CB
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1d56e75..827ae28 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -562,7 +562,7 @@
#define DTLS1_VERSION 0xfeff
#define DTLS1_2_VERSION 0xfefd
-#define TLS1_3_DRAFT_VERSION 0x7f10
+#define TLS1_3_DRAFT_VERSION 0x7f12
/* SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to
* |version|. If |version| is zero, the default minimum version is used. It
@@ -4536,6 +4536,7 @@
#define SSL_R_DUPLICATE_KEY_SHARE 264
#define SSL_R_NO_GROUPS_SPECIFIED 265
#define SSL_R_NO_SHARED_GROUP 266
+#define SSL_R_PRE_SHARED_KEY_MUST_BE_LAST 267
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 35d3145..dfaf78a 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -205,13 +205,14 @@
/* ExtensionType value from RFC4507 */
#define TLSEXT_TYPE_session_ticket 35
-/* ExtensionType values from draft-ietf-tls-tls13-16 */
+/* ExtensionType values from draft-ietf-tls-tls13-18 */
#define TLSEXT_TYPE_supported_groups 10
#define TLSEXT_TYPE_key_share 40
#define TLSEXT_TYPE_pre_shared_key 41
#define TLSEXT_TYPE_early_data 42
#define TLSEXT_TYPE_supported_versions 43
#define TLSEXT_TYPE_cookie 44
+#define TLSEXT_TYPE_psk_key_exchange_modes 45
/* ExtensionType value from RFC5746 */
#define TLSEXT_TYPE_renegotiate 0xff01
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 27cd7ba..5161f13 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -666,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) {
@@ -717,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);
@@ -758,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_complete_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) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 861f8bd..830e76b 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -748,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
@@ -792,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,
@@ -815,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);
@@ -832,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,
@@ -853,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. */
@@ -893,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];
@@ -922,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;
@@ -1054,14 +1062,20 @@
uint8_t *out_alert, CBS *contents);
int ssl_ext_key_share_add_serverhello(SSL *ssl, CBB *out);
+int ssl_ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out);
int ssl_ext_pre_shared_key_parse_serverhello(SSL *ssl, uint8_t *out_alert,
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);
+int ssl_ext_psk_key_exchange_modes_parse_clienthello(SSL *ssl,
+ uint8_t *out_alert,
+ 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. */
@@ -1598,12 +1612,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. */
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index c1d931f..2a7b7d4 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -511,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) {
@@ -593,7 +579,7 @@
X509_free(ret->x509_peer);
ret->x509_peer = NULL;
if (has_peer) {
- ret->x509_peer = parse_x509(&peer);
+ ret->x509_peer = ssl_parse_x509(&peer);
if (ret->x509_peer == NULL) {
goto err;
}
@@ -679,7 +665,7 @@
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;
}
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index c3809ef..7f6cf63 100644
--- a/ssl/ssl_cert.c
+++ b/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>
@@ -446,6 +447,20 @@
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,
uint8_t *out_leaf_sha256, CBS *cbs) {
STACK_OF(X509) *ret = sk_X509_new_null();
@@ -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;
}
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 5eede01..ac86baa 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -865,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));
@@ -915,27 +919,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;
}
@@ -964,28 +973,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;
}
@@ -2584,7 +2602,14 @@
!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() ||
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 3690548..24e4548 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1160,42 +1160,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;
}
@@ -1218,34 +1198,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 */);
}
@@ -1400,6 +1364,11 @@
return 1;
}
+ /* TLS 1.3 SCTs are included in the Certificate extensions. */
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+ 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);
@@ -1428,12 +1397,14 @@
static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert,
CBS *contents) {
+ ssl->s3->hs->scts_requested = 1;
return contents == NULL || CBS_len(contents) == 0;
}
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;
}
@@ -1915,11 +1886,32 @@
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 int ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
+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;
+}
+
+int ssl_ext_pre_shared_key_add_clienthello(SSL *ssl, CBB *out) {
uint16_t min_version, max_version;
if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
return 0;
@@ -1933,20 +1925,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);
}
@@ -1972,27 +1977,35 @@
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_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;
@@ -2018,6 +2031,49 @@
}
+/* 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);
+}
+
+int ssl_ext_psk_key_exchange_modes_parse_clienthello(SSL *ssl,
+ uint8_t *out_alert,
+ CBS *contents) {
+ 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 */
@@ -2039,21 +2095,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);
}
@@ -2496,9 +2549,9 @@
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,
dont_add_serverhello,
@@ -2627,7 +2680,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.
*
@@ -2655,6 +2709,11 @@
}
}
+ /* The PSK extension must be last, including after the padding. */
+ if (!ssl_ext_pre_shared_key_add_clienthello(ssl, &extensions)) {
+ goto err;
+ }
+
/* Discard empty extensions blocks. */
if (CBB_len(&extensions) == 0) {
CBB_discard_child(out);
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 87bdb86..abfcf0e 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1904,6 +1904,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/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
index 656a3d0..a997016 100644
--- a/ssl/test/runner/cipher_suites.go
+++ b/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/ssl/test/runner/common.go b/ssl/test/runner/common.go
index b55467a..446c8dc 100644
--- a/ssl/test/runner/common.go
+++ b/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
@@ -947,12 +943,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
@@ -999,6 +1000,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
@@ -1064,14 +1085,6 @@
// identity.
ExtraPSKIdentity bool
- // 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
-
// MissingKeyShare, if true, causes the TLS 1.3 implementation to skip
// sending a key_share extension and use the zero ECDHE secret
// instead.
@@ -1168,6 +1181,21 @@
// 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
}
func (c *Config) serverInit() {
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index bb036ad..39c2785 100644
--- a/ssl/test/runner/conn.go
+++ b/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.
@@ -1404,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,
@@ -1435,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)
@@ -1717,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{
@@ -1740,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 {
@@ -1752,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/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index dcae3e2..f8cf124 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -68,17 +68,19 @@
sctListSupported: true,
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
}
@@ -232,6 +238,7 @@
}
}
+ var pskCipherSuite *cipherSuite
if session != nil && c.config.time().Before(session.ticketExpiration) {
ticket := session.sessionTicket
if c.config.Bugs.FilterTicket != nil && len(ticket) > 0 {
@@ -246,20 +253,17 @@
}
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 {
@@ -319,7 +323,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
@@ -424,10 +432,10 @@
return err
}
keyShares[group] = curve
- hello.keyShares = append(hello.keyShares, keyShareEntry{
+ hello.keyShares = []keyShareEntry{{
group: group,
keyExchange: publicKey,
- })
+ }}
}
if c.config.Bugs.SecondClientHelloMissingKeyShare {
@@ -438,6 +446,9 @@
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()
@@ -604,11 +615,6 @@
// 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)
@@ -618,21 +624,19 @@
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: server sent invalid 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 {
@@ -657,9 +661,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 {
@@ -680,24 +684,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
@@ -738,6 +730,17 @@
return err
}
leaf := c.peerCertificates[0]
+ c.ocspResponse = certMsg.certificates[0].ocspResponse
+ c.sctList = certMsg.certificates[0].sctList
+
+ if c.config.Bugs.ExpectNoExtensionsOnIntermediate {
+ for _, cert := range certMsg.certificates[1:] {
+ if cert.ocspResponse != nil || cert.sctList != nil {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: unexpected extensions in the client certificate")
+ }
+ }
+ }
msg, err = c.readHandshake()
if err != nil {
@@ -790,7 +793,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())
@@ -855,8 +863,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)
@@ -969,7 +977,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())
@@ -1057,8 +1069,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())
@@ -1193,6 +1205,14 @@
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 len(serverExtensions.sctList) > 0 && c.vers >= VersionTLS13 {
+ return errors.New("tls: server advertised SCTs in ServerHello over TLS 1.3")
+ }
+
if serverExtensions.srtpProtectionProfile != 0 {
if serverExtensions.srtpMasterKeyIdentifier != "" {
return errors.New("tls: server selected SRTP MKI value")
@@ -1592,3 +1612,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/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 285587e..d2d7635 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/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,6 +147,8 @@
keyShares []keyShareEntry
trailingKeyShareData bool
pskIdentities []pskIdentity
+ pskKEModes []byte
+ pskBinders [][]uint8
hasEarlyData bool
earlyDataContext []byte
tls13Cookie []byte
@@ -159,13 +160,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,6 +193,8 @@
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) &&
@@ -203,13 +207,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 +241,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,16 +335,10 @@
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)
@@ -383,7 +396,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 +435,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()
@@ -614,52 +642,69 @@
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:]
}
+ 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 l != length-1 {
+ return false
+ }
+ m.pskKEModes = data[1:length]
case extensionEarlyData:
// draft-ietf-tls-tls13 section 6.3.2.5
if length < 1 {
@@ -793,7 +838,6 @@
keyShare keyShareEntry
hasPSKIdentity bool
pskIdentity uint16
- useCertAuth bool
earlyDataIndication bool
compressionMethod uint8
customExtension string
@@ -848,10 +892,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 +910,7 @@
protocolName.addBytes([]byte(m.unencryptedALPN))
}
} else {
- m.extensions.marshal(extensions, vers)
+ m.extensions.marshal(extensions)
if extensions.len() == 0 {
hello.discardChild()
}
@@ -962,11 +1002,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 +1036,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 +1068,6 @@
nextProtoNeg bool
nextProtos []string
ocspStapling bool
- ocspResponse []byte
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
@@ -1045,18 +1079,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 +1102,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 +1157,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 +1207,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 +1386,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 +1416,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 +1479,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 +1982,7 @@
raw []byte
version uint16
ticketLifetime uint32
- keModes []byte
- authModes []byte
+ ticketAgeAdd uint32
ticket []byte
customExtension string
hasGREASEExtension bool
@@ -1922,8 +1999,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 +2027,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 +2330,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/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 4b3df2d..1e0d313 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -16,6 +16,7 @@
"fmt"
"io"
"math/big"
+ "time"
)
// serverHandshakeState contains details of a server handshake in progress.
@@ -404,78 +405,75 @@
}
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 {
+ // 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()) {
+ 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")
+ }
+
+ 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 !cipherSuiteOk {
+ continue
+ }
+
}
- if !cipherSuiteOk {
+
+ // 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
}
- }
- // 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.suite = suite
+ 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,6 +481,15 @@
hs.hello.pskIdentity = 0
}
+ // 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 not resuming, select the cipher suite.
if hs.suite == nil {
var preferenceList, supportedList []uint16
@@ -515,26 +522,16 @@
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 +595,7 @@
}
if sendHelloRetryRequest {
+ oldClientHelloBytes := hs.clientHello.marshal()
hs.writeServerHash(helloRetryRequest.marshal())
c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal())
c.flushHandshake()
@@ -627,11 +625,11 @@
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 {
@@ -640,6 +638,7 @@
}
newClientHelloCopy.tls13Cookie = nil
}
+ newClientHelloCopy.pskBinders = oldClientHelloCopy.pskBinders
if !oldClientHelloCopy.equal(&newClientHelloCopy) {
return errors.New("tls: new ClientHello does not match")
@@ -649,6 +648,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 +737,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 +750,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 +778,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 +859,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 +889,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
}
@@ -941,15 +967,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()
@@ -1134,7 +1159,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
}
}
}
@@ -1340,7 +1365,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()
@@ -1428,7 +1457,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.
@@ -1876,3 +1907,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/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index dff8534..c311e99 100644
--- a/ssl/test/runner/prf.go
+++ b/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)
}
@@ -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())
}
@@ -459,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 (
@@ -474,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)
}
@@ -493,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/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 396d5f2..19ec131 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -170,6 +170,9 @@
var testOCSPResponse = []byte{1, 2, 3, 4}
var testSCTList = []byte{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, 4}, testSCTList...)
+
func initCertificates() {
for i := range testCerts {
cert, err := LoadX509KeyPair(path.Join(*resourceDir, testCerts[i].certFile), path.Join(*resourceDir, testCerts[i].keyFile))
@@ -5313,23 +5316,139 @@
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:",
+ })
+
+ // 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: []byte{1, 3, 3, 7},
},
},
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:",
})
}
@@ -5596,6 +5715,66 @@
shouldFail: true,
expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:",
})
+
+ 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:",
+ })
}
func addRenegotiationTests() {
@@ -7860,19 +8039,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},
},
@@ -7881,60 +8075,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() {
@@ -8593,33 +8764,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,
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index c0fe3c4..470f034 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -161,6 +161,7 @@
{ "-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/ssl/test/test_config.h b/ssl/test/test_config.h
index f4028cd..806edf1 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -120,6 +120,7 @@
int expect_cipher_aes = 0;
int expect_cipher_no_aes = 0;
std::string expect_peer_cert_file;
+ int resumption_delay = 0;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/ssl/tls13_both.c b/ssl/tls13_both.c
index 56ca9fc..242aa03 100644
--- a/ssl/tls13_both.c
+++ b/ssl/tls13_both.c
@@ -147,10 +147,10 @@
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) ||
+ 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;
}
@@ -164,7 +164,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) {
@@ -176,15 +176,138 @@
const int retain_sha256 =
ssl->server && ssl->ctx->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;
+ 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);
+ goto err;
+ }
+
+ switch (type) {
+ case TLSEXT_TYPE_status_request:
+ if (have_status_request) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ goto err;
+ }
+ status_request = extension;
+ have_status_request = 1;
+ break;
+ case TLSEXT_TYPE_certificate_timestamp:
+ if (have_sct) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ goto err;
+ }
+ sct = extension;
+ have_sct = 1;
+ break;
+ default:
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+ 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 (CBS_len(&sct) == 0) {
+ 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);
@@ -324,17 +447,77 @@
}
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_complete_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)) {
+ 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)) {
+ 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(
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index a5b2e85..e540b77 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -165,11 +165,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_complete_message(ssl, &cbb)) {
- CBB_cleanup(&cbb);
+ if (!ssl_write_client_hello(ssl)) {
return ssl_hs_error;
}
@@ -227,8 +223,8 @@
}
/* Parse out the extensions. */
- int have_key_share = 0, have_pre_shared_key = 0, have_sigalgs = 0;
- CBS key_share, pre_shared_key, sigalgs;
+ int have_key_share = 0, have_pre_shared_key = 0;
+ CBS key_share, pre_shared_key;
while (CBS_len(&extensions) != 0) {
uint16_t type;
CBS extension;
@@ -258,15 +254,6 @@
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);
@@ -274,8 +261,8 @@
}
}
- /* 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;
@@ -337,20 +324,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;
}
@@ -370,12 +354,6 @@
}
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 &&
@@ -505,7 +483,7 @@
!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;
}
@@ -621,11 +599,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;
}
@@ -711,13 +689,10 @@
ssl_session_refresh_time(ssl, session);
- CBS cbs, ke_modes, auth_modes, ticket, extensions;
+ 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) ||
@@ -728,13 +703,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/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 95132cc..c846d9c 100644
--- a/ssl/tls13_enc.c
+++ b/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,28 +120,6 @@
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));
@@ -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,115 @@
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;
+ }
+
+ if (CBS_len(&binder) != hash_len ||
+ CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) != 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index ca7b17e..0db16d9 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -110,16 +110,38 @@
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)) {
+ CBS psk_key_exchange_modes;
+ if (ssl_early_callback_get_extension(&client_hello, &psk_key_exchange_modes,
+ TLSEXT_TYPE_psk_key_exchange_modes) &&
+ !ssl_ext_psk_key_exchange_modes_parse_clienthello(
+ ssl, &alert, &psk_key_exchange_modes)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
return 0;
}
+ SSL_SESSION *session = NULL;
+ CBS binders;
+ if (hs->accept_psk_mode) {
+ CBS pre_shared_key;
+ if (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 0;
+ }
+
+ if (!ssl_ext_pre_shared_key_parse_clienthello(ssl, &session, &binders,
+ &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 ||
@@ -136,6 +158,13 @@
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) {
@@ -266,20 +295,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;
}
@@ -367,18 +393,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_complete_message(ssl, &cbb)) {
+ !ssl_ext_key_share_add_serverhello(ssl, &extensions) ||
+ !ssl_complete_message(ssl, &cbb)) {
goto err;
}
@@ -509,9 +525,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;
}
@@ -590,9 +606,9 @@
!tls13_process_finished(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;
}
@@ -611,19 +627,26 @@
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;
+ 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->timeout) ||
- !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->ticket_age_add) ||
!CBB_add_u16_length_prefixed(&body, &ticket) ||
!ssl_encrypt_ticket(ssl, &ticket, session) ||
!CBB_add_u16_length_prefixed(&body, &extensions)) {