Merge commit 'b2eaac1' into dtls
* commit 'b2eaac1':
Stop assuming chars are signed
Add tests for CBC record splitting
Fix tests that were failing with record splitting
Allow disabling record splitting at runtime
Add 1/n-1 record splitting
Enhance doc on ssl_write()
Conflicts:
include/polarssl/ssl.h
programs/ssl/ssl_client2.c
programs/ssl/ssl_server2.c
diff --git a/ChangeLog b/ChangeLog
index 3912037..f4551fb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -38,6 +38,7 @@
* Add compile-time option POLARSSL_X509_MAX_INTERMEDIATE_CA to limit the
length of an X.509 verification chain.
* Support for renegotiation can now be disabled at compile-time
+ * Support for 1/n-1 record splitting, a countermeasure against BEAST.
Bugfix
* Stack buffer overflow if ctr_drbg_update() is called with too large
diff --git a/include/polarssl/check_config.h b/include/polarssl/check_config.h
index 262ff44..d2c3643 100644
--- a/include/polarssl/check_config.h
+++ b/include/polarssl/check_config.h
@@ -303,6 +303,11 @@
#error "POLARSSL_SSL_SESSION_TICKETS_C defined, but not all prerequisites"
#endif
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING) && \
+ !defined(POLARSSL_SSL_PROTO_SSL3) && !defined(POLARSSL_SSL_PROTO_TLS1)
+#error "POLARSSL_SSL_CBC_RECORD_SPLITTING defined, but not all prerequisites"
+#endif
+
#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) && \
!defined(POLARSSL_X509_CRT_PARSE_C)
#error "POLARSSL_SSL_SERVER_NAME_INDICATION defined, but not all prerequisites"
diff --git a/include/polarssl/config.h b/include/polarssl/config.h
index 860a40b..6d76883 100644
--- a/include/polarssl/config.h
+++ b/include/polarssl/config.h
@@ -887,6 +887,18 @@
//#define POLARSSL_SSL_HW_RECORD_ACCEL
/**
+ * \def POLARSSL_SSL_CBC_RECORD_SPLITTING
+ *
+ * Enable 1/n-1 record splitting for CBC mode in SSLv3 and TLS 1.0.
+ *
+ * This is a countermeasure to the BEAST attack, which also minimizes the risk
+ * of interoperability issues compared to sending 0-length records.
+ *
+ * Comment this macro to disable 1/n-1 record splitting.
+ */
+#define POLARSSL_SSL_CBC_RECORD_SPLITTING
+
+/**
* \def POLARSSL_SSL_DISABLE_RENEGOTIATION
*
* Disable support for TLS renegotiation.
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index 8e0ba54..26110d8 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -265,6 +265,9 @@
#define SSL_SESSION_TICKETS_DISABLED 0
#define SSL_SESSION_TICKETS_ENABLED 1
+#define SSL_CBC_RECORD_SPLITTING_DISABLED -1
+#define SSL_CBC_RECORD_SPLITTING_ENABLED 0
+
/*
* DTLS retransmission states, see RFC 6347 4.2.4
*
@@ -943,6 +946,10 @@
#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH)
unsigned char mfl_code; /*!< MaxFragmentLength chosen by us */
#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+ char split_done; /*!< flag for record splitting:
+ -1 disabled, 0 todo, 1 done */
+#endif
/*
* PKI layer
@@ -1857,6 +1864,21 @@
int ssl_set_truncated_hmac( ssl_context *ssl, int truncate );
#endif /* POLARSSL_SSL_TRUNCATED_HMAC */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+/**
+ * \brief Enable / Disable 1/n-1 record splitting
+ * (Default: SSL_CBC_RECORD_SPLITTING_ENABLED)
+ *
+ * \note Only affects SSLv3 and TLS 1.0, not higher versions.
+ * Does not affect non-CBC ciphersuites in any version.
+ *
+ * \param ssl SSL context
+ * \param split SSL_CBC_RECORD_SPLITTING_ENABLED or
+ * SSL_CBC_RECORD_SPLITTING_DISABLED
+ */
+void ssl_set_cbc_record_splitting( ssl_context *ssl, char split );
+#endif /* POLARSSL_SSL_CBC_RECORD_SPLITTING */
+
#if defined(POLARSSL_SSL_SESSION_TICKETS)
/**
* \brief Enable / Disable session tickets
@@ -2147,11 +2169,13 @@
* it must be called later with the *same* arguments,
* until it returns a positive value.
*
- * \note When DTLS is in use, and a maximum fragment length was
- * either set with \c ssl_set_max_frag_len() or negotiated by
- * the peer, len must not not be greater than the maximum
- * fragment length, or POLARSSL_ERR_SSL_BAD_INPUT_DATA is
- * returned.
+ * \note If the requested length is greater than the maximum
+ * fragment length (either the built-in limit or the one set
+ * or negotiated with the peer), then:
+ * - with TLS, less bytes than requested are written. (In
+ * order to write larger messages, this function should be
+ * called in a loop.)
+ * - with DTLS, POLARSSL_ERR_SSL_BAD_INPUT_DATA is returned.
*/
int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len );
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index e25e565..12ee22e 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -5027,6 +5027,10 @@
ssl->out_msgtype = 0;
ssl->out_msglen = 0;
ssl->out_left = 0;
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+ if( ssl->split_done != SSL_CBC_RECORD_SPLITTING_DISABLED )
+ ssl->split_done = 0;
+#endif
ssl->transform_in = NULL;
ssl->transform_out = NULL;
@@ -5697,6 +5701,13 @@
}
#endif /* POLARSSL_SSL_TRUNCATED_HMAC */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+void ssl_set_cbc_record_splitting( ssl_context *ssl, char split )
+{
+ ssl->split_done = split;
+}
+#endif
+
void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy )
{
ssl->allow_legacy_renegotiation = allow_legacy;
@@ -6309,7 +6320,11 @@
/*
* Send application data to be encrypted by the SSL layer
*/
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+static int ssl_write_real( ssl_context *ssl, const unsigned char *buf, size_t len )
+#else
int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len )
+#endif
{
int ret;
#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH)
@@ -6393,6 +6408,45 @@
}
/*
+ * Write application data, doing 1/n-1 splitting if necessary.
+ *
+ * With non-blocking I/O, ssl_write_real() may return WANT_WRITE,
+ * then the caller will call us again with the same arguments, so
+ * remember wether we already did the split or not.
+ */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len )
+{
+ int ret;
+
+ if( ssl->split_done == SSL_CBC_RECORD_SPLITTING_DISABLED ||
+ len <= 1 ||
+ ssl->minor_ver > SSL_MINOR_VERSION_1 ||
+ cipher_get_cipher_mode( &ssl->transform_out->cipher_ctx_enc )
+ != POLARSSL_MODE_CBC )
+ {
+ return( ssl_write_real( ssl, buf, len ) );
+ }
+
+ if( ssl->split_done == 0 )
+ {
+ ssl->split_done = 1;
+ if( ( ret = ssl_write_real( ssl, buf, 1 ) ) < 0 )
+ return( ret );
+ }
+
+ if( ssl->split_done == 1 )
+ {
+ ssl->split_done = 0;
+ if( ( ret = ssl_write_real( ssl, buf + 1, len - 1 ) ) < 0 )
+ return( ret );
+ }
+
+ return( ret + 1 );
+}
+#endif /* POLARSSL_SSL_CBC_RECORD_SPLITTING */
+
+/*
* Notify the peer that the connection is being closed
*/
int ssl_close_notify( ssl_context *ssl )
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index b49a48e..0ddd802 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -93,6 +93,7 @@
#define DFL_AUTH_MODE SSL_VERIFY_REQUIRED
#define DFL_MFL_CODE SSL_MAX_FRAG_LEN_NONE
#define DFL_TRUNC_HMAC 0
+#define DFL_RECSPLIT -1
#define DFL_RECONNECT 0
#define DFL_RECO_DELAY 0
#define DFL_TICKETS SSL_SESSION_TICKETS_ENABLED
@@ -138,6 +139,7 @@
int auth_mode; /* verify mode for connection */
unsigned char mfl_code; /* code for maximum fragment length */
int trunc_hmac; /* negotiate truncated hmac or not */
+ int recsplit; /* enable record splitting? */
int reconnect; /* attempt to resume session */
int reco_delay; /* delay in seconds before resuming session */
int tickets; /* enable / disable session tickets */
@@ -285,6 +287,13 @@
#define USAGE_MAX_FRAG_LEN ""
#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+#define USAGE_RECSPLIT \
+ " recplit=%%d default: (library default)\n"
+#else
+#define USAGE_RECSPLIT
+#endif
+
#if defined(POLARSSL_TIMING_C)
#define USAGE_TIME \
" reco_delay=%%d default: 0 seconds\n"
@@ -373,6 +382,7 @@
USAGE_FALLBACK \
USAGE_EMS \
USAGE_ETM \
+ USAGE_RECSPLIT \
"\n" \
" min_version=%%s default: \"\" (ssl3)\n" \
" max_version=%%s default: \"\" (tls1_2)\n" \
@@ -471,6 +481,7 @@
opt.auth_mode = DFL_AUTH_MODE;
opt.mfl_code = DFL_MFL_CODE;
opt.trunc_hmac = DFL_TRUNC_HMAC;
+ opt.recsplit = DFL_RECSPLIT;
opt.reconnect = DFL_RECONNECT;
opt.reco_delay = DFL_RECO_DELAY;
opt.tickets = DFL_TICKETS;
@@ -743,6 +754,12 @@
if( opt.hs_to_min == 0 || opt.hs_to_max < opt.hs_to_min )
goto usage;
}
+ else if( strcmp( p, "recsplit" ) == 0 )
+ {
+ opt.recsplit = atoi( q );
+ if( opt.recsplit < 0 || opt.recsplit > 1 )
+ goto usage;
+ }
else
goto usage;
}
@@ -1058,6 +1075,13 @@
ssl_set_encrypt_then_mac( &ssl, opt.etm );
#endif
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+ if( opt.recsplit != DFL_RECSPLIT )
+ ssl_set_cbc_record_splitting( &ssl, opt.recsplit
+ ? SSL_CBC_RECORD_SPLITTING_ENABLED
+ : SSL_CBC_RECORD_SPLITTING_DISABLED );
+#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 1f9e97f..f2beaf1 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -192,14 +192,14 @@
char *sni; /* string describing sni information */
const char *alpn_string; /* ALPN supported protocols */
const char *dhm_file; /* the file with the DH parameters */
+ int extended_ms; /* allow negotiation of extended MS? */
+ int etm; /* allow negotiation of encrypt-then-MAC? */
int transport; /* TLS or DTLS? */
int cookies; /* Use cookies for DTLS? -1 to break them */
int anti_replay; /* Use anti-replay for DTLS? -1 for default */
uint32_t hs_to_min; /* Initial value of DTLS handshake timer */
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 )
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 2e6e1fd..3099adc 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -791,6 +791,62 @@
-s "received FALLBACK_SCSV" \
-S "inapropriate fallback"
+# Tests for CBC 1/n-1 record splitting
+
+run_test "CBC Record splitting: TLS 1.2, no splitting" \
+ "$P_SRV" \
+ "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+ request_size=123 force_version=tls1_2" \
+ 0 \
+ -s "Read from client: 123 bytes read" \
+ -S "Read from client: 1 bytes read" \
+ -S "122 bytes read"
+
+run_test "CBC Record splitting: TLS 1.1, no splitting" \
+ "$P_SRV" \
+ "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+ request_size=123 force_version=tls1_1" \
+ 0 \
+ -s "Read from client: 123 bytes read" \
+ -S "Read from client: 1 bytes read" \
+ -S "122 bytes read"
+
+run_test "CBC Record splitting: TLS 1.0, splitting" \
+ "$P_SRV" \
+ "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+ request_size=123 force_version=tls1" \
+ 0 \
+ -S "Read from client: 123 bytes read" \
+ -s "Read from client: 1 bytes read" \
+ -s "122 bytes read"
+
+run_test "CBC Record splitting: SSLv3, splitting" \
+ "$P_SRV" \
+ "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+ request_size=123 force_version=ssl3" \
+ 0 \
+ -S "Read from client: 123 bytes read" \
+ -s "Read from client: 1 bytes read" \
+ -s "122 bytes read"
+
+run_test "CBC Record splitting: TLS 1.0 RC4, no splitting" \
+ "$P_SRV" \
+ "$P_CLI force_ciphersuite=TLS-RSA-WITH-RC4-128-SHA \
+ request_size=123 force_version=tls1" \
+ 0 \
+ -s "Read from client: 123 bytes read" \
+ -S "Read from client: 1 bytes read" \
+ -S "122 bytes read"
+
+run_test "CBC Record splitting: TLS 1.0, splitting disabled" \
+ "$P_SRV" \
+ "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+ request_size=123 force_version=tls1 recsplit=0" \
+ 0 \
+ -s "Read from client: 123 bytes read" \
+ -S "Read from client: 1 bytes read" \
+ -S "122 bytes read"
+
# Tests for Session Tickets
run_test "Session resume using tickets: basic" \
@@ -2283,7 +2339,8 @@
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" \
+ "$P_CLI request_size=1 force_version=tls1_2 \
+ force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
0 \
-s "Read from client: 1 bytes read"
@@ -2328,7 +2385,7 @@
run_test "Large packet SSLv3 BlockCipher" \
"$P_SRV" \
- "$P_CLI request_size=16384 force_version=ssl3 \
+ "$P_CLI request_size=16384 force_version=ssl3 recsplit=0 \
force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \
0 \
-s "Read from client: 16384 bytes read"
@@ -2342,14 +2399,14 @@
run_test "Large packet TLS 1.0 BlockCipher" \
"$P_SRV" \
- "$P_CLI request_size=16384 force_version=tls1 \
+ "$P_CLI request_size=16384 force_version=tls1 recsplit=0 \
force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \
0 \
-s "Read from client: 16384 bytes read"
run_test "Large packet TLS 1.0 BlockCipher truncated MAC" \
"$P_SRV" \
- "$P_CLI request_size=16384 force_version=tls1 \
+ "$P_CLI request_size=16384 force_version=tls1 recsplit=0 \
force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA \
trunc_hmac=1" \
0 \
@@ -2402,7 +2459,8 @@
run_test "Large packet TLS 1.2 BlockCipher larger MAC" \
"$P_SRV" \
- "$P_CLI request_size=16384 force_version=tls1_2 force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
+ "$P_CLI request_size=16384 force_version=tls1_2 \
+ force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
0 \
-s "Read from client: 16384 bytes read"