Dear OpenSSL team!

I've found a bug when use TLS/1.2+RSA512+DHE cipher

Imagine that TLS/1.2 Client requests the following two Signature/Hash pairs
in its signature_algorithms(13) TLS extension of the ClientHello:
(RSA+SHA512) and (RSA+SHA256)

Imagine also that RSA512 certificate is selected by the server.

Since tls1_process_sigalgs() function selects only 1st algorithm from
the received ClientHello then server SSL sctucture's cert field will
be filled in with SHA512 hash algorithm and RSA512 private key.
This algorithms are incompatible since RSA512 can sign only messages
not more than 512bits = 64Bytes in length and PKCS1(SHA512) is
83 Bytes in length. Another words server SSL sctucture's cert field will
be filled in with CERT_PKEY entry with incompatible privatekey and digest
fields.

Another hand there is a possibility to choose SHA256 as hash algorithm
that is proposed by the Client.

There is also a weak error related to the improper digest algorithm choose
described above. When ssl3_send_server_key_exchange() function
eventually calls RSA_sign() the last function detects RSA512+SHA512
incompatibility and returns an error. ssl3_send_server_key_exchange()
after this error returns without sending anything. As a results the
connection
hangs: client waits for reply to ClientHello but server never send anything.
I think better way is to send a handshake failure or any more appropriate
alert.
If there are no appropriate alerts in TLS/1.2 the custom OpenSSL alert
may be sent.

I attach 2 patches:
patch1.patch - fix of the weak error - sending Handshake Failure alert
patch2.patch - the MAIN fix in this email - check digest/privatekey
compatibility

other attachements:
*-orig.pcap - the TLS/1.2+RSA512+DHE session for original openssl-1.0.2f
*-patch1.pcap - the same session when patch1.patch is applied
*-patch2.pcap - the same session when patch2.patch is applied
RootCA.pem - private key to decrypt session if required

The session is created by s_server/s_client with following commands:
./openssl s_server -cert RootCA.pem -key RootCA.pem -cipher
DHE-RSA-AES256-SHA256
./openssl s_client -connect localhost:4433 -cipher DHE-RSA-AES256-SHA256

Thanks,
Oleg Strelnikov

diff -ur openssl-1.0.1f-orig/ssl/s3_srvr.c openssl-1.0.1f/ssl/s3_srvr.c
--- openssl-1.0.1f-orig/ssl/s3_srvr.c	2014-01-06 17:47:42.000000000 +0400
+++ openssl-1.0.1f/ssl/s3_srvr.c	2014-02-06 15:05:26.000000000 +0400
@@ -1979,8 +1979,9 @@
 				if (!EVP_SignFinal(&md_ctx,&(p[2]),
 					(unsigned int *)&i,pkey))
 					{
+					al=SSL_AD_HANDSHAKE_FAILURE;
 					SSLerr(SSL_F_SSL3_SEND_SERVER_KEY_EXCHANGE,ERR_LIB_EVP);
-					goto err;
+					goto f_err;
 					}
 				s2n(i,p);
 				n+=i+2;
diff -ur openssl-1.0.1f-orig/ssl/t1_lib.c openssl-1.0.1f/ssl/t1_lib.c
--- openssl-1.0.1f-orig/ssl/t1_lib.c	2014-01-06 17:47:42.000000000 +0400
+++ openssl-1.0.1f/ssl/t1_lib.c	2014-02-06 15:27:21.000000000 +0400
@@ -2468,6 +2468,62 @@
 		}
 	}
 
+/* Check the digest/sign compatibility */
+
+static int tls_is_compatible(const EVP_MD *digest, const EVP_PKEY *privatekey)
+	{
+	if (privatekey)
+		switch(privatekey->type)
+			{
+#ifndef OPENSSL_NO_RSA
+			case EVP_PKEY_RSA:
+			switch(digest->type)
+				{
+#ifndef OPENSSL_NO_MD5
+				case NID_md5:
+#ifdef OPENSSL_FIPS
+				if (FIPS_mode())
+					break;
+#endif
+				break;
+#endif
+#ifndef OPENSSL_NO_SHA
+				case NID_sha1:
+				break;
+#endif
+#ifndef OPENSSL_NO_SHA256
+				case NID_sha224:
+				break;
+
+				case NID_sha256:
+				break;
+#endif
+#ifndef OPENSSL_NO_SHA512
+				case NID_sha384:
+				case NID_sha512:
+				if (BN_num_bits(privatekey->pkey.rsa->n) <= 512)
+					return 0;
+				break;
+#endif
+				default:
+				/* break */ ;
+				}
+			break;
+#endif /* OPENSSL_NO_RSA */
+#ifndef OPENSSL_NO_DSA
+			case EVP_PKEY_DSA:
+			break;
+#endif /* OPENSSL_NO_DSA */
+#ifndef OPENSSL_NO_EC
+			case EVP_PKEY_EC:
+			break;
+#endif /* OPENSSL_NO_EC */
+			default:
+			/* break */ ;
+			}
+	return 1;
+	}
+
 /* Set preferred digest for each key type */
 
 int tls1_process_sigalgs(SSL *s, const unsigned char *data, int dsize)
@@ -2515,7 +2571,9 @@
 		if (c->pkeys[idx].digest == NULL)
 			{
 			md = tls12_get_hash(hash_alg);
-			if (md)
+			if (md && tls_is_compatible(md, c->pkeys[idx].privatekey) &&
+				((idx != SSL_PKEY_RSA_SIGN) ||
+				tls_is_compatible(md, c->pkeys[SSL_PKEY_RSA_ENC].privatekey)))
 				{
 				c->pkeys[idx].digest = md;
 				if (idx == SSL_PKEY_RSA_SIGN)

Attachment: dhe+rsa512+sha512-orig.pcap
Description: application/vnd.tcpdump.pcap

Attachment: dhe+rsa512+sha512-patch1.pcap
Description: application/vnd.tcpdump.pcap

Attachment: dhe+rsa512+sha512-patch2.pcap
Description: application/vnd.tcpdump.pcap

Attachment: RootCA.pem
Description: application/x509-ca-cert

Reply via email to