components/gnutls/patches/04-cve-2013-1619.patch
changeset 4062 f45bb9cec48c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/gnutls/patches/04-cve-2013-1619.patch	Fri Mar 20 22:56:27 2015 -0700
@@ -0,0 +1,198 @@
+Source:
+http://www.gnutls.org/security.html
+Info:
+http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-1619
+The TLS implementation in GnuTLS before 2.12.23, 3.0.x before 3.0.28, and 3.1.x 
+before 3.1.7 does not properly consider timing side-channel attacks on a 
+noncompliant MAC check operation during the processing of malformed CBC 
+padding, which allows remote attackers to conduct distinguishing attacks and 
+plaintext-recovery attacks via statistical analysis of timing data for crafted 
+packets, a related issue to CVE-2013-0169.
+Status:
+Need to determine if this patch has been sent upstream.
+
+--- gnutls-2.8.6/lib/gnutls_cipher.c.orig	2013-05-21 14:38:08.865598248 +0530
++++ gnutls-2.8.6/lib/gnutls_cipher.c	2013-05-21 15:51:24.878786918 +0530
+@@ -418,6 +418,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,11 +472,12 @@ _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;
++  int preamble_size = 0;
+   uint8_t major, minor;
+   gnutls_protocol_t ver;
+   int hash_size =
+@@ -509,31 +553,23 @@ _gnutls_ciphertext2compressed (gnutls_se
+ 	  return GNUTLS_E_DECRYPTION_FAILED;
+ 	}
+ 
+-      pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */
++      pad = ciphertext.data[ciphertext.size - 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)
++      /* 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++)
++	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 ();
+@@ -552,14 +588,19 @@ _gnutls_ciphertext2compressed (gnutls_se
+       _gnutls_hmac (&td,
+ 		    UINT64DATA (session->connection_state.
+ 				read_sequence_number), 8);
++      preamble_size += 8;
+ 
+       _gnutls_hmac (&td, &type, 1);
++      preamble_size++;
+       if (ver >= GNUTLS_TLS1)
+ 	{			/* TLS 1.x */
+ 	  _gnutls_hmac (&td, &major, 1);
++	  preamble_size++;
+ 	  _gnutls_hmac (&td, &minor, 1);
++	  preamble_size++;
+ 	}
+       _gnutls_hmac (&td, &c_length, 2);
++      preamble_size += 2;
+ 
+       if (length > 0)
+ 	_gnutls_hmac (&td, ciphertext.data, length);
+@@ -567,21 +608,19 @@ _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;
+     }
+ 
+-  /* copy the decrypted stuff to compress_data.
++  /* copy the decrypted stuff to compressed_data.
+    */
+   if (compress_size < length)
+     {
+--- gnutls-2.8.6/lib/gnutls_hash_int.h.orig	2013-05-21 15:51:50.195114457 +0530
++++ gnutls-2.8.6/lib/gnutls_hash_int.h	2013-05-21 15:53:44.212046617 +0530
+@@ -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 */