Use RC4-MD5 AEAD in ssl/

This change adds the infrastructure to use stateful AEADs in ssl/ and
specifically wires in the stitched, RC4-MD5 AEAD. Over time, all
cipher suites will be supported via the AEAD interface and the old
EVP_CIPHER code will die off.

Change-Id: I44ed3ca2672e1342c6b632be08fee9272d113f8e
Reviewed-on: https://boringssl-review.googlesource.com/1044
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 32a37f6..2a7160f 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -225,7 +225,7 @@
 	SSL_MD5,
 	SSL_SSLV3,
 	SSL_NOT_EXP|SSL_MEDIUM,
-	SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF,
+	SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF|SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD,
 	128,
 	128,
 	},
@@ -4201,9 +4201,10 @@
  */
 long ssl_get_algorithm2(SSL *s)
 	{
+	static const unsigned long kMask = SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF;
 	long alg2 = s->s3->tmp.new_cipher->algorithm2;
 	if (s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_SHA256_PRF
-	    && alg2 == (SSL_HANDSHAKE_MAC_DEFAULT|TLS1_PRF))
+	    && (alg2 & kMask) == kMask)
 		return SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256;
 	return alg2;
 	}
diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index 2ccc9e5..79e52c8 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -359,7 +359,8 @@
 
 	if (c == NULL)
 		return 0;
-	if ((c->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) == 0)
+	if ((c->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) == 0 &&
+	    (c->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD) == 0)
 		return 0;
 
 #ifndef OPENSSL_NO_AES
@@ -374,7 +375,13 @@
 	case SSL_CHACHA20POLY1305:
 		*aead = EVP_aead_chacha20_poly1305();
 		return 1;
-		}
+	case SSL_RC4:
+		if (c->algorithm_mac == SSL_MD5)
+			*aead = EVP_aead_rc4_md5_tls();
+		else
+			return 0;
+		return 1;
+	}
 #endif
 
 	return 0;
@@ -455,44 +462,8 @@
 		*enc=ssl_cipher_methods[i];
 		}
 
-	switch (c->algorithm_mac)
-		{
-	case SSL_MD5:
-		i=SSL_MD_MD5_IDX;
-		break;
-	case SSL_SHA1:
-		i=SSL_MD_SHA1_IDX;
-		break;
-	case SSL_SHA256:
-		i=SSL_MD_SHA256_IDX;
-		break;
-	case SSL_SHA384:
-		i=SSL_MD_SHA384_IDX;
-		break;
-	case SSL_GOST94:
-		i = SSL_MD_GOST94_IDX;
-		break;
-	case SSL_GOST89MAC:
-		i = SSL_MD_GOST89MAC_IDX;
-		break;
-	default:
-		i= -1;
-		break;
-		}
-	if ((i < 0) || (i >= SSL_MD_NUM_IDX))
-	{
-		*md=NULL; 
-		if (mac_pkey_type!=NULL) *mac_pkey_type = NID_undef;
-		if (mac_secret_size!=NULL) *mac_secret_size = 0;
-		if (c->algorithm_mac == SSL_AEAD)
-			mac_pkey_type = NULL;
-	}
-	else
-	{
-		*md=ssl_digest_methods[i];
-		if (mac_pkey_type!=NULL) *mac_pkey_type = ssl_mac_pkey_id[i];
-		if (mac_secret_size!=NULL) *mac_secret_size = ssl_mac_secret_size[i];
-	}
+	if (!ssl_cipher_get_mac(s, md, mac_pkey_type, mac_secret_size))
+		return 0;
 
 	if ((*enc != NULL) &&
 	    (*md != NULL || (EVP_CIPHER_flags(*enc)&EVP_CIPH_FLAG_AEAD_CIPHER)) &&
@@ -528,6 +499,55 @@
 		return(0);
 	}
 
+int ssl_cipher_get_mac(const SSL_SESSION *s, const EVP_MD **md, int *mac_pkey_type, int *mac_secret_size)
+	{
+	int i;
+	const SSL_CIPHER *c;
+
+	c=s->cipher;
+	if (c == NULL) return(0);
+
+	switch (c->algorithm_mac)
+		{
+	case SSL_MD5:
+		i=SSL_MD_MD5_IDX;
+		break;
+	case SSL_SHA1:
+		i=SSL_MD_SHA1_IDX;
+		break;
+	case SSL_SHA256:
+		i=SSL_MD_SHA256_IDX;
+		break;
+	case SSL_SHA384:
+		i=SSL_MD_SHA384_IDX;
+		break;
+	case SSL_GOST94:
+		i = SSL_MD_GOST94_IDX;
+		break;
+	case SSL_GOST89MAC:
+		i = SSL_MD_GOST89MAC_IDX;
+		break;
+	default:
+		i= -1;
+		break;
+		}
+
+	if ((i < 0) || (i >= SSL_MD_NUM_IDX))
+		{
+		*md=NULL; 
+		if (mac_pkey_type!=NULL) *mac_pkey_type = NID_undef;
+		if (mac_secret_size!=NULL) *mac_secret_size = 0;
+		}
+	else
+		{
+		*md=ssl_digest_methods[i];
+		if (mac_pkey_type!=NULL) *mac_pkey_type = ssl_mac_pkey_id[i];
+		if (mac_secret_size!=NULL) *mac_secret_size = ssl_mac_secret_size[i];
+		}
+
+	return 1;
+	}
+
 int ssl_get_handshake_digest(int idx, long *mask, const EVP_MD **md) 
 {
 	if (idx <0||idx>=SSL_MD_NUM_IDX) 
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 4a2d4a5..17dc08b 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -384,6 +384,11 @@
  * 8-byte variable nonce.) */
 #define SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD (1<<22)
 
+/* SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD is a flag in SSL_CIPHER.algorithm2 which
+ * indicates that the AEAD is stateful and so doesn't take an nonce. This is
+ * only true of legacy cipher suites. */
+#define SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD (1<<28)
+
 /*
  * Export and cipher strength information. For each cipher we have to decide
  * whether it is exportable or not. This information is likely to change
@@ -1001,6 +1006,7 @@
 int ssl_cipher_get_evp_aead(const SSL_SESSION *s, const EVP_AEAD **aead);
 int ssl_cipher_get_evp(const SSL_SESSION *s,const EVP_CIPHER **enc,
 		       const EVP_MD **md,int *mac_pkey_type,int *mac_secret_size);
+int ssl_cipher_get_mac(const SSL_SESSION *s, const EVP_MD **md, int *mac_pkey_type, int *mac_secret_size);
 int ssl_get_handshake_digest(int i,long *mask,const EVP_MD **md);			   
 int ssl_get_handshake_digest(int i,long *mask,const EVP_MD **md);
 int ssl_cipher_get_cert_index(const SSL_CIPHER *c);
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index bdc5ae9..9b5c9f0 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -285,6 +285,7 @@
 err:
 	return ret;
 }
+
 static int tls1_generate_key_block(SSL *s, unsigned char *km,
 	     unsigned char *tmp, int num)
 	{
@@ -331,10 +332,30 @@
 
 static int tls1_change_cipher_state_aead(SSL *s, char is_read,
 	const unsigned char *key, unsigned key_len,
-	const unsigned char *iv, unsigned iv_len)
+	const unsigned char *iv, unsigned iv_len,
+	const unsigned char *mac_secret, unsigned mac_secret_len)
 	{
 	const EVP_AEAD *aead = s->s3->tmp.new_aead;
 	SSL_AEAD_CTX *aead_ctx;
+	/* mac_key_and_key is used to merge the MAC and cipher keys for an AEAD
+	 * which simulates pre-AEAD cipher suites. It needs to be large enough
+	 * to cope with the largest pair of keys. */
+	uint8_t mac_key_and_key[32 /* HMAC(SHA256) */ + 32 /* AES-256 */];
+
+	if (mac_secret_len > 0)
+		{
+		/* This is a "stateful" AEAD (for compatibility with pre-AEAD
+		 * cipher suites). */
+		if (mac_secret_len + key_len > sizeof(mac_key_and_key))
+			{
+			OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, ERR_R_INTERNAL_ERROR);
+			return 0;
+			}
+		memcpy(mac_key_and_key, mac_secret, mac_secret_len);
+		memcpy(mac_key_and_key + mac_secret_len, key, key_len);
+		key = mac_key_and_key;
+		key_len += mac_secret_len;
+		}
 
 	if (is_read)
 		{
@@ -359,7 +380,9 @@
 		}
 	memcpy(aead_ctx->fixed_nonce, iv, iv_len);
 	aead_ctx->fixed_nonce_len = iv_len;
-	aead_ctx->variable_nonce_len = 8;  /* always the case, currently. */
+	aead_ctx->variable_nonce_len = 8;  /* correct for all true AEADs so far. */
+	if (s->s3->tmp.new_cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD)
+		aead_ctx->variable_nonce_len = 0;
 	aead_ctx->variable_nonce_included_in_record =
 		(s->s3->tmp.new_cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD) != 0;
 	if (aead_ctx->variable_nonce_len + aead_ctx->fixed_nonce_len != EVP_AEAD_nonce_length(aead))
@@ -558,6 +581,15 @@
 	if (aead != NULL)
 		{
 		key_len = EVP_AEAD_key_length(aead);
+		/* For "stateful" AEADs (i.e. compatibility with pre-AEAD
+		 * cipher suites) the key length reported by
+		 * |EVP_AEAD_key_length| will include the MAC key bytes. */
+		if (key_len < mac_secret_len)
+			{
+			OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR);
+			return 0;
+			}
+		key_len -= mac_secret_len;
 		iv_len = SSL_CIPHER_AEAD_FIXED_NONCE_LEN(s->s3->tmp.new_cipher);
 		}
 	else
@@ -602,7 +634,8 @@
 	if (aead != NULL)
 		{
 		if (!tls1_change_cipher_state_aead(s, is_read,
-						   key, key_len, iv, iv_len))
+						   key, key_len, iv, iv_len,
+						   mac_secret, mac_secret_len))
 			return 0;
 		}
 	else
@@ -636,12 +669,24 @@
 		return(1);
 
 	if (s->session->cipher &&
-	    (s->session->cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD))
+	    ((s->session->cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_AEAD) ||
+	     (s->session->cipher->algorithm2 & SSL_CIPHER_ALGORITHM2_STATEFUL_AEAD)))
 		{
 		if (!ssl_cipher_get_evp_aead(s->session, &aead))
 			goto cipher_unavailable_err;
 		key_len = EVP_AEAD_key_length(aead);
 		iv_len = SSL_CIPHER_AEAD_FIXED_NONCE_LEN(s->session->cipher);
+		if (!ssl_cipher_get_mac(s->session, &hash, &mac_type, &mac_secret_size))
+			goto cipher_unavailable_err;
+		/* For "stateful" AEADs (i.e. compatibility with pre-AEAD
+		 * cipher suites) the key length reported by
+		 * |EVP_AEAD_key_length| will include the MAC key bytes. */
+		if (key_len < mac_secret_size)
+			{
+			OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR);
+			return 0;
+			}
+		key_len -= mac_secret_size;
 		}
 	else
 		{