Experimental encrypt-then-mac support.
authorDr. Stephen Henson <steve@openssl.org>
Fri, 22 Mar 2013 17:12:33 +0000 (17:12 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Sun, 8 Sep 2013 12:14:03 +0000 (13:14 +0100)
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.

13 files changed:
CHANGES
apps/s_cb.c
ssl/s2_clnt.c
ssl/s2_enc.c
ssl/s2_srvr.c
ssl/s3_enc.c
ssl/s3_pkt.c
ssl/ssl3.h
ssl/ssl_ciph.c
ssl/ssl_locl.h
ssl/ssl_txt.c
ssl/t1_enc.c
ssl/t1_lib.c

diff --git a/CHANGES b/CHANGES
index e2154a1..715da13 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,20 @@
 
  Changes between 1.0.x and 1.1.0  [xx XXX xxxx]
 
+  *) 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.
+
+     WARNING: EXPERIMENTAL, SUBJECT TO CHANGE.
+     [Steve Henson]
+
   *) Add callbacks supporting generation and retrieval of supplemental
      data entries.
      [Scott Deboy <sdeboy@apache.org>, Trevor Perrin and Ben Laurie]
index 8127e77..b1102ce 100644 (file)
@@ -1023,6 +1023,11 @@ void MS_CALLBACK tlsext_cb(SSL *s, int client_server, int type,
                extname = "next protocol";
                break;
 #endif
+#ifdef TLSEXT_TYPE_encrypt_then_mac
+               case TLSEXT_TYPE_encrypt_then_mac:
+               extname = "encrypt-then-mac";
+               break;
+#endif
 
                default:
                extname = "unknown";
index 03b6cf9..299389a 100644 (file)
@@ -623,7 +623,7 @@ static int client_master_key(SSL *s)
        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);
index ff3395f..1d08559 100644 (file)
@@ -68,7 +68,7 @@ int ssl2_enc_init(SSL *s, int client)
        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);
index 2cba426..a16c33a 100644 (file)
@@ -452,7 +452,7 @@ static int get_client_master_key(SSL *s)
 
        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);
index b334aba..f1b2641 100644 (file)
@@ -418,7 +418,7 @@ int ssl3_setup_key_block(SSL *s)
        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);
index 65b742c..3f936c0 100644 (file)
@@ -408,6 +408,30 @@ fprintf(stderr, "Record type=%d, Length=%d\n", rr->type, rr->length);
        /* 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 @@ printf("\n");
        /* 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 @@ static int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
         * 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 @@ static int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
        /* 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);
 
index 0ae97b4..5fd0279 100644 (file)
@@ -422,6 +422,8 @@ typedef struct ssl3_buffer_st
  * 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
 
index 99e6c2f..a5c417a 100644 (file)
@@ -485,7 +485,7 @@ static void load_builtin_compressions(void)
 #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 @@ int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,
                {
                const EVP_CIPHER *evp;
 
+               if (use_etm)
+                       return 1;
+
                if (s->ssl_version>>8 != TLS1_VERSION_MAJOR ||
                    s->ssl_version < TLS1_VERSION)
                        return 1;
index 5c074ac..a4ce8dd 100644 (file)
                ((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 @@ STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *meth,
                                             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);
index 093d840..20b95a2 100644 (file)
@@ -218,7 +218,7 @@ int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
                {
                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;
index 9b70793..7155fd0 100644 (file)
@@ -616,7 +616,7 @@ int tls1_setup_key_block(SSL *s)
        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 @@ int tls1_enc(SSL *s, int send)
 #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 @@ int tls1_mac(SSL *ssl, unsigned char *md, int send)
        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 @@ int tls1_mac(SSL *ssl, unsigned char *md, int send)
                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,
index 2a3eaeb..a471995 100644 (file)
@@ -1465,6 +1465,10 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
                        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 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
                                }
                        }
                }
+#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 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                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 @@ static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char
                                        }                                               
                                }
                        }
+#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 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
                               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 @@ static int ssl_scan_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char
                                        }
                                }                       
                        }
+#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;
                }