Prefer SHA-1 certificates for pre-1.2 clients
diff --git a/ChangeLog b/ChangeLog
index fd83b9e..b38d56f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
PolarSSL ChangeLog (Sorted per branch, date)
+= PolarSSL 1.3.10 released ???
+
+Features
+ * Certificate selection based on signature hash, prefering SHA-1 over SHA-2
+ for pre-1.2 clients when multiple certificates are available.
+
= PolarSSL 1.3.9 released 2014-10-20
Security
* Lowest common hash was selected from signature_algorithms extension in
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 211ab5d..04608ee 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -776,7 +776,7 @@
static int ssl_pick_cert( ssl_context *ssl,
const ssl_ciphersuite_t * ciphersuite_info )
{
- ssl_key_cert *cur, *list;
+ ssl_key_cert *cur, *list, *fallback = NULL;
pk_type_t pk_alg = ssl_get_ciphersuite_sig_pk_alg( ciphersuite_info );
#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION)
@@ -814,15 +814,36 @@
continue;
#endif
+ /*
+ * Try to select a SHA-1 certificate for pre-1.2 clients, but still
+ * present them a SHA-higher cert rather than failing if it's the only
+ * one we got that satisfies the other conditions.
+ */
+ if( ssl->minor_ver < SSL_MINOR_VERSION_3 &&
+ cur->cert->sig_md != POLARSSL_MD_SHA1 )
+ {
+ if( fallback == NULL )
+ fallback = cur;
+ continue;
+ }
+
/* If we get there, we got a winner */
break;
}
- if( cur == NULL )
- return( -1 );
+ if( cur != NULL )
+ {
+ ssl->handshake->key_cert = cur;
+ return( 0 );
+ }
- ssl->handshake->key_cert = cur;
- return( 0 );
+ if( fallback != NULL )
+ {
+ ssl->handshake->key_cert = fallback;
+ return( 0 );
+ }
+
+ return( -1 );
}
#endif /* POLARSSL_X509_CRT_PARSE_C */
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index f94808d..2cf4b6e 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -956,6 +956,60 @@
-C "! ssl_handshake returned" \
-S "X509 - Certificate verification failed"
+# Tests for certificate selection based on SHA verson
+
+run_test "Certificate hash: client TLS 1.2 -> SHA-2" \
+ "$P_SRV crt_file=data_files/server5.crt \
+ key_file=data_files/server5.key \
+ crt_file2=data_files/server5-sha1.crt \
+ key_file2=data_files/server5.key" \
+ "$P_CLI force_version=tls1_2" \
+ 0 \
+ -c "signed using.*ECDSA with SHA256" \
+ -C "signed using.*ECDSA with SHA1"
+
+run_test "Certificate hash: client TLS 1.1 -> SHA-1" \
+ "$P_SRV crt_file=data_files/server5.crt \
+ key_file=data_files/server5.key \
+ crt_file2=data_files/server5-sha1.crt \
+ key_file2=data_files/server5.key" \
+ "$P_CLI force_version=tls1_1" \
+ 0 \
+ -C "signed using.*ECDSA with SHA256" \
+ -c "signed using.*ECDSA with SHA1"
+
+run_test "Certificate hash: client TLS 1.0 -> SHA-1" \
+ "$P_SRV crt_file=data_files/server5.crt \
+ key_file=data_files/server5.key \
+ crt_file2=data_files/server5-sha1.crt \
+ key_file2=data_files/server5.key" \
+ "$P_CLI force_version=tls1" \
+ 0 \
+ -C "signed using.*ECDSA with SHA256" \
+ -c "signed using.*ECDSA with SHA1"
+
+run_test "Certificate hash: client TLS 1.1, no SHA-1 -> SHA-2 (order 1)" \
+ "$P_SRV crt_file=data_files/server5.crt \
+ key_file=data_files/server5.key \
+ crt_file2=data_files/server6.crt \
+ key_file2=data_files/server6.key" \
+ "$P_CLI force_version=tls1_1" \
+ 0 \
+ -c "serial number.*09" \
+ -c "signed using.*ECDSA with SHA256" \
+ -C "signed using.*ECDSA with SHA1"
+
+run_test "Certificate hash: client TLS 1.1, no SHA-1 -> SHA-2 (order 2)" \
+ "$P_SRV crt_file=data_files/server6.crt \
+ key_file=data_files/server6.key \
+ crt_file2=data_files/server5.crt \
+ key_file2=data_files/server5.key" \
+ "$P_CLI force_version=tls1_1" \
+ 0 \
+ -c "serial number.*0A" \
+ -c "signed using.*ECDSA with SHA256" \
+ -C "signed using.*ECDSA with SHA1"
+
# tests for SNI
run_test "SNI: no SNI callback" \