diff options
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.diff | 381 |
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); |