summaryrefslogtreecommitdiff
path: root/patches/source/gnutls/gnutls-2.8.6_CVE-2013-1619_CVE-2013-2116.diff
diff options
context:
space:
mode:
Diffstat (limited to 'patches/source/gnutls/gnutls-2.8.6_CVE-2013-1619_CVE-2013-2116.diff')
-rw-r--r--patches/source/gnutls/gnutls-2.8.6_CVE-2013-1619_CVE-2013-2116.diff381
1 files changed, 381 insertions, 0 deletions
diff --git a/patches/source/gnutls/gnutls-2.8.6_CVE-2013-1619_CVE-2013-2116.diff b/patches/source/gnutls/gnutls-2.8.6_CVE-2013-1619_CVE-2013-2116.diff
new file mode 100644
index 00000000..b5541e11
--- /dev/null
+++ b/patches/source/gnutls/gnutls-2.8.6_CVE-2013-1619_CVE-2013-2116.diff
@@ -0,0 +1,381 @@
+From 24b8c5e02c81e73c2c04022de72a1d354e333e6d Mon Sep 17 00:00:00 2001
+From: mancha <mancha1@hush.com>
+Date: Sun, 29 Sep 2013
+Subject: CVE-2013-1619 and CVE-2013-2116 [GNUTLS-SA-2013-1,GNUTLS-SA-2013-2]
+
+Fix to avoid a timing attack in TLS CBC record parsing (aka Lucky 13).
+
+For background, see http://www.isg.rhul.ac.uk/tls/Lucky13.html
+
+The fix for CVE-2013-2116 is folded into this patch since it addresses
+a problem introduced by the fix for CVE-2013-1619.
+
+This is a backport adaptation for use with GnuTLS 2.8.6.
+
+Relevant upstream commits:
+--------------------------
+https://gitorious.org/gnutls/gnutls/commit/458c67cf98740e
+https://gitorious.org/gnutls/gnutls/commit/93b7fcfa3297a9
+https://gitorious.org/gnutls/gnutls/commit/32a7367601a3fd
+https://gitorious.org/gnutls/gnutls/commit/63a331df6aa0ec
+
+---
+ gnutls_algorithms.c | 14 +++++
+ gnutls_algorithms.h | 3 +
+ gnutls_cipher.c | 132 ++++++++++++++++++++++++++++++++--------------------
+ gnutls_hash_int.h | 21 ++++++++
+ 4 files changed, 121 insertions(+), 49 deletions(-)
+
+--- a/lib/gnutls_cipher.c 2013-09-27
++++ b/lib/gnutls_cipher.c 2013-09-27
+@@ -287,6 +287,26 @@ calc_enc_length (gnutls_session_t sessio
+ return length;
+ }
+
++#define PREAMBLE_SIZE 16
++static inline int make_preamble(opaque* uint64_data, opaque type, uint16_t c_length, opaque ver, opaque* preamble)
++{
++ opaque minor = _gnutls_version_get_minor (ver);
++ opaque major = _gnutls_version_get_major (ver);
++ opaque *p = preamble;
++
++ memcpy(p, uint64_data, 8);
++ p+=8;
++ *p=type; p++;
++ if (_gnutls_version_has_variable_padding (ver))
++ { /* TLS 1.0 or higher */
++ *p = major; p++;
++ *p = minor; p++;
++ }
++ memcpy(p, &c_length, 2);
++ p+=2;
++ return p-preamble;
++}
++
+ /* This is the actual encryption
+ * Encrypts the given compressed datum, and puts the result to cipher_data,
+ * which has cipher_size size.
+@@ -304,11 +324,11 @@ _gnutls_compressed2ciphertext (gnutls_se
+ int length, ret;
+ digest_hd_st td;
+ uint8_t type = _type;
+- uint8_t major, minor;
++ opaque preamble[PREAMBLE_SIZE];
++ int preamble_size;
+ int hash_size =
+ _gnutls_hash_get_algo_len (session->security_parameters.
+ write_mac_algorithm);
+- gnutls_protocol_t ver;
+ int blocksize =
+ _gnutls_cipher_get_block_size (session->security_parameters.
+ write_bulk_cipher_algorithm);
+@@ -316,40 +336,27 @@ _gnutls_compressed2ciphertext (gnutls_se
+ _gnutls_cipher_is_block (session->security_parameters.
+ write_bulk_cipher_algorithm);
+ opaque *data_ptr;
+-
+-
+- ver = gnutls_protocol_get_version (session);
+- minor = _gnutls_version_get_minor (ver);
+- major = _gnutls_version_get_major (ver);
+-
++ int ver = gnutls_protocol_get_version (session);
+
+ /* Initialize MAC */
+- ret = mac_init (&td, session->security_parameters.write_mac_algorithm,
+- session->connection_state.write_mac_secret.data,
+- session->connection_state.write_mac_secret.size, ver);
+-
+- if (ret < 0
+- && session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL)
+- {
+- gnutls_assert ();
+- return ret;
+- }
+
+ c_length = _gnutls_conv_uint16 (compressed.size);
+
+ if (session->security_parameters.write_mac_algorithm != GNUTLS_MAC_NULL)
+ { /* actually when the algorithm in not the NULL one */
+- _gnutls_hmac (&td,
+- UINT64DATA (session->connection_state.
+- write_sequence_number), 8);
+-
+- _gnutls_hmac (&td, &type, 1);
+- if (ver >= GNUTLS_TLS1)
+- { /* TLS 1.0 or higher */
+- _gnutls_hmac (&td, &major, 1);
+- _gnutls_hmac (&td, &minor, 1);
+- }
+- _gnutls_hmac (&td, &c_length, 2);
++ digest_hd_st td;
++
++ ret = mac_init (&td, session->security_parameters.write_mac_algorithm,
++ session->connection_state.write_mac_secret.data,
++ session->connection_state.write_mac_secret.size, ver);
++
++ if (ret < 0)
++ {
++ gnutls_assert ();
++ return ret;
++ }
++ preamble_size = make_preamble( UINT64DATA (session->connection_state.write_sequence_number), type, c_length, ver, preamble);
++ _gnutls_hmac (&td, preamble, preamble_size);
+ _gnutls_hmac (&td, compressed.data, compressed.size);
+ mac_deinit (&td, MAC, ver);
+ }
+@@ -418,6 +425,49 @@ _gnutls_compressed2ciphertext (gnutls_se
+ return length;
+ }
+
++static void dummy_wait(gnutls_session_t session, gnutls_datum_t* plaintext,
++ unsigned pad_failed, unsigned int pad, unsigned total, int ver)
++{
++ /* this hack is only needed on CBC ciphers */
++ if (_gnutls_cipher_is_block (session->security_parameters.read_bulk_cipher_algorithm) == CIPHER_BLOCK)
++ {
++ uint8_t MAC[MAX_HASH_SIZE];
++ unsigned len;
++ digest_hd_st td;
++ int ret;
++
++ ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
++ session->connection_state.read_mac_secret.data,
++ session->connection_state.read_mac_secret.size, ver);
++
++ if (ret < 0)
++ return;
++
++ /* force an additional hash compression function evaluation to prevent timing
++ * attacks that distinguish between wrong-mac + correct pad, from wrong-mac + incorrect pad.
++ */
++ if (pad_failed == 0 && pad > 0)
++ {
++ len = _gnutls_get_hash_block_len(session->security_parameters.read_mac_algorithm);
++ if (len > 0)
++ {
++ /* This is really specific to the current hash functions.
++ * It should be removed once a protocol fix is in place.
++ */
++ if ((pad+total) % len > len-9 && total % len <= len-9)
++ {
++ if (len < plaintext->size)
++ _gnutls_hmac (&td, plaintext->data, len);
++ else
++ _gnutls_hmac (&td, plaintext->data, plaintext->size);
++ }
++ }
++ }
++
++ mac_deinit (&td, MAC, ver);
++ }
++}
++
+ /* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
+ * Returns the actual compressed packet size.
+ */
+@@ -429,38 +479,22 @@ _gnutls_ciphertext2compressed (gnutls_se
+ {
+ uint8_t MAC[MAX_HASH_SIZE];
+ uint16_t c_length;
+- uint8_t pad;
++ unsigned int pad = 0;
+ int length;
+ digest_hd_st td;
+ uint16_t blocksize;
+ int ret, i, pad_failed = 0;
+- uint8_t major, minor;
+- gnutls_protocol_t ver;
++ opaque preamble[PREAMBLE_SIZE];
++ int preamble_size = 0;
++ int ver = gnutls_protocol_get_version (session);
+ int hash_size =
+ _gnutls_hash_get_algo_len (session->security_parameters.
+ read_mac_algorithm);
+
+- ver = gnutls_protocol_get_version (session);
+- minor = _gnutls_version_get_minor (ver);
+- major = _gnutls_version_get_major (ver);
+-
+ blocksize =
+ _gnutls_cipher_get_block_size (session->security_parameters.
+ read_bulk_cipher_algorithm);
+
+- /* initialize MAC
+- */
+- ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
+- session->connection_state.read_mac_secret.data,
+- session->connection_state.read_mac_secret.size, ver);
+-
+- if (ret < 0
+- && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
+- {
+- gnutls_assert ();
+- return GNUTLS_E_INTERNAL_ERROR;
+- }
+-
+ /* actual decryption (inplace)
+ */
+ switch (_gnutls_cipher_is_block
+@@ -508,31 +542,25 @@ _gnutls_ciphertext2compressed (gnutls_se
+ gnutls_assert ();
+ return GNUTLS_E_DECRYPTION_FAILED;
+ }
+- pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */
+-
+- if ((int) pad > (int) ciphertext.size - hash_size)
+- {
+- gnutls_assert ();
+- _gnutls_record_log
+- ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
+- session, pad, ciphertext.size, hash_size);
+- /* We do not fail here. We check below for the
+- * the pad_failed. If zero means success.
+- */
+- pad_failed = GNUTLS_E_DECRYPTION_FAILED;
+- }
+-
+- length = ciphertext.size - hash_size - pad;
+-
+- /* Check the pading bytes (TLS 1.x)
++ pad = ciphertext.data[ciphertext.size - 1]; /* pad */
++ if (pad+1 > ciphertext.size-hash_size)
++ pad_failed = GNUTLS_E_DECRYPTION_FAILED;
++
++ /* Check the pading bytes (TLS 1.x).
++ * Note that we access all 256 bytes of ciphertext for padding check
++ * because there is a timing channel in that memory access (in certain CPUs
+ */
+ if (ver >= GNUTLS_TLS1 && pad_failed == 0)
+ for (i = 2; i < pad; i++)
+ {
+- if (ciphertext.data[ciphertext.size - i] !=
+- ciphertext.data[ciphertext.size - 1])
++ if (ciphertext.data[ciphertext.size - i] != pad)
+ pad_failed = GNUTLS_E_DECRYPTION_FAILED;
+ }
++
++ if (pad_failed)
++ pad = 0;
++ length = ciphertext.size - hash_size - pad - 1;
++
+ break;
+ default:
+ gnutls_assert ();
+@@ -548,17 +576,20 @@ _gnutls_ciphertext2compressed (gnutls_se
+ */
+ if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
+ {
+- _gnutls_hmac (&td,
+- UINT64DATA (session->connection_state.
+- read_sequence_number), 8);
+-
+- _gnutls_hmac (&td, &type, 1);
+- if (ver >= GNUTLS_TLS1)
+- { /* TLS 1.x */
+- _gnutls_hmac (&td, &major, 1);
+- _gnutls_hmac (&td, &minor, 1);
+- }
+- _gnutls_hmac (&td, &c_length, 2);
++ digest_hd_st td;
++
++ ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
++ session->connection_state.read_mac_secret.data,
++ session->connection_state.read_mac_secret.size, ver);
++
++ if (ret < 0)
++ {
++ gnutls_assert ();
++ return GNUTLS_E_INTERNAL_ERROR;
++ }
++
++ preamble_size = make_preamble( UINT64DATA (session->connection_state.read_sequence_number), type, c_length, ver, preamble);
++ _gnutls_hmac (&td, preamble, preamble_size);
+
+ if (length > 0)
+ _gnutls_hmac (&td, ciphertext.data, length);
+@@ -566,16 +597,14 @@ _gnutls_ciphertext2compressed (gnutls_se
+ mac_deinit (&td, MAC, ver);
+ }
+
+- /* This one was introduced to avoid a timing attack against the TLS
+- * 1.0 protocol.
+- */
+- if (pad_failed != 0)
+- return pad_failed;
+-
+ /* HMAC was not the same.
+ */
+- if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
++ if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0 || pad_failed != 0)
+ {
++ gnutls_datum_t compressed = {compress_data, compress_size};
++ /* HMAC was not the same. */
++ dummy_wait(session, &compressed, pad_failed, pad, length+preamble_size, ver);
++
+ gnutls_assert ();
+ return GNUTLS_E_DECRYPTION_FAILED;
+ }
+--- a/lib/gnutls_hash_int.h 2013-09-27
++++ b/lib/gnutls_hash_int.h 2013-09-27
+@@ -92,4 +92,25 @@ void _gnutls_mac_deinit_ssl3_handshake (
+
+ int _gnutls_hash_copy (digest_hd_st* dst_handle, digest_hd_st * src_handle);
+
++/* We shouldn't need to know that, but a work-around in decoding
++ * TLS record padding requires that.
++ */
++inline static size_t
++_gnutls_get_hash_block_len (gnutls_digest_algorithm_t algo)
++{
++ switch (algo)
++ {
++ case GNUTLS_DIG_MD5:
++ case GNUTLS_DIG_SHA1:
++ case GNUTLS_DIG_RMD160:
++ case GNUTLS_DIG_SHA256:
++ case GNUTLS_DIG_SHA384:
++ case GNUTLS_DIG_SHA512:
++ case GNUTLS_DIG_SHA224:
++ return 64;
++ default:
++ return 0;
++ }
++}
++
+ #endif /* GNUTLS_HASH_INT_H */
+--- a/lib/gnutls_algorithms.c 2013-09-27
++++ b/lib/gnutls_algorithms.c 2013-09-27
+@@ -1185,6 +1185,20 @@ _gnutls_version_is_supported (gnutls_ses
+ return 1;
+ }
+
++/* This function determines if the version specified can have
++ non-minimal padding. */
++int _gnutls_version_has_variable_padding (gnutls_protocol_t version)
++{
++ switch(version) {
++ case GNUTLS_TLS1_0:
++ case GNUTLS_TLS1_1:
++ case GNUTLS_TLS1_2:
++ return 1;
++ default:
++ return 0;
++ }
++}
++
+ /* Type to KX mappings */
+ gnutls_kx_algorithm_t
+ _gnutls_map_kx_get_kx (gnutls_credentials_type_t type, int server)
+--- a/lib/gnutls_algorithms.h 2013-09-27
++++ b/lib/gnutls_algorithms.h 2013-09-27
+@@ -41,6 +41,9 @@ int _gnutls_version_get_major (gnutls_pr
+ int _gnutls_version_get_minor (gnutls_protocol_t ver);
+ gnutls_protocol_t _gnutls_version_get (int major, int minor);
+
++/* Functions for feature checks */
++int _gnutls_version_has_variable_padding (gnutls_protocol_t version);
++
+ /* Functions for MACs. */
+ int _gnutls_mac_is_ok (gnutls_mac_algorithm_t algorithm);
+ gnutls_mac_algorithm_t _gnutls_x509_oid2mac_algorithm (const char *oid);