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) {