diff --git a/ChangeLog b/ChangeLog index e3774d413..89c87e057 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,11 +2,12 @@ PolarSSL ChangeLog (Sorted per branch, date) = 1.3 branch -Reminder: bump SONAME for ABI change (FALLBACK_SCSV, session-hash) +Reminder: bump SONAME for ABI change (FALLBACK_SCSV, session-hash, EtM) Features * Add support for FALLBACK_SCSV (draft-ietf-tls-downgrade-scsv) * Add support for Extended Master Secret (draft-ietf-tls-session-hash) + * Add support for Encrypt-then-MAC (RFC 7366) = PolarSSL 1.3.9 released 2014-10-20 Security diff --git a/include/polarssl/check_config.h b/include/polarssl/check_config.h index b4ae54144..80b037eb2 100644 --- a/include/polarssl/check_config.h +++ b/include/polarssl/check_config.h @@ -257,6 +257,13 @@ #error "Illegal protocol selection" #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) && \ + !defined(POLARSSL_SSL_PROTO_TLS1) && \ + !defined(POLARSSL_SSL_PROTO_TLS1_1) && \ + !defined(POLARSSL_SSL_PROTO_TLS1_2) +#error "POLARSSL_SSL_ENCRYPT_THEN_MAC defined, but not all prerequsites" +#endif + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) && \ !defined(POLARSSL_SSL_PROTO_TLS1) && \ !defined(POLARSSL_SSL_PROTO_TLS1_1) && \ diff --git a/include/polarssl/config.h b/include/polarssl/config.h index dfe2764e5..6e736e2b7 100644 --- a/include/polarssl/config.h +++ b/include/polarssl/config.h @@ -811,6 +811,24 @@ */ //#define POLARSSL_SSL_DEBUG_ALL +/** \def POLARSSL_SSL_ENCRYPT_THEN_MAC + * + * Enable support for Encrypt-then-MAC, RFC 7366. + * + * This allows peers that both support it to use a more robust protection for + * ciphersuites using CBC, providing deep resistance against timing attacks + * on the padding or underlying cipher. + * + * This only affects CBC ciphersuites, and is useless if none is defined. + * + * Requires: POLARSSL_SSL_PROTO_TLS1 or + * POLARSSL_SSL_PROTO_TLS1_1 or + * POLARSSL_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Encrypt-then-MAC + */ +#define POLARSSL_SSL_ENCRYPT_THEN_MAC + /** \def POLARSSL_SSL_EXTENDED_MASTER_SECRET * * Enable support for Extended Master Secret, aka Session Hash diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 7b5ec8e6e..82ed04e11 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -212,6 +212,9 @@ #define SSL_EXTENDED_MS_DISABLED 0 #define SSL_EXTENDED_MS_ENABLED 1 +#define SSL_ETM_DISABLED 0 +#define SSL_ETM_ENABLED 1 + #define SSL_COMPRESS_NULL 0 #define SSL_COMPRESS_DEFLATE 1 @@ -409,6 +412,7 @@ #define TLS_EXT_ALPN 16 +#define TLS_EXT_ENCRYPT_THEN_MAC 22 /* 0x16 */ #define TLS_EXT_EXTENDED_MASTER_SECRET 0x0017 /* 23 */ #define TLS_EXT_SESSION_TICKET 35 @@ -548,6 +552,10 @@ struct _ssl_session #if defined(POLARSSL_SSL_TRUNCATED_HMAC) int trunc_hmac; /*!< flag for truncated hmac activation */ #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + int encrypt_then_mac; /*!< flag for EtM activation */ +#endif }; /* @@ -713,6 +721,9 @@ struct _ssl_context #if defined(POLARSSL_SSL_FALLBACK_SCSV) && defined(POLARSSL_SSL_CLI_C) char fallback; /*!< flag for fallback connections */ #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + char encrypt_then_mac; /*!< flag for encrypt-then-mac */ +#endif #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) char extended_ms; /*!< flag for extended master secret */ #endif @@ -1425,6 +1436,21 @@ void ssl_set_min_version( ssl_context *ssl, int major, int minor ); void ssl_set_fallback( ssl_context *ssl, char fallback ); #endif /* POLARSSL_SSL_FALLBACK_SCSV && POLARSSL_SSL_CLI_C */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +/** + * \brief Enable or disable Encrypt-then-MAC + * (Default: SSL_ETM_ENABLED) + * + * \note This should always be enabled, it is a security + * improvement, and should not cause any interoperability + * issue (used only if the peer supports it too). + * + * \param ssl SSL context + * \param etm SSL_ETM_ENABLED or SSL_ETM_DISABLED + */ +void ssl_set_encrypt_then_mac( ssl_context *ssl, char etm ); +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) /** * \brief Enable or disable Extended Master Secret negotiation. diff --git a/library/ssl_cli.c b/library/ssl_cli.c index c40d62ec0..fd0a81165 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -359,6 +359,32 @@ static void ssl_write_truncated_hmac_ext( ssl_context *ssl, } #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +static void ssl_write_encrypt_then_mac_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->encrypt_then_mac == SSL_ETM_DISABLED || + ssl->max_minor_ver == SSL_MINOR_VERSION_0 ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "client hello, adding encrypt_then_mac " + "extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_ENCRYPT_THEN_MAC >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_ENCRYPT_THEN_MAC ) & 0xFF ); + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) static void ssl_write_extended_ms_ext( ssl_context *ssl, unsigned char *buf, size_t *olen ) @@ -688,6 +714,11 @@ static int ssl_write_client_hello( ssl_context *ssl ) ext_len += olen; #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + ssl_write_encrypt_then_mac_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) ssl_write_extended_ms_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; @@ -811,6 +842,26 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, } #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +static int ssl_parse_encrypt_then_mac_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( ssl->encrypt_then_mac == SSL_ETM_DISABLED || + ssl->minor_ver == SSL_MINOR_VERSION_0 || + len != 0 ) + { + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + ((void) buf); + + ssl->session_negotiate->encrypt_then_mac = SSL_ETM_ENABLED; + + return( 0 ); +} +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) static int ssl_parse_extended_ms_ext( ssl_context *ssl, const unsigned char *buf, @@ -1203,6 +1254,19 @@ static int ssl_parse_server_hello( ssl_context *ssl ) break; #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + case TLS_EXT_ENCRYPT_THEN_MAC: + SSL_DEBUG_MSG( 3, ( "found encrypt_then_mac extension" ) ); + + if( ( ret = ssl_parse_encrypt_then_mac_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) case TLS_EXT_EXTENDED_MASTER_SECRET: SSL_DEBUG_MSG( 3, ( "found extended_master_secret extension" ) ); diff --git a/library/ssl_srv.c b/library/ssl_srv.c index ad67c2215..6d8626cc2 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -635,6 +635,29 @@ static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, } #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +static int ssl_parse_encrypt_then_mac_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( len != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ((void) buf); + + if( ssl->encrypt_then_mac == SSL_ETM_ENABLED && + ssl->minor_ver != SSL_MINOR_VERSION_0 ) + { + ssl->session_negotiate->encrypt_then_mac = SSL_ETM_ENABLED; + } + + return( 0 ); +} +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) static int ssl_parse_extended_ms_ext( ssl_context *ssl, const unsigned char *buf, @@ -1523,6 +1546,16 @@ static int ssl_parse_client_hello( ssl_context *ssl ) break; #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + case TLS_EXT_ENCRYPT_THEN_MAC: + SSL_DEBUG_MSG( 3, ( "found encrypt then mac extension" ) ); + + ret = ssl_parse_encrypt_then_mac_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) case TLS_EXT_EXTENDED_MASTER_SECRET: SSL_DEBUG_MSG( 3, ( "found extended master secret extension" ) ); @@ -1682,6 +1715,49 @@ static void ssl_write_truncated_hmac_ext( ssl_context *ssl, } #endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +static void ssl_write_encrypt_then_mac_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + const ssl_ciphersuite_t *suite = NULL; + const cipher_info_t *cipher = NULL; + + if( ssl->session_negotiate->encrypt_then_mac == SSL_EXTENDED_MS_DISABLED || + ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + *olen = 0; + return; + } + + /* + * RFC 7366: "If a server receives an encrypt-then-MAC request extension + * from a client and then selects a stream or Authenticated Encryption + * with Associated Data (AEAD) ciphersuite, it MUST NOT send an + * encrypt-then-MAC response extension back to the client." + */ + if( ( suite = ssl_ciphersuite_from_id( + ssl->session_negotiate->ciphersuite ) ) == NULL || + ( cipher = cipher_info_from_type( suite->cipher ) ) == NULL || + cipher->mode != POLARSSL_MODE_CBC ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, adding encrypt then mac extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_ENCRYPT_THEN_MAC >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_ENCRYPT_THEN_MAC ) & 0xFF ); + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) static void ssl_write_extended_ms_ext( ssl_context *ssl, unsigned char *buf, @@ -2012,6 +2088,11 @@ static int ssl_write_server_hello( ssl_context *ssl ) ext_len += olen; #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + ssl_write_encrypt_then_mac_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) ssl_write_extended_ms_ext( ssl, p + 2 + ext_len, &olen ); ext_len += olen; diff --git a/library/ssl_tls.c b/library/ssl_tls.c index c8b7fa2a9..cc6a356a8 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -601,12 +601,23 @@ int ssl_derive_keys( ssl_context *ssl ) { /* * GenericBlockCipher: - * first multiple of blocklen greater than maclen - * + IV except for SSL3 and TLS 1.0 + * 1. if EtM is in use: one block plus MAC + * otherwise: * first multiple of blocklen greater than maclen + * 2. IV except for SSL3 and TLS 1.0 */ - transform->minlen = transform->maclen - + cipher_info->block_size - - transform->maclen % cipher_info->block_size; +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( session->encrypt_then_mac == SSL_ETM_ENABLED ) + { + transform->minlen = transform->maclen + + cipher_info->block_size; + } + else +#endif + { + transform->minlen = transform->maclen + + cipher_info->block_size + - transform->maclen % cipher_info->block_size; + } #if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) if( ssl->minor_ver == SSL_MINOR_VERSION_0 || @@ -1060,25 +1071,41 @@ static void ssl_mac( md_context_t *md_ctx, unsigned char *secret, } #endif /* POLARSSL_SSL_PROTO_SSL3 */ +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ + ( defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) ) ) +#define POLARSSL_SOME_MODES_USE_MAC +#endif + /* * Encryption/decryption functions */ static int ssl_encrypt_buf( ssl_context *ssl ) { size_t i; - const cipher_mode_t mode = cipher_get_cipher_mode( - &ssl->transform_out->cipher_ctx_enc ); + cipher_mode_t mode; + int auth_done = 0; SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) ); + if( ssl->session_out == NULL || ssl->transform_out == NULL ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + mode = cipher_get_cipher_mode( &ssl->transform_out->cipher_ctx_enc ); + /* - * Add MAC before encrypt, except for AEAD modes + * Add MAC before if needed */ -#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ - ( defined(POLARSSL_CIPHER_MODE_CBC) && \ - ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) ) ) - if( mode != POLARSSL_MODE_GCM && - mode != POLARSSL_MODE_CCM ) +#if defined(POLARSSL_SOME_MODES_USE_MAC) + if( mode == POLARSSL_MODE_STREAM || + ( mode == POLARSSL_MODE_CBC +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + && ssl->session_out->encrypt_then_mac == SSL_ETM_DISABLED +#endif + ) ) { #if defined(POLARSSL_SSL_PROTO_SSL3) if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) @@ -1113,6 +1140,7 @@ static int ssl_encrypt_buf( ssl_context *ssl ) ssl->transform_out->maclen ); ssl->out_msglen += ssl->transform_out->maclen; + auth_done++; } #endif /* AEAD not the only option */ @@ -1224,6 +1252,7 @@ static int ssl_encrypt_buf( ssl_context *ssl ) } ssl->out_msglen += taglen; + auth_done++; SSL_DEBUG_BUF( 4, "after encrypt: tag", enc_msg + enc_msglen, taglen ); } @@ -1312,6 +1341,40 @@ static int ssl_encrypt_buf( ssl_context *ssl ) ssl->transform_out->ivlen ); } #endif + +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( auth_done == 0 ) + { + /* + * MAC(MAC_write_key, seq_num + + * TLSCipherText.type + + * TLSCipherText.version + + * length_of( (IV +) ENC(...) ) + + * IV + // except for TLS 1.0 + * ENC(content + padding + padding_length)); + */ + unsigned char pseudo_hdr[13]; + + SSL_DEBUG_MSG( 3, ( "using encrypt then mac" ) ); + + memcpy( pseudo_hdr + 0, ssl->out_ctr, 8 ); + memcpy( pseudo_hdr + 8, ssl->out_hdr, 3 ); + pseudo_hdr[11] = (unsigned char)( ( ssl->out_msglen >> 8 ) & 0xFF ); + pseudo_hdr[12] = (unsigned char)( ( ssl->out_msglen ) & 0xFF ); + + SSL_DEBUG_BUF( 4, "MAC'd meta-data", pseudo_hdr, 13 ); + + md_hmac_update( &ssl->transform_out->md_ctx_enc, pseudo_hdr, 13 ); + md_hmac_update( &ssl->transform_out->md_ctx_enc, + ssl->out_iv, ssl->out_msglen ); + md_hmac_finish( &ssl->transform_out->md_ctx_enc, + ssl->out_iv + ssl->out_msglen ); + md_hmac_reset( &ssl->transform_out->md_ctx_enc ); + + ssl->out_msglen += ssl->transform_out->maclen; + auth_done++; + } +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ } else #endif /* POLARSSL_CIPHER_MODE_CBC && @@ -1321,6 +1384,13 @@ static int ssl_encrypt_buf( ssl_context *ssl ) return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); } + /* Make extra sure authentication was performed, exactly once */ + if( auth_done != 1 ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + for( i = 8; i > 0; i-- ) if( ++ssl->out_ctr[i - 1] != 0 ) break; @@ -1342,16 +1412,22 @@ static int ssl_encrypt_buf( ssl_context *ssl ) static int ssl_decrypt_buf( ssl_context *ssl ) { size_t i; - const cipher_mode_t mode = cipher_get_cipher_mode( - &ssl->transform_in->cipher_ctx_dec ); -#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ - ( defined(POLARSSL_CIPHER_MODE_CBC) && \ - ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) ) ) + cipher_mode_t mode; + int auth_done = 0; +#if defined(POLARSSL_SOME_MODES_USE_MAC) size_t padlen = 0, correct = 1; #endif SSL_DEBUG_MSG( 2, ( "=> decrypt buf" ) ); + if( ssl->session_in == NULL || ssl->transform_in == NULL ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + mode = cipher_get_cipher_mode( &ssl->transform_in->cipher_ctx_dec ); + if( ssl->in_msglen < ssl->transform_in->minlen ) { SSL_DEBUG_MSG( 1, ( "in_msglen (%d) < minlen (%d)", @@ -1448,6 +1524,7 @@ static int ssl_decrypt_buf( ssl_context *ssl ) return( ret ); } + auth_done++; if( olen != dec_msglen ) { @@ -1474,13 +1551,6 @@ static int ssl_decrypt_buf( ssl_context *ssl ) /* * Check immediate ciphertext sanity */ - if( ssl->in_msglen % ssl->transform_in->ivlen != 0 ) - { - SSL_DEBUG_MSG( 1, ( "msglen (%d) %% ivlen (%d) != 0", - ssl->in_msglen, ssl->transform_in->ivlen ) ); - return( POLARSSL_ERR_SSL_INVALID_MAC ); - } - #if defined(POLARSSL_SSL_PROTO_TLS1_1) || defined(POLARSSL_SSL_PROTO_TLS1_2) if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) minlen += ssl->transform_in->ivlen; @@ -1500,6 +1570,59 @@ static int ssl_decrypt_buf( ssl_context *ssl ) dec_msg = ssl->in_msg; dec_msg_result = ssl->in_msg; + /* + * Authenticate before decrypt if enabled + */ +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( ssl->session_in->encrypt_then_mac == SSL_ETM_ENABLED ) + { + unsigned char computed_mac[POLARSSL_SSL_MAX_MAC_SIZE]; + unsigned char pseudo_hdr[13]; + + SSL_DEBUG_MSG( 3, ( "using encrypt then mac" ) ); + + dec_msglen -= ssl->transform_in->maclen; + ssl->in_msglen -= ssl->transform_in->maclen; + + memcpy( pseudo_hdr + 0, ssl->in_ctr, 8 ); + memcpy( pseudo_hdr + 8, ssl->in_hdr, 3 ); + pseudo_hdr[11] = (unsigned char)( ( ssl->in_msglen >> 8 ) & 0xFF ); + pseudo_hdr[12] = (unsigned char)( ( ssl->in_msglen ) & 0xFF ); + + SSL_DEBUG_BUF( 4, "MAC'd meta-data", pseudo_hdr, 13 ); + + md_hmac_update( &ssl->transform_in->md_ctx_dec, pseudo_hdr, 13 ); + md_hmac_update( &ssl->transform_in->md_ctx_dec, + ssl->in_iv, ssl->in_msglen ); + md_hmac_finish( &ssl->transform_in->md_ctx_dec, computed_mac ); + md_hmac_reset( &ssl->transform_in->md_ctx_dec ); + + SSL_DEBUG_BUF( 4, "message mac", ssl->in_iv + ssl->in_msglen, + ssl->transform_in->maclen ); + SSL_DEBUG_BUF( 4, "computed mac", computed_mac, + ssl->transform_in->maclen ); + + if( safer_memcmp( ssl->in_iv + ssl->in_msglen, computed_mac, + ssl->transform_in->maclen ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "message mac does not match" ) ); + + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + auth_done++; + } +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ + + /* + * Check length sanity + */ + if( ssl->in_msglen % ssl->transform_in->ivlen != 0 ) + { + SSL_DEBUG_MSG( 1, ( "msglen (%d) %% ivlen (%d) != 0", + ssl->in_msglen, ssl->transform_in->ivlen ) ); + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + #if defined(POLARSSL_SSL_PROTO_TLS1_1) || defined(POLARSSL_SSL_PROTO_TLS1_2) /* * Initialize for prepended IV for block cipher in TLS v1.1 and up @@ -1544,7 +1667,8 @@ static int ssl_decrypt_buf( ssl_context *ssl ) padlen = 1 + ssl->in_msg[ssl->in_msglen - 1]; - if( ssl->in_msglen < ssl->transform_in->maclen + padlen ) + if( ssl->in_msglen < ssl->transform_in->maclen + padlen && + auth_done == 0 ) { #if defined(POLARSSL_SSL_DEBUG_ALL) SSL_DEBUG_MSG( 1, ( "msglen (%d) < maclen (%d) + padlen (%d)", @@ -1618,6 +1742,8 @@ static int ssl_decrypt_buf( ssl_context *ssl ) SSL_DEBUG_MSG( 1, ( "should never happen" ) ); return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); } + + ssl->in_msglen -= padlen; } else #endif /* POLARSSL_CIPHER_MODE_CBC && @@ -1631,17 +1757,15 @@ static int ssl_decrypt_buf( ssl_context *ssl ) ssl->in_msg, ssl->in_msglen ); /* - * Always compute the MAC (RFC4346, CBCTIME), except for AEAD of course + * Authenticate if not done yet. + * Compute the MAC regardless of the padding result (RFC4346, CBCTIME). */ -#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ - ( defined(POLARSSL_CIPHER_MODE_CBC) && \ - ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) ) ) - if( mode != POLARSSL_MODE_GCM && - mode != POLARSSL_MODE_CCM ) +#if defined(POLARSSL_SOME_MODES_USE_MAC) + if( auth_done == 0 ) { unsigned char tmp[POLARSSL_SSL_MAX_MAC_SIZE]; - ssl->in_msglen -= ( ssl->transform_in->maclen + padlen ); + ssl->in_msglen -= ssl->transform_in->maclen; ssl->in_hdr[3] = (unsigned char)( ssl->in_msglen >> 8 ); ssl->in_hdr[4] = (unsigned char)( ssl->in_msglen ); @@ -1711,6 +1835,7 @@ static int ssl_decrypt_buf( ssl_context *ssl ) #endif correct = 0; } + auth_done++; /* * Finally check the correct flag @@ -1718,7 +1843,14 @@ static int ssl_decrypt_buf( ssl_context *ssl ) if( correct == 0 ) return( POLARSSL_ERR_SSL_INVALID_MAC ); } -#endif /* AEAD not the only option */ +#endif /* POLARSSL_SOME_MODES_USE_MAC */ + + /* Make extra sure authentication was performed, exactly once */ + if( auth_done != 1 ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } if( ssl->in_msglen == 0 ) { @@ -1754,6 +1886,10 @@ static int ssl_decrypt_buf( ssl_context *ssl ) return( 0 ); } +#undef MAC_NONE +#undef MAC_PLAINTEXT +#undef MAC_CIPHERTEXT + #if defined(POLARSSL_ZLIB_SUPPORT) /* * Compression/decompression functions @@ -3116,6 +3252,12 @@ void ssl_handshake_wrapup( ssl_context *ssl ) if( ssl->session ) { +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + /* RFC 7366 3.1: keep the EtM state */ + ssl->session_negotiate->encrypt_then_mac = + ssl->session->encrypt_then_mac; +#endif + ssl_session_free( ssl->session ); polarssl_free( ssl->session ); } @@ -3474,6 +3616,10 @@ int ssl_init( ssl_context *ssl ) memset( ssl-> in_ctr, 0, SSL_BUFFER_LEN ); memset( ssl->out_ctr, 0, SSL_BUFFER_LEN ); +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + ssl->encrypt_then_mac = SSL_ETM_ENABLED; +#endif + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) ssl->extended_ms = SSL_EXTENDED_MS_ENABLED; #endif @@ -4025,6 +4171,13 @@ void ssl_set_fallback( ssl_context *ssl, char fallback ) } #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +void ssl_set_encrypt_then_mac( ssl_context *ssl, char etm ) +{ + ssl->encrypt_then_mac = etm; +} +#endif + #if defined(POLARSSL_SSL_EXTENDED_MASTER_SECRET) void ssl_set_extended_master_secret( ssl_context *ssl, char ems ) { diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index de33fbcd4..82877c616 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -97,6 +97,7 @@ int main( int argc, char *argv[] ) #define DFL_ALPN_STRING NULL #define DFL_FALLBACK -1 #define DFL_EXTENDED_MS -1 +#define DFL_ETM -1 #define GET_REQUEST "GET %s HTTP/1.0\r\nExtra-header: " #define GET_REQUEST_END "\r\n\r\n" @@ -136,6 +137,7 @@ struct options const char *alpn_string; /* ALPN supported protocols */ int fallback; /* is this a fallback connection? */ int extended_ms; /* negotiate extended master secret? */ + char etm; /* negotiate encrypt then mac? ? */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -302,6 +304,13 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) #define USAGE_EMS "" #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +#define USAGE_ETM \ + " etm=0/1 default: (library default: on)\n" +#else +#define USAGE_ETM "" +#endif + #define USAGE \ "\n usage: ssl_client2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -333,6 +342,7 @@ static int my_verify( void *data, x509_crt *crt, int depth, int *flags ) USAGE_ALPN \ USAGE_FALLBACK \ USAGE_EMS \ + USAGE_ETM \ "\n" \ " min_version=%%s default: \"\" (ssl3)\n" \ " max_version=%%s default: \"\" (tls1_2)\n" \ @@ -435,6 +445,7 @@ int main( int argc, char *argv[] ) opt.alpn_string = DFL_ALPN_STRING; opt.fallback = DFL_FALLBACK; opt.extended_ms = DFL_EXTENDED_MS; + opt.etm = DFL_ETM; for( i = 1; i < argc; i++ ) { @@ -559,6 +570,15 @@ int main( int argc, char *argv[] ) default: goto usage; } } + else if( strcmp( p, "etm" ) == 0 ) + { + switch( atoi( q ) ) + { + case 0: opt.etm = SSL_ETM_DISABLED; break; + case 1: opt.etm = SSL_ETM_ENABLED; break; + default: goto usage; + } + } else if( strcmp( p, "min_version" ) == 0 ) { if( strcmp( q, "ssl3" ) == 0 ) @@ -927,6 +947,11 @@ int main( int argc, char *argv[] ) ssl_set_extended_master_secret( &ssl, opt.extended_ms ); #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( opt.etm != DFL_ETM ) + ssl_set_encrypt_then_mac( &ssl, opt.etm ); +#endif + #if defined(POLARSSL_SSL_ALPN) if( opt.alpn_string != NULL ) if( ( ret = ssl_set_alpn_protocols( &ssl, alpn_list ) ) != 0 ) diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 35a156bdf..08f2ff864 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -117,6 +117,7 @@ int main( int argc, char *argv[] ) #define DFL_ALPN_STRING NULL #define DFL_DHM_FILE NULL #define DFL_EXTENDED_MS -1 +#define DFL_ETM -1 #define LONG_RESPONSE "

01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \ "02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \ @@ -178,6 +179,7 @@ struct options const char *alpn_string; /* ALPN supported protocols */ const char *dhm_file; /* the file with the DH parameters */ char extended_ms; /* allow negotiation of extended MS? */ + char etm; /* allow negotiation of encrypt-then-MAC? */ } opt; static void my_debug( void *ctx, int level, const char *str ) @@ -308,6 +310,13 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len ) #define USAGE_EMS "" #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) +#define USAGE_ETM \ + " etm=0/1 default: (library default: on)\n" +#else +#define USAGE_ETM "" +#endif + #define USAGE \ "\n usage: ssl_server2 param=<>...\n" \ "\n acceptable parameters:\n" \ @@ -334,6 +343,7 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len ) USAGE_MAX_FRAG_LEN \ USAGE_ALPN \ USAGE_EMS \ + USAGE_ETM \ "\n" \ " min_version=%%s default: \"ssl3\"\n" \ " max_version=%%s default: \"tls1_2\"\n" \ @@ -724,6 +734,7 @@ int main( int argc, char *argv[] ) opt.alpn_string = DFL_ALPN_STRING; opt.dhm_file = DFL_DHM_FILE; opt.extended_ms = DFL_EXTENDED_MS; + opt.etm = DFL_ETM; for( i = 1; i < argc; i++ ) { @@ -900,6 +911,15 @@ int main( int argc, char *argv[] ) default: goto usage; } } + else if( strcmp( p, "etm" ) == 0 ) + { + switch( atoi( q ) ) + { + case 0: opt.etm = SSL_ETM_DISABLED; break; + case 1: opt.etm = SSL_ETM_ENABLED; break; + default: goto usage; + } + } else if( strcmp( p, "tickets" ) == 0 ) { opt.tickets = atoi( q ); @@ -1282,6 +1302,11 @@ int main( int argc, char *argv[] ) ssl_set_extended_master_secret( &ssl, opt.extended_ms ); #endif +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( opt.etm != DFL_ETM ) + ssl_set_encrypt_then_mac( &ssl, opt.etm ); +#endif + #if defined(POLARSSL_SSL_ALPN) if( opt.alpn_string != NULL ) if( ( ret = ssl_set_alpn_protocols( &ssl, alpn_list ) ) != 0 ) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 93ab9368a..f1ff91720 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -248,14 +248,14 @@ run_test() { if is_polar "$SRV_CMD"; then if grep "Performing the SSL/TLS handshake" $SRV_OUT >/dev/null; then :; else - fail "server failed to start" + fail "server or client failed to reach handshake stage" return fi fi if is_polar "$CLI_CMD"; then if grep "Performing the SSL/TLS handshake" $CLI_OUT >/dev/null; then :; else - fail "client failed to start" + fail "server or client failed to reach handshake stage" return fi fi @@ -441,6 +441,92 @@ run_test "Truncated HMAC: actual test" \ 0 \ -s "dumping 'computed mac' (10 bytes)" +# Tests for Encrypt-then-MAC extension + +run_test "Encrypt then MAC: default" \ + "$P_SRV debug_level=3 \ + force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA" \ + "$P_CLI debug_level=3" \ + 0 \ + -c "client hello, adding encrypt_then_mac extension" \ + -s "found encrypt then mac extension" \ + -s "server hello, adding encrypt then mac extension" \ + -c "found encrypt_then_mac extension" \ + -c "using encrypt then mac" \ + -s "using encrypt then mac" + +run_test "Encrypt then MAC: client enabled, server disabled" \ + "$P_SRV debug_level=3 etm=0 \ + force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA" \ + "$P_CLI debug_level=3 etm=1" \ + 0 \ + -c "client hello, adding encrypt_then_mac extension" \ + -s "found encrypt then mac extension" \ + -S "server hello, adding encrypt then mac extension" \ + -C "found encrypt_then_mac extension" \ + -C "using encrypt then mac" \ + -S "using encrypt then mac" + +run_test "Encrypt then MAC: client enabled, aead cipher" \ + "$P_SRV debug_level=3 etm=1 \ + force_ciphersuite=TLS-RSA-WITH-AES-128-GCM-SHA256" \ + "$P_CLI debug_level=3 etm=1" \ + 0 \ + -c "client hello, adding encrypt_then_mac extension" \ + -s "found encrypt then mac extension" \ + -S "server hello, adding encrypt then mac extension" \ + -C "found encrypt_then_mac extension" \ + -C "using encrypt then mac" \ + -S "using encrypt then mac" + +run_test "Encrypt then MAC: client enabled, stream cipher" \ + "$P_SRV debug_level=3 etm=1 \ + force_ciphersuite=TLS-RSA-WITH-RC4-128-SHA" \ + "$P_CLI debug_level=3 etm=1" \ + 0 \ + -c "client hello, adding encrypt_then_mac extension" \ + -s "found encrypt then mac extension" \ + -S "server hello, adding encrypt then mac extension" \ + -C "found encrypt_then_mac extension" \ + -C "using encrypt then mac" \ + -S "using encrypt then mac" + +run_test "Encrypt then MAC: client disabled, server enabled" \ + "$P_SRV debug_level=3 etm=1 \ + force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA" \ + "$P_CLI debug_level=3 etm=0" \ + 0 \ + -C "client hello, adding encrypt_then_mac extension" \ + -S "found encrypt then mac extension" \ + -S "server hello, adding encrypt then mac extension" \ + -C "found encrypt_then_mac extension" \ + -C "using encrypt then mac" \ + -S "using encrypt then mac" + +run_test "Encrypt then MAC: client SSLv3, server enabled" \ + "$P_SRV debug_level=3 \ + force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA" \ + "$P_CLI debug_level=3 force_version=ssl3" \ + 0 \ + -C "client hello, adding encrypt_then_mac extension" \ + -S "found encrypt then mac extension" \ + -S "server hello, adding encrypt then mac extension" \ + -C "found encrypt_then_mac extension" \ + -C "using encrypt then mac" \ + -S "using encrypt then mac" + +run_test "Encrypt then MAC: client enabled, server SSLv3" \ + "$P_SRV debug_level=3 force_version=ssl3 \ + force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA" \ + "$P_CLI debug_level=3" \ + 0 \ + -c "client hello, adding encrypt_then_mac extension" \ + -s "found encrypt then mac extension" \ + -S "server hello, adding encrypt then mac extension" \ + -C "found encrypt_then_mac extension" \ + -C "using encrypt then mac" \ + -S "using encrypt then mac" + # Tests for Extended Master Secret extension run_test "Extended Master Secret: default" \ @@ -1779,6 +1865,13 @@ run_test "Small packet TLS 1.0 BlockCipher" \ 0 \ -s "Read from client: 1 bytes read" +run_test "Small packet TLS 1.0 BlockCipher without EtM" \ + "$P_SRV" \ + "$P_CLI request_size=1 force_version=tls1 etm=0 \ + force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \ + 0 \ + -s "Read from client: 1 bytes read" + run_test "Small packet TLS 1.0 BlockCipher truncated MAC" \ "$P_SRV" \ "$P_CLI request_size=1 force_version=tls1 \ @@ -1802,6 +1895,13 @@ run_test "Small packet TLS 1.1 BlockCipher" \ 0 \ -s "Read from client: 1 bytes read" +run_test "Small packet TLS 1.1 BlockCipher without EtM" \ + "$P_SRV" \ + "$P_CLI request_size=1 force_version=tls1_1 etm=0 \ + force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \ + 0 \ + -s "Read from client: 1 bytes read" + run_test "Small packet TLS 1.1 StreamCipher" \ "$P_SRV" \ "$P_CLI request_size=1 force_version=tls1_1 \ @@ -1832,6 +1932,13 @@ run_test "Small packet TLS 1.2 BlockCipher" \ 0 \ -s "Read from client: 1 bytes read" +run_test "Small packet TLS 1.2 BlockCipher without EtM" \ + "$P_SRV" \ + "$P_CLI request_size=1 force_version=tls1_2 etm=0 \ + force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \ + 0 \ + -s "Read from client: 1 bytes read" + run_test "Small packet TLS 1.2 BlockCipher larger MAC" \ "$P_SRV" \ "$P_CLI request_size=1 force_version=tls1_2 force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \