From 5e3ff62c345c976cd1ffbcc5e6042f55264977f5 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Fri, 22 Mar 2013 17:12:33 +0000 Subject: [PATCH] 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. --- CHANGES | 14 ++++++++++++++ apps/s_cb.c | 5 +++++ ssl/s2_clnt.c | 2 +- ssl/s2_enc.c | 2 +- ssl/s2_srvr.c | 2 +- ssl/s3_enc.c | 2 +- ssl/s3_pkt.c | 35 +++++++++++++++++++++++++++++++++-- ssl/ssl3.h | 2 ++ ssl/ssl_ciph.c | 5 ++++- ssl/ssl_locl.h | 8 +++++++- ssl/ssl_txt.c | 2 +- ssl/t1_enc.c | 8 ++++---- ssl/t1_lib.c | 39 +++++++++++++++++++++++++++++++++++++++ 13 files changed, 113 insertions(+), 13 deletions(-) diff --git a/CHANGES b/CHANGES index e2154a1c30..715da13318 100644 --- 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 , Trevor Perrin and Ben Laurie] diff --git a/apps/s_cb.c b/apps/s_cb.c index 8127e77143..b1102ceed6 100644 --- a/apps/s_cb.c +++ b/apps/s_cb.c @@ -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"; diff --git a/ssl/s2_clnt.c b/ssl/s2_clnt.c index 03b6cf9673..299389addc 100644 --- a/ssl/s2_clnt.c +++ b/ssl/s2_clnt.c @@ -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); diff --git a/ssl/s2_enc.c b/ssl/s2_enc.c index ff3395f459..1d08559407 100644 --- a/ssl/s2_enc.c +++ b/ssl/s2_enc.c @@ -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); diff --git a/ssl/s2_srvr.c b/ssl/s2_srvr.c index 2cba426bb7..a16c33a65f 100644 --- a/ssl/s2_srvr.c +++ b/ssl/s2_srvr.c @@ -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); diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index b334aba5d3..f1b2641853 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -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); diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c index 65b742c119..3f936c0905 100644 --- a/ssl/s3_pkt.c +++ b/ssl/s3_pkt.c @@ -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); diff --git a/ssl/ssl3.h b/ssl/ssl3.h index 0ae97b4206..5fd02791fa 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -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 diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c index 99e6c2f126..a5c417a9ed 100644 --- a/ssl/ssl_ciph.c +++ b/ssl/ssl_ciph.c @@ -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; diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 5c074ace84..a4ce8dd32a 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 @@ 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); diff --git a/ssl/ssl_txt.c b/ssl/ssl_txt.c index 093d84076f..20b95a2829 100644 --- a/ssl/ssl_txt.c +++ b/ssl/ssl_txt.c @@ -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; diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 9b707938ba..7155fd06bc 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -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, diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 2a3eaeb26f..a471995a19 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -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; } -- 2.34.1