Control: tags -1 +patch

Hi Thijs,

Thijs Kinkhorst <th...@debian.org> writes:
> Nadhem Alfardan and Kenny Paterson have discovered a weakness in the handling
> of CBC ciphersuites in SSL, TLS and DTLS. Their attack exploits timing
> differences arising during MAC processing. Details of this attack can be
> found at: http://www.isg.rhul.ac.uk/tls/
>
> The issue has been fixed in upstream yaSSL 2.5.0:
> http://www.yassl.com/yaSSL/Docs-cyassl-changelog.html
Currently, MySQL uses yaSSL 2.2.2. yaSSL has released version 2.2.2d
which addresses this problem.

I downloaded yassl-2.2.2.zip from
http://fossies.org/unix/privat/yassl-2.2.2.zip and yassl-2.2.2d.zip from
http://yassl.com/yaSSL/download

I then created a git repo in 2.2.2 and copied over the files from
2.2.2d. The following files differ:

$ git status | grep 'modified' | grep -v '\.in$' | grep -v 
'\(INSTALL\|README\|aclocal.m4\|config.guess\|config.sub\|configure\|depcomp\|install-sh\|ltmain.sh\|missing\|mkinstalldirs\)'
#       modified:   include/openssl/ssl.h
#       modified:   include/yassl_error.hpp
#       modified:   include/yassl_types.hpp
#       modified:   src/handshake.cpp
#       modified:   src/yassl_error.cpp
#       modified:   src/yassl_imp.cpp
#       modified:   taocrypt/include/asn.hpp
#       modified:   taocrypt/include/sha.hpp
#       modified:   taocrypt/src/asn.cpp

I then created a patch and modified it so that it (somewhat) applies to
the MySQL source:

git diff include/openssl/ssl.h include/yassl_error.hpp include/yassl_types.hpp 
src/handshake.cpp src/yassl_error.cpp src/yassl_imp.cpp 
taocrypt/include/asn.hpp taocrypt/include/sha.hpp taocrypt/src/asn.cpp > 
yassl.patch
sed -i 's,\([iw]\)/,\1/extra/yassl/,g' yassl.patch
dos2unix yassl.patch

Then, I used quilt to get the patch in shape:

cd /tmp/mysql-5.5-5.5.30+dfsg
export QUILT_PATCHES=debian/patches
quilt import ../yassl-2.2.2/yassl.patch
quilt push -f
# apply 4 hunks of the patch manually
quilt refresh

I attached the result to this email, hopefully that helps.
Note that I didn’t compile and test MySQL.

-- 
Best regards,
Michael
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/include/openssl/ssl.h
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/include/openssl/ssl.h	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/include/openssl/ssl.h	2013-03-27 10:58:30.861636193 +0100
@@ -35,7 +35,7 @@
 #include "rsa.h"
 
 
-#define YASSL_VERSION "2.2.2"
+#define YASSL_VERSION "2.2.2d"
 
 
 #if defined(__cplusplus)
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/include/yassl_error.hpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/include/yassl_error.hpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/include/yassl_error.hpp	2013-03-27 10:58:30.861636193 +0100
@@ -53,7 +53,8 @@
     badVersion_error    = 117,
     compress_error      = 118,
     decompress_error    = 119,
-    pms_version_error   = 120
+    pms_version_error   = 120,
+    sanityCipher_error  = 121
 
     // !!!! add error message to .cpp !!!!
 
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/include/yassl_types.hpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/include/yassl_types.hpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/include/yassl_types.hpp	2013-03-27 10:58:30.861636193 +0100
@@ -220,7 +220,11 @@
 const int MAX_RECORD_SIZE   = 16384; // 2^14, max size by standard
 const int COMPRESS_EXTRA    = 1024;  // extra compression possible addition
 const int SESSION_FLUSH_COUNT = 256;  // when to flush session cache
-
+const int MAX_PAD_SIZE        = 256;  // max TLS padding size
+const int COMPRESS_CONSTANT   =  13;  // compression calculation constant
+const int COMPRESS_UPPER      =  55;  // compression calculation numerator
+const int COMPRESS_LOWER      =  64;  // compression calculation denominator
+const int COMPRESS_DUMMY_SIZE =  64;  // compression dummy round size 
 
 typedef uint8 Cipher;             // first byte is always 0x00 for SSLv3 & TLS
 
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/src/handshake.cpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/src/handshake.cpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/src/handshake.cpp	2013-03-27 11:00:12.856176496 +0100
@@ -221,12 +221,45 @@
 }
 
 
+// sanity checks on encrypted message size
+static int sanity_check_message(SSL& ssl, uint msgSz)
+{
+    uint minSz = 0;
+
+    if (ssl.getSecurity().get_parms().cipher_type_ == block) {
+        uint blockSz = ssl.getCrypto().get_cipher().get_blockSize();
+        if (msgSz % blockSz)
+            return -1;
+
+        minSz = ssl.getSecurity().get_parms().hash_size_ + 1;  // pad byte too
+        if (blockSz > minSz)
+            minSz = blockSz;
+
+        if (ssl.isTLSv1_1())
+            minSz += blockSz;   // explicit IV
+    }
+    else {      // stream
+        minSz = ssl.getSecurity().get_parms().hash_size_;
+    }
+
+    if (msgSz < minSz)
+        return -1;
+
+    return 0;
+}
+
+
 // decrypt input message in place, store size in case needed later
 void decrypt_message(SSL& ssl, input_buffer& input, uint sz)
 {
     input_buffer plain(sz);
     opaque*      cipher = input.get_buffer() + input.get_current();
 
+    if (sanity_check_message(ssl, sz) != 0) {
+        ssl.SetError(sanityCipher_error);
+        return;
+    }
+
     ssl.useCrypto().use_cipher().decrypt(plain.get_buffer(), cipher, sz);
     memcpy(cipher, plain.get_buffer(), sz);
     ssl.useSecurity().use_parms().encrypt_size_ = sz;
@@ -774,6 +807,8 @@
                     return 0;
                 }
                 decrypt_message(ssl, buffer, hdr.length_);
+		if (ssl.GetError())
+                    return 0;
             }
                 
             mySTL::auto_ptr<Message> msg(mf.CreateObject(hdr.type_));
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/src/yassl_error.cpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/src/yassl_error.cpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/src/yassl_error.cpp	2013-03-27 10:58:30.861636193 +0100
@@ -144,6 +144,10 @@
         strncpy(buffer, "bad PreMasterSecret version error", max);
         break;
 
+    case sanityCipher_error :
+        strncpy(buffer, "sanity check on cipher text size error", max);
+        break;
+
         // openssl errors
     case SSL_ERROR_WANT_READ :
         strncpy(buffer, "the read operation would block", max);
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/src/yassl_imp.cpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/src/yassl_imp.cpp	2013-03-27 10:52:16.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/src/yassl_imp.cpp	2013-03-27 11:03:29.017365789 +0100
@@ -972,29 +972,193 @@
 }
 
 
+// check all bytes for equality 
+static int constant_compare(const byte* a, const byte* b, int len)
+{
+    int good = 0;
+    int bad  = 0;
+
+    for (int i = 0; i < len; i++) {
+        if (a[i] == b[i])
+            good++;
+        else
+            bad++;
+    }
+
+    if (good == len)
+        return 0;
+    else
+        return 0 - bad;  // failure
+}
+
+
+// check bytes for pad value
+static int pad_check(const byte* input, byte pad, int len)
+{
+    int good = 0;
+    int bad  = 0;
+
+    for (int i = 0; i < len; i++) {
+        if (input[i] == pad)
+            good++;
+        else
+            bad++;
+    }
+
+    if (good == len)
+        return 0;
+    else
+        return 0 - bad;  // failure
+}
+
+
+// get number of compression rounds
+static inline int get_rounds(int pLen, int padLen, int t)
+{
+    int  roundL1 = 1;  // round ups 
+    int  roundL2 = 1;
+
+    int L1 = COMPRESS_CONSTANT + pLen - t;
+    int L2 = COMPRESS_CONSTANT + pLen - padLen - 1 - t;
+
+    L1 -= COMPRESS_UPPER;
+    L2 -= COMPRESS_UPPER;
+
+    if ( (L1 % COMPRESS_LOWER) == 0)
+        roundL1 = 0;
+    if ( (L2 % COMPRESS_LOWER) == 0)
+        roundL2 = 0;
+
+    L1 /= COMPRESS_LOWER;
+    L2 /= COMPRESS_LOWER;
+
+    L1 += roundL1;
+    L2 += roundL2;
+
+    return L1 - L2;
+}
+
+
+// do compression rounds on dummy data
+static inline void compress_rounds(SSL& ssl, int rounds, const byte* dummy)
+{
+    if (rounds) {
+        Digest* digest = NULL;
+
+        MACAlgorithm ma = ssl.getSecurity().get_parms().mac_algorithm_;
+        if (ma == sha) 
+            digest = NEW_YS SHA;
+        else if (ma == md5)
+            digest = NEW_YS MD5;
+        else if (ma == rmd)
+            digest = NEW_YS RMD;
+        else
+            return;
+
+        for (int i = 0; i < rounds; i++)
+            digest->update(dummy, COMPRESS_LOWER);
+
+        ysDelete(digest);    
+    }
+}
+
+
+// timing resistant pad verification
+static int timing_verify(SSL& ssl, const byte* input, int padLen, int t,
+                         int pLen)
+{
+    byte verify[SHA_LEN];
+    byte dummy[MAX_PAD_SIZE];
+
+    memset(dummy, 1, sizeof(dummy));
+
+    if ( (t + padLen + 1) > pLen) {
+        pad_check(dummy, (byte)padLen, MAX_PAD_SIZE);
+        if (ssl.isTLS())
+            TLS_hmac(ssl, verify, input, pLen - t, application_data, 1);
+        else
+            hmac(ssl, verify, input, pLen - t, application_data, 1);
+        constant_compare(verify, input + pLen - t, t);
+
+        return -1;
+    }
+
+    if (pad_check(input + pLen - (padLen + 1), (byte)padLen, padLen + 1) != 0) {
+        pad_check(dummy, (byte)padLen, MAX_PAD_SIZE - padLen - 1);
+        if (ssl.isTLS())
+            TLS_hmac(ssl, verify, input, pLen - t, application_data, 1);
+        else
+            hmac(ssl, verify, input, pLen - t, application_data, 1);
+        constant_compare(verify, input + pLen - t, t);
+
+        return -1;
+    }
+
+    pad_check(dummy, (byte)padLen, MAX_PAD_SIZE - padLen - 1);
+    if (ssl.isTLS())
+        TLS_hmac(ssl, verify, input, pLen - padLen - 1 - t, application_data,1);
+    else
+        hmac(ssl, verify, input, pLen - padLen - 1 - t, application_data, 1);
+
+    compress_rounds(ssl, get_rounds(pLen, padLen, t), dummy);
+
+    if (constant_compare(verify, input + (pLen - padLen - 1 - t), t) != 0)
+        return -1;
+
+    return 0;
+}
+
+
 // Process handler for Data
 void Data::Process(input_buffer& input, SSL& ssl)
 {
     int msgSz = ssl.getSecurity().get_parms().encrypt_size_;
     int pad   = 0, padSz = 0;
     int ivExtra = 0;
+    int digestSz = ssl.getCrypto().get_digest().get_digestSize();
+    const byte* rawData = input.get_buffer() + input.get_current();
+    opaque verify[SHA_LEN];
 
     if (ssl.getSecurity().get_parms().cipher_type_ == block) {
         if (ssl.isTLSv1_1())  // IV
             ivExtra = ssl.getCrypto().get_cipher().get_blockSize();
         pad = *(input.get_buffer() + input.get_current() + msgSz -ivExtra - 1);
         padSz = 1;
+
+        if (ssl.isTLS()) {
+            if (timing_verify(ssl, rawData, pad,digestSz, msgSz-ivExtra) != 0) {
+                ssl.SetError(verify_error);
+                return;
+            }
+        }
+        else {   // SSLv3, some don't do this padding right
+            int sz3 = msgSz - digestSz - pad - 1; 
+            hmac(ssl, verify, rawData, sz3, application_data, true);
+            if (constant_compare(verify, rawData + sz3, digestSz) != 0) {
+                ssl.SetError(verify_error);
+                return;
+            }
+        } 
     }
-    int digestSz = ssl.getCrypto().get_digest().get_digestSize();
+    else {  // stream
+        int streamSz = msgSz - digestSz; 
+        if (ssl.isTLS())
+            TLS_hmac(ssl, verify, rawData, streamSz, application_data, true);
+        else
+            hmac(ssl, verify, rawData, streamSz, application_data, true);
+        if (constant_compare(verify, rawData + streamSz, digestSz) != 0) {
+            ssl.SetError(verify_error);
+            return;
+        }
+    }
+
     int dataSz = msgSz - ivExtra - digestSz - pad - padSz;
-    opaque verify[SHA_LEN];
 
     if (dataSz < 0) {
         ssl.SetError(bad_input);
         return;
     }
 
-    const byte* rawData = input.get_buffer() + input.get_current();
 
     // read data
     if (dataSz) {                               // could be compressed
@@ -1013,27 +1177,10 @@
             input.read(data->get_buffer(), dataSz);
             data->add_size(dataSz);
         }
-
-        if (ssl.isTLS())
-            TLS_hmac(ssl, verify, rawData, dataSz, application_data, true);
-        else
-            hmac(ssl, verify, rawData, dataSz, application_data, true);
     }
 
-    // read mac and skip fill
-    opaque mac[SHA_LEN];
-    input.read(mac, digestSz);
-    input.set_current(input.get_current() + pad + padSz);
-
-    // verify
-    if (dataSz) {
-        if (memcmp(mac, verify, digestSz)) {
-            ssl.SetError(verify_error);
-            return;
-        }
-    }
-    else 
-        ssl.get_SEQIncrement(true);  // even though no data, increment verify
+    // advance past mac and fill
+    input.set_current(input.get_current() + digestSz + pad + padByte);
 }
 
 
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/taocrypt/include/asn.hpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/taocrypt/include/asn.hpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/taocrypt/include/asn.hpp	2013-03-27 10:58:30.865636136 +0100
@@ -112,7 +112,7 @@
     MAX_LENGTH_SZ =  5,    
     MAX_SEQ_SZ    =  5,    // enum(seq|con) + length(4)
     MAX_ALGO_SIZE =  9,
-    MAX_DIGEST_SZ = 25,    // SHA + enum(Bit or Octet) + length(4)
+    MAX_DIGEST_SZ = 69,    // SHA512 + enum(Bit or Octet) + length(4)
     DSA_SIG_SZ    = 40,
     ASN_NAME_MAX  = 512    // max total of all included names
 };
@@ -258,8 +258,11 @@
 
 
 enum ContentType { HUH = 651 };
-enum SigType  { SHAwDSA = 517, MD2wRSA = 646, MD5wRSA = 648, SHAwRSA =649};
-enum HashType { MD2h = 646, MD5h = 649, SHAh = 88 };
+enum SigType  { SHAwDSA = 517, MD2wRSA = 646, MD5wRSA = 648, SHAwRSA = 649,
+                SHA256wRSA = 655, SHA384wRSA = 656, SHA512wRSA = 657,
+                SHA256wDSA = 416 };
+enum HashType { MD2h = 646, MD5h = 649, SHAh = 88, SHA256h = 414, SHA384h = 415,
+                SHA512h = 416 };
 enum KeyType  { DSAk = 515, RSAk = 645 };     // sums of algo OID
 
 
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/taocrypt/include/sha.hpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/taocrypt/include/sha.hpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/taocrypt/include/sha.hpp	2013-03-27 10:58:30.865636136 +0100
@@ -158,6 +158,12 @@
     void Transform();
 };
 
+enum { MAX_SHA2_DIGEST_SIZE = 64 };   // SHA512
+
+#else
+
+enum { MAX_SHA2_DIGEST_SIZE = 32 };   // SHA256
+
 #endif // WORD64_AVAILABLE
 
 
Index: mysql-5.5-5.5.30+dfsg/extra/yassl/taocrypt/src/asn.cpp
===================================================================
--- mysql-5.5-5.5.30+dfsg.orig/extra/yassl/taocrypt/src/asn.cpp	2013-03-27 10:56:31.000000000 +0100
+++ mysql-5.5-5.5.30+dfsg/extra/yassl/taocrypt/src/asn.cpp	2013-03-27 10:58:30.865636136 +0100
@@ -972,12 +972,26 @@
         hasher.reset(NEW_TC SHA);
         ht = SHAh;
     }
+    else if (signatureOID_ == SHA256wRSA || signatureOID_ == SHA256wDSA) {
+        hasher.reset(NEW_TC SHA256);
+        ht = SHA256h;
+    }
+#ifdef WORD64_AVAILABLE
+    else if (signatureOID_ == SHA384wRSA) {
+        hasher.reset(NEW_TC SHA384);
+        ht = SHA384h;
+    }
+    else if (signatureOID_ == SHA512wRSA) {
+        hasher.reset(NEW_TC SHA512);
+        ht = SHA512h;
+    }
+#endif
     else {
         source_.SetError(UNKOWN_SIG_E);
         return false;
     }
 
-    byte digest[SHA::DIGEST_SIZE];      // largest size
+    byte digest[MAX_SHA2_DIGEST_SIZE];      // largest size
 
     hasher->Update(source_.get_buffer() + certBegin_, sigIndex_ - certBegin_);
     hasher->Final(digest);
@@ -1050,6 +1064,12 @@
                                       0x02, 0x05, 0x05, 0x00  };
     static const byte md2AlgoID[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
                                       0x02, 0x02, 0x05, 0x00};
+    static const byte sha256AlgoID[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                         0x04, 0x02, 0x01, 0x05, 0x00 };
+    static const byte sha384AlgoID[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                         0x04, 0x02, 0x02, 0x05, 0x00 };
+    static const byte sha512AlgoID[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                         0x04, 0x02, 0x03, 0x05, 0x00 };
 
     int algoSz = 0;
     const byte* algoName = 0;
@@ -1060,6 +1080,21 @@
         algoName = shaAlgoID;
         break;
 
+    case SHA256h:
+        algoSz = sizeof(sha256AlgoID);
+        algoName = sha256AlgoID;
+        break;
+
+    case SHA384h:
+        algoSz = sizeof(sha384AlgoID);
+        algoName = sha384AlgoID;
+        break;
+
+    case SHA512h:
+        algoSz = sizeof(sha512AlgoID);
+        algoName = sha512AlgoID;
+        break;
+
     case MD2h:
         algoSz = sizeof(md2AlgoID);
         algoName = md2AlgoID;

Reply via email to