diff --git a/ChangeLog b/ChangeLog index 2ad2b068e..4fb00fe05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,11 +12,12 @@ API Changes = 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 3c6dcf1cf..262ff44ed 100644 --- a/include/polarssl/check_config.h +++ b/include/polarssl/check_config.h @@ -283,6 +283,13 @@ #error "POLARSSL_SSL_DTLS_BADMAC_LIMIT defined, but not all prerequisites" #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 c3dff4441..2aa959710 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 8cfbd4c46..de3cf4078 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -221,6 +221,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 @@ -442,6 +445,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 @@ -585,6 +589,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 }; /* @@ -795,6 +803,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 @@ -1754,6 +1765,21 @@ int 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 104bcecb7..f11f28473 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 ) @@ -770,6 +796,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; @@ -898,6 +929,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, @@ -1395,6 +1446,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 553a2fa57..cdbb028b0 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -665,6 +665,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, @@ -1731,6 +1754,16 @@ read_record_header: 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" ) ); @@ -1939,6 +1972,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, @@ -2344,6 +2420,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 3ab0125b5..366455fa6 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -675,12 +675,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 || @@ -1134,6 +1145,46 @@ static void ssl_mac( md_context_t *md_ctx, unsigned char *secret, } #endif /* POLARSSL_SSL_PROTO_SSL3 */ +#define MAC_NONE 0 +#define MAC_PLAINTEXT 1 +#define MAC_CIPHERTEXT 2 + +/* + * Is MAC applied on ciphertext, cleartext or not at all? + */ +static char ssl_get_mac_order( ssl_context *ssl, + const ssl_session *session, + cipher_mode_t mode ) +{ +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) + if( mode == POLARSSL_MODE_STREAM ) + return( MAC_PLAINTEXT ); +#endif + +#if defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) ) + if( mode == POLARSSL_MODE_CBC ) + { +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( session != NULL && session->encrypt_then_mac == SSL_ETM_ENABLED ) + { + SSL_DEBUG_MSG( 3, ( "using encrypt then mac" ) ); + return( MAC_CIPHERTEXT ); + } +#endif + + return( MAC_PLAINTEXT ); + } +#endif + + /* Unused if AEAD is the only option */ + ((void) ssl); + ((void) session); + ((void) mode); + + return( MAC_NONE ); +} + /* * Encryption/decryption functions */ @@ -1141,17 +1192,19 @@ static int ssl_encrypt_buf( ssl_context *ssl ) { const cipher_mode_t mode = cipher_get_cipher_mode( &ssl->transform_out->cipher_ctx_enc ); + char mac_order; SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) ); + mac_order = ssl_get_mac_order( ssl, ssl->session_out, mode ); + /* - * 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( mac_order == MAC_PLAINTEXT ) { #if defined(POLARSSL_SSL_PROTO_SSL3) if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) @@ -1387,6 +1440,37 @@ static int ssl_encrypt_buf( ssl_context *ssl ) ssl->transform_out->ivlen ); } #endif + +#if defined(POLARSSL_SSL_ENCRYPT_THEN_MAC) + if( mac_order == MAC_CIPHERTEXT ) + { + /* + * 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]; + + 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; + } +#endif /* POLARSSL_SSL_ENCRYPT_THEN_MAC */ } else #endif /* POLARSSL_CIPHER_MODE_CBC && @@ -1413,6 +1497,7 @@ static int ssl_decrypt_buf( ssl_context *ssl ) ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) ) ) size_t padlen = 0, correct = 1; #endif + char mac_order; SSL_DEBUG_MSG( 2, ( "=> decrypt buf" ) ); @@ -1423,6 +1508,8 @@ static int ssl_decrypt_buf( ssl_context *ssl ) return( POLARSSL_ERR_SSL_INVALID_MAC ); } + mac_order = ssl_get_mac_order( ssl, ssl->session_in, mode ); + #if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) if( mode == POLARSSL_MODE_STREAM ) { @@ -1538,13 +1625,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; @@ -1564,6 +1644,56 @@ 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( mac_order == MAC_CIPHERTEXT ) + { + unsigned char computed_mac[POLARSSL_SSL_MAX_MAC_SIZE]; + unsigned char pseudo_hdr[13]; + + 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 ); + } + } +#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 @@ -1608,7 +1738,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 && + mac_order == MAC_PLAINTEXT ) { #if defined(POLARSSL_SSL_DEBUG_ALL) SSL_DEBUG_MSG( 1, ( "msglen (%d) < maclen (%d) + padlen (%d)", @@ -1682,6 +1813,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 && @@ -1695,17 +1828,17 @@ 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( mac_order == MAC_PLAINTEXT ) { 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_len[0] = (unsigned char)( ssl->in_msglen >> 8 ); ssl->in_len[1] = (unsigned char)( ssl->in_msglen ); @@ -1829,6 +1962,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 @@ -4336,6 +4473,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 ); } @@ -4763,6 +4906,10 @@ int ssl_init( ssl_context *ssl ) /* No error is possible, SSL_TRANSPORT_STREAM always valid */ (void) ssl_set_transport( ssl, SSL_TRANSPORT_STREAM ); +#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 @@ -5459,6 +5606,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 bb2e177e5..4aeec553d 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -102,6 +102,7 @@ int main( int argc, char *argv[] ) #define DFL_HS_TO_MAX 0 #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" @@ -146,6 +147,7 @@ struct options uint32_t hs_to_max; /* Max value of DTLS handshake timer */ 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 ) @@ -321,6 +323,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" \ @@ -356,6 +365,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" \ @@ -463,6 +473,7 @@ int main( int argc, char *argv[] ) opt.hs_to_max = DFL_HS_TO_MAX; opt.fallback = DFL_FALLBACK; opt.extended_ms = DFL_EXTENDED_MS; + opt.etm = DFL_ETM; for( i = 1; i < argc; i++ ) { @@ -605,6 +616,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 ) @@ -1022,6 +1042,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 287ec52cc..5cb086a77 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -128,6 +128,7 @@ int main( int argc, char *argv[] ) #define DFL_HS_TO_MAX 0 #define DFL_BADMAC_LIMIT -1 #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" \ @@ -196,6 +197,7 @@ struct options uint32_t hs_to_max; /* Max value of DTLS handshake timer */ int badmac_limit; /* Limit of records with bad MAC */ 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 ) @@ -357,6 +359,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" \ @@ -390,6 +399,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" \ @@ -794,6 +804,7 @@ int main( int argc, char *argv[] ) opt.hs_to_max = DFL_HS_TO_MAX; opt.badmac_limit = DFL_BADMAC_LIMIT; opt.extended_ms = DFL_EXTENDED_MS; + opt.etm = DFL_ETM; for( i = 1; i < argc; i++ ) { @@ -998,6 +1009,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 ); @@ -1435,6 +1455,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 0505d66e5..b594c3e23 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -362,14 +362,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 @@ -567,6 +567,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" \ @@ -1972,6 +2058,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 \ @@ -1995,6 +2088,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 \ @@ -2025,6 +2125,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" \