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
{