Adding support for draft 21 as a TLS 1.3 variant. Change-Id: I46686aea9b68105cfe70a11db0e88052781e179c Reviewed-on: https://boringssl-review.googlesource.com/22164 Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: David Benjamin <davidben@google.com> Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index a2d3ce7..2dd278c 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -592,6 +592,7 @@ #define DTLS1_2_VERSION 0xfefd #define TLS1_3_DRAFT_VERSION 0x7f12 +#define TLS1_3_DRAFT21_VERSION 0x7f15 #define TLS1_3_EXPERIMENT_VERSION 0x7e01 #define TLS1_3_EXPERIMENT2_VERSION 0x7e02 #define TLS1_3_EXPERIMENT3_VERSION 0x7e03 @@ -3234,6 +3235,7 @@ tls13_experiment = 1, tls13_experiment2 = 2, tls13_experiment3 = 3, + tls13_draft21 = 4, }; // SSL_CTX_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index 6754cec..ae0be88 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h
@@ -299,6 +299,7 @@ #define SSL3_MT_CLIENT_HELLO 1 #define SSL3_MT_SERVER_HELLO 2 #define SSL3_MT_NEW_SESSION_TICKET 4 +#define SSL3_MT_END_OF_EARLY_DATA 5 #define SSL3_MT_HELLO_RETRY_REQUEST 6 #define SSL3_MT_ENCRYPTED_EXTENSIONS 8 #define SSL3_MT_CERTIFICATE 11 @@ -313,6 +314,7 @@ #define SSL3_MT_KEY_UPDATE 24 #define SSL3_MT_NEXT_PROTO 67 #define SSL3_MT_CHANNEL_ID 203 +#define SSL3_MT_MESSAGE_HASH 254 #define DTLS1_MT_HELLO_VERIFY_REQUEST 3 // The following are legacy aliases for consumers which use
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 0237e32..f62ee1f 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h
@@ -214,6 +214,7 @@ #define TLSEXT_TYPE_cookie 44 #define TLSEXT_TYPE_psk_key_exchange_modes 45 #define TLSEXT_TYPE_ticket_early_data_info 46 +#define TLSEXT_TYPE_certificate_authorities 47 // ExtensionType value from RFC5746 #define TLSEXT_TYPE_renegotiate 0xff01
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc index 48466fb..f907939 100644 --- a/ssl/handshake_client.cc +++ b/ssl/handshake_client.cc
@@ -500,9 +500,8 @@ return ssl_hs_ok; } - if (!tls13_init_early_key_schedule(hs) || - !tls13_advance_key_schedule(hs, ssl->session->master_key, - ssl->session->master_key_length) || + if (!tls13_init_early_key_schedule(hs, ssl->session->master_key, + ssl->session->master_key_length) || !tls13_derive_early_secrets(hs) || !tls13_set_traffic_key(ssl, evp_aead_seal, hs->early_traffic_secret, hs->hash_len)) {
diff --git a/ssl/internal.h b/ssl/internal.h index 2b3335b..55bece9 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -392,6 +392,10 @@ // call this function before the version is determined. uint16_t ssl_protocol_version(const SSL *ssl); +// ssl_is_draft21 returns whether the version corresponds to a draft21 TLS 1.3 +// variant. +bool ssl_is_draft21(uint16_t version); + // ssl_is_resumption_experiment returns whether the version corresponds to a // TLS 1.3 resumption experiment. bool ssl_is_resumption_experiment(uint16_t version); @@ -543,6 +547,16 @@ // to call this function after the handshake buffer is released. bool InitHash(uint16_t version, const SSL_CIPHER *cipher); + // UpdateForHelloRetryRequest resets the rolling hash with the + // HelloRetryRequest construction. It returns true on success and false on + // failure. It is an error to call this function before the handshake buffer + // is released. + bool UpdateForHelloRetryRequest(); + + // CopyHashContext copies the hash context into |ctx| and returns true on + // success. + bool CopyHashContext(EVP_MD_CTX *ctx); + Span<const uint8_t> buffer() { return MakeConstSpan(reinterpret_cast<const uint8_t *>(buffer_->data), buffer_->length); @@ -1157,6 +1171,9 @@ uint8_t *out_alert, CBS *cbs); +// ssl_has_client_CAs returns there are configured CAs. +bool ssl_has_client_CAs(SSL *ssl); + // ssl_add_client_CA_list adds the configured CA list to |cbb| in the format // used by a TLS CertificateRequest message. It returns one on success and zero // on error. @@ -1177,14 +1194,16 @@ // TLS 1.3 key derivation. // tls13_init_key_schedule initializes the handshake hash and key derivation -// 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_HANDSHAKE *hs); +// state, and incorporates the PSK. 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_HANDSHAKE *hs, const uint8_t *psk, + size_t psk_len); // tls13_init_early_key_schedule initializes the handshake hash and key -// derivation state from the resumption secret to derive the early secrets. It -// returns one on success and zero on error. -int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs); +// derivation state from the resumption secret and incorporates the PSK to +// derive the early secrets. It returns one on success and zero on error. +int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk, + size_t psk_len); // tls13_advance_key_schedule incorporates |in| into the key schedule with // HKDF-Extract. It returns one on success and zero on error. @@ -1231,6 +1250,11 @@ int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, int is_server); +// tls13_derive_session_psk calculates the PSK for this session based on the +// resumption master secret and |nonce|. It returns true on success, and false +// on failure. +bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce); + // 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.
diff --git a/ssl/s3_pkt.cc b/ssl/s3_pkt.cc index 285abb3..e6518ba 100644 --- a/ssl/s3_pkt.cc +++ b/ssl/s3_pkt.cc
@@ -306,7 +306,7 @@ if (type == SSL3_RT_HANDSHAKE) { // If reading 0-RTT data, reject handshake data. 0-RTT data is terminated // by an alert. - if (is_early_data_read) { + if (!ssl_is_draft21(ssl->version) && is_early_data_read) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); *out_alert = SSL_AD_UNEXPECTED_MESSAGE; return ssl_open_record_error; @@ -335,7 +335,8 @@ // Handle the end_of_early_data alert. static const uint8_t kEndOfEarlyData[2] = {SSL3_AL_WARNING, TLS1_AD_END_OF_EARLY_DATA}; - if (is_early_data_read && type == SSL3_RT_ALERT && body == kEndOfEarlyData) { + if (!ssl_is_draft21(ssl->version) && is_early_data_read && + type == SSL3_RT_ALERT && body == kEndOfEarlyData) { // Stop accepting early data. ssl->s3->hs->can_early_read = false; return ssl_open_record_discard;
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc index cd586a0..9a3eef3 100644 --- a/ssl/ssl_cert.cc +++ b/ssl/ssl_cert.cc
@@ -704,6 +704,17 @@ return ret; } +bool ssl_has_client_CAs(SSL *ssl) { + STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA; + if (names == NULL) { + names = ssl->ctx->client_CA; + } + if (names == NULL) { + return false; + } + return sk_CRYPTO_BUFFER_num(names) > 0; +} + int ssl_add_client_CA_list(SSL *ssl, CBB *cbb) { CBB child, name_cbb; if (!CBB_add_u16_length_prefixed(cbb, &child)) {
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index ab88134..122313f 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc
@@ -986,6 +986,13 @@ // Process any buffered post-handshake messages. SSLMessage msg; if (ssl->method->get_message(ssl, &msg)) { + // If we received an interrupt in early read (EndOfEarlyData), loop again + // for the handshake to process it. + if (SSL_in_init(ssl)) { + ssl->s3->hs->can_early_read = false; + continue; + } + // Handle the post-handshake message and try again. if (!ssl_do_post_handshake(ssl, msg)) { ssl_set_read_error(ssl);
diff --git a/ssl/ssl_transcript.cc b/ssl/ssl_transcript.cc index 8486e5f..2033dfd 100644 --- a/ssl/ssl_transcript.cc +++ b/ssl/ssl_transcript.cc
@@ -209,6 +209,30 @@ return EVP_MD_CTX_md(hash_.get()); } +bool SSLTranscript::UpdateForHelloRetryRequest() { + if (buffer_) { + buffer_->length = 0; + } + + uint8_t old_hash[EVP_MAX_MD_SIZE]; + size_t hash_len; + if (!GetHash(old_hash, &hash_len)) { + return false; + } + const uint8_t header[4] = {SSL3_MT_MESSAGE_HASH, 0, 0, + static_cast<uint8_t>(hash_len)}; + if (!EVP_DigestInit_ex(hash_.get(), Digest(), nullptr) || + !Update(header) || + !Update(MakeConstSpan(old_hash, hash_len))) { + return false; + } + return true; +} + +bool SSLTranscript::CopyHashContext(EVP_MD_CTX *ctx) { + return EVP_MD_CTX_copy_ex(ctx, hash_.get()); +} + bool SSLTranscript::Update(Span<const uint8_t> in) { // Depending on the state of the handshake, either the handshake buffer may be // active, the rolling hash, or both.
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc index ccae6ef..1f30b41 100644 --- a/ssl/ssl_versions.cc +++ b/ssl/ssl_versions.cc
@@ -35,6 +35,7 @@ return true; case TLS1_3_DRAFT_VERSION: + case TLS1_3_DRAFT21_VERSION: case TLS1_3_EXPERIMENT_VERSION: case TLS1_3_EXPERIMENT2_VERSION: case TLS1_3_EXPERIMENT3_VERSION: @@ -63,6 +64,7 @@ TLS1_3_EXPERIMENT2_VERSION, TLS1_3_EXPERIMENT_VERSION, TLS1_3_DRAFT_VERSION, + TLS1_3_DRAFT21_VERSION, TLS1_2_VERSION, TLS1_1_VERSION, TLS1_VERSION, @@ -106,6 +108,7 @@ static const char *ssl_version_to_string(uint16_t version) { switch (version) { case TLS1_3_DRAFT_VERSION: + case TLS1_3_DRAFT21_VERSION: case TLS1_3_EXPERIMENT_VERSION: case TLS1_3_EXPERIMENT2_VERSION: case TLS1_3_EXPERIMENT3_VERSION: @@ -138,6 +141,7 @@ switch (version) { // Report TLS 1.3 draft versions as TLS 1.3 in the public API. case TLS1_3_DRAFT_VERSION: + case TLS1_3_DRAFT21_VERSION: case TLS1_3_EXPERIMENT_VERSION: case TLS1_3_EXPERIMENT2_VERSION: case TLS1_3_EXPERIMENT3_VERSION: @@ -152,6 +156,7 @@ // used in context where that does not matter. static bool api_version_to_wire(uint16_t *out, uint16_t version) { if (version == TLS1_3_DRAFT_VERSION || + version == TLS1_3_DRAFT21_VERSION || version == TLS1_3_EXPERIMENT_VERSION || version == TLS1_3_EXPERIMENT2_VERSION || version == TLS1_3_EXPERIMENT3_VERSION) { @@ -301,34 +306,42 @@ bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) { SSL *const ssl = hs->ssl; - // As a client, only allow the configured TLS 1.3 variant. As a server, - // support all TLS 1.3 variants as long as tls13_variant is set to a - // non-default value. - if (ssl->server) { - if (ssl->tls13_variant == tls13_default && - (version == TLS1_3_EXPERIMENT_VERSION || - version == TLS1_3_EXPERIMENT2_VERSION || - version == TLS1_3_EXPERIMENT3_VERSION)) { - return false; - } - } else { - if ((ssl->tls13_variant != tls13_experiment && - version == TLS1_3_EXPERIMENT_VERSION) || - (ssl->tls13_variant != tls13_experiment2 && - version == TLS1_3_EXPERIMENT2_VERSION) || - (ssl->tls13_variant != tls13_experiment3 && - version == TLS1_3_EXPERIMENT3_VERSION) || - (ssl->tls13_variant != tls13_default && - version == TLS1_3_DRAFT_VERSION)) { - return false; - } + uint16_t protocol_version; + if (!method_supports_version(ssl->method, version) || + !ssl_protocol_version_from_wire(&protocol_version, version) || + hs->min_version > protocol_version || + protocol_version > hs->max_version) { + return false; } - uint16_t protocol_version; - return method_supports_version(ssl->method, version) && - ssl_protocol_version_from_wire(&protocol_version, version) && - hs->min_version <= protocol_version && - protocol_version <= hs->max_version; + // TLS 1.3 variants must additionally match |tls13_variant|. + if (protocol_version != TLS1_3_VERSION || + (ssl->tls13_variant == tls13_experiment && + version == TLS1_3_EXPERIMENT_VERSION) || + (ssl->tls13_variant == tls13_experiment2 && + version == TLS1_3_EXPERIMENT2_VERSION) || + (ssl->tls13_variant == tls13_experiment3 && + version == TLS1_3_EXPERIMENT3_VERSION) || + (ssl->tls13_variant == tls13_draft21 && + version == TLS1_3_DRAFT21_VERSION) || + (ssl->tls13_variant == tls13_default && + version == TLS1_3_DRAFT_VERSION)) { + return true; + } + + // The server, when not configured at |tls13_default|, should additionally + // enable all variants, except draft-21 which is implemented solely for QUIC + // interop testing and will not be deployed. Currently, this is to implement + // the draft-18 vs. experiments field trials. In the future, this will be to + // transition cleanly to a future draft-22 which hopefully includes the + // deployability fixes. + if (ssl->server && + ssl->tls13_variant != tls13_default && + version != TLS1_3_DRAFT21_VERSION) { + return true; + } + + return false; } bool ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb) { @@ -375,6 +388,10 @@ return false; } +bool ssl_is_draft21(uint16_t version) { + return version == TLS1_3_DRAFT21_VERSION; +} + bool ssl_is_resumption_experiment(uint16_t version) { return version == TLS1_3_EXPERIMENT_VERSION || version == TLS1_3_EXPERIMENT2_VERSION ||
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc index 47224ea..a7bfec4 100644 --- a/ssl/t1_lib.cc +++ b/ssl/t1_lib.cc
@@ -1800,11 +1800,21 @@ static bool ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { SSL *const ssl = hs->ssl; + hs->needs_psk_binder = false; if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL || ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION) { return true; } + // Per draft-ietf-tls-tls13-21 section 4.1.4, skip offering the session if the + // selected cipher in HelloRetryRequest does not match. This avoids performing + // the transcript hash transformation for multiple hashes. + if (hs->received_hello_retry_request && + ssl_is_draft21(ssl->version) && + ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) { + return true; + } + struct OPENSSL_timeval now; ssl_get_current_time(ssl, &now); uint32_t ticket_age = 1000 * (now.tv_sec - ssl->session->time);
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index d439cb5..7eca21d 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc
@@ -1278,6 +1278,9 @@ if (!config->use_client_ca_list.empty()) { if (config->use_client_ca_list == "<NULL>") { SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr); + } else if (config->use_client_ca_list == "<EMPTY>") { + bssl::UniquePtr<STACK_OF(X509_NAME)> names; + SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release()); } else { bssl::UniquePtr<STACK_OF(X509_NAME)> names = DecodeHexX509Names(config->use_client_ca_list); @@ -2396,14 +2399,13 @@ } if (expect_new_session) { - bool got_early_data_info = + bool got_early_data = GetTestState(ssl)->new_session->ticket_max_early_data != 0; - if (config->expect_early_data_info != got_early_data_info) { - fprintf( - stderr, - "new session did%s include ticket_early_data_info, but we expected " - "the opposite\n", - got_early_data_info ? "" : " not"); + if (config->expect_ticket_supports_early_data != got_early_data) { + fprintf(stderr, + "new session did%s support early data, but we expected the " + "opposite\n", + got_early_data ? "" : " not"); return false; } }
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index eee1337..9e0deef 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go
@@ -34,6 +34,7 @@ // A draft version of TLS 1.3 that is sent over the wire for the current draft. const ( tls13DraftVersion = 0x7f12 + tls13Draft21Version = 0x7f15 tls13ExperimentVersion = 0x7e01 tls13Experiment2Version = 0x7e02 tls13Experiment3Version = 0x7e03 @@ -44,10 +45,12 @@ TLS13Experiment = 1 TLS13Experiment2 = 2 TLS13Experiment3 = 3 + TLS13Draft21 = 4 ) var allTLSWireVersions = []uint16{ tls13DraftVersion, + tls13Draft21Version, tls13Experiment3Version, tls13Experiment2Version, tls13ExperimentVersion, @@ -91,6 +94,7 @@ typeServerHello uint8 = 2 typeHelloVerifyRequest uint8 = 3 typeNewSessionTicket uint8 = 4 + typeEndOfEarlyData uint8 = 5 // draft-ietf-tls-tls13-21 typeHelloRetryRequest uint8 = 6 // draft-ietf-tls-tls13-16 typeEncryptedExtensions uint8 = 8 // draft-ietf-tls-tls13-16 typeCertificate uint8 = 11 @@ -104,6 +108,7 @@ typeKeyUpdate uint8 = 24 // draft-ietf-tls-tls13-16 typeNextProtocol uint8 = 67 // Not IANA assigned typeChannelID uint8 = 203 // Not IANA assigned + typeMessageHash uint8 = 254 // draft-ietf-tls-tls13-21 ) // TLS compression types. @@ -131,6 +136,7 @@ extensionCookie uint16 = 44 // draft-ietf-tls-tls13-16 extensionPSKKeyExchangeModes uint16 = 45 // draft-ietf-tls-tls13-18 extensionTicketEarlyDataInfo uint16 = 46 // draft-ietf-tls-tls13-18 + extensionCertificateAuthorities uint16 = 47 // draft-ietf-tls-tls13-21 extensionCustom uint16 = 1234 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionRenegotiationInfo uint16 = 0xff01 @@ -456,8 +462,7 @@ // MaxEarlyDataSize controls the maximum number of bytes that the // server will accept in early data and advertise in a // NewSessionTicketMsg. If 0, no early data will be accepted and - // the TicketEarlyDataInfo extension in the NewSessionTicketMsg - // will be omitted. + // the early_data extension in the NewSessionTicketMsg will be omitted. MaxEarlyDataSize uint32 // SRTPProtectionProfiles, if not nil, is the list of SRTP @@ -581,10 +586,14 @@ // message. SkipFinished bool - // SkipEndOfEarlyData causes the implementation to skip the - // end_of_early_data alert. + // SkipEndOfEarlyData causes the implementation to skip + // end_of_early_data. SkipEndOfEarlyData bool + // NonEmptyEndOfEarlyData causes the implementation to end an extra byte in the + // EndOfEarlyData. + NonEmptyEndOfEarlyData bool + // SkipCertificateVerify, if true causes peer to skip sending a // CertificateVerify message after the Certificate message. SkipCertificateVerify bool @@ -755,6 +764,10 @@ // if a TLS 1.3 PSK is offered. ExpectNoTLS13PSK bool + // ExpectNoTLS13PSKAfterHRR, if true, causes the server to fail the connection + // if a TLS 1.3 PSK is offered after HRR. + ExpectNoTLS13PSKAfterHRR bool + // RequireExtendedMasterSecret, if true, requires that the peer support // the extended master secret option. RequireExtendedMasterSecret bool @@ -1054,13 +1067,13 @@ // receipt of a NewSessionTicket message. ExpectNoNewSessionTicket bool - // DuplicateTicketEarlyDataInfo causes an extra empty extension of - // ticket_early_data_info to be sent in NewSessionTicket. - DuplicateTicketEarlyDataInfo bool + // DuplicateTicketEarlyData causes an extra empty extension of early_data to + // be sent in NewSessionTicket. + DuplicateTicketEarlyData bool - // ExpectTicketEarlyDataInfo, if true, means that the client will fail upon - // absence of the ticket_early_data_info extension. - ExpectTicketEarlyDataInfo bool + // ExpectTicketEarlyData, if true, means that the client will fail upon + // absence of the early_data extension. + ExpectTicketEarlyData bool // ExpectTicketAge, if non-zero, is the expected age of the ticket that the // server receives from the client. @@ -1308,6 +1321,10 @@ // the specified curve in a HelloRetryRequest. SendHelloRetryRequestCurve CurveID + // SendHelloRetryRequestCipherSuite, if non-zero, causes the server to send + // the specified cipher suite in a HelloRetryRequest. + SendHelloRetryRequestCipherSuite uint16 + // SendHelloRetryRequestCookie, if not nil, contains a cookie to be // sent by the server in HelloRetryRequest. SendHelloRetryRequestCookie []byte @@ -1345,6 +1362,14 @@ // a TLS 1.3 CertificateRequest. SendRequestContext []byte + // OmitCertificateRequestAlgorithms, if true, omits the signature_algorithm + // extension in a TLS 1.3 CertificateRequest. + OmitCertificateRequestAlgorithms bool + + // SendCustomCertificateRequest, if non-zero, send an additional custom + // extension in a TLS 1.3 CertificateRequest. + SendCustomCertificateRequest uint16 + // SendSNIWarningAlert, if true, causes the server to send an // unrecognized_name alert before the ServerHello. SendSNIWarningAlert bool @@ -1432,6 +1457,10 @@ // renegotiation handshakes. RenegotiationCertificate *Certificate + // ExpectNoCertificateAuthoritiesExtension, if true, causes the client to + // reject CertificateRequest with the CertificateAuthorities extension. + ExpectNoCertificateAuthoritiesExtension bool + // UseLegacySigningAlgorithm, if non-zero, is the signature algorithm // to use when signing in TLS 1.1 and earlier where algorithms are not // negotiated. @@ -1576,7 +1605,7 @@ switch vers { case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: return vers, true - case tls13DraftVersion, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version: + case tls13DraftVersion, tls13Draft21Version, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version: return VersionTLS13, true } } @@ -1584,6 +1613,10 @@ return 0, false } +func isDraft21(vers uint16) bool { + return vers == tls13Draft21Version +} + func isResumptionExperiment(vers uint16) bool { return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Experiment3Version } @@ -1603,6 +1636,7 @@ if (c.TLS13Variant != TLS13Experiment && wireVers == tls13ExperimentVersion) || (c.TLS13Variant != TLS13Experiment2 && wireVers == tls13Experiment2Version) || (c.TLS13Variant != TLS13Experiment3 && wireVers == tls13Experiment3Version) || + (c.TLS13Variant != TLS13Draft21 && wireVers == tls13Draft21Version) || (c.TLS13Variant != TLS13Default && wireVers == tls13DraftVersion) { return 0, false }
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index 71b52f2..6a2c59c 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go
@@ -1274,7 +1274,8 @@ m = new(helloRetryRequestMsg) case typeNewSessionTicket: m = &newSessionTicketMsg{ - version: c.vers, + vers: c.wireVersion, + isDTLS: c.isDTLS, } case typeEncryptedExtensions: m = new(encryptedExtensionsMsg) @@ -1284,6 +1285,7 @@ } case typeCertificateRequest: m = &certificateRequestMsg{ + vers: c.wireVersion, hasSignatureAlgorithm: c.vers >= VersionTLS12, hasRequestContext: c.vers >= VersionTLS13, } @@ -1309,6 +1311,8 @@ m = new(channelIDMsg) case typeKeyUpdate: m = new(keyUpdateMsg) + case typeEndOfEarlyData: + m = new(endOfEarlyDataMsg) default: return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } @@ -1469,8 +1473,8 @@ return errors.New("tls: no GREASE ticket extension found") } - if c.config.Bugs.ExpectTicketEarlyDataInfo && newSessionTicket.maxEarlyDataSize == 0 { - return errors.New("tls: no ticket_early_data_info extension found") + if c.config.Bugs.ExpectTicketEarlyData && newSessionTicket.maxEarlyDataSize == 0 { + return errors.New("tls: no early_data ticket extension found") } if c.config.Bugs.ExpectNoNewSessionTicket { @@ -1497,6 +1501,10 @@ earlyALPN: c.clientProtocol, } + if isDraft21(c.wireVersion) { + session.masterSecret = deriveSessionPSK(cipherSuite, c.wireVersion, c.resumptionSecret, newSessionTicket.ticketNonce) + } + cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) _, ok := c.config.ClientSessionCache.Get(cacheKey) if !ok || !c.config.Bugs.UseFirstSessionTicket { @@ -1537,7 +1545,7 @@ if c.config.Bugs.RejectUnsolicitedKeyUpdate { return errors.New("tls: unexpected KeyUpdate message") } - if err := c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.in.trafficSecret)); err != nil { + if err := c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.wireVersion, c.in.trafficSecret)); err != nil { return err } if keyUpdate.keyUpdateRequest == keyUpdateRequested { @@ -1571,7 +1579,7 @@ return errors.New("tls: received invalid KeyUpdate message") } - return c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.in.trafficSecret)) + return c.useInTrafficSecret(c.in.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.wireVersion, c.in.trafficSecret)) } func (c *Conn) Renegotiate() error { @@ -1789,9 +1797,16 @@ } if c.vers >= VersionTLS13 { - // TODO(davidben): What should we do with useContext? See - // https://github.com/tlswg/tls13-spec/issues/546 - return hkdfExpandLabel(c.cipherSuite.hash(), c.exporterSecret, label, context, length), nil + if isDraft21(c.wireVersion) { + hash := c.cipherSuite.hash() + exporterKeyingLabel := []byte("exporter") + contextHash := hash.New() + contextHash.Write(context) + exporterContext := hash.New().Sum(nil) + derivedSecret := hkdfExpandLabel(c.cipherSuite.hash(), c.wireVersion, c.exporterSecret, label, exporterContext, hash.Size()) + return hkdfExpandLabel(c.cipherSuite.hash(), c.wireVersion, derivedSecret, exporterKeyingLabel, contextHash.Sum(nil), length), nil + } + return hkdfExpandLabel(c.cipherSuite.hash(), c.wireVersion, c.exporterSecret, label, context, length), nil } seedLen := len(c.clientRandom) + len(c.serverRandom) @@ -1825,7 +1840,7 @@ return false } -func (c *Conn) SendNewSessionTicket() error { +func (c *Conn) SendNewSessionTicket(nonce []byte) error { if c.isClient || c.vers < VersionTLS13 { return errors.New("tls: cannot send post-handshake NewSessionTicket") } @@ -1845,12 +1860,17 @@ // TODO(davidben): Allow configuring these values. m := &newSessionTicketMsg{ - version: c.vers, - ticketLifetime: uint32(24 * time.Hour / time.Second), - duplicateEarlyDataInfo: c.config.Bugs.DuplicateTicketEarlyDataInfo, - customExtension: c.config.Bugs.CustomTicketExtension, - ticketAgeAdd: ticketAgeAdd, - maxEarlyDataSize: c.config.MaxEarlyDataSize, + vers: c.wireVersion, + isDTLS: c.isDTLS, + ticketLifetime: uint32(24 * time.Hour / time.Second), + duplicateEarlyDataExtension: c.config.Bugs.DuplicateTicketEarlyData, + customExtension: c.config.Bugs.CustomTicketExtension, + ticketAgeAdd: ticketAgeAdd, + maxEarlyDataSize: c.config.MaxEarlyDataSize, + } + + if isDraft21(c.wireVersion) { + m.ticketNonce = nonce } if c.config.Bugs.SendTicketLifetime != 0 { @@ -1868,6 +1888,10 @@ earlyALPN: []byte(c.clientProtocol), } + if isDraft21(c.wireVersion) { + state.masterSecret = deriveSessionPSK(c.cipherSuite, c.wireVersion, c.resumptionSecret, nonce) + } + if !c.config.Bugs.SendEmptySessionTicket { var err error m.ticket, err = c.encryptTicket(&state) @@ -1901,7 +1925,7 @@ if err := c.flushHandshake(); err != nil { return err } - c.useOutTrafficSecret(c.out.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.out.trafficSecret)) + c.useOutTrafficSecret(c.out.wireVersion, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), c.wireVersion, c.out.trafficSecret)) return nil }
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index a07a815..ba34647 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go
@@ -372,7 +372,14 @@ c.writeV2Record(helloBytes) } else { if len(hello.pskIdentities) > 0 { - generatePSKBinders(hello, pskCipherSuite, session.masterSecret, []byte{}, c.config) + version := session.wireVersion + // We may have a pre-1.3 session if SendBothTickets is + // set. Fill in an arbitrary TLS 1.3 version to compute + // the binder. + if session.vers < VersionTLS13 { + version = tls13DraftVersion + } + generatePSKBinders(version, hello, pskCipherSuite, session.masterSecret, []byte{}, []byte{}, c.config) } helloBytes = hello.marshal() @@ -403,10 +410,16 @@ // Derive early write keys and set Conn state to allow early writes. if sendEarlyData { - finishedHash := newFinishedHash(session.vers, pskCipherSuite) + finishedHash := newFinishedHash(session.wireVersion, c.isDTLS, pskCipherSuite) finishedHash.addEntropy(session.masterSecret) finishedHash.Write(helloBytes) - earlyTrafficSecret := finishedHash.deriveSecret(earlyTrafficLabel) + + earlyLabel := earlyTrafficLabel + if isDraft21(session.wireVersion) { + earlyLabel = earlyTrafficLabelDraft21 + } + + earlyTrafficSecret := finishedHash.deriveSecret(earlyLabel) c.useOutTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret) for _, earlyData := range c.config.Bugs.SendEarlyData { if _, err := c.writeRecord(recordTypeApplicationData, earlyData); err != nil { @@ -514,7 +527,7 @@ hello.raw = nil if len(hello.pskIdentities) > 0 { - generatePSKBinders(hello, pskCipherSuite, session.masterSecret, append(helloBytes, helloRetryRequest.marshal()...), c.config) + generatePSKBinders(c.wireVersion, hello, pskCipherSuite, session.masterSecret, helloBytes, helloRetryRequest.marshal(), c.config) } secondHelloBytes = hello.marshal() @@ -584,13 +597,19 @@ serverHello: serverHello, hello: hello, suite: suite, - finishedHash: newFinishedHash(c.vers, suite), + finishedHash: newFinishedHash(c.wireVersion, c.isDTLS, suite), keyShares: keyShares, session: session, } hs.writeHash(helloBytes, hs.c.sendHandshakeSeq-1) if haveHelloRetryRequest { + if isDraft21(c.wireVersion) { + err = hs.finishedHash.UpdateForHelloRetryRequest() + if err != nil { + return err + } + } hs.writeServerHash(helloRetryRequest.marshal()) hs.writeClientHash(secondHelloBytes) } @@ -735,8 +754,10 @@ if err != nil { return err } + hs.finishedHash.nextSecret() hs.finishedHash.addEntropy(ecdheSecret) } else { + hs.finishedHash.nextSecret() hs.finishedHash.addEntropy(zeroSecret) } @@ -746,10 +767,17 @@ } } + clientLabel := clientHandshakeTrafficLabel + serverLabel := serverHandshakeTrafficLabel + if isDraft21(c.wireVersion) { + clientLabel = clientHandshakeTrafficLabelDraft21 + serverLabel = serverHandshakeTrafficLabelDraft21 + } + // Derive handshake traffic keys and switch read key to handshake // traffic key. - clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel) - serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel) + clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientLabel) + serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverLabel) if err := c.useInTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret); err != nil { return err } @@ -791,6 +819,10 @@ return errors.New("tls: non-empty certificate request context sent in handshake") } + if c.config.Bugs.ExpectNoCertificateAuthoritiesExtension && certReq.hasCAExtension { + return errors.New("tls: expected no certificate_authorities extension") + } + if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences { certReq.signatureAlgorithms = c.config.signSignatureAlgorithms() } @@ -879,10 +911,21 @@ // The various secrets do not incorporate the client's final leg, so // derive them now before updating the handshake context. + hs.finishedHash.nextSecret() hs.finishedHash.addEntropy(zeroSecret) - clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel) - serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel) - c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel) + + clientLabel = clientApplicationTrafficLabel + serverLabel = serverApplicationTrafficLabel + exportLabel := exporterLabel + if isDraft21(c.wireVersion) { + clientLabel = clientApplicationTrafficLabelDraft21 + serverLabel = serverApplicationTrafficLabelDraft21 + exportLabel = exporterLabelDraft21 + } + + clientTrafficSecret := hs.finishedHash.deriveSecret(clientLabel) + serverTrafficSecret := hs.finishedHash.deriveSecret(serverLabel) + c.exporterSecret = hs.finishedHash.deriveSecret(exportLabel) // Switch to application data keys on read. In particular, any alerts // from the client certificate are read over these keys. @@ -890,8 +933,8 @@ return err } - // If we're expecting 0.5-RTT messages from the server, read them - // now. + // If we're expecting 0.5-RTT messages from the server, read them now. + var deferredTickets []*newSessionTicketMsg if encryptedExtensions.extensions.hasEarlyData { // BoringSSL will always send two tickets half-RTT when // negotiating 0-RTT. @@ -904,9 +947,8 @@ if !ok { return errors.New("tls: expected half-RTT ticket") } - if err := c.processTLS13NewSessionTicket(newSessionTicket, hs.suite); err != nil { - return err - } + // Defer processing until the resumption secret is computed. + deferredTickets = append(deferredTickets, newSessionTicket) } for _, expectedMsg := range c.config.Bugs.ExpectHalfRTTData { if err := c.readRecord(recordTypeApplicationData); err != nil { @@ -922,12 +964,19 @@ // Send EndOfEarlyData and then switch write key to handshake // traffic key. - if c.out.cipher != nil && !c.config.Bugs.SkipEndOfEarlyData { + if encryptedExtensions.extensions.hasEarlyData && c.out.cipher != nil && !c.config.Bugs.SkipEndOfEarlyData { if c.config.Bugs.SendStrayEarlyHandshake { helloRequest := new(helloRequestMsg) c.writeRecord(recordTypeHandshake, helloRequest.marshal()) } - c.sendAlert(alertEndOfEarlyData) + if isDraft21(c.wireVersion) { + endOfEarlyData := new(endOfEarlyDataMsg) + endOfEarlyData.nonEmpty = c.config.Bugs.NonEmptyEndOfEarlyData + c.writeRecord(recordTypeHandshake, endOfEarlyData.marshal()) + hs.writeClientHash(endOfEarlyData.marshal()) + } else { + c.sendAlert(alertEndOfEarlyData) + } } if isResumptionClientCCSExperiment(c.wireVersion) { @@ -1022,7 +1071,18 @@ // Switch to application data keys. c.useOutTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret) - c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel) + resumeLabel := resumptionLabel + if isDraft21(c.wireVersion) { + resumeLabel = resumptionLabelDraft21 + } + + c.resumptionSecret = hs.finishedHash.deriveSecret(resumeLabel) + for _, ticket := range deferredTickets { + if err := c.processTLS13NewSessionTicket(ticket, hs.suite); err != nil { + return err + } + } + return nil } @@ -1498,6 +1558,7 @@ // session ID or session ticket will be attached. session := &ClientSessionState{ vers: c.vers, + wireVersion: c.wireVersion, cipherSuite: hs.suite.id, masterSecret: hs.masterSecret, handshakeHash: hs.finishedHash.Sum(), @@ -1731,7 +1792,7 @@ copy(b[len(b)-len(xb):], xb) } -func generatePSKBinders(hello *clientHelloMsg, pskCipherSuite *cipherSuite, psk, transcript []byte, config *Config) { +func generatePSKBinders(version uint16, hello *clientHelloMsg, pskCipherSuite *cipherSuite, psk, firstClientHello, helloRetryRequest []byte, config *Config) { if config.Bugs.SendNoPSKBinder { return } @@ -1757,7 +1818,11 @@ helloBytes := hello.marshal() binderSize := len(hello.pskBinders)*(binderLen+1) + 2 truncatedHello := helloBytes[:len(helloBytes)-binderSize] - binder := computePSKBinder(psk, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello) + binderLabel := resumptionPSKBinderLabel + if isDraft21(version) { + binderLabel = resumptionPSKBinderLabelDraft21 + } + binder := computePSKBinder(psk, version, binderLabel, pskCipherSuite, firstClientHello, helloRetryRequest, truncatedHello) if config.Bugs.SendShortPSKBinder { binder = binder[:binderLen] }
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index bf9cb7f..aa6b463 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go
@@ -1406,6 +1406,7 @@ type helloRetryRequestMsg struct { raw []byte vers uint16 + cipherSuite uint16 hasSelectedGroup bool selectedGroup CurveID cookie []byte @@ -1422,6 +1423,9 @@ retryRequestMsg.addU8(typeHelloRetryRequest) retryRequest := retryRequestMsg.addU24LengthPrefixed() retryRequest.addU16(m.vers) + if isDraft21(m.vers) { + retryRequest.addU16(m.cipherSuite) + } extensions := retryRequest.addU16LengthPrefixed() count := 1 @@ -1456,8 +1460,13 @@ return false } m.vers = uint16(data[4])<<8 | uint16(data[5]) - extLen := int(data[6])<<8 | int(data[7]) - data = data[8:] + data = data[6:] + if isDraft21(m.vers) { + m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + } + extLen := int(data[0])<<8 | int(data[1]) + data = data[2:] if len(data) != extLen || len(data) == 0 { return false } @@ -1870,7 +1879,8 @@ } type certificateRequestMsg struct { - raw []byte + raw []byte + vers uint16 // hasSignatureAlgorithm indicates whether this message includes a list // of signature and hash functions. This change was introduced with TLS // 1.2. @@ -1884,6 +1894,8 @@ requestContext []byte signatureAlgorithms []signatureAlgorithm certificateAuthorities [][]byte + hasCAExtension bool + customExtension uint16 } func (m *certificateRequestMsg) marshal() []byte { @@ -1899,33 +1911,119 @@ if m.hasRequestContext { requestContext := body.addU8LengthPrefixed() requestContext.addBytes(m.requestContext) + extensions := newByteBuilder() + if isDraft21(m.vers) { + extensions = body.addU16LengthPrefixed() + if m.hasSignatureAlgorithm { + extensions.addU16(extensionSignatureAlgorithms) + signatureAlgorithms := extensions.addU16LengthPrefixed().addU16LengthPrefixed() + for _, sigAlg := range m.signatureAlgorithms { + signatureAlgorithms.addU16(uint16(sigAlg)) + } + } + if len(m.certificateAuthorities) > 0 { + extensions.addU16(extensionCertificateAuthorities) + certificateAuthorities := extensions.addU16LengthPrefixed().addU16LengthPrefixed() + for _, ca := range m.certificateAuthorities { + caEntry := certificateAuthorities.addU16LengthPrefixed() + caEntry.addBytes(ca) + } + } + } else { + if m.hasSignatureAlgorithm { + signatureAlgorithms := body.addU16LengthPrefixed() + for _, sigAlg := range m.signatureAlgorithms { + signatureAlgorithms.addU16(uint16(sigAlg)) + } + } + + certificateAuthorities := body.addU16LengthPrefixed() + for _, ca := range m.certificateAuthorities { + caEntry := certificateAuthorities.addU16LengthPrefixed() + caEntry.addBytes(ca) + } + extensions = body.addU16LengthPrefixed() + } + + if m.customExtension > 0 { + extensions.addU16(m.customExtension) + extensions.addU16LengthPrefixed() + } } else { certificateTypes := body.addU8LengthPrefixed() certificateTypes.addBytes(m.certificateTypes) - } - if m.hasSignatureAlgorithm { - signatureAlgorithms := body.addU16LengthPrefixed() - for _, sigAlg := range m.signatureAlgorithms { - signatureAlgorithms.addU16(uint16(sigAlg)) + if m.hasSignatureAlgorithm { + signatureAlgorithms := body.addU16LengthPrefixed() + for _, sigAlg := range m.signatureAlgorithms { + signatureAlgorithms.addU16(uint16(sigAlg)) + } } - } - certificateAuthorities := body.addU16LengthPrefixed() - for _, ca := range m.certificateAuthorities { - caEntry := certificateAuthorities.addU16LengthPrefixed() - caEntry.addBytes(ca) - } - - if m.hasRequestContext { - // Emit no certificate extensions. - body.addU16(0) + certificateAuthorities := body.addU16LengthPrefixed() + for _, ca := range m.certificateAuthorities { + caEntry := certificateAuthorities.addU16LengthPrefixed() + caEntry.addBytes(ca) + } } m.raw = builder.finish() return m.raw } +func parseSignatureAlgorithms(data []byte) ([]signatureAlgorithm, []byte, bool) { + if len(data) < 2 { + return nil, nil, false + } + sigAlgsLen := int(data[0])<<8 | int(data[1]) + data = data[2:] + if sigAlgsLen&1 != 0 { + return nil, nil, false + } + if len(data) < int(sigAlgsLen) { + return nil, nil, false + } + numSigAlgs := sigAlgsLen / 2 + signatureAlgorithms := make([]signatureAlgorithm, numSigAlgs) + for i := range signatureAlgorithms { + signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1]) + data = data[2:] + } + + return signatureAlgorithms, data, true +} + +func parseCAs(data []byte) ([][]byte, []byte, bool) { + if len(data) < 2 { + return nil, nil, false + } + casLength := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if len(data) < int(casLength) { + return nil, nil, false + } + + cas := data[:casLength] + data = data[casLength:] + + var certificateAuthorities [][]byte + for len(cas) > 0 { + if len(cas) < 2 { + return nil, nil, false + } + caLen := uint16(cas[0])<<8 | uint16(cas[1]) + cas = cas[2:] + + if len(cas) < int(caLen) { + return nil, nil, false + } + + certificateAuthorities = append(certificateAuthorities, cas[:caLen]) + cas = cas[caLen:] + } + return certificateAuthorities, data, true +} + func (m *certificateRequestMsg) unmarshal(data []byte) bool { m.raw = data @@ -1942,6 +2040,70 @@ m.requestContext = make([]byte, contextLen) copy(m.requestContext, data[1:]) data = data[1+contextLen:] + if isDraft21(m.vers) { + 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:] + switch extension { + case extensionSignatureAlgorithms: + sigAlgs, rest, ok := parseSignatureAlgorithms(contents) + if !ok || len(rest) != 0 { + return false + } + m.signatureAlgorithms = sigAlgs + case extensionCertificateAuthorities: + cas, rest, ok := parseCAs(contents) + if !ok || len(rest) != 0 { + return false + } + m.hasCAExtension = true + m.certificateAuthorities = cas + } + } + } else { + if m.hasSignatureAlgorithm { + sigAlgs, rest, ok := parseSignatureAlgorithms(data) + if !ok { + return false + } + m.signatureAlgorithms = sigAlgs + data = rest + } + + cas, rest, ok := parseCAs(data) + if !ok { + return false + } + m.certificateAuthorities = cas + data = rest + + // Ignore certificate extensions. + if len(data) < 2 { + return false + } + extsLength := int(data[0])<<8 | int(data[1]) + if len(data) < 2+extsLength { + return false + } + data = data[2+extsLength:] + } } else { numCertTypes := int(data[0]) if len(data) < 1+numCertTypes { @@ -1950,66 +2112,22 @@ m.certificateTypes = make([]byte, numCertTypes) copy(m.certificateTypes, data[1:]) data = data[1+numCertTypes:] - } - if m.hasSignatureAlgorithm { - if len(data) < 2 { - return false - } - sigAlgsLen := uint16(data[0])<<8 | uint16(data[1]) - data = data[2:] - if sigAlgsLen&1 != 0 { - return false - } - if len(data) < int(sigAlgsLen) { - return false - } - numSigAlgs := sigAlgsLen / 2 - m.signatureAlgorithms = make([]signatureAlgorithm, numSigAlgs) - for i := range m.signatureAlgorithms { - m.signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1]) - data = data[2:] - } - } - - if len(data) < 2 { - return false - } - casLength := uint16(data[0])<<8 | uint16(data[1]) - data = data[2:] - if len(data) < int(casLength) { - return false - } - cas := make([]byte, casLength) - copy(cas, data) - data = data[casLength:] - - m.certificateAuthorities = nil - for len(cas) > 0 { - if len(cas) < 2 { - return false - } - caLen := uint16(cas[0])<<8 | uint16(cas[1]) - cas = cas[2:] - - if len(cas) < int(caLen) { - return false + if m.hasSignatureAlgorithm { + sigAlgs, rest, ok := parseSignatureAlgorithms(data) + if !ok { + return false + } + m.signatureAlgorithms = sigAlgs + data = rest } - m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) - cas = cas[caLen:] - } - - if m.hasRequestContext { - // Ignore certificate extensions. - if len(data) < 2 { + cas, rest, ok := parseCAs(data) + if !ok { return false } - extsLength := int(data[0])<<8 | int(data[1]) - if len(data) < 2+extsLength { - return false - } - data = data[2+extsLength:] + m.certificateAuthorities = cas + data = rest } if len(data) > 0 { @@ -2090,15 +2208,17 @@ } type newSessionTicketMsg struct { - raw []byte - version uint16 - ticketLifetime uint32 - ticketAgeAdd uint32 - ticket []byte - maxEarlyDataSize uint32 - customExtension string - duplicateEarlyDataInfo bool - hasGREASEExtension bool + raw []byte + vers uint16 + isDTLS bool + ticketLifetime uint32 + ticketAgeAdd uint32 + ticketNonce []byte + ticket []byte + maxEarlyDataSize uint32 + customExtension string + duplicateEarlyDataExtension bool + hasGREASEExtension bool } func (m *newSessionTicketMsg) marshal() []byte { @@ -2106,25 +2226,37 @@ return m.raw } + version, ok := wireToVersion(m.vers, m.isDTLS) + if !ok { + panic("unknown version") + } + // See http://tools.ietf.org/html/rfc5077#section-3.3 ticketMsg := newByteBuilder() ticketMsg.addU8(typeNewSessionTicket) body := ticketMsg.addU24LengthPrefixed() body.addU32(m.ticketLifetime) - if m.version >= VersionTLS13 { + if version >= VersionTLS13 { body.addU32(m.ticketAgeAdd) + if isDraft21(m.vers) { + body.addU8LengthPrefixed().addBytes(m.ticketNonce) + } } ticket := body.addU16LengthPrefixed() ticket.addBytes(m.ticket) - if m.version >= VersionTLS13 { + if version >= VersionTLS13 { extensions := body.addU16LengthPrefixed() if m.maxEarlyDataSize > 0 { - extensions.addU16(extensionTicketEarlyDataInfo) + extID := extensionTicketEarlyDataInfo + if isDraft21(m.vers) { + extID = extensionEarlyData + } + extensions.addU16(extID) extensions.addU16LengthPrefixed().addU32(m.maxEarlyDataSize) - if m.duplicateEarlyDataInfo { - extensions.addU16(extensionTicketEarlyDataInfo) + if m.duplicateEarlyDataExtension { + extensions.addU16(extID) extensions.addU16LengthPrefixed().addU32(m.maxEarlyDataSize) } } @@ -2141,18 +2273,32 @@ func (m *newSessionTicketMsg) unmarshal(data []byte) bool { m.raw = data + version, ok := wireToVersion(m.vers, m.isDTLS) + if !ok { + panic("unknown version") + } + if len(data) < 8 { return false } m.ticketLifetime = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) data = data[8:] - if m.version >= VersionTLS13 { + if version >= VersionTLS13 { if len(data) < 4 { return false } m.ticketAgeAdd = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) data = data[4:] + if isDraft21(m.vers) { + nonceLen := int(data[0]) + data = data[1:] + if len(data) < nonceLen { + return false + } + m.ticketNonce = data[:nonceLen] + data = data[nonceLen:] + } } if len(data) < 2 { @@ -2164,14 +2310,14 @@ return false } - if m.version >= VersionTLS13 && ticketLen == 0 { + if version >= VersionTLS13 && ticketLen == 0 { return false } m.ticket = data[:ticketLen] data = data[ticketLen:] - if m.version >= VersionTLS13 { + if version >= VersionTLS13 { if len(data) < 2 { return false } @@ -2182,6 +2328,11 @@ return false } + extID := extensionTicketEarlyDataInfo + if isDraft21(m.vers) { + extID = extensionEarlyData + } + for len(data) != 0 { if len(data) < 4 { return false @@ -2194,7 +2345,7 @@ } switch extension { - case extensionTicketEarlyDataInfo: + case extID: if length != 4 { return false } @@ -2381,6 +2532,21 @@ return m.keyUpdateRequest == keyUpdateNotRequested || m.keyUpdateRequest == keyUpdateRequested } +type endOfEarlyDataMsg struct { + nonEmpty bool +} + +func (m *endOfEarlyDataMsg) marshal() []byte { + if m.nonEmpty { + return []byte{typeEndOfEarlyData, 0, 0, 1, 42} + } + return []byte{typeEndOfEarlyData, 0, 0, 0} +} + +func (*endOfEarlyDataMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + // ssl3NoCertificateMsg is a dummy message to handle SSL 3.0 using a warning // alert in the handshake. type ssl3NoCertificateMsg struct{}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 0bf14eb..dd6f48f 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go
@@ -412,7 +412,7 @@ hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite } - hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash = newFinishedHash(c.wireVersion, c.isDTLS, hs.suite) hs.finishedHash.discardHandshakeBuffer() hs.writeClientHash(hs.clientHello.marshal()) @@ -496,7 +496,7 @@ // 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 { + if err := verifyPSKBinder(c.wireVersion, hs.clientHello, hs.sessionState, binderToVerify, []byte{}, []byte{}); err != nil { return err } } @@ -520,8 +520,13 @@ ResendHelloRetryRequest: var sendHelloRetryRequest bool + cipherSuite := hs.suite.id + if config.Bugs.SendHelloRetryRequestCipherSuite != 0 { + cipherSuite = config.Bugs.SendHelloRetryRequestCipherSuite + } helloRetryRequest := &helloRetryRequestMsg{ vers: c.wireVersion, + cipherSuite: cipherSuite, duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions, } @@ -571,6 +576,12 @@ } if sendHelloRetryRequest { + if isDraft21(c.wireVersion) { + if err := hs.finishedHash.UpdateForHelloRetryRequest(); err != nil { + return err + } + } + oldClientHelloBytes := hs.clientHello.marshal() hs.writeServerHash(helloRetryRequest.marshal()) c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()) @@ -592,6 +603,10 @@ } hs.writeClientHash(newClientHello.marshal()) + if config.Bugs.ExpectNoTLS13PSKAfterHRR && len(newClientHello.pskIdentities) > 0 { + return fmt.Errorf("tls: client offered unexpected PSK identities after HelloRetryRequest") + } + if newClientHello.hasEarlyData { return errors.New("tls: EarlyData sent in new ClientHello") } @@ -626,11 +641,15 @@ // PSK binders and obfuscated ticket age are both updated in the // second ClientHello. - if len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) { - return errors.New("tls: PSK identity count from old and new ClientHello do not match") - } - for i, identity := range oldClientHelloCopy.pskIdentities { - newClientHelloCopy.pskIdentities[i].obfuscatedTicketAge = identity.obfuscatedTicketAge + if isDraft21(c.wireVersion) && len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) { + newClientHelloCopy.pskIdentities = oldClientHelloCopy.pskIdentities + } else { + if len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) { + return errors.New("tls: PSK identity count from old and new ClientHello do not match") + } + for i, identity := range oldClientHelloCopy.pskIdentities { + newClientHelloCopy.pskIdentities[i].obfuscatedTicketAge = identity.obfuscatedTicketAge + } } newClientHelloCopy.pskBinders = oldClientHelloCopy.pskBinders newClientHelloCopy.hasEarlyData = oldClientHelloCopy.hasEarlyData @@ -648,7 +667,7 @@ // AcceptAnyBinder is set. See https://crbug.com/115. if hs.sessionState != nil && !config.Bugs.AcceptAnySession { binderToVerify := newClientHello.pskBinders[pskIndex] - if err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...)); err != nil { + if err := verifyPSKBinder(c.wireVersion, newClientHello, hs.sessionState, binderToVerify, oldClientHelloBytes, helloRetryRequest.marshal()); err != nil { return err } } @@ -662,7 +681,12 @@ } } if encryptedExtensions.extensions.hasEarlyData { - earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyTrafficLabel) + earlyLabel := earlyTrafficLabel + if isDraft21(c.wireVersion) { + earlyLabel = earlyTrafficLabelDraft21 + } + + earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyLabel) if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil { return err } @@ -716,6 +740,7 @@ c.sendAlert(alertHandshakeFailure) return err } + hs.finishedHash.nextSecret() hs.finishedHash.addEntropy(ecdheSecret) hs.hello.hasKeyShare = true @@ -740,6 +765,7 @@ } } } else { + hs.finishedHash.nextSecret() hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret()) } @@ -760,11 +786,18 @@ c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) } + clientLabel := clientHandshakeTrafficLabel + serverLabel := serverHandshakeTrafficLabel + if isDraft21(c.wireVersion) { + clientLabel = clientHandshakeTrafficLabelDraft21 + serverLabel = serverHandshakeTrafficLabelDraft21 + } + // Switch to handshake traffic keys. - serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel) + serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverLabel) c.useOutTrafficSecret(c.wireVersion, hs.suite, serverHandshakeTrafficSecret) // Derive handshake traffic read key, but don't switch yet. - clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel) + clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientLabel) // Send EncryptedExtensions. hs.writeServerHash(encryptedExtensions.marshal()) @@ -779,9 +812,11 @@ if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := &certificateRequestMsg{ - hasSignatureAlgorithm: true, + vers: c.wireVersion, + hasSignatureAlgorithm: !config.Bugs.OmitCertificateRequestAlgorithms, hasRequestContext: true, requestContext: config.Bugs.SendRequestContext, + customExtension: config.Bugs.SendCustomCertificateRequest, } if !config.Bugs.NoSignatureAlgorithms { certReq.signatureAlgorithms = config.verifySignatureAlgorithms() @@ -896,10 +931,21 @@ // The various secrets do not incorporate the client's final leg, so // derive them now before updating the handshake context. + hs.finishedHash.nextSecret() hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret()) - clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel) - serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel) - c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel) + + clientLabel = clientApplicationTrafficLabel + serverLabel = serverApplicationTrafficLabel + exportLabel := exporterLabel + if isDraft21(c.wireVersion) { + clientLabel = clientApplicationTrafficLabelDraft21 + serverLabel = serverApplicationTrafficLabelDraft21 + exportLabel = exporterLabelDraft21 + } + + clientTrafficSecret := hs.finishedHash.deriveSecret(clientLabel) + serverTrafficSecret := hs.finishedHash.deriveSecret(serverLabel) + c.exporterSecret = hs.finishedHash.deriveSecret(exportLabel) // Switch to application data keys on write. In particular, any alerts // from the client certificate are sent over these keys. @@ -912,13 +958,27 @@ } } - // Read end_of_early_data alert. + // Read end_of_early_data. if encryptedExtensions.extensions.hasEarlyData { - if err := c.readRecord(recordTypeAlert); err != errEndOfEarlyDataAlert { - if err == nil { - panic("readRecord(recordTypeAlert) returned nil") + if isDraft21(c.wireVersion) { + msg, err := c.readHandshake() + if err != nil { + return err } - return err + + endOfEarlyData, ok := msg.(*endOfEarlyDataMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(endOfEarlyData, msg) + } + hs.writeClientHash(endOfEarlyData.marshal()) + } else { + if err := c.readRecord(recordTypeAlert); err != errEndOfEarlyDataAlert { + if err == nil { + panic("readRecord(recordTypeAlert) returned nil") + } + return err + } } } @@ -1040,14 +1100,20 @@ } c.cipherSuite = hs.suite - c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel) + + resumeLabel := resumptionLabel + if isDraft21(c.wireVersion) { + resumeLabel = resumptionLabelDraft21 + } + + c.resumptionSecret = hs.finishedHash.deriveSecret(resumeLabel) // TODO(davidben): Allow configuring the number of tickets sent for // testing. if !c.config.SessionTicketsDisabled && foundKEMode { ticketCount := 2 for i := 0; i < ticketCount; i++ { - c.SendNewSessionTicket() + c.SendNewSessionTicket([]byte{byte(i)}) } } return nil @@ -1408,7 +1474,7 @@ hs.hello.extensions.ocspStapling = true } - hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash = newFinishedHash(c.wireVersion, c.isDTLS, hs.suite) hs.finishedHash.discardHandshakeBuffer() hs.writeClientHash(hs.clientHello.marshal()) hs.writeServerHash(hs.hello.marshal()) @@ -1460,7 +1526,7 @@ } } - hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash = newFinishedHash(c.wireVersion, c.isDTLS, hs.suite) hs.writeClientHash(hs.clientHello.marshal()) hs.writeServerHash(hs.hello.marshal()) @@ -1514,6 +1580,7 @@ if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := &certificateRequestMsg{ + vers: c.wireVersion, certificateTypes: config.ClientCertificateTypes, } if certReq.certificateTypes == nil { @@ -1787,6 +1854,8 @@ } m := new(newSessionTicketMsg) + m.vers = c.wireVersion + m.isDTLS = c.isDTLS if c.config.Bugs.SendTicketLifetime != 0 { m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second) } @@ -2022,7 +2091,7 @@ return val&0x0f0f == 0x0a0a && val&0xff == val>>8 } -func verifyPSKBinder(clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, transcript []byte) error { +func verifyPSKBinder(version uint16, clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, firstClientHello, helloRetryRequest []byte) error { binderLen := 2 for _, binder := range clientHello.pskBinders { binderLen += 1 + len(binder) @@ -2035,7 +2104,11 @@ return errors.New("tls: Unknown cipher suite for PSK in session") } - binder := computePSKBinder(sessionState.masterSecret, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello) + binderLabel := resumptionPSKBinderLabel + if isDraft21(version) { + binderLabel = resumptionPSKBinderLabelDraft21 + } + binder := computePSKBinder(sessionState.masterSecret, version, binderLabel, pskCipherSuite, firstClientHello, helloRetryRequest, truncatedHello) if !bytes.Equal(binder, binderToVerify) { return errors.New("tls: PSK binder does not verify") }
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go index cfc383d..6fa3c4c 100644 --- a/ssl/test/runner/prf.go +++ b/ssl/test/runner/prf.go
@@ -180,9 +180,14 @@ return } -func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { +func newFinishedHash(wireVersion uint16, isDTLS bool, cipherSuite *cipherSuite) finishedHash { var ret finishedHash + version, ok := wireToVersion(wireVersion, isDTLS) + if !ok { + panic("unknown version") + } + if version >= VersionTLS12 { ret.hash = cipherSuite.hash() @@ -207,6 +212,7 @@ ret.buffer = []byte{} ret.version = version + ret.wireVersion = wireVersion return ret } @@ -226,13 +232,28 @@ // full buffer is required. buffer []byte - version uint16 - prf func(result, secret, label, seed []byte) + version uint16 + wireVersion uint16 + prf func(result, secret, label, seed []byte) // secret, in TLS 1.3, is the running input secret. secret []byte } +func (h *finishedHash) UpdateForHelloRetryRequest() (err error) { + data := newByteBuilder() + data.addU8(typeMessageHash) + data.addU24(h.hash.Size()) + data.addBytes(h.Sum()) + h.client = h.hash.New() + h.server = h.hash.New() + if h.buffer != nil { + h.buffer = []byte{} + } + h.Write(data.finish()) + return nil +} + func (h *finishedHash) Write(msg []byte) (n int, err error) { h.client.Write(msg) h.server.Write(msg) @@ -307,7 +328,7 @@ return out } - clientFinishedKey := hkdfExpandLabel(h.hash, baseKey, finishedLabel, nil, h.hash.Size()) + clientFinishedKey := hkdfExpandLabel(h.hash, h.wireVersion, baseKey, finishedLabel, nil, h.hash.Size()) finishedHMAC := hmac.New(h.hash.New, clientFinishedKey) finishedHMAC.Write(h.appendContextHashes(nil)) return finishedHMAC.Sum(nil) @@ -326,7 +347,7 @@ return out } - serverFinishedKey := hkdfExpandLabel(h.hash, baseKey, finishedLabel, nil, h.hash.Size()) + serverFinishedKey := hkdfExpandLabel(h.hash, h.wireVersion, baseKey, finishedLabel, nil, h.hash.Size()) finishedHMAC := hmac.New(h.hash.New, serverFinishedKey) finishedHMAC.Write(h.appendContextHashes(nil)) return finishedHMAC.Sum(nil) @@ -374,20 +395,33 @@ h.secret = hkdfExtract(h.hash.New, h.secret, ikm) } +func (h *finishedHash) nextSecret() { + if isDraft21(h.wireVersion) { + derivedLabel := []byte("derived") + h.secret = hkdfExpandLabel(h.hash, h.wireVersion, h.secret, derivedLabel, h.hash.New().Sum(nil), h.hash.Size()) + } +} + // hkdfExpandLabel implements TLS 1.3's HKDF-Expand-Label function, as defined // in section 7.1 of draft-ietf-tls-tls13-16. -func hkdfExpandLabel(hash crypto.Hash, secret, label, hashValue []byte, length int) []byte { +func hkdfExpandLabel(hash crypto.Hash, version uint16, secret, label, hashValue []byte, length int) []byte { if len(label) > 255 || len(hashValue) > 255 { panic("hkdfExpandLabel: label or hashValue too long") } - hkdfLabel := make([]byte, 3+9+len(label)+1+len(hashValue)) + + versionLabel := []byte("TLS 1.3, ") + if isDraft21(version) { + versionLabel = []byte("tls13 ") + } + + hkdfLabel := make([]byte, 3+len(versionLabel)+len(label)+1+len(hashValue)) x := hkdfLabel x[0] = byte(length >> 8) x[1] = byte(length) - x[2] = byte(9 + len(label)) + x[2] = byte(len(versionLabel) + len(label)) x = x[3:] - copy(x, []byte("TLS 1.3, ")) - x = x[9:] + copy(x, versionLabel) + x = x[len(versionLabel):] copy(x, label) x = x[len(label):] x[0] = byte(len(hashValue)) @@ -414,12 +448,25 @@ applicationTrafficLabel = []byte("application traffic secret") exporterLabel = []byte("exporter master secret") resumptionLabel = []byte("resumption master secret") + + externalPSKBinderLabelDraft21 = []byte("ext binder") + resumptionPSKBinderLabelDraft21 = []byte("res binder") + earlyTrafficLabelDraft21 = []byte("c e traffic") + clientHandshakeTrafficLabelDraft21 = []byte("c hs traffic") + serverHandshakeTrafficLabelDraft21 = []byte("s hs traffic") + clientApplicationTrafficLabelDraft21 = []byte("c ap traffic") + serverApplicationTrafficLabelDraft21 = []byte("s ap traffic") + applicationTrafficLabelDraft21 = []byte("traffic upd") + exporterLabelDraft21 = []byte("exp master") + resumptionLabelDraft21 = []byte("res master") + + resumptionPSKLabel = []byte("resumption") ) // 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(label []byte) []byte { - return hkdfExpandLabel(h.hash, h.secret, label, h.appendContextHashes(nil), h.hash.Size()) + return hkdfExpandLabel(h.hash, h.wireVersion, h.secret, label, h.appendContextHashes(nil), h.hash.Size()) } // The following are context strings for CertificateVerify in TLS 1.3. @@ -458,21 +505,34 @@ // deriveTrafficAEAD derives traffic keys and constructs an AEAD given a traffic // secret. 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)) + key := hkdfExpandLabel(suite.hash(), version, secret, keyTLS13, nil, suite.keyLen) + iv := hkdfExpandLabel(suite.hash(), version, secret, ivTLS13, nil, suite.ivLen(version)) return suite.aead(version, key, iv) } -func updateTrafficSecret(hash crypto.Hash, secret []byte) []byte { - return hkdfExpandLabel(hash, secret, applicationTrafficLabel, nil, hash.Size()) +func updateTrafficSecret(hash crypto.Hash, version uint16, secret []byte) []byte { + trafficLabel := applicationTrafficLabel + if isDraft21(version) { + trafficLabel = applicationTrafficLabelDraft21 + } + return hkdfExpandLabel(hash, version, secret, trafficLabel, nil, hash.Size()) } -func computePSKBinder(psk, label []byte, cipherSuite *cipherSuite, transcript, truncatedHello []byte) []byte { - finishedHash := newFinishedHash(VersionTLS13, cipherSuite) +func computePSKBinder(psk []byte, version uint16, label []byte, cipherSuite *cipherSuite, clientHello, helloRetryRequest, truncatedHello []byte) []byte { + finishedHash := newFinishedHash(version, false, cipherSuite) finishedHash.addEntropy(psk) binderKey := finishedHash.deriveSecret(label) - finishedHash.Write(transcript) + finishedHash.Write(clientHello) + if isDraft21(version) && len(helloRetryRequest) != 0 { + finishedHash.UpdateForHelloRetryRequest() + } + finishedHash.Write(helloRetryRequest) finishedHash.Write(truncatedHello) return finishedHash.clientSum(binderKey) } + +func deriveSessionPSK(suite *cipherSuite, version uint16, masterSecret []byte, nonce []byte) []byte { + hash := suite.hash() + return hkdfExpandLabel(hash, version, masterSecret, resumptionPSKLabel, nonce, hash.Size()) +}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 0ce50bc..ce50fa8 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -1305,6 +1305,13 @@ tls13Variant: TLS13Default, }, { + name: "TLS13Draft21", + version: VersionTLS13, + excludeFlag: "-no-tls13", + versionWire: tls13Draft21Version, + tls13Variant: TLS13Draft21, + }, + { name: "TLS13Experiment", version: VersionTLS13, excludeFlag: "-no-tls13", @@ -3130,7 +3137,7 @@ }, }, shouldFail: true, - expectedError: ":UNKNOWN_CIPHER_RETURNED:", + expectedError: ":WRONG_CIPHER_RETURNED:", }) // The server must be tolerant to bogus ciphers. @@ -3783,6 +3790,25 @@ "-use-client-ca-list", "<NULL>", }, }) + + // Test that an empty client CA list doesn't send a CA extension. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13Draft21-Empty-Client-CA-List", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + ExpectNoCertificateAuthoritiesExtension: true, + }, + }, + tls13Variant: TLS13Draft21, + flags: []string{ + "-require-any-client-certificate", + "-use-client-ca-list", "<EMPTY>", + }, + }) + } func addExtendedMasterSecretTests() { @@ -4153,7 +4179,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-accept-early-data", "-on-resume-shim-writes-first", }, @@ -4182,7 +4208,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-accept-early-data", "-on-resume-read-with-unfinished-write", "-on-resume-shim-writes-first", @@ -4211,7 +4237,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", "-on-resume-read-with-unfinished-write", "-on-resume-shim-writes-first", @@ -5145,14 +5171,13 @@ } // When running and shim have different TLS 1.3 variants enabled, // shim clients are expected to fall back to TLS 1.2, while shim - // servers support both variants when enabled when the experiment is - // enabled. + // servers support multiple variants. expectedServerVersion := expectedVersion expectedClientVersion := expectedVersion if expectedVersion == VersionTLS13 && runnerVers.tls13Variant != shimVers.tls13Variant { expectedClientVersion = VersionTLS12 expectedServerVersion = VersionTLS12 - if shimVers.tls13Variant != TLS13Default { + if shimVers.tls13Variant != TLS13Default && runnerVers.tls13Variant != TLS13Draft21 { expectedServerVersion = VersionTLS13 } } @@ -9181,7 +9206,7 @@ flags: []string{ "-enable-client-custom-extension", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", }, }) @@ -9200,7 +9225,7 @@ flags: []string{ "-enable-client-custom-extension", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-accept-early-data", }, }) @@ -9221,7 +9246,7 @@ flags: []string{ "-enable-client-custom-extension", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", }, }) @@ -9252,7 +9277,7 @@ flags: []string{ "-enable-client-custom-extension", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", }, }) @@ -9274,7 +9299,7 @@ flags: []string{ "-enable-server-custom-extension", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", }, }) @@ -10036,7 +10061,7 @@ }, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", }, }) @@ -10057,7 +10082,7 @@ MaxVersion: VersionTLS13, MaxEarlyDataSize: 16384, Bugs: ProtocolBugs{ - DuplicateTicketEarlyDataInfo: true, + DuplicateTicketEarlyData: true, }, }, shouldFail: true, @@ -10071,7 +10096,7 @@ config: Config{ MaxVersion: VersionTLS13, Bugs: ProtocolBugs{ - ExpectTicketEarlyDataInfo: true, + ExpectTicketEarlyData: true, }, }, flags: []string{ @@ -10956,7 +10981,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-accept-early-data", "-on-resume-shim-writes-first", }, @@ -10980,7 +11005,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", "-on-resume-shim-writes-first", }, @@ -11221,6 +11246,49 @@ }) testCases = append(testCases, testCase{ + name: "TLS13Draft21-HelloRetryRequest-CipherChange", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_AES_128_GCM_SHA256, + SendHelloRetryRequestCipherSuite: TLS_CHACHA20_POLY1305_SHA256, + }, + }, + tls13Variant: TLS13Draft21, + shouldFail: true, + expectedError: ":WRONG_CIPHER_RETURNED:", + }) + + // Test that the client does not offer a PSK in the second ClientHello if the + // HelloRetryRequest is incompatible with it. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13Draft21-HelloRetryRequest-NonResumableCipher", + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{ + TLS_AES_128_GCM_SHA256, + }, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + ExpectNoTLS13PSKAfterHRR: true, + }, + CipherSuites: []uint16{ + TLS_AES_256_GCM_SHA384, + }, + }, + tls13Variant: TLS13Draft21, + resumeSession: true, + expectResumeRejected: true, + }) + + testCases = append(testCases, testCase{ name: "DisabledCurve-HelloRetryRequest", config: Config{ MaxVersion: VersionTLS13, @@ -11446,6 +11514,42 @@ }) testCases = append(testCases, testCase{ + name: "TLS13-UnknownInCertificateRequest", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + Bugs: ProtocolBugs{ + SendCustomCertificateRequest: 0x1212, + }, + }, + tls13Variant: TLS13Draft21, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + + testCases = append(testCases, testCase{ + name: "TLS13-MissingSignatureAlgorithmsInCertificateRequest", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + Bugs: ProtocolBugs{ + OmitCertificateRequestAlgorithms: true, + }, + }, + tls13Variant: TLS13Draft21, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + + testCases = append(testCases, testCase{ testType: serverTest, name: "TLS13-TrailingKeyShareData", config: Config{ @@ -11524,7 +11628,7 @@ expectResumeRejected: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", "-on-resume-shim-writes-first", "-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile), @@ -11552,7 +11656,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", }, }) @@ -11576,7 +11680,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", }, shouldFail: true, expectedError: ":UNEXPECTED_EXTENSION:", @@ -11597,7 +11701,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", }, shouldFail: true, expectedError: ":WRONG_VERSION_ON_EARLY_DATA:", @@ -11623,7 +11727,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", }, shouldFail: true, @@ -11656,7 +11760,7 @@ flags: []string{ "-advertise-alpn", "\x03foo\x03bar", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", "-on-initial-expect-alpn", "foo", "-on-resume-expect-alpn", "foo", @@ -11682,7 +11786,7 @@ flags: []string{ "-advertise-alpn", "\x03foo\x03bar", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", "-on-initial-expect-alpn", "", "-on-resume-expect-alpn", "", @@ -11709,7 +11813,7 @@ flags: []string{ "-advertise-alpn", "\x03foo\x03bar", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-reject-early-data", "-on-initial-expect-alpn", "foo", "-on-resume-expect-alpn", "foo", @@ -11741,7 +11845,7 @@ flags: []string{ "-advertise-alpn", "\x03foo\x03bar", "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-on-initial-expect-alpn", "foo", "-on-resume-expect-alpn", "foo", "-on-retry-expect-alpn", "bar", @@ -11763,7 +11867,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-no-offer-early-data", "-on-initial-advertise-alpn", "\x03foo", "-on-resume-advertise-alpn", "\x03bar", @@ -11884,7 +11988,7 @@ expectedError: ":UNEXPECTED_EXTENSION_ON_EARLY_DATA:", flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile), }, }) @@ -11906,7 +12010,7 @@ expectChannelID: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile), "-expect-reject-early-data", }, @@ -11924,7 +12028,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile), "-expect-accept-early-data", }, @@ -12030,7 +12134,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-accept-early-data", "-expect-version", strconv.Itoa(VersionTLS13), }, @@ -12055,7 +12159,7 @@ resumeSession: true, flags: []string{ "-enable-early-data", - "-expect-early-data-info", + "-expect-ticket-supports-early-data", "-expect-accept-early-data", }, shouldFail: true, @@ -12088,6 +12192,34 @@ expectedError: ":DIGEST_CHECK_FAILED:", expectedLocalError: "remote error: error decrypting message", }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13Draft21-Server-NonEmptyEndOfEarlyData", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + SendEarlyData: [][]byte{{1, 2, 3, 4}}, + ExpectEarlyDataAccepted: true, + NonEmptyEndOfEarlyData: true, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-early-data", + "-expect-ticket-supports-early-data", + "-expect-accept-early-data", + }, + tls13Variant: TLS13Draft21, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + testCases = append(testCases, testCase{ testType: serverTest, name: "TLS13-ServerSkipCertificateVerify",
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index 3354851..2d9f725 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc
@@ -81,7 +81,8 @@ { "-tls-unique", &TestConfig::tls_unique }, { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal }, { "-expect-no-session", &TestConfig::expect_no_session }, - { "-expect-early-data-info", &TestConfig::expect_early_data_info }, + { "-expect-ticket-supports-early-data", + &TestConfig::expect_ticket_supports_early_data }, { "-use-ticket-callback", &TestConfig::use_ticket_callback }, { "-renew-ticket", &TestConfig::renew_ticket }, { "-enable-early-data", &TestConfig::enable_early_data },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index b96d9e5..b742f94 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h
@@ -87,7 +87,7 @@ bool tls_unique = false; bool expect_ticket_renewal = false; bool expect_no_session = false; - bool expect_early_data_info = false; + bool expect_ticket_supports_early_data = false; bool expect_accept_early_data = false; bool expect_reject_early_data = false; bool expect_no_offer_early_data = false;
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc index b6307ee..9c09309 100644 --- a/ssl/tls13_client.cc +++ b/ssl/tls13_client.cc
@@ -63,8 +63,10 @@ } CBS body = msg.body, extensions; - uint16_t server_version; + uint16_t server_version, cipher_suite = 0; if (!CBS_get_u16(&body, &server_version) || + (ssl_is_draft21(ssl->version) && + !CBS_get_u16(&body, &cipher_suite)) || !CBS_get_u16_length_prefixed(&body, &extensions) || // HelloRetryRequest may not be empty. CBS_len(&extensions) == 0 || @@ -74,6 +76,26 @@ return ssl_hs_error; } + if (ssl_is_draft21(ssl->version)) { + const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite); + // Check if the cipher is a TLS 1.3 cipher. + if (cipher == NULL || + SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) || + SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_hs_error; + } + + hs->new_cipher = cipher; + + if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) || + !hs->transcript.UpdateForHelloRetryRequest()) { + return ssl_hs_error; + } + } + + bool have_cookie, have_key_share; CBS cookie, key_share; const SSL_EXTENSION_TYPE ext_types[] = { @@ -206,16 +228,20 @@ OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE); + // Check if the cipher is a TLS 1.3 cipher. const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite); - if (cipher == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED); + if (cipher == nullptr || + SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) || + SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return ssl_hs_error; } - // Check if the cipher is a TLS 1.3 cipher. - if (SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) || - SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) { + // Check that the cipher matches the one in the HelloRetryRequest. + if (ssl_is_draft21(ssl->version) && + hs->received_hello_retry_request && + hs->new_cipher != cipher) { OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return ssl_hs_error; @@ -302,18 +328,16 @@ hs->new_session->cipher = cipher; hs->new_cipher = cipher; - // The PRF hash is now known. Set up the key schedule. - if (!tls13_init_key_schedule(hs)) { - return ssl_hs_error; - } + size_t hash_len = + EVP_MD_size(ssl_get_handshake_digest(ssl_protocol_version(ssl), cipher)); - // Incorporate the PSK into the running secret. + // Set up the key schedule and incorporate the PSK into the running secret. if (ssl->s3->session_reused) { - if (!tls13_advance_key_schedule(hs, hs->new_session->master_key, - hs->new_session->master_key_length)) { + if (!tls13_init_key_schedule(hs, hs->new_session->master_key, + hs->new_session->master_key_length)) { return ssl_hs_error; } - } else if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len)) { + } else if (!tls13_init_key_schedule(hs, kZeroes, hash_len)) { return ssl_hs_error; } @@ -445,37 +469,81 @@ return ssl_hs_ok; } - CBS body = msg.body, context, supported_signature_algorithms; - if (!CBS_get_u8_length_prefixed(&body, &context) || - // The request context is always empty during the handshake. - CBS_len(&context) != 0 || - !CBS_get_u16_length_prefixed(&body, &supported_signature_algorithms) || - CBS_len(&supported_signature_algorithms) == 0 || - !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - return ssl_hs_error; - } - uint8_t alert = SSL_AD_DECODE_ERROR; - UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names = - ssl_parse_client_CA_list(ssl, &alert, &body); - if (!ca_names) { - ssl_send_alert(ssl, SSL3_AL_FATAL, alert); - return ssl_hs_error; - } + if (ssl_is_draft21(ssl->version)) { + bool have_sigalgs = false, have_ca = false; + CBS sigalgs, ca; + const SSL_EXTENSION_TYPE ext_types[] = { + {TLSEXT_TYPE_signature_algorithms, &have_sigalgs, &sigalgs}, + {TLSEXT_TYPE_certificate_authorities, &have_ca, &ca}, + }; - // Ignore extensions. - CBS extensions; - if (!CBS_get_u16_length_prefixed(&body, &extensions) || - CBS_len(&body) != 0) { - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - return ssl_hs_error; + CBS body = msg.body, context, extensions, supported_signature_algorithms; + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!CBS_get_u8_length_prefixed(&body, &context) || + // The request context is always empty during the handshake. + CBS_len(&context) != 0 || + !CBS_get_u16_length_prefixed(&body, &extensions) || + CBS_len(&body) != 0 || + !ssl_parse_extensions(&extensions, &alert, ext_types, + OPENSSL_ARRAY_SIZE(ext_types), + 1 /* accept unknown */) || + (have_ca && CBS_len(&ca) == 0) || + !have_sigalgs || + !CBS_get_u16_length_prefixed(&sigalgs, + &supported_signature_algorithms) || + CBS_len(&supported_signature_algorithms) == 0 || + !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) { + ssl_send_alert(ssl, SSL3_AL_FATAL, alert); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } + + if (have_ca) { + hs->ca_names = ssl_parse_client_CA_list(ssl, &alert, &ca); + if (!hs->ca_names) { + ssl_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + } else { + hs->ca_names.reset(sk_CRYPTO_BUFFER_new_null()); + if (!hs->ca_names) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + } + } else { + CBS body = msg.body, context, supported_signature_algorithms; + if (!CBS_get_u8_length_prefixed(&body, &context) || + // The request context is always empty during the handshake. + CBS_len(&context) != 0 || + !CBS_get_u16_length_prefixed(&body, &supported_signature_algorithms) || + CBS_len(&supported_signature_algorithms) == 0 || + !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) { + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } + + uint8_t alert = SSL_AD_DECODE_ERROR; + hs->ca_names = ssl_parse_client_CA_list(ssl, &alert, &body); + if (!hs->ca_names) { + ssl_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + // Ignore extensions. + CBS extensions; + if (!CBS_get_u16_length_prefixed(&body, &extensions) || + CBS_len(&body) != 0) { + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } } hs->cert_request = true; - hs->ca_names = std::move(ca_names); ssl->ctx->x509_method->hs_flush_cached_ca_names(hs); if (!ssl_hash_message(hs, msg)) { @@ -557,9 +625,19 @@ if (ssl->early_data_accepted) { hs->can_early_write = false; - if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING, - TLS1_AD_END_OF_EARLY_DATA)) { - return ssl_hs_error; + if (ssl_is_draft21(ssl->version)) { + ScopedCBB cbb; + CBB body; + if (!ssl->method->init_message(ssl, cbb.get(), &body, + SSL3_MT_END_OF_EARLY_DATA) || + !ssl_add_message_cbb(ssl, cbb.get())) { + return ssl_hs_error; + } + } else { + if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING, + TLS1_AD_END_OF_EARLY_DATA)) { + return ssl_hs_error; + } } } @@ -790,9 +868,11 @@ ssl_session_rebase_time(ssl, session.get()); uint32_t server_timeout; - CBS body = msg.body, ticket, extensions; + CBS body = msg.body, ticket_nonce, ticket, extensions; if (!CBS_get_u32(&body, &server_timeout) || !CBS_get_u32(&body, &session->ticket_age_add) || + (ssl_is_draft21(ssl->version) && + !CBS_get_u8_length_prefixed(&body, &ticket_nonce)) || !CBS_get_u16_length_prefixed(&body, &ticket) || !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) || !CBS_get_u16_length_prefixed(&body, &extensions) || @@ -808,12 +888,18 @@ session->timeout = server_timeout; } + if (!tls13_derive_session_psk(session.get(), ticket_nonce)) { + return 0; + } + // Parse out the extensions. bool have_early_data_info = false; CBS early_data_info; + uint16_t ext_id = ssl_is_draft21(ssl->version) + ? TLSEXT_TYPE_early_data + : TLSEXT_TYPE_ticket_early_data_info; const SSL_EXTENSION_TYPE ext_types[] = { - {TLSEXT_TYPE_ticket_early_data_info, &have_early_data_info, - &early_data_info}, + {ext_id, &have_early_data_info, &early_data_info}, }; uint8_t alert = SSL_AD_DECODE_ERROR;
diff --git a/ssl/tls13_enc.cc b/ssl/tls13_enc.cc index 6c7e1da..14f4a78 100644 --- a/ssl/tls13_enc.cc +++ b/ssl/tls13_enc.cc
@@ -46,32 +46,33 @@ return 1; } -int tls13_init_key_schedule(SSL_HANDSHAKE *hs) { +int tls13_init_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk, + size_t psk_len) { if (!init_key_schedule(hs, ssl_protocol_version(hs->ssl), hs->new_cipher)) { return 0; } hs->transcript.FreeBuffer(); - return 1; + return HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), psk, + psk_len, hs->secret, hs->hash_len); } -int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs) { +int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk, + size_t psk_len) { SSL *const ssl = hs->ssl; return init_key_schedule(hs, ssl_session_protocol_version(ssl->session), - ssl->session->cipher); + ssl->session->cipher) && + HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), psk, + psk_len, hs->secret, hs->hash_len); } -int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in, - size_t len) { - return HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), in, - len, hs->secret, hs->hash_len); -} - -static int hkdf_expand_label(uint8_t *out, const EVP_MD *digest, - const uint8_t *secret, size_t secret_len, - const uint8_t *label, size_t label_len, - const uint8_t *hash, size_t hash_len, size_t len) { - static const char kTLS13LabelVersion[] = "TLS 1.3, "; +static int hkdf_expand_label(uint8_t *out, uint16_t version, + const EVP_MD *digest, const uint8_t *secret, + size_t secret_len, const uint8_t *label, + size_t label_len, const uint8_t *hash, + size_t hash_len, size_t len) { + const char *kTLS13LabelVersion = + ssl_is_draft21(version) ? "tls13 " : "TLS 1.3, "; ScopedCBB cbb; CBB child; @@ -96,6 +97,34 @@ return ret; } +static const char kTLS13LabelDerived[] = "derived"; + +int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in, + size_t len) { + SSL *const ssl = hs->ssl; + + // Draft 18 does not include the extra Derive-Secret step. + if (ssl_is_draft21(ssl->version)) { + uint8_t derive_context[EVP_MAX_MD_SIZE]; + unsigned derive_context_len; + if (!EVP_Digest(nullptr, 0, derive_context, &derive_context_len, + hs->transcript.Digest(), nullptr)) { + return 0; + } + + if (!hkdf_expand_label(hs->secret, ssl->version, hs->transcript.Digest(), + hs->secret, hs->hash_len, + (const uint8_t *)kTLS13LabelDerived, + strlen(kTLS13LabelDerived), derive_context, + derive_context_len, hs->hash_len)) { + return 0; + } + } + + return HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), in, + len, hs->secret, hs->hash_len); +} + // derive_secret derives a secret of length |len| and writes the result in |out| // with the given label and the current base secret and most recently-saved // handshake context. It returns one on success and zero on error. @@ -107,9 +136,10 @@ return 0; } - return hkdf_expand_label(out, hs->transcript.Digest(), hs->secret, - hs->hash_len, label, label_len, context_hash, - context_hash_len, len); + return hkdf_expand_label(out, SSL_get_session(hs->ssl)->ssl_version, + hs->transcript.Digest(), hs->secret, hs->hash_len, + label, label_len, context_hash, context_hash_len, + len); } int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction, @@ -136,16 +166,18 @@ // Derive the key. 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", 3, NULL, 0, key_len)) { + if (!hkdf_expand_label(key, session->ssl_version, digest, traffic_secret, + traffic_secret_len, (const uint8_t *)"key", 3, NULL, 0, + key_len)) { return 0; } // Derive the IV. 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", 2, NULL, 0, iv_len)) { + if (!hkdf_expand_label(iv, session->ssl_version, digest, traffic_secret, + traffic_secret_len, (const uint8_t *)"iv", 2, NULL, 0, + iv_len)) { return 0; } @@ -195,28 +227,49 @@ static const char kTLS13LabelServerApplicationTraffic[] = "server application traffic secret"; +static const char kTLS13Draft21LabelExporter[] = "exp master"; +static const char kTLS13Draft21LabelEarlyExporter[] = "e exp master"; + +static const char kTLS13Draft21LabelClientEarlyTraffic[] = "c e traffic"; +static const char kTLS13Draft21LabelClientHandshakeTraffic[] = "c hs traffic"; +static const char kTLS13Draft21LabelServerHandshakeTraffic[] = "s hs traffic"; +static const char kTLS13Draft21LabelClientApplicationTraffic[] = "c ap traffic"; +static const char kTLS13Draft21LabelServerApplicationTraffic[] = "s ap traffic"; + int tls13_derive_early_secrets(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; + uint16_t version = SSL_get_session(ssl)->ssl_version; + + const char *early_traffic_label = ssl_is_draft21(version) + ? kTLS13Draft21LabelClientEarlyTraffic + : kTLS13LabelClientEarlyTraffic; + const char *early_exporter_label = ssl_is_draft21(version) + ? kTLS13Draft21LabelEarlyExporter + : kTLS13LabelEarlyExporter; return derive_secret(hs, hs->early_traffic_secret, hs->hash_len, - (const uint8_t *)kTLS13LabelClientEarlyTraffic, - strlen(kTLS13LabelClientEarlyTraffic)) && + (const uint8_t *)early_traffic_label, + strlen(early_traffic_label)) && ssl_log_secret(ssl, "CLIENT_EARLY_TRAFFIC_SECRET", hs->early_traffic_secret, hs->hash_len) && derive_secret(hs, ssl->s3->early_exporter_secret, hs->hash_len, - (const uint8_t *)kTLS13LabelEarlyExporter, - strlen(kTLS13LabelEarlyExporter)); + (const uint8_t *)early_exporter_label, + strlen(early_exporter_label)); } int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; + const char *client_label = ssl_is_draft21(ssl->version) + ? kTLS13Draft21LabelClientHandshakeTraffic + : kTLS13LabelClientHandshakeTraffic; + const char *server_label = ssl_is_draft21(ssl->version) + ? kTLS13Draft21LabelServerHandshakeTraffic + : kTLS13LabelServerHandshakeTraffic; return derive_secret(hs, hs->client_handshake_secret, hs->hash_len, - (const uint8_t *)kTLS13LabelClientHandshakeTraffic, - strlen(kTLS13LabelClientHandshakeTraffic)) && + (const uint8_t *)client_label, strlen(client_label)) && ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET", hs->client_handshake_secret, hs->hash_len) && derive_secret(hs, hs->server_handshake_secret, hs->hash_len, - (const uint8_t *)kTLS13LabelServerHandshakeTraffic, - strlen(kTLS13LabelServerHandshakeTraffic)) && + (const uint8_t *)server_label, strlen(server_label)) && ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET", hs->server_handshake_secret, hs->hash_len); } @@ -224,25 +277,33 @@ int tls13_derive_application_secrets(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; ssl->s3->exporter_secret_len = hs->hash_len; + const char *client_label = ssl_is_draft21(ssl->version) + ? kTLS13Draft21LabelClientApplicationTraffic + : kTLS13LabelClientApplicationTraffic; + const char *server_label = ssl_is_draft21(ssl->version) + ? kTLS13Draft21LabelServerApplicationTraffic + : kTLS13LabelServerApplicationTraffic; + const char *exporter_label = ssl_is_draft21(ssl->version) + ? kTLS13Draft21LabelExporter + : kTLS13LabelExporter; return derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len, - (const uint8_t *)kTLS13LabelClientApplicationTraffic, - strlen(kTLS13LabelClientApplicationTraffic)) && + (const uint8_t *)client_label, strlen(client_label)) && ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0", hs->client_traffic_secret_0, hs->hash_len) && derive_secret(hs, hs->server_traffic_secret_0, hs->hash_len, - (const uint8_t *)kTLS13LabelServerApplicationTraffic, - strlen(kTLS13LabelServerApplicationTraffic)) && + (const uint8_t *)server_label, strlen(server_label)) && ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0", hs->server_traffic_secret_0, hs->hash_len) && derive_secret(hs, ssl->s3->exporter_secret, hs->hash_len, - (const uint8_t *)kTLS13LabelExporter, - strlen(kTLS13LabelExporter)) && + (const uint8_t *)exporter_label, + strlen(exporter_label)) && ssl_log_secret(ssl, "EXPORTER_SECRET", ssl->s3->exporter_secret, hs->hash_len); } static const char kTLS13LabelApplicationTraffic[] = "application traffic secret"; +static const char kTLS13Draft21LabelApplicationTraffic[] = "traffic upd"; int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) { uint8_t *secret; @@ -255,11 +316,14 @@ secret_len = ssl->s3->write_traffic_secret_len; } + const char *traffic_label = ssl_is_draft21(ssl->version) + ? kTLS13Draft21LabelApplicationTraffic + : kTLS13LabelApplicationTraffic; + const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl)); - if (!hkdf_expand_label(secret, digest, secret, secret_len, - (const uint8_t *)kTLS13LabelApplicationTraffic, - strlen(kTLS13LabelApplicationTraffic), NULL, 0, - secret_len)) { + if (!hkdf_expand_label(secret, ssl->version, digest, secret, secret_len, + (const uint8_t *)traffic_label, strlen(traffic_label), + NULL, 0, secret_len)) { return 0; } @@ -267,30 +331,33 @@ } static const char kTLS13LabelResumption[] = "resumption master secret"; +static const char kTLS13Draft21LabelResumption[] = "res master"; int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) { if (hs->hash_len > SSL_MAX_MASTER_KEY_LENGTH) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return 0; } - + const char *resumption_label = ssl_is_draft21(hs->ssl->version) + ? kTLS13Draft21LabelResumption + : kTLS13LabelResumption; hs->new_session->master_key_length = hs->hash_len; return derive_secret( hs, hs->new_session->master_key, hs->new_session->master_key_length, - (const uint8_t *)kTLS13LabelResumption, strlen(kTLS13LabelResumption)); + (const uint8_t *)resumption_label, strlen(resumption_label)); } 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) { +static int tls13_verify_data(const EVP_MD *digest, uint16_t version, + 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, + if (!hkdf_expand_label(key, version, 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) { @@ -312,35 +379,79 @@ uint8_t context_hash[EVP_MAX_MD_SIZE]; size_t context_hash_len; if (!hs->transcript.GetHash(context_hash, &context_hash_len) || - !tls13_verify_data(hs->transcript.Digest(), out, out_len, traffic_secret, - hs->hash_len, context_hash, context_hash_len)) { + !tls13_verify_data(hs->transcript.Digest(), hs->ssl->version, out, + out_len, traffic_secret, hs->hash_len, context_hash, + context_hash_len)) { return 0; } return 1; } +static const char kTLS13LabelResumptionPSK[] = "resumption"; + +bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce) { + if (!ssl_is_draft21(session->ssl_version)) { + return true; + } + + const EVP_MD *digest = ssl_session_get_digest(session); + return hkdf_expand_label(session->master_key, session->ssl_version, digest, + session->master_key, session->master_key_length, + (const uint8_t *)kTLS13LabelResumptionPSK, + strlen(kTLS13LabelResumptionPSK), nonce.data(), + nonce.size(), session->master_key_length); +} + +static const char kTLS13LabelExportKeying[] = "exporter"; + 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, - int use_context) { - const uint8_t *hash = NULL; - size_t hash_len = 0; + const uint8_t *context_in, + size_t context_in_len, int use_context) { + const uint8_t *context = NULL; + size_t context_len = 0; if (use_context) { - hash = context; - hash_len = context_len; + context = context_in; + context_len = context_in_len; + } + + if (!ssl_is_draft21(ssl->version)) { + const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl)); + return hkdf_expand_label( + out, ssl->version, digest, ssl->s3->exporter_secret, + ssl->s3->exporter_secret_len, (const uint8_t *)label, label_len, + context, context_len, out_len); } const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl)); - return hkdf_expand_label(out, digest, ssl->s3->exporter_secret, - ssl->s3->exporter_secret_len, (const uint8_t *)label, - label_len, hash, hash_len, out_len); + + uint8_t hash[EVP_MAX_MD_SIZE]; + uint8_t export_context[EVP_MAX_MD_SIZE]; + uint8_t derived_secret[EVP_MAX_MD_SIZE]; + unsigned hash_len; + unsigned export_context_len; + unsigned derived_secret_len = EVP_MD_size(digest); + if (!EVP_Digest(context, context_len, hash, &hash_len, digest, NULL) || + !EVP_Digest(NULL, 0, export_context, &export_context_len, digest, NULL)) { + return 0; + } + return hkdf_expand_label( + derived_secret, ssl->version, digest, ssl->s3->exporter_secret, + ssl->s3->exporter_secret_len, (const uint8_t *)label, label_len, + export_context, export_context_len, derived_secret_len) && + hkdf_expand_label( + out, ssl->version, digest, derived_secret, derived_secret_len, + (const uint8_t *)kTLS13LabelExportKeying, + strlen(kTLS13LabelExportKeying), hash, hash_len, out_len); } static const char kTLS13LabelPSKBinder[] = "resumption psk binder key"; +static const char kTLS13Draft21LabelPSKBinder[] = "res binder"; -static int tls13_psk_binder(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) { +static int tls13_psk_binder(uint8_t *out, uint16_t version, + 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)) { @@ -353,15 +464,17 @@ NULL, 0)) { return 0; } + const char *binder_label = ssl_is_draft21(version) + ? kTLS13Draft21LabelPSKBinder + : kTLS13LabelPSKBinder; 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)) { + if (!hkdf_expand_label(binder_key, version, digest, early_secret, hash_len, + (const uint8_t *)binder_label, strlen(binder_label), + binder_context, binder_context_len, hash_len) || + !tls13_verify_data(digest, version, out, &len, binder_key, hash_len, + context, context_len)) { return 0; } @@ -381,6 +494,7 @@ ScopedEVP_MD_CTX ctx; uint8_t context[EVP_MAX_MD_SIZE]; unsigned context_len; + if (!EVP_DigestInit_ex(ctx.get(), digest, NULL) || !EVP_DigestUpdate(ctx.get(), hs->transcript.buffer().data(), hs->transcript.buffer().size()) || @@ -390,7 +504,8 @@ } uint8_t verify_data[EVP_MAX_MD_SIZE] = {0}; - if (!tls13_psk_binder(verify_data, digest, ssl->session->master_key, + if (!tls13_psk_binder(verify_data, ssl->session->ssl_version, digest, + ssl->session->master_key, ssl->session->master_key_length, context, context_len, hash_len)) { return 0; @@ -422,7 +537,7 @@ uint8_t verify_data[EVP_MAX_MD_SIZE] = {0}; CBS binder; - if (!tls13_psk_binder(verify_data, hs->transcript.Digest(), + if (!tls13_psk_binder(verify_data, hs->ssl->version, hs->transcript.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.
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc index 89c9d46..9afd0d4 100644 --- a/ssl/tls13_server.cc +++ b/ssl/tls13_server.cc
@@ -160,33 +160,46 @@ ssl_session_rebase_time(ssl, hs->new_session.get()); for (int i = 0; i < kNumTickets; i++) { - if (!RAND_bytes((uint8_t *)&hs->new_session->ticket_age_add, 4)) { + UniquePtr<SSL_SESSION> session( + SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_INCLUDE_NONAUTH)); + if (!session) { return 0; } - hs->new_session->ticket_age_add_valid = 1; + if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) { + return 0; + } + session->ticket_age_add_valid = 1; if (ssl->cert->enable_early_data) { - hs->new_session->ticket_max_early_data = kMaxEarlyDataAccepted; + session->ticket_max_early_data = kMaxEarlyDataAccepted; } + static_assert(kNumTickets < 256, "Too many tickets"); + uint8_t nonce[] = {static_cast<uint8_t>(i)}; + ScopedCBB cbb; - CBB body, ticket, extensions; + CBB body, nonce_cbb, ticket, extensions; if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_NEW_SESSION_TICKET) || - !CBB_add_u32(&body, hs->new_session->timeout) || - !CBB_add_u32(&body, hs->new_session->ticket_age_add) || + !CBB_add_u32(&body, session->timeout) || + !CBB_add_u32(&body, session->ticket_age_add) || + (ssl_is_draft21(ssl->version) && + (!CBB_add_u8_length_prefixed(&body, &nonce_cbb) || + !CBB_add_bytes(&nonce_cbb, nonce, sizeof(nonce)))) || !CBB_add_u16_length_prefixed(&body, &ticket) || - !ssl_encrypt_ticket(ssl, &ticket, hs->new_session.get()) || + !tls13_derive_session_psk(session.get(), nonce) || + !ssl_encrypt_ticket(ssl, &ticket, session.get()) || !CBB_add_u16_length_prefixed(&body, &extensions)) { return 0; } if (ssl->cert->enable_early_data) { CBB early_data_info; - if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) || + if (!CBB_add_u16(&extensions, ssl_is_draft21(ssl->version) + ? TLSEXT_TYPE_early_data + : TLSEXT_TYPE_ticket_early_data_info) || !CBB_add_u16_length_prefixed(&extensions, &early_data_info) || - !CBB_add_u32(&early_data_info, - hs->new_session->ticket_max_early_data) || + !CBB_add_u32(&early_data_info, session->ticket_max_early_data) || !CBB_flush(&extensions)) { return 0; } @@ -244,8 +257,11 @@ // The PRF hash is now known. Set up the key schedule and hash the // ClientHello. - if (!tls13_init_key_schedule(hs) || - !ssl_hash_message(hs, msg)) { + if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher)) { + return ssl_hs_error; + } + + if (!ssl_hash_message(hs, msg)) { return ssl_hs_error; } @@ -429,13 +445,16 @@ return ssl_hs_error; } - // Incorporate the PSK into the running secret. + size_t hash_len = EVP_MD_size( + ssl_get_handshake_digest(ssl_protocol_version(ssl), hs->new_cipher)); + + // Set up the key schedule and incorporate the PSK into the running secret. if (ssl->s3->session_reused) { - if (!tls13_advance_key_schedule(hs, hs->new_session->master_key, + if (!tls13_init_key_schedule(hs, hs->new_session->master_key, hs->new_session->master_key_length)) { return ssl_hs_error; } - } else if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len)) { + } else if (!tls13_init_key_schedule(hs, kZeroes, hash_len)) { return ssl_hs_error; } @@ -454,6 +473,10 @@ ssl->early_data_accepted = false; ssl->s3->skip_early_data = true; ssl->method->next_message(ssl); + if (ssl_is_draft21(ssl->version) && + !hs->transcript.UpdateForHelloRetryRequest()) { + return ssl_hs_error; + } hs->tls13_state = state_send_hello_retry_request; return ssl_hs_ok; } @@ -473,6 +496,8 @@ if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_HELLO_RETRY_REQUEST) || !CBB_add_u16(&body, ssl->version) || + (ssl_is_draft21(ssl->version) && + !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher))) || !tls1_get_shared_group(hs, &group_id) || !CBB_add_u16_length_prefixed(&body, &extensions) || !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) || @@ -582,16 +607,48 @@ // Send a CertificateRequest, if necessary. if (hs->cert_request) { - CBB sigalgs_cbb; - if (!ssl->method->init_message(ssl, cbb.get(), &body, - SSL3_MT_CERTIFICATE_REQUEST) || - !CBB_add_u8(&body, 0 /* no certificate_request_context. */) || - !CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) || - !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb) || - !ssl_add_client_CA_list(ssl, &body) || - !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) || - !ssl_add_message_cbb(ssl, cbb.get())) { - return ssl_hs_error; + if (ssl_is_draft21(ssl->version)) { + CBB cert_request_extensions, sigalg_contents, sigalgs_cbb; + if (!ssl->method->init_message(ssl, cbb.get(), &body, + SSL3_MT_CERTIFICATE_REQUEST) || + !CBB_add_u8(&body, 0 /* no certificate_request_context. */) || + !CBB_add_u16_length_prefixed(&body, &cert_request_extensions) || + !CBB_add_u16(&cert_request_extensions, + TLSEXT_TYPE_signature_algorithms) || + !CBB_add_u16_length_prefixed(&cert_request_extensions, + &sigalg_contents) || + !CBB_add_u16_length_prefixed(&sigalg_contents, &sigalgs_cbb) || + !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb)) { + return ssl_hs_error; + } + + if (ssl_has_client_CAs(ssl)) { + CBB ca_contents; + if (!CBB_add_u16(&cert_request_extensions, + TLSEXT_TYPE_certificate_authorities) || + !CBB_add_u16_length_prefixed(&cert_request_extensions, + &ca_contents) || + !ssl_add_client_CA_list(ssl, &ca_contents) || + !CBB_flush(&cert_request_extensions)) { + return ssl_hs_error; + } + } + + if (!ssl_add_message_cbb(ssl, cbb.get())) { + return ssl_hs_error; + } + } else { + CBB sigalgs_cbb; + if (!ssl->method->init_message(ssl, cbb.get(), &body, + SSL3_MT_CERTIFICATE_REQUEST) || + !CBB_add_u8(&body, 0 /* no certificate_request_context. */) || + !CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) || + !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb) || + !ssl_add_client_CA_list(ssl, &body) || + !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) || + !ssl_add_message_cbb(ssl, cbb.get())) { + return ssl_hs_error; + } } } @@ -648,6 +705,15 @@ // the wire sooner and also avoids triggering a write on |SSL_read| when // processing the client Finished. This requires computing the client // Finished early. See draft-ietf-tls-tls13-18, section 4.5.1. + if (ssl_is_draft21(ssl->version)) { + static const uint8_t kEndOfEarlyData[4] = {SSL3_MT_END_OF_EARLY_DATA, 0, + 0, 0}; + if (!hs->transcript.Update(kEndOfEarlyData)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return ssl_hs_error; + } + } + size_t finished_len; if (!tls13_finished_mac(hs, hs->expected_client_finished, &finished_len, 0 /* client */)) { @@ -698,11 +764,30 @@ } static enum ssl_hs_wait_t do_process_end_of_early_data(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; hs->tls13_state = state_process_change_cipher_spec; - // If early data was accepted, the ChangeCipherSpec message will be in the - // discarded early data. - if (hs->early_data_offered && !hs->ssl->early_data_accepted) { - return ssl_hs_ok; + if (hs->early_data_offered) { + // If early data was not accepted, the EndOfEarlyData and ChangeCipherSpec + // message will be in the discarded early data. + if (!hs->ssl->early_data_accepted) { + return ssl_hs_ok; + } + if (ssl_is_draft21(ssl->version)) { + SSLMessage msg; + if (!ssl->method->get_message(ssl, &msg)) { + return ssl_hs_read_message; + } + + if (!ssl_check_message_type(ssl, msg, SSL3_MT_END_OF_EARLY_DATA)) { + return ssl_hs_error; + } + if (CBS_len(&msg.body) != 0) { + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } + ssl->method->next_message(ssl); + } } return ssl_is_resumption_client_ccs_experiment(hs->ssl->version) ? ssl_hs_read_change_cipher_spec
diff --git a/ssl/tls_record.cc b/ssl/tls_record.cc index bc641fa..a062012 100644 --- a/ssl/tls_record.cc +++ b/ssl/tls_record.cc
@@ -320,7 +320,8 @@ if (type == SSL3_RT_ALERT) { // Return end_of_early_data alerts as-is for the caller to process. - if (out->size() == 2 && + if (!ssl_is_draft21(ssl->version) && + out->size() == 2 && (*out)[0] == SSL3_AL_WARNING && (*out)[1] == TLS1_AD_END_OF_EARLY_DATA) { *out_type = type;
diff --git a/tool/client.cc b/tool/client.cc index 2ec381f..873cea0 100644 --- a/tool/client.cc +++ b/tool/client.cc
@@ -323,6 +323,10 @@ *out = tls13_default; return true; } + if (in == "draft21") { + *out = tls13_draft21; + return true; + } if (in == "experiment") { *out = tls13_experiment; return true;