File CVE-2013-1619.patch of Package gnutls.openSUSE_12.1_Update

Index: gnutls-3.0.3/lib/gnutls_cipher.c
===================================================================
--- gnutls-3.0.3.orig/lib/gnutls_cipher.c
+++ gnutls-3.0.3/lib/gnutls_cipher.c
@@ -422,6 +422,36 @@ compressed_to_ciphertext (gnutls_session
   return length;
 }
 
+static void dummy_wait(record_parameters_st * params, gnutls_datum_t* plaintext, 
+                       unsigned pad_failed, unsigned int pad, unsigned total)
+{
+  /* this hack is only needed on CBC ciphers */
+  if (_gnutls_cipher_is_block (params->cipher_algorithm) == CIPHER_BLOCK)
+    {
+      unsigned len;
+
+      /* 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)
+                    _gnutls_auth_cipher_add_auth (&params->read.cipher_state, plaintext->data, len);
+                  else
+                    _gnutls_auth_cipher_add_auth (&params->read.cipher_state, plaintext->data, plaintext->size);
+                }
+            }
+        }
+    }
+}
 
 /* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
  * Returns the actual compressed packet size.
@@ -435,10 +465,12 @@ ciphertext_to_compressed (gnutls_session
                           uint64* sequence)
 {
   uint8_t tag[MAX_HASH_SIZE];
-  uint8_t pad;
+  unsigned int pad = 0, i;
   int length, length_to_decrypt;
   uint16_t blocksize;
-  int ret, i, pad_failed = 0;
+  int ret;
+  unsigned int tmp_pad_failed = 0;
+  unsigned int pad_failed = 0;
   opaque preamble[MAX_PREAMBLE_SIZE];
   int preamble_size;
   int ver = gnutls_protocol_get_version (session);
@@ -516,7 +548,7 @@ ciphertext_to_compressed (gnutls_session
           ciphertext->data += blocksize;
         }
 
-      if (ciphertext->size < tag_size)
+      if (ciphertext->size < tag_size+1)
 	      return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
 
       /* we don't use the auth_cipher interface here, since
@@ -529,40 +561,30 @@ ciphertext_to_compressed (gnutls_session
              ciphertext->data, ciphertext->size)) < 0)
         return gnutls_assert_val(ret);
 
-      pad = ciphertext->data[ciphertext->size - 1] + 1;   /* pad */
-      
-      if ((int) pad > (int) ciphertext->size - tag_size)
-        {
-          gnutls_assert ();
-          _gnutls_record_log
-            ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
-             session, pad, ciphertext->size, tag_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 - tag_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 (ver != GNUTLS_SSL3 && pad_failed == 0)
-        for (i = 2; i < pad; i++)
+        for (i = 2; i <= MIN(256, ciphertext->size); i++)
           {
-            if (ciphertext->data[ciphertext->size - i] !=
-                ciphertext->data[ciphertext->size - 1])
-              pad_failed = GNUTLS_E_DECRYPTION_FAILED;
+            tmp_pad_failed |= (ciphertext->data[ciphertext->size - i] != pad);
+            pad_failed |= ((i<= (1+pad)) & (tmp_pad_failed));
           }
 
-      if (length < 0)
+      if (pad_failed != 0 || (1+pad > ((int) ciphertext->size - tag_size)))
       {
-	      /* Setting a proper length to prevent timing differences in
-	       * processing of records with invalid encryption.
+          /* We do not fail here. We check below for the
+           * the pad_failed. If zero means success.
 	       */
-	      length = ciphertext->size - tag_size;
+          pad_failed = 1;
+          pad = 0;
       }
 
+      length = ciphertext->size - tag_size - pad - 1;
+
       /* Pass the type, version, length and compressed through
        * MAC.
        */
@@ -581,16 +603,17 @@ ciphertext_to_compressed (gnutls_session
   if (ret < 0)
     return gnutls_assert_val(ret);
 
-  /* This one was introduced to avoid a timing attack against the TLS
-   * 1.0 protocol.
-   */
   if (pad_failed != 0)
     return gnutls_assert_val(pad_failed);
 
-  /* HMAC was not the same. 
-   */
   if (memcmp (tag, &ciphertext->data[length], tag_size) != 0)
-    return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+    {
+      gnutls_datum_t compressed = {compress_data, compress_size};
+      /* HMAC was not the same. */
+      dummy_wait(params, &compressed, pad_failed, pad, length+preamble_size);
+
+      return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+    }
 
   /* copy the decrypted stuff to compress_data.
    */
@@ -602,3 +625,4 @@ ciphertext_to_compressed (gnutls_session
 
   return length;
 }
+
Index: gnutls-3.0.3/lib/gnutls_hash_int.h
===================================================================
--- gnutls-3.0.3.orig/lib/gnutls_hash_int.h
+++ gnutls-3.0.3/lib/gnutls_hash_int.h
@@ -179,4 +179,25 @@ inline static int IS_SHA(gnutls_digest_a
   return 0;
 }
 
+/* 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 */
openSUSE Build Service is sponsored by