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>kris/onging/CECPQ3_patch15
@@ -578,6 +578,7 @@ OPENSSL_EXPORT int DTLSv1_handle_timeout(SSL *ssl); | |||
#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 @@ OPENSSL_EXPORT int SSL_total_renegotiations(const SSL *ssl); | |||
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 | |||
@@ -272,6 +272,7 @@ OPENSSL_COMPILE_ASSERT( | |||
#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 | |||
@@ -674,7 +674,7 @@ int ssl_write_client_hello(SSL_HANDSHAKE *hs) { | |||
/* 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 @@ static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) { | |||
} | |||
/* 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; | |||
@@ -266,7 +266,15 @@ int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len) { | |||
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; | |||
@@ -523,7 +523,13 @@ int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len) { | |||
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; | |||
@@ -34,6 +34,7 @@ int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) { | |||
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 @@ int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) { | |||
* 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 @@ static int set_version_bound(const SSL_PROTOCOL_METHOD *method, uint16_t *out, | |||
* 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 @@ static uint16_t ssl_version(const SSL *ssl) { | |||
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 @@ static const char *ssl_get_version(int version) { | |||
/* 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 @@ int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) { | |||
* 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; | |||
@@ -33,18 +33,21 @@ const ( | |||
// 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 @@ const ( | |||
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 @@ func (c *Config) defaultCurves() map[CurveID]bool { | |||
// 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 | |||
} | |||
@@ -762,6 +762,11 @@ RestartReadRecord: | |||
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]) | |||
@@ -35,7 +35,7 @@ func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) { | |||
switch vers { | |||
case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: | |||
return vers, true | |||
case tls13DraftVersion, tls13ExperimentVersion: | |||
case tls13DraftVersion, tls13ExperimentVersion, tls13RecordTypeExperimentVersion: | |||
return VersionTLS13, true | |||
} | |||
} | |||
@@ -573,7 +573,11 @@ ResendHelloRetryRequest: | |||
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 @@ ResendHelloRetryRequest: | |||
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() | |||
@@ -1280,6 +1280,13 @@ var tlsVersions = []tlsVersion{ | |||
versionWire: tls13ExperimentVersion, | |||
tls13Variant: TLS13Experiment, | |||
}, | |||
{ | |||
name: "TLS13RecordTypeExperiment", | |||
version: VersionTLS13, | |||
excludeFlag: "-no-tls13", | |||
versionWire: tls13RecordTypeExperimentVersion, | |||
tls13Variant: TLS13RecordTypeExperiment, | |||
}, | |||
} | |||
func allVersions(protocol protocol) []tlsVersion { | |||
@@ -4016,6 +4023,34 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) { | |||
}, | |||
}) | |||
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", | |||
@@ -4144,6 +4179,28 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) { | |||
}, | |||
}) | |||
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", | |||
@@ -10475,6 +10532,19 @@ func addTLS13HandshakeTests() { | |||
flags: []string{"-tls13-variant", "1"}, | |||
}) | |||
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", | |||
@@ -11001,6 +11071,32 @@ func addTLS13HandshakeTests() { | |||
}, | |||
}) | |||
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", | |||
@@ -124,8 +124,8 @@ static const struct argument kArguments[] = { | |||
"-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 @@ bool Client(const std::vector<std::string> &args) { | |||
} | |||
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) { | |||