Add cipher suite settings for TLS ≥ 1.0.
This change adds the ability to configure ciphers specifically for
TLS ≥ 1.0. This compliments the existing ability to specify ciphers
for TLS ≥ 1.1.
This is useful because TLS 1.0 is the first version not to suffer from
POODLE. (Assuming that it's implemented correctly[1].) Thus one might
wish to reserve RC4 solely for SSLv3.
[1] https://www.imperialviolet.org/2014/12/08/poodleagain.html
Change-Id: I774d5336fead48f03d8a0a3cf80c369692ee60df
Reviewed-on: https://boringssl-review.googlesource.com/5793
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 332a62f..bde16a4 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1265,6 +1265,17 @@
struct ssl_cipher_preference_list_st *cipher_list;
/* same as above but sorted for lookup */
STACK_OF(SSL_CIPHER) *cipher_list_by_id;
+
+ /* cipher_list_tls10 is the list of ciphers when TLS 1.0 or greater is in
+ * use. This only applies to server connections as, for clients, the version
+ * number is known at connect time and so the cipher list can be set then. If
+ * |cipher_list_tls11| is non-NULL then this applies only to TLS 1.0
+ * connections.
+ *
+ * TODO(agl): this exists to assist in the death of SSLv3. It can hopefully
+ * be removed after that. */
+ struct ssl_cipher_preference_list_st *cipher_list_tls10;
+
/* cipher_list_tls11 is the list of ciphers when TLS 1.1 or greater is in
* use. This only applies to server connections as, for clients, the version
* number is known at connect time and so the cipher list can be set then. */
@@ -2099,6 +2110,7 @@
size_t curves_len);
OPENSSL_EXPORT int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str);
+OPENSSL_EXPORT int SSL_CTX_set_cipher_list_tls10(SSL_CTX *, const char *str);
OPENSSL_EXPORT int SSL_CTX_set_cipher_list_tls11(SSL_CTX *, const char *str);
OPENSSL_EXPORT long SSL_CTX_set_timeout(SSL_CTX *ctx, long t);
OPENSSL_EXPORT long SSL_CTX_get_timeout(const SSL_CTX *ctx);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 64e31e5..7125215 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -451,6 +451,11 @@
return s->ctx->cipher_list_tls11;
}
+ if (s->version >= TLS1_VERSION && s->ctx != NULL &&
+ s->ctx->cipher_list_tls10 != NULL) {
+ return s->ctx->cipher_list_tls10;
+ }
+
if (s->ctx != NULL && s->ctx->cipher_list != NULL) {
return s->ctx->cipher_list;
}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index b2eeb37..fee9523 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1081,6 +1081,11 @@
return s->ctx->cipher_list_tls11->ciphers;
}
+ if (s->version >= TLS1_VERSION && s->ctx != NULL &&
+ s->ctx->cipher_list_tls10 != NULL) {
+ return s->ctx->cipher_list_tls10->ciphers;
+ }
+
if (s->ctx != NULL && s->ctx->cipher_list != NULL) {
return s->ctx->cipher_list->ciphers;
}
@@ -1149,6 +1154,20 @@
return 1;
}
+int SSL_CTX_set_cipher_list_tls10(SSL_CTX *ctx, const char *str) {
+ STACK_OF(SSL_CIPHER) *sk;
+
+ sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls10, NULL, str);
+ if (sk == NULL) {
+ return 0;
+ } else if (sk_SSL_CIPHER_num(sk) == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
+ return 0;
+ }
+
+ return 1;
+}
+
int SSL_CTX_set_cipher_list_tls11(SSL_CTX *ctx, const char *str) {
STACK_OF(SSL_CIPHER) *sk;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index fa0cb75..97044ab 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -599,7 +599,23 @@
return nullptr;
}
- if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL")) {
+ std::string cipher_list = "ALL";
+ if (!config->cipher.empty()) {
+ cipher_list = config->cipher;
+ SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+ if (!SSL_CTX_set_cipher_list(ssl_ctx.get(), cipher_list.c_str())) {
+ return nullptr;
+ }
+
+ if (!config->cipher_tls10.empty() &&
+ !SSL_CTX_set_cipher_list_tls10(ssl_ctx.get(),
+ config->cipher_tls10.c_str())) {
+ return nullptr;
+ }
+ if (!config->cipher_tls11.empty() &&
+ !SSL_CTX_set_cipher_list_tls11(ssl_ctx.get(),
+ config->cipher_tls11.c_str())) {
return nullptr;
}
@@ -1027,10 +1043,6 @@
if (config->install_ddos_callback) {
SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
}
- if (!config->cipher.empty() &&
- !SSL_set_cipher_list(ssl.get(), config->cipher.c_str())) {
- return false;
- }
if (!config->reject_peer_renegotiations) {
/* Renegotiations are disabled by default. */
SSL_set_reject_peer_renegotiations(ssl.get(), 0);
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 6284ef6..8124382 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -2092,6 +2092,94 @@
shouldFail: true,
expectedError: "BAD_DH_P_LENGTH",
})
+
+ // versionSpecificCiphersTest specifies a test for the TLS 1.0 and TLS
+ // 1.1 specific cipher suite settings. A server is setup with the given
+ // cipher lists and then a connection is made for each member of
+ // expectations. The cipher suite that the server selects must match
+ // the specified one.
+ var versionSpecificCiphersTest = []struct {
+ ciphersDefault, ciphersTLS10, ciphersTLS11 string
+ // expectations is a map from TLS version to cipher suite id.
+ expectations map[uint16]uint16
+ }{
+ {
+ // Test that the null case (where no version-specific ciphers are set)
+ // works as expected.
+ "RC4-SHA:AES128-SHA", // default ciphers
+ "", // no ciphers specifically for TLS ≥ 1.0
+ "", // no ciphers specifically for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS11: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS12: TLS_RSA_WITH_RC4_128_SHA,
+ },
+ },
+ {
+ // With ciphers_tls10 set, TLS 1.0, 1.1 and 1.2 should get a different
+ // cipher.
+ "RC4-SHA:AES128-SHA", // default
+ "AES128-SHA", // these ciphers for TLS ≥ 1.0
+ "", // no ciphers specifically for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS11: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS12: TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ },
+ {
+ // With ciphers_tls11 set, TLS 1.1 and 1.2 should get a different
+ // cipher.
+ "RC4-SHA:AES128-SHA", // default
+ "", // no ciphers specifically for TLS ≥ 1.0
+ "AES128-SHA", // these ciphers for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS11: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS12: TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ },
+ {
+ // With both ciphers_tls10 and ciphers_tls11 set, ciphers_tls11 should
+ // mask ciphers_tls10 for TLS 1.1 and 1.2.
+ "RC4-SHA:AES128-SHA", // default
+ "AES128-SHA", // these ciphers for TLS ≥ 1.0
+ "AES256-SHA", // these ciphers for TLS ≥ 1.1
+ map[uint16]uint16{
+ VersionSSL30: TLS_RSA_WITH_RC4_128_SHA,
+ VersionTLS10: TLS_RSA_WITH_AES_128_CBC_SHA,
+ VersionTLS11: TLS_RSA_WITH_AES_256_CBC_SHA,
+ VersionTLS12: TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ },
+ }
+
+ for i, test := range versionSpecificCiphersTest {
+ for version, expectedCipherSuite := range test.expectations {
+ flags := []string{"-cipher", test.ciphersDefault}
+ if len(test.ciphersTLS10) > 0 {
+ flags = append(flags, "-cipher-tls10", test.ciphersTLS10)
+ }
+ if len(test.ciphersTLS11) > 0 {
+ flags = append(flags, "-cipher-tls11", test.ciphersTLS11)
+ }
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: fmt.Sprintf("VersionSpecificCiphersTest-%d-%x", i, version),
+ config: Config{
+ MaxVersion: version,
+ MinVersion: version,
+ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA},
+ },
+ flags: flags,
+ expectedCipher: expectedCipherSuite,
+ })
+ }
+ }
}
func addBadECDSASignatureTests() {
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index edf3d6e..7a87fd5 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -112,6 +112,8 @@
{ "-psk-identity", &TestConfig::psk_identity },
{ "-srtp-profiles", &TestConfig::srtp_profiles },
{ "-cipher", &TestConfig::cipher },
+ { "-cipher-tls10", &TestConfig::cipher_tls10 },
+ { "-cipher-tls11", &TestConfig::cipher_tls11 },
{ "-export-label", &TestConfig::export_label },
{ "-export-context", &TestConfig::export_context },
};
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index d6ccda2..a41af49 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -69,6 +69,8 @@
bool fail_ddos_callback = false;
bool fail_second_ddos_callback = false;
std::string cipher;
+ std::string cipher_tls10;
+ std::string cipher_tls11;
bool handshake_never_done = false;
int export_keying_material = 0;
std::string export_label;