diff options
author | Steven Valdez <svaldez@google.com> | 2017-11-07 17:09:52 -0500 |
---|---|---|
committer | CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> | 2017-11-11 06:24:55 +0000 |
commit | 964b2377d0e6c625a96cb95bea665512352e547a (patch) | |
tree | 2b62c224abe9769a22fb4bef85102cadb7400183 | |
parent | 3bcbb375521e5628cc9bd8e38cfcb7d9d8645513 (diff) | |
download | boringssl-964b2377d0e6c625a96cb95bea665512352e547a.zip boringssl-964b2377d0e6c625a96cb95bea665512352e547a.tar.gz boringssl-964b2377d0e6c625a96cb95bea665512352e547a.tar.bz2 |
Implement PR 1091 (TLS 1.3 draft '22').
This introduces a wire change to Experiment2/Experiment3 over 0RTT, however
as there is never going to be a 0RTT deployment with Experiment2/Experiment3,
this is valid.
Change-Id: Id541d195cbc4bbb3df7680ae2a02b53bb8ae3eab
Reviewed-on: https://boringssl-review.googlesource.com/22744
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
-rw-r--r-- | include/openssl/ssl.h | 2 | ||||
-rw-r--r-- | ssl/handshake.cc | 1 | ||||
-rw-r--r-- | ssl/handshake_client.cc | 6 | ||||
-rw-r--r-- | ssl/internal.h | 7 | ||||
-rw-r--r-- | ssl/ssl_versions.cc | 24 | ||||
-rw-r--r-- | ssl/test/runner/common.go | 32 | ||||
-rw-r--r-- | ssl/test/runner/conn.go | 12 | ||||
-rw-r--r-- | ssl/test/runner/handshake_client.go | 21 | ||||
-rw-r--r-- | ssl/test/runner/handshake_messages.go | 50 | ||||
-rw-r--r-- | ssl/test/runner/handshake_server.go | 19 | ||||
-rw-r--r-- | ssl/test/runner/runner.go | 92 | ||||
-rw-r--r-- | ssl/tls13_both.cc | 6 | ||||
-rw-r--r-- | ssl/tls13_client.cc | 99 | ||||
-rw-r--r-- | ssl/tls13_server.cc | 112 | ||||
-rw-r--r-- | ssl/tls_record.cc | 15 | ||||
-rw-r--r-- | tool/client.cc | 4 |
16 files changed, 392 insertions, 110 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 3540a25..8c36ad5 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -593,6 +593,7 @@ OPENSSL_EXPORT int DTLSv1_handle_timeout(SSL *ssl); #define TLS1_3_DRAFT_VERSION 0x7f12 #define TLS1_3_DRAFT21_VERSION 0x7f15 +#define TLS1_3_DRAFT22_VERSION 0x7e04 #define TLS1_3_EXPERIMENT_VERSION 0x7e01 #define TLS1_3_EXPERIMENT2_VERSION 0x7e02 #define TLS1_3_EXPERIMENT3_VERSION 0x7e03 @@ -3255,6 +3256,7 @@ enum tls13_variant_t { tls13_experiment2 = 2, tls13_experiment3 = 3, tls13_draft21 = 4, + tls13_draft22 = 5, }; // SSL_CTX_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the diff --git a/ssl/handshake.cc b/ssl/handshake.cc index ed11484..3b446a8 100644 --- a/ssl/handshake.cc +++ b/ssl/handshake.cc @@ -127,6 +127,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg) scts_requested(false), needs_psk_binder(false), received_hello_retry_request(false), + sent_hello_retry_request(false), received_custom_extension(false), handshake_finalized(false), accept_psk_mode(false), diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc index f907939..583aceb 100644 --- a/ssl/handshake_client.cc +++ b/ssl/handshake_client.cc @@ -500,6 +500,12 @@ static enum ssl_hs_wait_t do_enter_early_data(SSL_HANDSHAKE *hs) { return ssl_hs_ok; } + ssl->s3->aead_write_ctx->SetVersionIfNullCipher(ssl->session->ssl_version); + if (ssl_is_draft22(ssl->session->ssl_version) && + !ssl->method->add_change_cipher_spec(ssl)) { + return ssl_hs_error; + } + if (!tls13_init_early_key_schedule(hs, ssl->session->master_key, ssl->session->master_key_length) || !tls13_derive_early_secrets(hs) || diff --git a/ssl/internal.h b/ssl/internal.h index 55bece9..6c14383 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -396,6 +396,10 @@ uint16_t ssl_protocol_version(const SSL *ssl); // variant. bool ssl_is_draft21(uint16_t version); +// ssl_is_draft22 returns whether the version corresponds to a draft22 TLS 1.3 +// variant. +bool ssl_is_draft22(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); @@ -1004,6 +1008,8 @@ struct SSLMessage { // Channel ID, are all enabled. #define SSL_MAX_HANDSHAKE_FLIGHT 7 +extern const uint8_t kHelloRetryRequest[SSL3_RANDOM_SIZE]; + // ssl_max_handshake_message_len returns the maximum number of bytes permitted // in a handshake message for |ssl|. size_t ssl_max_handshake_message_len(const SSL *ssl); @@ -1438,6 +1444,7 @@ struct SSL_HANDSHAKE { bool needs_psk_binder:1; bool received_hello_retry_request:1; + bool sent_hello_retry_request:1; bool received_custom_extension:1; diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc index 1f30b41..a356ed1 100644 --- a/ssl/ssl_versions.cc +++ b/ssl/ssl_versions.cc @@ -36,6 +36,7 @@ bool ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) { case TLS1_3_DRAFT_VERSION: case TLS1_3_DRAFT21_VERSION: + case TLS1_3_DRAFT22_VERSION: case TLS1_3_EXPERIMENT_VERSION: case TLS1_3_EXPERIMENT2_VERSION: case TLS1_3_EXPERIMENT3_VERSION: @@ -60,6 +61,7 @@ bool ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) { // decreasing preference. static const uint16_t kTLSVersions[] = { + TLS1_3_DRAFT22_VERSION, TLS1_3_EXPERIMENT3_VERSION, TLS1_3_EXPERIMENT2_VERSION, TLS1_3_EXPERIMENT_VERSION, @@ -109,6 +111,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_DRAFT22_VERSION: case TLS1_3_EXPERIMENT_VERSION: case TLS1_3_EXPERIMENT2_VERSION: case TLS1_3_EXPERIMENT3_VERSION: @@ -142,6 +145,7 @@ static uint16_t wire_version_to_api(uint16_t 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_DRAFT22_VERSION: case TLS1_3_EXPERIMENT_VERSION: case TLS1_3_EXPERIMENT2_VERSION: case TLS1_3_EXPERIMENT3_VERSION: @@ -157,6 +161,7 @@ static uint16_t wire_version_to_api(uint16_t version) { 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_DRAFT22_VERSION || version == TLS1_3_EXPERIMENT_VERSION || version == TLS1_3_EXPERIMENT2_VERSION || version == TLS1_3_EXPERIMENT3_VERSION) { @@ -324,6 +329,8 @@ bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) { version == TLS1_3_EXPERIMENT3_VERSION) || (ssl->tls13_variant == tls13_draft21 && version == TLS1_3_DRAFT21_VERSION) || + (ssl->tls13_variant == tls13_draft22 && + version == TLS1_3_DRAFT22_VERSION) || (ssl->tls13_variant == tls13_default && version == TLS1_3_DRAFT_VERSION)) { return true; @@ -389,28 +396,35 @@ bool ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert, } bool ssl_is_draft21(uint16_t version) { - return version == TLS1_3_DRAFT21_VERSION; + return version == TLS1_3_DRAFT21_VERSION || version == TLS1_3_DRAFT22_VERSION; +} + +bool ssl_is_draft22(uint16_t version) { + return version == TLS1_3_DRAFT22_VERSION; } bool ssl_is_resumption_experiment(uint16_t version) { return version == TLS1_3_EXPERIMENT_VERSION || version == TLS1_3_EXPERIMENT2_VERSION || - version == TLS1_3_EXPERIMENT3_VERSION; + version == TLS1_3_EXPERIMENT3_VERSION || + version == TLS1_3_DRAFT22_VERSION; } bool ssl_is_resumption_variant(enum tls13_variant_t variant) { return variant == tls13_experiment || variant == tls13_experiment2 || - variant == tls13_experiment3; + variant == tls13_experiment3 || variant == tls13_draft22; } bool ssl_is_resumption_client_ccs_experiment(uint16_t version) { return version == TLS1_3_EXPERIMENT_VERSION || - version == TLS1_3_EXPERIMENT2_VERSION; + version == TLS1_3_EXPERIMENT2_VERSION || + version == TLS1_3_DRAFT22_VERSION; } bool ssl_is_resumption_record_version_experiment(uint16_t version) { return version == TLS1_3_EXPERIMENT2_VERSION || - version == TLS1_3_EXPERIMENT3_VERSION; + version == TLS1_3_EXPERIMENT3_VERSION || + version == TLS1_3_DRAFT22_VERSION; } } // namespace bssl diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 9e0deef..4564b0f 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -38,6 +38,7 @@ const ( tls13ExperimentVersion = 0x7e01 tls13Experiment2Version = 0x7e02 tls13Experiment3Version = 0x7e03 + tls13Draft22Version = 0x7e04 ) const ( @@ -46,10 +47,12 @@ const ( TLS13Experiment2 = 2 TLS13Experiment3 = 3 TLS13Draft21 = 4 + TLS13Draft22 = 5 ) var allTLSWireVersions = []uint16{ tls13DraftVersion, + tls13Draft22Version, tls13Draft21Version, tls13Experiment3Version, tls13Experiment2Version, @@ -148,6 +151,12 @@ const ( scsvRenegotiation uint16 = 0x00ff ) +var tls13HelloRetryRequest = []uint8{ + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, + 0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, + 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c, +} + // CurveID is the type of a TLS identifier for an elliptic curve. See // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8 type CurveID uint16 @@ -619,6 +628,10 @@ type ProtocolBugs struct { // messages. FragmentAcrossChangeCipherSpec bool + // SendExtraChangeCipherSpec causes the implementation to send extra + // ChangeCipherSpec messages. + SendExtraChangeCipherSpec int + // SendUnencryptedFinished, if true, causes the Finished message to be // send unencrypted before ChangeCipherSpec rather than after it. SendUnencryptedFinished bool @@ -1605,7 +1618,7 @@ func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) { switch vers { case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: return vers, true - case tls13DraftVersion, tls13Draft21Version, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version: + case tls13DraftVersion, tls13Draft22Version, tls13Draft21Version, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version: return VersionTLS13, true } } @@ -1614,19 +1627,27 @@ func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) { } func isDraft21(vers uint16) bool { - return vers == tls13Draft21Version + return vers == tls13Draft21Version || vers == tls13Draft22Version +} + +func isDraft22(vers uint16) bool { + return vers == tls13Draft22Version } func isResumptionExperiment(vers uint16) bool { - return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Experiment3Version + return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Experiment3Version || vers == tls13Draft22Version } func isResumptionClientCCSExperiment(vers uint16) bool { - return vers == tls13ExperimentVersion || vers == tls13Experiment2Version + return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Draft22Version } func isResumptionRecordVersionExperiment(vers uint16) bool { - return vers == tls13Experiment2Version || vers == tls13Experiment3Version + return vers == tls13Experiment2Version || vers == tls13Experiment3Version || vers == tls13Draft22Version +} + +func isResumptionRecordVersionVariant(variant int) bool { + return variant == TLS13Experiment2 || variant == TLS13Experiment3 || variant == TLS13Draft22 } // isSupportedVersion checks if the specified wire version is acceptable. If so, @@ -1636,6 +1657,7 @@ func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) if (c.TLS13Variant != TLS13Experiment && wireVers == tls13ExperimentVersion) || (c.TLS13Variant != TLS13Experiment2 && wireVers == tls13Experiment2Version) || (c.TLS13Variant != TLS13Experiment3 && wireVers == tls13Experiment3Version) || + (c.TLS13Variant != TLS13Draft22 && wireVers == tls13Draft22Version) || (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 6a2c59c..5359462 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go @@ -39,6 +39,7 @@ type Conn struct { vers uint16 // TLS version haveVers bool // version has been negotiated config *Config // configuration passed to constructor + hadHelloRetryRequest bool handshakeComplete bool skipEarlyData bool // On a server, indicates that the client is sending early data that must be skipped over. didResume bool // whether this connection was a session resumption @@ -1152,7 +1153,6 @@ func (c *Conn) doWriteRecord(typ recordType, data []byte) (n int, err error) { } if isResumptionRecordVersionExperiment(c.wireVersion) || isResumptionRecordVersionExperiment(c.out.wireVersion) { vers = VersionTLS12 - } else { } if c.config.Bugs.SendRecordVersion != 0 { @@ -1322,6 +1322,14 @@ func (c *Conn) readHandshake() (interface{}, error) { // so pass in a fresh copy that won't be overwritten. data = append([]byte(nil), data...) + if data[0] == typeServerHello && len(data) >= 38 { + vers := uint16(data[4])<<8 | uint16(data[5]) + if vers == VersionTLS12 && bytes.Equal(data[6:38], tls13HelloRetryRequest) { + m = new(helloRetryRequestMsg) + m.(*helloRetryRequestMsg).isServerHello = true + } + } + if !m.unmarshal(data) { return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } @@ -1936,7 +1944,7 @@ func (c *Conn) sendFakeEarlyData(len int) error { payload[0] = byte(recordTypeApplicationData) payload[1] = 3 payload[2] = 1 - if c.config.TLS13Variant == TLS13Experiment2 || c.config.TLS13Variant == TLS13Experiment3 { + if isResumptionRecordVersionVariant(c.config.TLS13Variant) { payload[1] = 3 payload[2] = 3 } diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index ba34647..7c8dbb5 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -419,6 +419,12 @@ NextCipherSuite: earlyLabel = earlyTrafficLabelDraft21 } + if !c.config.Bugs.SkipChangeCipherSpec && isDraft22(session.wireVersion) { + c.wireVersion = session.wireVersion + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + c.wireVersion = 0 + } + earlyTrafficSecret := finishedHash.deriveSecret(earlyLabel) c.useOutTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret) for _, earlyData := range c.config.Bugs.SendEarlyData { @@ -482,6 +488,13 @@ NextCipherSuite: helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg) var secondHelloBytes []byte if haveHelloRetryRequest { + c.hadHelloRetryRequest = true + if isDraft22(c.wireVersion) { + if err := c.readRecord(recordTypeChangeCipherSpec); err != nil { + return err + } + } + c.out.resetCipher() if len(helloRetryRequest.cookie) > 0 { hello.tls13Cookie = helloRetryRequest.cookie @@ -761,7 +774,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { hs.finishedHash.addEntropy(zeroSecret) } - if isResumptionExperiment(c.wireVersion) { + if isResumptionExperiment(c.wireVersion) && !c.hadHelloRetryRequest { if err := c.readRecord(recordTypeChangeCipherSpec); err != nil { return err } @@ -979,7 +992,11 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { } } - if isResumptionClientCCSExperiment(c.wireVersion) { + if !c.config.Bugs.SkipChangeCipherSpec && isResumptionClientCCSExperiment(c.wireVersion) && !hs.hello.hasEarlyData { + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + + for i := 0; i < c.config.Bugs.SendExtraChangeCipherSpec; i++ { c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) } diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index aa6b463..d5e3951 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -1406,6 +1406,8 @@ func (m *serverExtensions) unmarshal(data []byte, version uint16) bool { type helloRetryRequestMsg struct { raw []byte vers uint16 + isServerHello bool + sessionId []byte cipherSuite uint16 hasSelectedGroup bool selectedGroup CurveID @@ -1420,11 +1422,25 @@ func (m *helloRetryRequestMsg) marshal() []byte { } retryRequestMsg := newByteBuilder() - retryRequestMsg.addU8(typeHelloRetryRequest) + if isDraft22(m.vers) { + retryRequestMsg.addU8(typeServerHello) + } else { + retryRequestMsg.addU8(typeHelloRetryRequest) + } retryRequest := retryRequestMsg.addU24LengthPrefixed() - retryRequest.addU16(m.vers) - if isDraft21(m.vers) { + + if isDraft22(m.vers) { + retryRequest.addU16(VersionTLS12) + retryRequest.addBytes(tls13HelloRetryRequest) + sessionId := retryRequest.addU8LengthPrefixed() + sessionId.addBytes(m.sessionId) retryRequest.addU16(m.cipherSuite) + retryRequest.addU8(0) + } else { + retryRequest.addU16(m.vers) + if isDraft21(m.vers) { + retryRequest.addU16(m.cipherSuite) + } } extensions := retryRequest.addU16LengthPrefixed() @@ -1434,6 +1450,11 @@ func (m *helloRetryRequestMsg) marshal() []byte { } for i := 0; i < count; i++ { + if isDraft22(m.vers) { + extensions.addU16(extensionSupportedVersions) + extensions.addU16(2) // Length + extensions.addU16(m.vers) + } if m.hasSelectedGroup { extensions.addU16(extensionKeyShare) extensions.addU16(2) // length @@ -1461,9 +1482,25 @@ func (m *helloRetryRequestMsg) unmarshal(data []byte) bool { } m.vers = uint16(data[4])<<8 | uint16(data[5]) data = data[6:] - if isDraft21(m.vers) { + if m.isServerHello { + if len(data) < 33 { + return false + } + data = data[32:] // Random + sessionIdLen := int(data[0]) + if sessionIdLen > 32 || len(data) < 1+sessionIdLen+3 { + return false + } + m.sessionId = data[1 : 1+sessionIdLen] + data = data[1+sessionIdLen:] m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) data = data[2:] + data = data[1:] // Compression Method + } else { + 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:] @@ -1482,6 +1519,11 @@ func (m *helloRetryRequestMsg) unmarshal(data []byte) bool { } switch extension { + case extensionSupportedVersions: + if length != 2 || !m.isServerHello { + return false + } + m.vers = uint16(data[0])<<8 | uint16(data[1]) case extensionKeyShare: if length != 2 { return false diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index dd6f48f..2513a00 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -526,6 +526,7 @@ ResendHelloRetryRequest: } helloRetryRequest := &helloRetryRequestMsg{ vers: c.wireVersion, + sessionId: hs.clientHello.sessionId, cipherSuite: cipherSuite, duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions, } @@ -587,6 +588,10 @@ ResendHelloRetryRequest: c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()) c.flushHandshake() + if !c.config.Bugs.SkipChangeCipherSpec && isDraft22(c.wireVersion) { + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + if hs.clientHello.hasEarlyData { c.skipEarlyData = true } @@ -686,6 +691,12 @@ ResendHelloRetryRequest: earlyLabel = earlyTrafficLabelDraft21 } + if isDraft22(c.wireVersion) { + if err := c.readRecord(recordTypeChangeCipherSpec); err != nil { + return err + } + } + earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyLabel) if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil { return err @@ -782,7 +793,11 @@ ResendHelloRetryRequest: } c.flushHandshake() - if isResumptionExperiment(c.wireVersion) { + if !c.config.Bugs.SkipChangeCipherSpec && isResumptionExperiment(c.wireVersion) && !sendHelloRetryRequest { + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + + for i := 0; i < c.config.Bugs.SendExtraChangeCipherSpec; i++ { c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) } @@ -982,7 +997,7 @@ ResendHelloRetryRequest: } } - if isResumptionClientCCSExperiment(c.wireVersion) && !c.skipEarlyData { + if isResumptionClientCCSExperiment(c.wireVersion) && !c.skipEarlyData && !encryptedExtensions.extensions.hasEarlyData { if err := c.readRecord(recordTypeChangeCipherSpec); err != nil { return err } diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index ce50fa8..b6aa4a1 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -1312,6 +1312,13 @@ var tlsVersions = []tlsVersion{ tls13Variant: TLS13Draft21, }, { + name: "TLS13Draft22", + version: VersionTLS13, + excludeFlag: "-no-tls13", + versionWire: tls13Draft22Version, + tls13Variant: TLS13Draft22, + }, + { name: "TLS13Experiment", version: VersionTLS13, excludeFlag: "-no-tls13", @@ -4160,6 +4167,37 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) { }) tests = append(tests, testCase{ + name: "TLS13Draft22-HelloRetryRequest-Client", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + // P-384 requires a HelloRetryRequest against BoringSSL's default + // configuration. Assert this with ExpectMissingKeyShare. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + ExpectMissingKeyShare: true, + }, + }, + tls13Variant: TLS13Draft22, + // Cover HelloRetryRequest during an ECDHE-PSK resumption. + resumeSession: true, + }) + + tests = append(tests, testCase{ + testType: serverTest, + name: "TLS13Draft22-HelloRetryRequest-Server", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + // Require a HelloRetryRequest for every curve. + DefaultCurves: []CurveID{}, + }, + tls13Variant: TLS13Draft22, + // Cover HelloRetryRequest during an ECDHE-PSK resumption. + resumeSession: true, + }) + + tests = append(tests, testCase{ testType: clientTest, name: "TLS13-EarlyData-TooMuchData-Client", config: Config{ @@ -5196,7 +5234,7 @@ func addVersionNegotiationTests() { serverVers := expectedServerVersion if expectedServerVersion >= VersionTLS13 { serverVers = VersionTLS10 - if runnerVers.tls13Variant == TLS13Experiment2 || runnerVers.tls13Variant == TLS13Experiment3 { + if runnerVers.tls13Variant == TLS13Experiment2 || runnerVers.tls13Variant == TLS13Experiment3 || runnerVers.tls13Variant == TLS13Draft22 { serverVers = VersionTLS12 } } @@ -11734,6 +11772,58 @@ func addTLS13HandshakeTests() { expectedError: ":UNEXPECTED_EXTENSION:", }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13Draft22-SkipChangeCipherSpec-Client", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + tls13Variant: TLS13Draft22, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13Draft22-SkipChangeCipherSpec-Server", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + tls13Variant: TLS13Draft22, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13Draft22-TooManyChangeCipherSpec-Client", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendExtraChangeCipherSpec: 33, + }, + }, + tls13Variant: TLS13Draft22, + shouldFail: true, + expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13Draft22-TooManyChangeCipherSpec-Server", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendExtraChangeCipherSpec: 33, + }, + }, + tls13Variant: TLS13Draft22, + shouldFail: true, + expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", + }) + fooString := "foo" barString := "bar" diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc index 5796bf4..57acbcb 100644 --- a/ssl/tls13_both.cc +++ b/ssl/tls13_both.cc @@ -37,6 +37,12 @@ namespace bssl { // without being able to return application data. static const uint8_t kMaxKeyUpdates = 32; +const uint8_t kHelloRetryRequest[SSL3_RANDOM_SIZE] = { + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, + 0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, + 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c, +}; + bool tls13_get_cert_verify_signature_input( SSL_HANDSHAKE *hs, Array<uint8_t> *out, enum ssl_cert_verify_context_t cert_verify_context) { diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc index 9c09309..0013e61 100644 --- a/ssl/tls13_client.cc +++ b/ssl/tls13_client.cc @@ -36,7 +36,6 @@ enum client_hs_state_t { state_read_hello_retry_request = 0, state_send_second_client_hello, state_read_server_hello, - state_process_change_cipher_spec, state_read_encrypted_extensions, state_read_certificate_request, state_read_server_certificate, @@ -57,23 +56,52 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { if (!ssl->method->get_message(ssl, &msg)) { return ssl_hs_read_message; } - if (msg.type != SSL3_MT_HELLO_RETRY_REQUEST) { - hs->tls13_state = state_read_server_hello; - return ssl_hs_ok; - } - CBS body = msg.body, extensions; - 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 || - CBS_len(&body) != 0) { - OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); - ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); - return ssl_hs_error; + CBS extensions; + uint16_t cipher_suite = 0; + if (ssl_is_draft22(ssl->version)) { + if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) { + return ssl_hs_error; + } + + CBS body = msg.body, server_random, session_id; + uint16_t server_version; + if (!CBS_get_u16(&body, &server_version) || + !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) || + !CBS_get_u8_length_prefixed(&body, &session_id) || + !CBS_get_u16(&body, &cipher_suite) || + !CBS_skip(&body, 1) || + !CBS_get_u16_length_prefixed(&body, &extensions) || + CBS_len(&extensions) == 0 || + CBS_len(&body) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + if (!CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) { + hs->tls13_state = state_read_server_hello; + return ssl_hs_ok; + } + } else { + if (msg.type != SSL3_MT_HELLO_RETRY_REQUEST) { + hs->tls13_state = state_read_server_hello; + return ssl_hs_ok; + } + + CBS body = msg.body; + uint16_t server_version; + 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 || + CBS_len(&body) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } } if (ssl_is_draft21(ssl->version)) { @@ -96,11 +124,13 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { } - bool have_cookie, have_key_share; - CBS cookie, key_share; + bool have_cookie, have_key_share, have_supported_versions; + CBS cookie, key_share, supported_versions; const SSL_EXTENSION_TYPE ext_types[] = { {TLSEXT_TYPE_key_share, &have_key_share, &key_share}, {TLSEXT_TYPE_cookie, &have_cookie, &cookie}, + {TLSEXT_TYPE_supported_versions, &have_supported_versions, + &supported_versions}, }; uint8_t alert = SSL_AD_DECODE_ERROR; @@ -111,6 +141,11 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { return ssl_hs_error; } + if (!ssl_is_draft22(ssl->version) && have_supported_versions) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION); + return ssl_hs_error; + } if (have_cookie) { CBS cookie_value; if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) || @@ -359,20 +394,8 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { if (!tls13_advance_key_schedule(hs, dhe_secret.data(), dhe_secret.size()) || !ssl_hash_message(hs, msg) || - !tls13_derive_handshake_secrets(hs)) { - return ssl_hs_error; - } - - ssl->method->next_message(ssl); - hs->tls13_state = state_process_change_cipher_spec; - return ssl_is_resumption_experiment(ssl->version) - ? ssl_hs_read_change_cipher_spec - : ssl_hs_ok; -} - -static enum ssl_hs_wait_t do_process_change_cipher_spec(SSL_HANDSHAKE *hs) { - SSL *const ssl = hs->ssl; - if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret, + !tls13_derive_handshake_secrets(hs) || + !tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret, hs->hash_len)) { return ssl_hs_error; } @@ -388,6 +411,7 @@ static enum ssl_hs_wait_t do_process_change_cipher_spec(SSL_HANDSHAKE *hs) { } } + ssl->method->next_message(ssl); hs->tls13_state = state_read_encrypted_extensions; return ssl_hs_ok; } @@ -642,9 +666,7 @@ static enum ssl_hs_wait_t do_send_end_of_early_data(SSL_HANDSHAKE *hs) { } if (hs->early_data_offered) { - if ((ssl_is_resumption_client_ccs_experiment(ssl->version) && - !ssl->method->add_change_cipher_spec(ssl)) || - !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret, + if (!tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret, hs->hash_len)) { return ssl_hs_error; } @@ -767,9 +789,6 @@ enum ssl_hs_wait_t tls13_client_handshake(SSL_HANDSHAKE *hs) { case state_read_server_hello: ret = do_read_server_hello(hs); break; - case state_process_change_cipher_spec: - ret = do_process_change_cipher_spec(hs); - break; case state_read_encrypted_extensions: ret = do_read_encrypted_extensions(hs); break; @@ -824,8 +843,6 @@ const char *tls13_client_handshake_state(SSL_HANDSHAKE *hs) { return "TLS 1.3 client send_second_client_hello"; case state_read_server_hello: return "TLS 1.3 client read_server_hello"; - case state_process_change_cipher_spec: - return "TLS 1.3 client process_change_cipher_spec"; case state_read_encrypted_extensions: return "TLS 1.3 client read_encrypted_extensions"; case state_read_certificate_request: diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc index 9afd0d4..1040ace 100644 --- a/ssl/tls13_server.cc +++ b/ssl/tls13_server.cc @@ -47,7 +47,6 @@ enum server_hs_state_t { state_send_server_certificate_verify, state_send_server_finished, state_read_second_client_flight, - state_process_change_cipher_spec, state_process_end_of_early_data, state_read_client_certificate, state_read_client_certificate_verify, @@ -490,23 +489,55 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { static enum ssl_hs_wait_t do_send_hello_retry_request(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; - ScopedCBB cbb; - CBB body, extensions; - uint16_t group_id; - 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) || - !CBB_add_u16(&extensions, 2 /* length */) || - !CBB_add_u16(&extensions, group_id) || - !ssl_add_message_cbb(ssl, cbb.get())) { - return ssl_hs_error; + + + if (ssl_is_draft22(ssl->version)) { + ScopedCBB cbb; + CBB body, session_id, extensions; + uint16_t group_id; + if (!ssl->method->init_message(ssl, cbb.get(), &body, + SSL3_MT_SERVER_HELLO) || + !CBB_add_u16(&body, TLS1_2_VERSION) || + !CBB_add_bytes(&body, kHelloRetryRequest, SSL3_RANDOM_SIZE) || + !CBB_add_u8_length_prefixed(&body, &session_id) || + !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len) || + !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher)) || + !CBB_add_u8(&body, 0 /* no compression */) || + !tls1_get_shared_group(hs, &group_id) || + !CBB_add_u16_length_prefixed(&body, &extensions) || + !CBB_add_u16(&extensions, TLSEXT_TYPE_supported_versions) || + !CBB_add_u16(&extensions, 2 /* length */) || + !CBB_add_u16(&extensions, ssl->version) || + !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) || + !CBB_add_u16(&extensions, 2 /* length */) || + !CBB_add_u16(&extensions, group_id) || + !ssl_add_message_cbb(ssl, cbb.get())) { + return ssl_hs_error; + } + + if (!ssl->method->add_change_cipher_spec(ssl)) { + return ssl_hs_error; + } + } else { + ScopedCBB cbb; + CBB body, extensions; + uint16_t group_id; + 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) || + !CBB_add_u16(&extensions, 2 /* length */) || + !CBB_add_u16(&extensions, group_id) || + !ssl_add_message_cbb(ssl, cbb.get())) { + return ssl_hs_error; + } } + hs->sent_hello_retry_request = true; hs->tls13_state = state_read_second_client_hello; return ssl_hs_flush; } @@ -576,6 +607,7 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { } if (ssl_is_resumption_experiment(ssl->version) && + (!ssl_is_draft22(ssl->version) || !hs->sent_hello_retry_request) && !ssl->method->add_change_cipher_spec(ssl)) { return ssl_hs_error; } @@ -756,46 +788,35 @@ static enum ssl_hs_wait_t do_read_second_client_flight(SSL_HANDSHAKE *hs) { hs->can_early_write = true; hs->can_early_read = true; hs->in_early_data = true; - hs->tls13_state = state_process_end_of_early_data; - return ssl_hs_read_end_of_early_data; } hs->tls13_state = state_process_end_of_early_data; - return ssl_hs_ok; + return ssl->early_data_accepted ? ssl_hs_read_end_of_early_data : ssl_hs_ok; } 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 (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 (hs->ssl->early_data_accepted) { + 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; + 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); } - ssl->method->next_message(ssl); } } - return ssl_is_resumption_client_ccs_experiment(hs->ssl->version) - ? ssl_hs_read_change_cipher_spec - : ssl_hs_ok; -} - -static enum ssl_hs_wait_t do_process_change_cipher_spec(SSL_HANDSHAKE *hs) { - SSL *const ssl = hs->ssl; if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret, hs->hash_len)) { return ssl_hs_error; @@ -973,9 +994,6 @@ enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) { case state_process_end_of_early_data: ret = do_process_end_of_early_data(hs); break; - case state_process_change_cipher_spec: - ret = do_process_change_cipher_spec(hs); - break; case state_read_client_certificate: ret = do_read_client_certificate(hs); break; @@ -1028,8 +1046,6 @@ const char *tls13_server_handshake_state(SSL_HANDSHAKE *hs) { return "TLS 1.3 server send_server_finished"; case state_read_second_client_flight: return "TLS 1.3 server read_second_client_flight"; - case state_process_change_cipher_spec: - return "TLS 1.3 server process_change_cipher_spec"; case state_process_end_of_early_data: return "TLS 1.3 server process_end_of_early_data"; case state_read_client_certificate: diff --git a/ssl/tls_record.cc b/ssl/tls_record.cc index a062012..7e8e968 100644 --- a/ssl/tls_record.cc +++ b/ssl/tls_record.cc @@ -247,6 +247,21 @@ enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, *out_consumed = in.size() - CBS_len(&cbs); + if (ssl->s3->have_version && + ssl_is_resumption_experiment(ssl->version) && + SSL_in_init(ssl) && + type == SSL3_RT_CHANGE_CIPHER_SPEC && + ciphertext_len == 1 && + CBS_data(&body)[0] == 1) { + ssl->s3->empty_record_count++; + if (ssl->s3->empty_record_count > kMaxEmptyRecords) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_EMPTY_FRAGMENTS); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; + } + return ssl_open_record_discard; + } + // Skip early data received when expecting a second ClientHello if we rejected // 0RTT. if (ssl->s3->skip_early_data && diff --git a/tool/client.cc b/tool/client.cc index ea4d7fe..57e1b6e 100644 --- a/tool/client.cc +++ b/tool/client.cc @@ -340,6 +340,10 @@ static bool GetTLS13Variant(tls13_variant_t *out, const std::string &in) { *out = tls13_experiment3; return true; } + if (in == "draft22") { + *out = tls13_draft22; + return true; + } return false; } |