]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
cipher: separated CBC w/o EtM handling
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Thu, 7 Jun 2018 07:54:50 +0000 (09:54 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Tue, 12 Jun 2018 07:31:03 +0000 (09:31 +0200)
This would allow to further modify for more invasive work-arounds.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
lib/Makefile.am
lib/cipher-cbc.c [new file with mode: 0644]
lib/cipher.c
lib/cipher.h

index 64c7110d850f82e38422c9d400e5e5b24ddc4bd0..11de0a05bf9618f3a00b0ae9ef18a46a7a392938 100644 (file)
@@ -66,7 +66,7 @@ SRP_COBJECTS = srp.c
 PSK_COBJECTS = psk.c
 
 COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls13.c \
-       mbuffers.c buffers.c handshake.c num.c errors.c dh.c kx.c       \
+       mbuffers.c buffers.c handshake.c num.c errors.c dh.c kx.c cipher-cbc.c \
        priority.c hash_int.c cipher_int.c session.c db.c x509_b64.c    \
        hello_ext.c auth.c sslv2_compat.c datum.c session_pack.c mpi.c \
        pk.c cert-cred.c global.c constate.c anon_cred.c pkix_asn1_tab.c gnutls_asn1_tab.c      \
diff --git a/lib/cipher-cbc.c b/lib/cipher-cbc.c
new file mode 100644 (file)
index 0000000..133595b
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2000-2013 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Nikos Mavrogiannopoulos
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "cipher.h"
+
+static void dummy_wait(record_parameters_st *params,
+                      const uint8_t *data, size_t data_size,
+                      unsigned int mac_data, unsigned int max_mac_data)
+{
+       /* this hack is only needed on CBC ciphers when Encrypt-then-MAC mode
+        * is not supported by the peer. */
+       unsigned v;
+       unsigned int tag_size =
+           _gnutls_auth_cipher_tag_len(&params->read.ctx.tls12);
+       unsigned hash_block = _gnutls_mac_block_size(params->mac);
+
+       /* force additional hash compression function evaluations to prevent timing
+        * attacks that distinguish between wrong-mac + correct pad, from wrong-mac + incorrect pad.
+        */
+
+       if (params->mac && params->mac->id == GNUTLS_MAC_SHA384)
+               /* v = 1 for the hash function padding + 16 for message length */
+               v = 17;
+       else /* v = 1 for the hash function padding + 8 for message length */
+               v = 9;
+
+       if (hash_block > 0) {
+               int max_blocks = (max_mac_data+v+hash_block-1)/hash_block;
+               int hashed_blocks = (mac_data+v+hash_block-1)/hash_block;
+               unsigned to_hash;
+
+               max_blocks -= hashed_blocks;
+               if (max_blocks < 1)
+                       return;
+
+               to_hash = max_blocks * hash_block;
+               if ((unsigned)to_hash+1+tag_size < data_size) {
+                       _gnutls_auth_cipher_add_auth
+                                   (&params->read.ctx.tls12,
+                                    data+data_size-tag_size-to_hash-1,
+                                    to_hash);
+               }
+       }
+}
+
+/* Verifies the CBC HMAC. That's a special case as it tries to avoid
+ * any leaks which could make CBC ciphersuites without EtM usable as an
+ * oracle to attacks.
+ */
+int cbc_mac_verify(gnutls_session_t session, record_parameters_st *params,
+                  uint8_t preamble[MAX_PREAMBLE_SIZE],
+                  content_type_t type,
+                  gnutls_uint64 *sequence,
+                  const uint8_t *data, size_t data_size,
+                  size_t tag_size)
+{
+       int ret;
+       const version_entry_st *ver = get_version(session);
+       unsigned int tmp_pad_failed = 0;
+       unsigned int pad_failed = 0;
+       unsigned int pad, i, length;
+       const uint8_t *tag_ptr = NULL;
+       unsigned preamble_size;
+       uint8_t tag[MAX_HASH_SIZE];
+
+       pad = data[data_size - 1];      /* pad */
+
+       /* 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).
+        */
+#ifdef ENABLE_SSL3
+       if (ver->id != GNUTLS_SSL3)
+#endif
+               for (i = 2; i <= MIN(256, data_size); i++) {
+                       tmp_pad_failed |=
+                           (data[data_size - i] != pad);
+                       pad_failed |=
+                           ((i <= (1 + pad)) & (tmp_pad_failed));
+               }
+
+       if (unlikely
+           (pad_failed != 0
+            || (1 + pad > ((int) data_size - tag_size)))) {
+               /* We do not fail here. We check below for the
+                * the pad_failed. If zero means success.
+                */
+               pad_failed = 1;
+               pad = 0;
+       }
+
+       length = data_size - tag_size - pad - 1;
+       tag_ptr = &data[length];
+
+       /* Pass the type, version, length and plain through
+        * MAC.
+        */
+       preamble_size =
+           _gnutls_make_preamble(UINT64DATA(*sequence), type,
+                                 length, ver, preamble);
+
+       ret =
+           _gnutls_auth_cipher_add_auth(&params->read.
+                                        ctx.tls12, preamble,
+                                        preamble_size);
+       if (unlikely(ret < 0))
+               return gnutls_assert_val(ret);
+
+       ret =
+           _gnutls_auth_cipher_add_auth(&params->read.
+                                        ctx.tls12,
+                                        data, length);
+       if (unlikely(ret < 0))
+               return gnutls_assert_val(ret);
+
+       ret =
+           _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag,
+                                   tag_size);
+       if (unlikely(ret < 0))
+               return gnutls_assert_val(ret);
+
+       if (unlikely
+           (gnutls_memcmp(tag, tag_ptr, tag_size) != 0 || pad_failed != 0)) {
+               /* HMAC was not the same. */
+               dummy_wait(params, data, data_size,
+                          length + preamble_size,
+                          preamble_size + data_size - tag_size - 1);
+
+               return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+       }
+
+       return length;
+}
index af5ee2a271ef655eae6e3f9f0e4d769dbe1b6b98..c8bdeb542807cd419c801c21b42b0b2a19461751 100644 (file)
@@ -209,14 +209,12 @@ calc_enc_length_stream(gnutls_session_t session, int data_size,
        return length;
 }
 
-#define MAX_PREAMBLE_SIZE 16
-
 /* generates the authentication data (data to be hashed only
  * and are not to be sent). Returns their size.
  */
-static inline int
-make_preamble(uint8_t * uint64_data, uint8_t type, unsigned int length,
-             const version_entry_st * ver, uint8_t * preamble)
+int
+_gnutls_make_preamble(uint8_t * uint64_data, uint8_t type, unsigned int length,
+                     const version_entry_st * ver, uint8_t preamble[MAX_PREAMBLE_SIZE])
 {
        uint8_t *p = preamble;
        uint16_t c_length;
@@ -372,8 +370,8 @@ encrypt_packet(gnutls_session_t session,
                ret = plain->size;
 
        preamble_size =
-           make_preamble(UINT64DATA(params->write.sequence_number),
-                         type, ret, ver, preamble);
+           _gnutls_make_preamble(UINT64DATA(params->write.sequence_number),
+                                 type, ret, ver, preamble);
 
        if (algo_type == CIPHER_BLOCK || algo_type == CIPHER_STREAM) {
                /* add the authenticated data */
@@ -495,48 +493,6 @@ encrypt_packet_tls13(gnutls_session_t session,
        return cipher_size;
 }
 
-static void dummy_wait(record_parameters_st *params,
-                      gnutls_datum_t *plaintext,
-                      unsigned int mac_data, unsigned int max_mac_data)
-{
-       /* this hack is only needed on CBC ciphers when Encrypt-then-MAC mode
-        * is not supported by the peer. */
-       if (_gnutls_cipher_type(params->cipher) == CIPHER_BLOCK) {
-               unsigned v;
-               unsigned int tag_size =
-                   _gnutls_auth_cipher_tag_len(&params->read.ctx.tls12);
-               unsigned hash_block = _gnutls_mac_block_size(params->mac);
-
-               /* force additional hash compression function evaluations to prevent timing
-                * attacks that distinguish between wrong-mac + correct pad, from wrong-mac + incorrect pad.
-                */
-
-               if (params->mac && params->mac->id == GNUTLS_MAC_SHA384)
-                       /* v = 1 for the hash function padding + 16 for message length */
-                       v = 17;
-               else /* v = 1 for the hash function padding + 8 for message length */
-                       v = 9;
-
-               if (hash_block > 0) {
-                       int max_blocks = (max_mac_data+v+hash_block-1)/hash_block;
-                       int hashed_blocks = (mac_data+v+hash_block-1)/hash_block;
-                       unsigned to_hash;
-
-                       max_blocks -= hashed_blocks;
-                       if (max_blocks < 1)
-                               return;
-
-                       to_hash = max_blocks * hash_block;
-                       if ((unsigned)to_hash+1+tag_size < plaintext->size) {
-                               _gnutls_auth_cipher_add_auth
-                                           (&params->read.ctx.tls12,
-                                            plaintext->data+plaintext->size-tag_size-to_hash-1,
-                                            to_hash);
-                       }
-               }
-       }
-
-}
 
 /* Deciphers the ciphertext packet, and puts the result to plain.
  * Returns the actual plaintext packet size.
@@ -551,12 +507,10 @@ decrypt_packet(gnutls_session_t session,
        uint8_t tag[MAX_HASH_SIZE];
        uint8_t nonce[MAX_CIPHER_IV_SIZE];
        const uint8_t *tag_ptr = NULL;
-       unsigned int pad = 0, i;
+       unsigned int pad = 0;
        int length, length_to_decrypt;
        uint16_t blocksize;
        int ret;
-       unsigned int tmp_pad_failed = 0;
-       unsigned int pad_failed = 0;
        uint8_t preamble[MAX_PREAMBLE_SIZE];
        unsigned int preamble_size = 0;
        const version_entry_st *ver = get_version(session);
@@ -582,9 +536,9 @@ decrypt_packet(gnutls_session_t session,
                if (unlikely(ciphertext->size < tag_size))
                        return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
 
-               preamble_size = make_preamble(UINT64DATA(*sequence),
-                                       type, ciphertext->size-tag_size,
-                                       ver, preamble);
+               preamble_size = _gnutls_make_preamble(UINT64DATA(*sequence),
+                                                     type, ciphertext->size-tag_size,
+                                                     ver, preamble);
 
                ret = _gnutls_auth_cipher_add_auth(&params->read.
                                                   ctx.tls12, preamble,
@@ -658,8 +612,8 @@ decrypt_packet(gnutls_session_t session,
                 * MAC.
                 */
                preamble_size =
-                   make_preamble(UINT64DATA(*sequence), type,
-                                 length, ver, preamble);
+                   _gnutls_make_preamble(UINT64DATA(*sequence), type,
+                                         length, ver, preamble);
 
 
                if (unlikely
@@ -698,8 +652,8 @@ decrypt_packet(gnutls_session_t session,
                 * MAC.
                 */
                preamble_size =
-                   make_preamble(UINT64DATA(*sequence), type,
-                                 length, ver, preamble);
+                   _gnutls_make_preamble(UINT64DATA(*sequence), type,
+                                         length, ver, preamble);
 
                ret =
                    _gnutls_auth_cipher_add_auth(&params->read.
@@ -729,6 +683,17 @@ decrypt_packet(gnutls_session_t session,
                if (unlikely(ret < 0))
                        return gnutls_assert_val(ret);
 
+               ret =
+                   _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag,
+                                           tag_size);
+               if (unlikely(ret < 0))
+                       return gnutls_assert_val(ret);
+
+               if (unlikely
+                   (gnutls_memcmp(tag, tag_ptr, tag_size) != 0)) {
+                       return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+               }
+
                break;
        case CIPHER_BLOCK:
                if (unlikely(ciphertext->size < blocksize))
@@ -780,56 +745,13 @@ decrypt_packet(gnutls_session_t session,
                        if (unlikely(ret < 0))
                                return gnutls_assert_val(ret);
 
-                       pad = plain->data[ciphertext->size - 1];        /* pad */
-
-                       /* 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).
-                        */
-#ifdef ENABLE_SSL3
-                       if (ver->id != GNUTLS_SSL3)
-#endif
-                               for (i = 2; i <= MIN(256, ciphertext->size); i++) {
-                                       tmp_pad_failed |=
-                                           (plain->
-                                            data[ciphertext->size - i] != pad);
-                                       pad_failed |=
-                                           ((i <= (1 + pad)) & (tmp_pad_failed));
-                               }
-
-                       if (unlikely
-                           (pad_failed != 0
-                            || (1 + pad > ((int) ciphertext->size - tag_size)))) {
-                               /* We do not fail here. We check below for the
-                                * the pad_failed. If zero means success.
-                                */
-                               pad_failed = 1;
-                               pad = 0;
-                       }
-
-                       length = ciphertext->size - tag_size - pad - 1;
-                       tag_ptr = &plain->data[length];
-
-                       /* Pass the type, version, length and plain through
-                        * MAC.
-                        */
-                       preamble_size =
-                           make_preamble(UINT64DATA(*sequence), type,
-                                         length, ver, preamble);
-
-                       ret =
-                           _gnutls_auth_cipher_add_auth(&params->read.
-                                                        ctx.tls12, preamble,
-                                                        preamble_size);
+                       ret = cbc_mac_verify(session, params, preamble, type,
+                                            sequence, plain->data, ciphertext->size,
+                                            tag_size);
                        if (unlikely(ret < 0))
                                return gnutls_assert_val(ret);
 
-                       ret =
-                           _gnutls_auth_cipher_add_auth(&params->read.
-                                                        ctx.tls12,
-                                                        plain->data, length);
-                       if (unlikely(ret < 0))
-                               return gnutls_assert_val(ret);
+                       length = ret;
                } else { /* EtM */
                        ret =
                            _gnutls_cipher_decrypt2(&params->read.ctx.tls12.
@@ -851,24 +773,6 @@ decrypt_packet(gnutls_session_t session,
                return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
        }
 
-       /* STREAM or BLOCK arrive here */
-       if (etm == 0) {
-               ret =
-                   _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag,
-                                           tag_size);
-               if (unlikely(ret < 0))
-                       return gnutls_assert_val(ret);
-
-               if (unlikely
-                   (gnutls_memcmp(tag, tag_ptr, tag_size) != 0 || pad_failed != 0)) {
-                       /* HMAC was not the same. */
-                       dummy_wait(params, plain,
-                                  length + preamble_size,
-                                  preamble_size + ciphertext->size - tag_size - 1);
-
-                       return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
-               }
-       }
 
        return length;
 }
index 0d7c74adaf8f12e9ac28d3c6cf87c4780f4f0289..3cebaa1cbb83dff24d1e8cfa403849b5aa992ee7 100644 (file)
@@ -30,3 +30,16 @@ int _gnutls_decrypt(gnutls_session_t session,
                    gnutls_datum_t * ciphertext, gnutls_datum_t * output,
                    content_type_t *type, record_parameters_st * params,
                    gnutls_uint64 * sequence);
+
+#define MAX_PREAMBLE_SIZE 16
+
+int
+_gnutls_make_preamble(uint8_t * uint64_data, uint8_t type, unsigned int length,
+                     const version_entry_st * ver, uint8_t preamble[MAX_PREAMBLE_SIZE]);
+
+int cbc_mac_verify(gnutls_session_t session, record_parameters_st *params,
+                  uint8_t preamble[MAX_PREAMBLE_SIZE],
+                  content_type_t type,
+                  gnutls_uint64 *sequence,
+                  const uint8_t *data, size_t data_size,
+                  size_t tag_size);