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..c884e7cc4 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,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->session_negotiate->encrypt_then_mac == SSL_EXTENDED_MS_DISABLED || + ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + *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 +2071,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..e8f2b7fd4 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1071,6 +1071,15 @@ static int ssl_encrypt_buf( ssl_context *ssl ) SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) ); +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( ssl->session_out != NULL && + ssl->session_out->encrypt_then_mac == SSL_ETM_ENABLED ) + { + // WIP + SSL_DEBUG_MSG( 3, ( "using encrypt then mac" ) ); + } +#endif + /* * Add MAC before encrypt, except for AEAD modes */ @@ -3474,6 +3483,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 +4038,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 d4cde0f14..444322bf4 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? */ char 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 38bc89b7a..6db8571ff 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -247,14 +247,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 @@ -440,6 +440,63 @@ 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" \ + "$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" \ + "$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" \ + "$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" \ + "$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" \ + "$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" \