Fix DTLS_ANY_VERSION and add tests.
This fixes bugs that kept the tests from working:
- Resolve DTLS version and cookie before the session.
- In DTLS_ANY_VERSION, ServerHello should be read with first_packet = 1. This
is a regression from f2fedefdcaf62f10b566f55858c25f35112072ea. We'll want to
do the same for TLS, but first let's change this to a boolean has_version in a
follow-up.
Things not yet fixed:
- DTLS code is not EVP_AEAD-aware. Those ciphers are disabled for now.
- On the client, DTLS_ANY_VERSION creates SSL_SESSIONs with the wrong
ssl_version. The tests pass because we no longer enforce the match as of
e37216f56009fbf48c3a1e733b7a546ca6dfc2af. (In fact, we've gone from the server
ignoring ssl_version and client enforcing to the client mostly ignoring
ssl_version and the server enforcing.)
- ssl3_send_client_hello's ssl_version check checks for equality against
s->version rather than >.
Change-Id: I5a0dde221b2009413df9b9443882b9bf3b29519c
Reviewed-on: https://boringssl-review.googlesource.com/2403
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index cc7980f..71c1c9b 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -317,6 +317,10 @@
{
if (ciph->algorithm_enc == SSL_RC4)
return NULL;
+ /* TODO(davidben): EVP_AEAD does not work in DTLS yet. */
+ if (ciph->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD ||
+ ciph->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD)
+ return NULL;
}
return ciph;
diff --git a/ssl/d1_pkt.c b/ssl/d1_pkt.c
index dae47d7..9356580 100644
--- a/ssl/d1_pkt.c
+++ b/ssl/d1_pkt.c
@@ -587,7 +587,15 @@
{
if (version != s->version)
{
- /* unexpected version, silently discard */
+ /* The record's version doesn't match, so
+ * silently drop it.
+ *
+ * TODO(davidben): This doesn't work. The DTLS
+ * record layer is not packet-based, so the
+ * remainder of the packet isn't dropped and we
+ * get a framing error. It's also unclear what
+ * it means to silently drop a record in a
+ * packet containing two records. */
rr->length = 0;
s->packet_length = 0;
goto again;
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 66f32d5..6f47e95 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -805,6 +805,11 @@
uint8_t compression_method;
unsigned long mask_ssl;
+ /* DTLS_ANY_VERSION does not sniff the version ahead of time,
+ * so disable the version check. */
+ if (SSL_IS_DTLS(s))
+ s->first_packet = 1;
+
n=s->method->ssl_get_message(s,
SSL3_ST_CR_SRVR_HELLO_A,
SSL3_ST_CR_SRVR_HELLO_B,
@@ -813,6 +818,9 @@
SSL_GET_MESSAGE_HASH_MESSAGE,
&ok);
+ if (SSL_IS_DTLS(s))
+ s->first_packet = 0;
+
if (!ok) return((int)n);
CBS_init(&server_hello, s->init_msg, n);
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index f3b3779..fa4a591 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -851,50 +851,6 @@
/* Load the client random. */
memcpy(s->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
- s->hit=0;
- /* Versions before 0.9.7 always allow clients to resume sessions in renegotiation.
- * 0.9.7 and later allow this by default, but optionally ignore resumption requests
- * with flag SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (it's a new flag rather
- * than a change to default behavior so that applications relying on this for security
- * won't even compile against older library versions).
- *
- * 1.0.1 and later also have a function SSL_renegotiate_abbreviated() to request
- * renegotiation but not a new session (s->new_session remains unset): for servers,
- * this essentially just means that the SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
- * setting will be ignored.
- */
- if ((s->new_session && (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)))
- {
- if (!ssl_get_new_session(s,1))
- goto err;
- }
- else
- {
- i = ssl_get_prev_session(s, &early_ctx);
- if (i == PENDING_SESSION)
- {
- ret = PENDING_SESSION;
- goto err;
- }
- else if (i == -1)
- {
- goto err;
- }
-
- /* Only resume if the session's version matches the negotiated
- * version: most clients do not accept a mismatch. */
- if (i == 1 && s->version == s->session->ssl_version)
- {
- s->hit = 1;
- }
- else
- {
- /* No session was found or it was unacceptable. */
- if (!ssl_get_new_session(s, 1))
- goto err;
- }
- }
-
if (SSL_IS_DTLS(s))
{
CBS cookie;
@@ -956,7 +912,50 @@
al = SSL_AD_PROTOCOL_VERSION;
goto f_err;
}
- s->session->ssl_version = s->version;
+ }
+ }
+
+ s->hit=0;
+ /* Versions before 0.9.7 always allow clients to resume sessions in renegotiation.
+ * 0.9.7 and later allow this by default, but optionally ignore resumption requests
+ * with flag SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (it's a new flag rather
+ * than a change to default behavior so that applications relying on this for security
+ * won't even compile against older library versions).
+ *
+ * 1.0.1 and later also have a function SSL_renegotiate_abbreviated() to request
+ * renegotiation but not a new session (s->new_session remains unset): for servers,
+ * this essentially just means that the SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+ * setting will be ignored.
+ */
+ if ((s->new_session && (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)))
+ {
+ if (!ssl_get_new_session(s,1))
+ goto err;
+ }
+ else
+ {
+ i = ssl_get_prev_session(s, &early_ctx);
+ if (i == PENDING_SESSION)
+ {
+ ret = PENDING_SESSION;
+ goto err;
+ }
+ else if (i == -1)
+ {
+ goto err;
+ }
+
+ /* Only resume if the session's version matches the negotiated
+ * version: most clients do not accept a mismatch. */
+ if (i == 1 && s->version == s->session->ssl_version)
+ {
+ s->hit = 1;
+ }
+ else
+ {
+ /* No session was found or it was unacceptable. */
+ if (!ssl_get_new_session(s, 1))
+ goto err;
}
}
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index a4dd06d..0202407 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -229,14 +229,10 @@
const SSL_METHOD *method;
if (config->is_dtls) {
- // TODO(davidben): Get DTLS 1.2 working and test the version negotiation
- // codepath. This doesn't currently work because
- // - Session resumption is broken: https://crbug.com/403378
- // - DTLS hasn't been updated for EVP_AEAD.
if (config->is_server) {
- method = DTLSv1_server_method();
+ method = DTLS_server_method();
} else {
- method = DTLSv1_client_method();
+ method = DTLS_client_method();
}
} else {
if (config->is_server) {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index f106e60..79f8ee0 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -889,11 +889,12 @@
name string
version uint16
flag string
+ hasDTLS bool
}{
- {"SSL3", VersionSSL30, "-no-ssl3"},
- {"TLS1", VersionTLS10, "-no-tls1"},
- {"TLS11", VersionTLS11, "-no-tls11"},
- {"TLS12", VersionTLS12, "-no-tls12"},
+ {"SSL3", VersionSSL30, "-no-ssl3", false},
+ {"TLS1", VersionTLS10, "-no-tls1", true},
+ {"TLS11", VersionTLS11, "-no-tls11", false},
+ {"TLS12", VersionTLS12, "-no-tls12", true},
}
var testCipherSuites = []struct {
@@ -935,10 +936,21 @@
{"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
}
+func hasComponent(suiteName, component string) bool {
+ return strings.Contains("-"+suiteName+"-", "-"+component+"-")
+}
+
func isTLS12Only(suiteName string) bool {
- return strings.HasSuffix(suiteName, "-GCM") ||
- strings.HasSuffix(suiteName, "-SHA256") ||
- strings.HasSuffix(suiteName, "-SHA384")
+ return hasComponent(suiteName, "GCM") ||
+ hasComponent(suiteName, "SHA256") ||
+ hasComponent(suiteName, "SHA384")
+}
+
+func isDTLSCipher(suiteName string) bool {
+ // TODO(davidben): AES-GCM exists in DTLS 1.2 but is currently
+ // broken because DTLS is not EVP_AEAD-aware.
+ return !hasComponent(suiteName, "RC4") &&
+ !hasComponent(suiteName, "GCM")
}
func addCipherSuiteTests() {
@@ -949,7 +961,7 @@
var cert Certificate
var certFile string
var keyFile string
- if strings.Contains(suite.name, "ECDSA") {
+ if hasComponent(suite.name, "ECDSA") {
cert = getECDSACertificate()
certFile = ecdsaCertificateFile
keyFile = ecdsaKeyFile
@@ -960,7 +972,7 @@
}
var flags []string
- if strings.HasPrefix(suite.name, "PSK-") || strings.Contains(suite.name, "-PSK-") {
+ if hasComponent(suite.name, "PSK") {
flags = append(flags,
"-psk", psk,
"-psk-identity", pskIdentity)
@@ -1003,8 +1015,7 @@
resumeSession: true,
})
- // TODO(davidben): Fix DTLS 1.2 support and test that.
- if ver.version == VersionTLS10 && strings.Index(suite.name, "RC4") == -1 {
+ if ver.hasDTLS && isDTLSCipher(suite.name) {
testCases = append(testCases, testCase{
testType: clientTest,
protocol: dtls,
@@ -1593,31 +1604,43 @@
}
for _, runnerVers := range tlsVersions {
- expectedVersion := shimVers.version
- if runnerVers.version < shimVers.version {
- expectedVersion = runnerVers.version
+ protocols := []protocol{tls}
+ if runnerVers.hasDTLS && shimVers.hasDTLS {
+ protocols = append(protocols, dtls)
}
- suffix := shimVers.name + "-" + runnerVers.name
+ for _, protocol := range protocols {
+ expectedVersion := shimVers.version
+ if runnerVers.version < shimVers.version {
+ expectedVersion = runnerVers.version
+ }
- testCases = append(testCases, testCase{
- testType: clientTest,
- name: "VersionNegotiation-Client-" + suffix,
- config: Config{
- MaxVersion: runnerVers.version,
- },
- flags: flags,
- expectedVersion: expectedVersion,
- })
+ suffix := shimVers.name + "-" + runnerVers.name
+ if protocol == dtls {
+ suffix += "-DTLS"
+ }
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "VersionNegotiation-Server-" + suffix,
- config: Config{
- MaxVersion: runnerVers.version,
- },
- flags: flags,
- expectedVersion: expectedVersion,
- })
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: clientTest,
+ name: "VersionNegotiation-Client-" + suffix,
+ config: Config{
+ MaxVersion: runnerVers.version,
+ },
+ flags: flags,
+ expectedVersion: expectedVersion,
+ })
+
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "VersionNegotiation-Server-" + suffix,
+ config: Config{
+ MaxVersion: runnerVers.version,
+ },
+ flags: flags,
+ expectedVersion: expectedVersion,
+ })
+ }
}
}
}
@@ -1904,69 +1927,80 @@
}
func addResumptionVersionTests() {
- // TODO(davidben): Once DTLS 1.2 is working, test that as well.
for _, sessionVers := range tlsVersions {
for _, resumeVers := range tlsVersions {
- suffix := "-" + sessionVers.name + "-" + resumeVers.name
-
- testCases = append(testCases, testCase{
- name: "Resume-Client" + suffix,
- resumeSession: true,
- config: Config{
- MaxVersion: sessionVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- Bugs: ProtocolBugs{
- AllowSessionVersionMismatch: true,
- },
- },
- expectedVersion: sessionVers.version,
- resumeConfig: &Config{
- MaxVersion: resumeVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- Bugs: ProtocolBugs{
- AllowSessionVersionMismatch: true,
- },
- },
- expectedResumeVersion: resumeVers.version,
- })
-
- testCases = append(testCases, testCase{
- name: "Resume-Client-NoResume" + suffix,
- flags: []string{"-expect-session-miss"},
- resumeSession: true,
- config: Config{
- MaxVersion: sessionVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- },
- expectedVersion: sessionVers.version,
- resumeConfig: &Config{
- MaxVersion: resumeVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- },
- newSessionsOnResume: true,
- expectedResumeVersion: resumeVers.version,
- })
-
- var flags []string
- if sessionVers.version != resumeVers.version {
- flags = append(flags, "-expect-session-miss")
+ protocols := []protocol{tls}
+ if sessionVers.hasDTLS && resumeVers.hasDTLS {
+ protocols = append(protocols, dtls)
}
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "Resume-Server" + suffix,
- flags: flags,
- resumeSession: true,
- config: Config{
- MaxVersion: sessionVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- },
- expectedVersion: sessionVers.version,
- resumeConfig: &Config{
- MaxVersion: resumeVers.version,
- CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
- },
- expectedResumeVersion: resumeVers.version,
- })
+ for _, protocol := range protocols {
+ suffix := "-" + sessionVers.name + "-" + resumeVers.name
+ if protocol == dtls {
+ suffix += "-DTLS"
+ }
+
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "Resume-Client" + suffix,
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ AllowSessionVersionMismatch: true,
+ },
+ },
+ expectedVersion: sessionVers.version,
+ resumeConfig: &Config{
+ MaxVersion: resumeVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ Bugs: ProtocolBugs{
+ AllowSessionVersionMismatch: true,
+ },
+ },
+ expectedResumeVersion: resumeVers.version,
+ })
+
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ name: "Resume-Client-NoResume" + suffix,
+ flags: []string{"-expect-session-miss"},
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ },
+ expectedVersion: sessionVers.version,
+ resumeConfig: &Config{
+ MaxVersion: resumeVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ },
+ newSessionsOnResume: true,
+ expectedResumeVersion: resumeVers.version,
+ })
+
+ var flags []string
+ if sessionVers.version != resumeVers.version {
+ flags = append(flags, "-expect-session-miss")
+ }
+ testCases = append(testCases, testCase{
+ protocol: protocol,
+ testType: serverTest,
+ name: "Resume-Server" + suffix,
+ flags: flags,
+ resumeSession: true,
+ config: Config{
+ MaxVersion: sessionVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ },
+ expectedVersion: sessionVers.version,
+ resumeConfig: &Config{
+ MaxVersion: resumeVers.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ },
+ expectedResumeVersion: resumeVers.version,
+ })
+ }
}
}
}