CVE-2014-3572: Cryptographic Issues (CWE-310)
value: The ssl3_get_key_exchange function in s3_clnt.c in OpenSSL before 0.9.8zd, 1.0.0 before 1.0.0p, and 1.0.1 before 1.0.1k allows remote SSL servers to conduct ECDHE-to-ECDH downgrade attacks and trigger a loss of forward secrecy by omitting the ServerKeyExchange message.
CWE description: Weaknesses in this category are related to the design and implementation of data confidentiality and integrity. Frequently these deal with the use of encoding techniques, encryption libraries, and hashing algorithms. The weaknesses in this category could lead to a degradation of the quality data if they are not addressed.
Published: 2015-01-09T02:59Z
Repository: https://github.com/openssl/openssl
Language: Other
Files changed: CHANGES, ssl/s3_clnt.c
Commit date: 2015-01-05 22:59:32 +0000
Commit message: ECDH downgrade bug fix.

Fix bug where an OpenSSL client would accept a handshake using an
ephemeral ECDH ciphersuites with the server key exchange message omitted.

Thanks to Karthikeyan Bhargavan for reporting this issue.

CVE-2014-3572
Reviewed-by: Matt Caswell <matt@openssl.org>

Diff: diff --git a/CHANGES b/CHANGES
index c444b24a2..0252eb570 100644
--- a/CHANGES
+++ b/CHANGES
@@ -657,10 +657,17 @@
      X509_CINF_set_modified, X509_CINF_get_issuer, X509_CINF_get_extensions and
      X509_CINF_get_signature were reverted post internal team review.
 
  Changes between 1.0.1j and 1.0.1k [xx XXX xxxx]
 
+  *) Abort handshake if server key exchange message is omitted for ephemeral
+     ECDH ciphersuites.
+
+     Thanks to Karthikeyan Bhargavan for reporting this issue.
+     (CVE-2014-3572)
+     [Steve Henson]
+
   *) Ensure that the session ID context of an SSL is updated when its
      SSL_CTX is updated via SSL_set_SSL_CTX.
 
      The session ID context is typically set from the parent SSL_CTX,
      and can vary with the CTX.
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 4ca2774f2..2313fbc1e 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1374,28 +1374,42 @@ int ssl3_get_key_exchange(SSL *s)
 	EC_POINT *srvr_ecpoint = NULL;
 	int curve_nid = 0;
 	int encoded_pt_len = 0;
 #endif
 
+	EVP_MD_CTX_init(&md_ctx);
+
 	/* use same message size as in ssl3_get_certificate_request()
 	 * as ServerKeyExchange message may be skipped */
 	n=s->method->ssl_get_message(s,
 		SSL3_ST_CR_KEY_EXCH_A,
 		SSL3_ST_CR_KEY_EXCH_B,
 		-1,
 		s->max_cert_list,
 		&ok);
 	if (!ok) return((int)n);
 
+	alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
+
 	if (s->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE)
 		{
+		/*
+		 * Can't skip server key exchange if this is an ephemeral
+		 * ciphersuite.
+		 */
+		if (alg_k & (SSL_kDHE|SSL_kECDHE))
+			{
+			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE, SSL_R_UNEXPECTED_MESSAGE);
+			al = SSL_AD_UNEXPECTED_MESSAGE;
+			goto f_err;
+			}
 #ifndef OPENSSL_NO_PSK
 		/* In plain PSK ciphersuite, ServerKeyExchange can be
 		   omitted if no identity hint is sent. Set
 		   session->sess_cert anyway to avoid problems
 		   later.*/
-		if (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)
+		if (alg_k & SSL_kPSK)
 			{
 			s->session->sess_cert=ssl_sess_cert_new();
 			if (s->ctx->psk_identity_hint)
 				OPENSSL_free(s->ctx->psk_identity_hint);
 			s->ctx->psk_identity_hint = NULL;
@@ -1436,13 +1450,11 @@ int ssl3_get_key_exchange(SSL *s)
 		}
 
 	/* Total length of the parameters including the length prefix */
 	param_len=0;
 
-	alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
 	alg_a=s->s3->tmp.new_cipher->algorithm_auth;
-	EVP_MD_CTX_init(&md_ctx);
 
 	al=SSL_AD_DECODE_ERROR;
 
 #ifndef OPENSSL_NO_PSK
 	if (alg_k & SSL_kPSK)

Vulnerable code: 		if (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)
	alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
	EVP_MD_CTX_init(&md_ctx);
Fixed code:   *) Abort handshake if server key exchange message is omitted for ephemeral
     ECDH ciphersuites.

     Thanks to Karthikeyan Bhargavan for reporting this issue.
     (CVE-2014-3572)
     [Steve Henson]

	EVP_MD_CTX_init(&md_ctx);

	alg_k=s->s3->tmp.new_cipher->algorithm_mkey;

		/*
		 * Can't skip server key exchange if this is an ephemeral
		 * ciphersuite.
		 */
		if (alg_k & (SSL_kDHE|SSL_kECDHE))
			{
			SSLerr(SSL_F_SSL3_GET_KEY_EXCHANGE, SSL_R_UNEXPECTED_MESSAGE);
			al = SSL_AD_UNEXPECTED_MESSAGE;
			goto f_err;
			}
		if (alg_k & SSL_kPSK)