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;