Be strict about expecting a server Certificate message.

Introduce a ssl_cipher_has_server_public_key to save the repeated
NULL/PSK/RSA_PSK[*] check. Don't allow skipping to ServerKeyExchange when
expecting Certificate; the messages expected are determined by the cipher
suite. The ssl3_get_server_public_key call is already guarded.

As the previous test demonstrates, this is safe because of the
ssl3_check_cert_and_algorithm call, but avoid the looseness in the parsing
there.

[*] NB: we don't implement RSA_PSK, and OpenSSL has never implemented it.

Change-Id: I0571e6bcbeb8eb883f77878bdc98d1aa3a287cf3
Reviewed-on: https://boringssl-review.googlesource.com/1156
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 35c399f..6c10a32 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -313,11 +313,7 @@
 				s->init_num=0;
 				break;
 				}
-			/* Check if it is anon DH/ECDH */
-			/* or non-RSA PSK */
-			if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) &&
-			    !((s->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK) &&
-			      !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kRSA)))
+			if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher))
 				{
 				ret=ssl3_get_server_certificate(s);
 				if (ret <= 0) goto end;
@@ -1118,24 +1114,12 @@
 	n=s->method->ssl_get_message(s,
 		SSL3_ST_CR_CERT_A,
 		SSL3_ST_CR_CERT_B,
-		-1,
+		SSL3_MT_CERTIFICATE,
 		s->max_cert_list,
 		&ok);
 
 	if (!ok) return((int)n);
 
-	if (s->s3->tmp.message_type == SSL3_MT_SERVER_KEY_EXCHANGE)
-		{
-		s->s3->tmp.reuse_message=1;
-		return(1);
-		}
-
-	if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE)
-		{
-		al=SSL_AD_UNEXPECTED_MESSAGE;
-		OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, SSL_R_BAD_MESSAGE_TYPE);
-		goto f_err;
-		}
 	CBS_init(&cbs, (uint8_t *)s->init_msg, n);
 
 	if ((sk=sk_X509_new_null()) == NULL)
@@ -1732,9 +1716,7 @@
 		}
 	else
 		{
-		if (!(alg_a & SSL_aNULL) &&
-			/* Among PSK ciphers only RSA_PSK needs a public key */
-			!((alg_a & SSL_aPSK) && !(alg_k & SSL_kRSA)))
+		if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher))
 			{
 			/* Might be wrong key type, check it */
 			if (ssl3_check_cert_and_algorithm(s))
@@ -2916,13 +2898,13 @@
 	DH *dh;
 #endif
 
+	/* we don't have a certificate */
+	if (!ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher))
+		return 1;
+
 	alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
 	alg_a=s->s3->tmp.new_cipher->algorithm_auth;
 
-	/* we don't have a certificate */
-	if ((alg_a & SSL_aNULL) || ((alg_a & SSL_aPSK) && !(alg_k & SSL_kRSA)))
-		return(1);
-
 	sc=s->session->sess_cert;
 	if (sc == NULL)
 		{
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 2a57edf..c8d5d2b 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -351,12 +351,7 @@
 
 		case SSL3_ST_SW_CERT_A:
 		case SSL3_ST_SW_CERT_B:
-			/* Check if it is anon DH or anon ECDH, */
-			/* non-RSA PSK or SRP */
-			if (!(s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL)
-				/* Among PSK ciphersuites only RSA_PSK uses server certificate */
-				&& !(s->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK &&
-					 !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kRSA)))
+			if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher))
 				{
 				ret=ssl3_send_server_certificate(s);
 				if (ret <= 0) goto end;
@@ -1776,9 +1771,7 @@
 			n+=2+nr[i];
 			}
 
-		if (!(alg_a & SSL_aNULL)
-			/* Among PSK ciphersuites only RSA uses a certificate */
-			&& !((alg_a & SSL_aPSK) && !(alg_k & SSL_kRSA)))
+		if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher))
 			{
 			if ((pkey=ssl_get_sign_pkey(s,s->s3->tmp.new_cipher,&md))
 				== NULL)
diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index d7f3fba..4c4bb67 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -1867,3 +1867,19 @@
 	{
 	return ssl->method->get_cipher_by_char(ptr);
 	}
+
+/* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a
+ * server public key in the key exchange, sent in a server Certificate
+ * message. Otherwise it returns 0. */
+int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher)
+	{
+	/* Anonymous ciphers do not include a server certificate. */
+	if (cipher->algorithm_auth & SSL_aNULL)
+		return 0;
+	/* Neither do PSK ciphers, except for RSA_PSK. */
+	if ((cipher->algorithm_auth & SSL_aPSK) &&
+		!(cipher->algorithm_mkey & SSL_kRSA))
+		return 0;
+	/* All other ciphers include it. */
+	return 1;
+	}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 63e7dcc..ed3a0b5 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -993,6 +993,8 @@
 int ssl_get_handshake_digest(int i,long *mask,const EVP_MD **md);
 int ssl_cipher_get_cert_index(const SSL_CIPHER *c);
 const SSL_CIPHER *ssl_get_cipher_by_char(SSL *ssl, const unsigned char *ptr);
+int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher);
+
 int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) *chain);
 int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) *chain);
 int ssl_cert_add0_chain_cert(CERT *c, X509 *x);
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 0b210cd..1207e9a 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -198,7 +198,7 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: ":MISSING_RSA_SIGNING_CERT:",
+		expectedError: ":UNEXPECTED_MESSAGE:",
 	},
 }