Implement ContentType TLS 1.3 variant. This implements PR #1051 (https://github.com/tlswg/tls13-spec/pull/1051). Local experiments were not able to replicate the claims in the PR, but implement this anyway for comparison purposes. Change-Id: Ic9baf5e671f9a44565020466a553dd08f5ec0f1b Reviewed-on: https://boringssl-review.googlesource.com/17844 Reviewed-by: Steven Valdez <svaldez@google.com> Commit-Queue: Steven Valdez <svaldez@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 35ef083..16aeaff 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -578,6 +578,7 @@ #define TLS1_3_DRAFT_VERSION 0x7f12 #define TLS1_3_EXPERIMENT_VERSION 0x7e01 +#define TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION 0x7a12 /* SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to * |version|. If |version| is zero, the default minimum version is used. It @@ -3139,6 +3140,7 @@ enum tls13_variant_t { tls13_default = 0, tls13_experiment = 1, + tls13_record_type_experiment = 2, }; /* 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 98648c4..2b241ba 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h
@@ -272,6 +272,7 @@ #define SSL3_RT_ALERT 21 #define SSL3_RT_HANDSHAKE 22 #define SSL3_RT_APPLICATION_DATA 23 +#define SSL3_RT_PLAINTEXT_HANDSHAKE 24 /* Pseudo content type for SSL/TLS header info */ #define SSL3_RT_HEADER 0x100
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc index 2c37ec0..48fe052 100644 --- a/ssl/handshake_client.cc +++ b/ssl/handshake_client.cc
@@ -674,7 +674,7 @@ /* In TLS 1.3 experimental encodings, send a fake placeholder session ID * when we do not otherwise have one to send. */ if (hs->max_version >= TLS1_3_VERSION && - ssl->tls13_variant != tls13_default && + ssl->tls13_variant == tls13_experiment && !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) { return 0; } @@ -759,7 +759,7 @@ } /* Initialize a random session ID for the experimental TLS 1.3 variant. */ - if (ssl->tls13_variant != tls13_default) { + if (ssl->tls13_variant == tls13_experiment) { hs->session_id_len = sizeof(hs->session_id); if (!RAND_bytes(hs->session_id, hs->session_id_len)) { return -1;
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc index 5eb364d..79f71fa 100644 --- a/ssl/s3_both.cc +++ b/ssl/s3_both.cc
@@ -266,7 +266,15 @@ todo = ssl->max_send_fragment; } - if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, msg + added, todo)) { + uint8_t type = SSL3_RT_HANDSHAKE; + if (ssl->server && + ssl->s3->have_version && + ssl->version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION && + ssl->s3->aead_write_ctx == NULL) { + type = SSL3_RT_PLAINTEXT_HANDSHAKE; + } + + if (!add_record_to_flight(ssl, type, msg + added, todo)) { goto err; } added += todo;
diff --git a/ssl/s3_pkt.cc b/ssl/s3_pkt.cc index beaa08e..4ae2e34 100644 --- a/ssl/s3_pkt.cc +++ b/ssl/s3_pkt.cc
@@ -523,7 +523,13 @@ return -1; } - if (rr->type != SSL3_RT_HANDSHAKE) { + /* Accept server_plaintext_handshake records when the content type TLS 1.3 + * variant is enabled. */ + if (rr->type != SSL3_RT_HANDSHAKE && + !(!ssl->server && + ssl->tls13_variant == tls13_record_type_experiment && + ssl->s3->aead_read_ctx == NULL && + rr->type == SSL3_RT_PLAINTEXT_HANDSHAKE)) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); return -1;
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc index 387eee7..8b54bd2 100644 --- a/ssl/ssl_versions.cc +++ b/ssl/ssl_versions.cc
@@ -34,6 +34,7 @@ case TLS1_3_DRAFT_VERSION: case TLS1_3_EXPERIMENT_VERSION: + case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION: *out = TLS1_3_VERSION; return 1; @@ -55,8 +56,9 @@ * decreasing preference. */ static const uint16_t kTLSVersions[] = { - TLS1_3_DRAFT_VERSION, TLS1_3_EXPERIMENT_VERSION, + TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION, + TLS1_3_DRAFT_VERSION, TLS1_2_VERSION, TLS1_1_VERSION, TLS1_VERSION, @@ -98,7 +100,8 @@ * everywhere to refer to any draft TLS 1.3 versions. In this direction, we * map it to some representative TLS 1.3 draft version. */ if (version == TLS1_3_DRAFT_VERSION || - version == TLS1_3_EXPERIMENT_VERSION) { + version == TLS1_3_EXPERIMENT_VERSION || + version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION); return 0; } @@ -238,7 +241,8 @@ int SSL_version(const SSL *ssl) { uint16_t ret = ssl_version(ssl); /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */ - if (ret == TLS1_3_DRAFT_VERSION || ret == TLS1_3_EXPERIMENT_VERSION) { + if (ret == TLS1_3_DRAFT_VERSION || ret == TLS1_3_EXPERIMENT_VERSION || + ret == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) { return TLS1_3_VERSION; } return ret; @@ -249,6 +253,7 @@ /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */ case TLS1_3_DRAFT_VERSION: case TLS1_3_EXPERIMENT_VERSION: + case TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION: return "TLSv1.3"; case TLS1_2_VERSION: @@ -301,12 +306,15 @@ * non-default value. */ if (ssl->server) { if (ssl->tls13_variant == tls13_default && - version == TLS1_3_EXPERIMENT_VERSION) { + (version == TLS1_3_EXPERIMENT_VERSION || + version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION)) { return 0; } } else { if ((ssl->tls13_variant != tls13_experiment && version == TLS1_3_EXPERIMENT_VERSION) || + (ssl->tls13_variant != tls13_record_type_experiment && + version == TLS1_3_RECORD_TYPE_EXPERIMENT_VERSION) || (ssl->tls13_variant != tls13_default && version == TLS1_3_DRAFT_VERSION)) { return 0;
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index b40f222..a3c744c 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go
@@ -33,18 +33,21 @@ // A draft version of TLS 1.3 that is sent over the wire for the current draft. const ( - tls13DraftVersion = 0x7f12 - tls13ExperimentVersion = 0x7e01 + tls13DraftVersion = 0x7f12 + tls13ExperimentVersion = 0x7e01 + tls13RecordTypeExperimentVersion = 0x7a12 ) const ( - TLS13Default = 0 - TLS13Experiment = 1 + TLS13Default = 0 + TLS13Experiment = 1 + TLS13RecordTypeExperiment = 2 ) var allTLSWireVersions = []uint16{ tls13DraftVersion, tls13ExperimentVersion, + tls13RecordTypeExperimentVersion, VersionTLS12, VersionTLS11, VersionTLS10, @@ -71,10 +74,11 @@ type recordType uint8 const ( - recordTypeChangeCipherSpec recordType = 20 - recordTypeAlert recordType = 21 - recordTypeHandshake recordType = 22 - recordTypeApplicationData recordType = 23 + recordTypeChangeCipherSpec recordType = 20 + recordTypeAlert recordType = 21 + recordTypeHandshake recordType = 22 + recordTypeApplicationData recordType = 23 + recordTypePlaintextHandshake recordType = 24 ) // TLS handshake message types. @@ -1485,6 +1489,7 @@ // false. func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) { if (c.TLS13Variant != TLS13Experiment && wireVers == tls13ExperimentVersion) || + (c.TLS13Variant != TLS13RecordTypeExperiment && wireVers == tls13RecordTypeExperimentVersion) || (c.TLS13Variant != TLS13Default && wireVers == tls13DraftVersion) { return 0, false }
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index a50029f..c974bd4 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go
@@ -762,6 +762,11 @@ return 0, nil, c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received")) } + // Accept server_plaintext_handshake records when the content type TLS 1.3 variant is enabled. + if c.isClient && c.in.cipher == nil && c.config.TLS13Variant == TLS13RecordTypeExperiment && want == recordTypeHandshake && typ == recordTypePlaintextHandshake { + typ = recordTypeHandshake + } + vers := uint16(b.data[1])<<8 | uint16(b.data[2]) n := int(b.data[3])<<8 | int(b.data[4])
diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go index b0b6c18..72369d6 100644 --- a/ssl/test/runner/dtls.go +++ b/ssl/test/runner/dtls.go
@@ -35,7 +35,7 @@ switch vers { case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: return vers, true - case tls13DraftVersion, tls13ExperimentVersion: + case tls13DraftVersion, tls13ExperimentVersion, tls13RecordTypeExperimentVersion: return VersionTLS13, true } }
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 25230a4..35d005e 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go
@@ -573,7 +573,11 @@ if sendHelloRetryRequest { oldClientHelloBytes := hs.clientHello.marshal() hs.writeServerHash(helloRetryRequest.marshal()) - c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()) + if c.vers == tls13RecordTypeExperimentVersion { + c.writeRecord(recordTypePlaintextHandshake, helloRetryRequest.marshal()) + } else { + c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()) + } c.flushHandshake() if hs.clientHello.hasEarlyData { @@ -751,7 +755,11 @@ toWrite = append(toWrite, typeEncryptedExtensions) c.writeRecord(recordTypeHandshake, toWrite) } else { - c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + if c.vers == tls13RecordTypeExperimentVersion { + c.writeRecord(recordTypePlaintextHandshake, hs.hello.marshal()) + } else { + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + } } c.flushHandshake()
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 0005725..86f5f71 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -1280,6 +1280,13 @@ versionWire: tls13ExperimentVersion, tls13Variant: TLS13Experiment, }, + { + name: "TLS13RecordTypeExperiment", + version: VersionTLS13, + excludeFlag: "-no-tls13", + versionWire: tls13RecordTypeExperimentVersion, + tls13Variant: TLS13RecordTypeExperiment, + }, } func allVersions(protocol protocol) []tlsVersion { @@ -4018,6 +4025,34 @@ tests = append(tests, testCase{ testType: clientTest, + name: "TLS13RecordTypeExperiment-EarlyData-Client", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + TLS13Variant: TLS13RecordTypeExperiment, + MaxEarlyDataSize: 16384, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + TLS13Variant: TLS13RecordTypeExperiment, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}}, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-early-data", + "-expect-early-data-info", + "-expect-accept-early-data", + "-on-resume-shim-writes-first", + "-tls13-variant", "2", + }, + }) + + tests = append(tests, testCase{ + testType: clientTest, name: "TLS13-EarlyData-TooMuchData-Client", config: Config{ MaxVersion: VersionTLS13, @@ -4146,6 +4181,28 @@ tests = append(tests, testCase{ testType: serverTest, + name: "TLS13RecordTypeExperiment-EarlyData-Server", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + TLS13Variant: TLS13RecordTypeExperiment, + Bugs: ProtocolBugs{ + SendEarlyData: [][]byte{{1, 2, 3, 4}}, + ExpectEarlyDataAccepted: true, + ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}}, + }, + }, + messageCount: 2, + resumeSession: true, + flags: []string{ + "-enable-early-data", + "-expect-accept-early-data", + "-tls13-variant", "2", + }, + }) + + tests = append(tests, testCase{ + testType: serverTest, name: "TLS13-MaxEarlyData-Server", config: Config{ MaxVersion: VersionTLS13, @@ -10477,6 +10534,19 @@ testCases = append(testCases, testCase{ testType: serverTest, + name: "SkipEarlyData-TLS13RecordTypeExperiment", + config: Config{ + MaxVersion: VersionTLS13, + TLS13Variant: TLS13RecordTypeExperiment, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + }, + }, + flags: []string{"-tls13-variant", "2"}, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, name: "SkipEarlyData-OmitEarlyDataExtension", config: Config{ MaxVersion: VersionTLS13, @@ -11003,6 +11073,32 @@ testCases = append(testCases, testCase{ testType: clientTest, + name: "TLS13RecordTypeExperiment-EarlyData-Reject-Client", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + TLS13Variant: TLS13RecordTypeExperiment, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + TLS13Variant: TLS13RecordTypeExperiment, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + AlwaysRejectEarlyData: true, + }, + }, + resumeSession: true, + flags: []string{ + "-enable-early-data", + "-expect-early-data-info", + "-expect-reject-early-data", + "-on-resume-shim-writes-first", + "-tls13-variant", "2", + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, name: "TLS13-EarlyData-RejectTicket-Client", config: Config{ MaxVersion: VersionTLS13,
diff --git a/tool/client.cc b/tool/client.cc index e28e15a..f7a8259 100644 --- a/tool/client.cc +++ b/tool/client.cc
@@ -124,8 +124,8 @@ "-early-data", kOptionalArgument, "Allow early data", }, { - "-tls13-variant", kBooleanArgument, - "Enable the experimental TLS 1.3 variant", + "-tls13-variant", kOptionalArgument, + "Enable the specified experimental TLS 1.3 variant", }, { "-ed25519", kBooleanArgument, "Advertise Ed25519 support", @@ -464,7 +464,9 @@ } if (args_map.count("-tls13-variant") != 0) { - SSL_CTX_set_tls13_variant(ctx.get(), tls13_experiment); + SSL_CTX_set_tls13_variant(ctx.get(), + static_cast<enum tls13_variant_t>( + atoi(args_map["-tls13-variant"].c_str()))); } if (args_map.count("-ed25519") != 0) {