Experimental encrypt-then-mac support.

Experimental support for encrypt then mac from
draft-gutmann-tls-encrypt-then-mac-02.txt

To enable it set the appropriate extension number (0x10 for the test server)
using e.g. -DTLSEXT_TYPE_encrypt_then_mac=0x10

For non-compliant peers (i.e. just about everything) this should have no
effect.
diff --git a/ssl/s2_clnt.c b/ssl/s2_clnt.c
index 03b6cf9..299389a 100644
--- a/ssl/s2_clnt.c
+++ b/ssl/s2_clnt.c
@@ -623,7 +623,7 @@
 	if (s->state == SSL2_ST_SEND_CLIENT_MASTER_KEY_A)
 		{
 
-		if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL))
+		if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL, 0))
 			{
 			ssl2_return_error(s,SSL2_PE_NO_CIPHER);
 			SSLerr(SSL_F_CLIENT_MASTER_KEY,SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS);
diff --git a/ssl/s2_enc.c b/ssl/s2_enc.c
index ff3395f..1d08559 100644
--- a/ssl/s2_enc.c
+++ b/ssl/s2_enc.c
@@ -68,7 +68,7 @@
 	const EVP_MD *md;
 	int num;
 
-	if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL))
+	if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL, 0))
 		{
 		ssl2_return_error(s,SSL2_PE_NO_CIPHER);
 		SSLerr(SSL_F_SSL2_ENC_INIT,SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS);
diff --git a/ssl/s2_srvr.c b/ssl/s2_srvr.c
index 2cba426..a16c33a 100644
--- a/ssl/s2_srvr.c
+++ b/ssl/s2_srvr.c
@@ -452,7 +452,7 @@
 
 	is_export=SSL_C_IS_EXPORT(s->session->cipher);
 	
-	if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL))
+	if (!ssl_cipher_get_evp(s->session,&c,&md,NULL,NULL,NULL, 0))
 		{
 		ssl2_return_error(s,SSL2_PE_NO_CIPHER);
 		SSLerr(SSL_F_GET_CLIENT_MASTER_KEY,SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS);
diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c
index b334aba..f1b2641 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -418,7 +418,7 @@
 	if (s->s3->tmp.key_block_length != 0)
 		return(1);
 
-	if (!ssl_cipher_get_evp(s->session,&c,&hash,NULL,NULL,&comp))
+	if (!ssl_cipher_get_evp(s->session,&c,&hash,NULL,NULL,&comp, 0))
 		{
 		SSLerr(SSL_F_SSL3_SETUP_KEY_BLOCK,SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
 		return(0);
diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c
index 65b742c..3f936c0 100644
--- a/ssl/s3_pkt.c
+++ b/ssl/s3_pkt.c
@@ -408,6 +408,30 @@
 	/* decrypt in place in 'rr->input' */
 	rr->data=rr->input;
 	rr->orig_len=rr->length;
+	/* If in encrypt-then-mac mode calculate mac from encrypted record.
+	 * All the details below are public so no timing details can leak.
+	 */
+	if (SSL_USE_ETM(s) && s->read_hash)
+		{
+		unsigned char *mac;
+		mac_size=EVP_MD_CTX_size(s->read_hash);
+		OPENSSL_assert(mac_size <= EVP_MAX_MD_SIZE);
+		if (rr->length < mac_size)
+			{
+			al=SSL_AD_DECODE_ERROR;
+			SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_LENGTH_TOO_SHORT);
+			goto f_err;
+			}
+		rr->length -= mac_size;
+		mac = rr->data + rr->length;
+		i=s->method->ssl3_enc->mac(s,md,0 /* not send */);
+		if (i < 0 || CRYPTO_memcmp(md, mac, (size_t)mac_size) != 0)
+			{
+			al=SSL_AD_BAD_RECORD_MAC;
+			SSLerr(SSL_F_SSL3_GET_RECORD,SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
+			goto f_err;
+			}
+		}
 
 	enc_err = s->method->ssl3_enc->enc(s,0);
 	/* enc_err is:
@@ -430,7 +454,7 @@
 	/* r->length is now the compressed data plus mac */
 	if ((sess != NULL) &&
 	    (s->enc_read_ctx != NULL) &&
-	    (EVP_MD_CTX_md(s->read_hash) != NULL))
+	    (EVP_MD_CTX_md(s->read_hash) != NULL) && !SSL_USE_ETM(s))
 		{
 		/* s->read_hash != NULL => mac_size != -1 */
 		unsigned char *mac = NULL;
@@ -820,7 +844,7 @@
 	 * from wr->input.  Length should be wr->length.
 	 * wr->data still points in the wb->buf */
 
-	if (mac_size != 0)
+	if (!SSL_USE_ETM(s) && mac_size != 0)
 		{
 		if (s->method->ssl3_enc->mac(s,&(p[wr->length + eivlen]),1) < 0)
 			goto err;
@@ -840,6 +864,13 @@
 	/* ssl3_enc can only have an error on read */
 	s->method->ssl3_enc->enc(s,1);
 
+	if (SSL_USE_ETM(s) && mac_size != 0)
+		{
+		if (s->method->ssl3_enc->mac(s,p + wr->length,1) < 0)
+			goto err;
+		wr->length+=mac_size;
+		}
+
 	/* record length after mac and block padding */
 	s2n(wr->length,plen);
 
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index 0ae97b4..5fd0279 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -422,6 +422,8 @@
  * effected, but we can't prevent that.
  */
 #define SSL3_FLAGS_SGC_RESTART_DONE		0x0040
+/* Set if we encrypt then mac instead of usual mac then encrypt */
+#define TLS1_FLAGS_ENCRYPT_THEN_MAC		0x0080
 
 #ifndef OPENSSL_NO_SSL_INTERN
 
diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index 99e6c2f..a5c417a 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -485,7 +485,7 @@
 #endif
 
 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,SSL_COMP **comp)
+	     const EVP_MD **md, int *mac_pkey_type, int *mac_secret_size,SSL_COMP **comp, int use_etm)
 	{
 	int i;
 	const SSL_CIPHER *c;
@@ -617,6 +617,9 @@
 		{
 		const EVP_CIPHER *evp;
 
+		if (use_etm)
+			return 1;
+
 		if (s->ssl_version>>8 != TLS1_VERSION_MAJOR ||
 		    s->ssl_version < TLS1_VERSION)
 			return 1;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 5c074ac..a4ce8dd 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -460,6 +460,12 @@
 		((SSL_IS_DTLS(s) && s->client_version <= DTLS1_2_VERSION) || \
 		(!SSL_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION))
 
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+#define SSL_USE_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC)
+#else
+#define SSL_USE_ETM(s) (0)
+#endif
+
 /* Mostly for SSLv3 */
 #define SSL_PKEY_RSA_ENC	0
 #define SSL_PKEY_RSA_SIGN	1
@@ -982,7 +988,7 @@
 					     const char *rule_str, CERT *c);
 void ssl_update_cache(SSL *s, int mode);
 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, SSL_COMP **comp);
+		       const EVP_MD **md,int *mac_pkey_type,int *mac_secret_size, SSL_COMP **comp, int use_etm);
 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);
diff --git a/ssl/ssl_txt.c b/ssl/ssl_txt.c
index 093d840..20b95a2 100644
--- a/ssl/ssl_txt.c
+++ b/ssl/ssl_txt.c
@@ -218,7 +218,7 @@
 		{
 		SSL_COMP *comp = NULL;
 
-		ssl_cipher_get_evp(x,NULL,NULL,NULL,NULL,&comp);
+		ssl_cipher_get_evp(x,NULL,NULL,NULL,NULL,&comp, 0);
 		if (comp == NULL)
 			{
 			if (BIO_printf(bp,"\n    Compression: %d",x->compress_meth) <= 0) goto err;
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 9b70793..7155fd0 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -616,7 +616,7 @@
 	if (s->s3->tmp.key_block_length != 0)
 		return(1);
 
-	if (!ssl_cipher_get_evp(s->session,&c,&hash,&mac_type,&mac_secret_size,&comp))
+	if (!ssl_cipher_get_evp(s->session,&c,&hash,&mac_type,&mac_secret_size,&comp, SSL_USE_ETM(s)))
 		{
 		SSLerr(SSL_F_TLS1_SETUP_KEY_BLOCK,SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
 		return(0);
@@ -874,7 +874,7 @@
 #endif	/* KSSL_DEBUG */
 
 		ret = 1;
-		if (EVP_MD_CTX_md(s->read_hash) != NULL)
+		if (!SSL_USE_ETM(s) && EVP_MD_CTX_md(s->read_hash) != NULL)
 			mac_size = EVP_MD_CTX_size(s->read_hash);
 		if ((bs != 1) && !send)
 			ret = tls1_cbc_remove_padding(s, rec, bs, mac_size);
@@ -1026,7 +1026,7 @@
 	header[11]=(rec->length)>>8;
 	header[12]=(rec->length)&0xff;
 
-	if (!send &&
+	if (!send && !SSL_USE_ETM(ssl) &&
 	    EVP_CIPHER_CTX_mode(ssl->enc_read_ctx) == EVP_CIPH_CBC_MODE &&
 	    ssl3_cbc_record_digest_supported(mac_ctx))
 		{
@@ -1050,7 +1050,7 @@
 		t=EVP_DigestSignFinal(mac_ctx,md,&md_size);
 		OPENSSL_assert(t > 0);
 #ifdef OPENSSL_FIPS
-		if (!send && FIPS_mode())
+		if (!send && !SSL_USE_ETM(ssl) && FIPS_mode())
 			tls_fips_digest_extra(
 	    				ssl->enc_read_ctx,
 					mac_ctx, rec->input,
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 2a3eaeb..a471995 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1465,6 +1465,10 @@
 			ret += outlen;
 			}
 		}
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+	s2n(TLSEXT_TYPE_encrypt_then_mac,ret);
+	s2n(0,ret);
+#endif
 
 	if ((extdatalen = ret-p-2) == 0)
 		return p;
@@ -1700,6 +1704,21 @@
 				}
 			}
 		}
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+	if (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC)
+		{
+		/* Don't use encrypt_then_mac if AEAD: might want
+		 * to disable for other ciphersuites too.
+		 */
+		if (s->s3->tmp.new_cipher->algorithm_mac == SSL_AEAD)
+			s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
+		else
+			{
+			s2n(TLSEXT_TYPE_encrypt_then_mac,ret);
+			s2n(0,ret);
+			}
+		}
+#endif
 
 	if (s->s3->alpn_selected)
 		{
@@ -1934,6 +1953,10 @@
 		s->cert->pkeys[i].valid_flags = 0;
 		}
 
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+	s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
+#endif
+
 	if (data >= (d+n-2))
 		goto ri_check;
 	n2s(data,len);
@@ -2452,6 +2475,10 @@
 					}						
 				}
 			}
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+		else if (type == TLSEXT_TYPE_encrypt_then_mac)
+			s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
+#endif
 
 		data+=size;
 		}
@@ -2538,6 +2565,10 @@
 	                       SSL_TLSEXT_HB_DONT_SEND_REQUESTS);
 #endif
 
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+	s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
+#endif
+
 	if (data >= (d+n-2))
 		goto ri_check;
 
@@ -2789,6 +2820,14 @@
 					}
 				}			
 			}
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+		else if (type == TLSEXT_TYPE_encrypt_then_mac)
+			{
+			/* Ignore if inappropriate ciphersuite */
+			if (s->s3->tmp.new_cipher->algorithm_mac != SSL_AEAD)
+				s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
+			}
+#endif
  
 		data += size;
 		}