Description: fix "Lucky Thirteen" timing side-channel TLS attack Origin: backport, https://gitorious.org/gnutls/gnutls/commit/7b65049a81ea02a92fef934318a680afd55e98d2 Origin: backport, https://gitorious.org/gnutls/gnutls/commit/458c67cf98740e7b12404f6c30e0d5317d56fd30 Origin: backport, https://gitorious.org/gnutls/gnutls/commit/93b7fcfa3297a9123630704668b2946f602b910e Index: gnutls26-2.12.14/lib/gnutls_cipher.c =================================================================== --- gnutls26-2.12.14.orig/lib/gnutls_cipher.c 2013-02-25 11:31:09.000000000 -0500 +++ gnutls26-2.12.14/lib/gnutls_cipher.c 2013-02-25 11:31:36.094933637 -0500 @@ -448,6 +448,48 @@ return length; } +static void dummy_wait(record_parameters_st * params, 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 (params->cipher_algorithm) == CIPHER_BLOCK) + { + uint8_t MAC[MAX_HASH_SIZE]; + unsigned len; + digest_hd_st td; + int ret; + + ret = mac_init (&td, params->mac_algorithm, + params->read.mac_secret.data, + params->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(params->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) + mac_hash (&td, plaintext->data, len, ver); + else + mac_hash (&td, plaintext->data, plaintext->size, ver); + } + } + } + + 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. @@ -461,12 +503,12 @@ { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; - uint8_t pad; + unsigned int pad = 0; int length; uint16_t blocksize; int ret, i, pad_failed = 0; opaque preamble[PREAMBLE_SIZE]; - int preamble_size; + int preamble_size = 0; int ver = gnutls_protocol_get_version (session); int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm); @@ -518,31 +560,23 @@ 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; + pad = ciphertext.data[ciphertext.size - 1]; /* pad */ - /* Check the pading bytes (TLS 1.x) + /* 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 (_gnutls_version_has_variable_padding (ver) && pad_failed == 0) - for (i = 2; i < pad; i++) + 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 (); @@ -581,24 +615,19 @@ mac_deinit (&td, MAC, ver); } - /* This one was introduced to avoid a timing attack against the TLS - * 1.0 protocol. - */ - if (pad_failed != 0) - { - gnutls_assert (); - 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(params, &compressed, pad_failed, pad, length+preamble_size, ver); + gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } - /* copy the decrypted stuff to compress_data. + /* copy the decrypted stuff to compressed_data. */ if (compress_size < length) { Index: gnutls26-2.12.14/lib/gnutls_hash_int.h =================================================================== --- gnutls26-2.12.14.orig/lib/gnutls_hash_int.h 2011-09-17 18:16:53.000000000 -0400 +++ gnutls26-2.12.14/lib/gnutls_hash_int.h 2013-02-25 11:31:33.302933565 -0500 @@ -97,4 +97,25 @@ 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 */