Merge branch 'etm' into dtls
* etm:
Fix warning in reduced config
Update Changelog for EtM
Keep EtM state across renegotiations
Adjust minimum length for EtM
Don't send back EtM extension if not using CBC
Fix for the RFC erratum
Implement EtM
Preparation for EtM
Implement initial negotiation of EtM
Conflicts:
include/polarssl/check_config.h
diff --git a/ChangeLog b/ChangeLog
index 2ad2b06..4fb00fe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,11 +12,12 @@
= 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 3c6dcf1..262ff44 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 c3dff44..2aa9597 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 8cfbd4c..de3cf40 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 @@
#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 @@
#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 @@
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 104bcec..f11f284 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -359,6 +359,32 @@
}
#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 @@
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 @@
}
#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 @@
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 553a2fa..cdbb028 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -665,6 +665,29 @@
}
#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 @@
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 @@
}
#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 @@
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 3ab0125..366455f 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -675,12 +675,23 @@
{
/*
* 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 @@
}
#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 @@
{
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 @@
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 @@
( 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 @@
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 @@
/*
* 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 @@
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 @@
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 @@
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 @@
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 @@
return( 0 );
}
+#undef MAC_NONE
+#undef MAC_PLAINTEXT
+#undef MAC_CIPHERTEXT
+
#if defined(POLARSSL_ZLIB_SUPPORT)
/*
* Compression/decompression functions
@@ -4336,6 +4473,12 @@
*/
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 @@
/* 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 @@
}
#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 bb2e177..4aeec55 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -102,6 +102,7 @@
#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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 287ec52..5cb086a 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -128,6 +128,7 @@
#define DFL_HS_TO_MAX 0
#define DFL_BADMAC_LIMIT -1
#define DFL_EXTENDED_MS -1
+#define DFL_ETM -1
#define LONG_RESPONSE "<p>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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 0505d66..b594c3e 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -362,14 +362,14 @@
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 @@
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 @@
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 @@
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 @@
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" \