~ubuntu-branches/ubuntu/lucid/konversation/lucid-updates

« back to all changes in this revision

Viewing changes to .pc/kubuntu_02_cve-2014-8483.diff/src/cipher.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2014-11-04 17:40:19 UTC
  • mfrom: (2.4.4 experimental)
  • Revision ID: package-import@ubuntu.com-20141104174019-djxmmslchwd6uowc
Tags: 1.2.3-1ubuntu2.1
* SECURITY UPDATE: out-of-bounds read on a heap-allocated array LP: #1389296
  - Add kubuntu_02_cve-2014-8483.diff to verify read bounds
  - CVE-2014-8483
  - https://www.kde.org/info/security/advisory-20140923-1.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  This program is free software; you can redistribute it and/or modify
 
3
  it under the terms of the GNU General Public License as published by
 
4
  the Free Software Foundation; either version 2 of the License, or
 
5
  (at your option) any later version.
 
6
*/
 
7
 
 
8
/*
 
9
  Copyright (C) 1997 Robey Pointer <robeypointer@gmail.com>
 
10
  Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
 
11
  Copyright (C) 2009 Travis McHenry <tmchenryaz@cox.net>
 
12
*/
 
13
 
 
14
#include "cipher.h"
 
15
#include "preferences.h"
 
16
 
 
17
#include <KDebug>
 
18
 
 
19
 
 
20
namespace Konversation
 
21
{
 
22
    Cipher::Cipher()
 
23
    {
 
24
        m_primeNum = QCA::BigInteger("12745216229761186769575009943944198619149164746831579719941140425076456621824834322853258804883232842877311723249782818608677050956745409379781245497526069657222703636504651898833151008222772087491045206203033063108075098874712912417029101508315117935752962862335062591404043092163187352352197487303798807791605274487594646923");
 
25
        setType("blowfish");
 
26
    }
 
27
 
 
28
    Cipher::Cipher(QByteArray key, QString cipherType)
 
29
    {
 
30
        m_primeNum = QCA::BigInteger("12745216229761186769575009943944198619149164746831579719941140425076456621824834322853258804883232842877311723249782818608677050956745409379781245497526069657222703636504651898833151008222772087491045206203033063108075098874712912417029101508315117935752962862335062591404043092163187352352197487303798807791605274487594646923");
 
31
        setKey(key);
 
32
        setType(cipherType);
 
33
    }
 
34
 
 
35
    Cipher::~Cipher()
 
36
    {
 
37
    }
 
38
 
 
39
    bool Cipher::setKey(QByteArray key)
 
40
    {
 
41
        if(key.isEmpty())
 
42
            return false;
 
43
 
 
44
        if(key.mid(0,4).toLower() == "ecb:")
 
45
        {
 
46
            m_cbc = false;
 
47
            m_key = key.mid(4);
 
48
        }
 
49
        //strip cbc: if included
 
50
        else if(key.mid(0,4).toLower() == "cbc:")
 
51
        {
 
52
            m_cbc = true;
 
53
            m_key = key.mid(4);
 
54
        }
 
55
        else
 
56
        {
 
57
            if(Preferences::self()->encryptionType())
 
58
                m_cbc = true;
 
59
            else
 
60
                m_cbc = false;
 
61
            m_key = key;
 
62
        }
 
63
        return true;
 
64
    }
 
65
 
 
66
    bool Cipher::setType(const QString &type)
 
67
    {
 
68
        //TODO check QCA::isSupported()
 
69
        m_type = type;
 
70
        return true;
 
71
    }
 
72
 
 
73
    QByteArray Cipher::decrypt(QByteArray cipherText)
 
74
    {
 
75
        QByteArray pfx = "(e) ";
 
76
        bool error = false; // used to flag non cbc, seems like good practice not to parse w/o regard for set encryption type
 
77
 
 
78
        //if we get cbc
 
79
        if(cipherText.mid(0,5) == "+OK *")
 
80
        {
 
81
            //if we have cbc
 
82
            if(m_cbc)
 
83
                cipherText = cipherText.mid(5);
 
84
            //if we don't
 
85
            else
 
86
            {
 
87
                cipherText = cipherText.mid(5);
 
88
                pfx = "ERROR_NONECB: ";
 
89
                error = true;
 
90
            }
 
91
        }
 
92
        //if we get ecb
 
93
        else if(cipherText.mid(0,4) == "+OK " || cipherText.mid(0,5) == "mcps ")
 
94
        {
 
95
            //if we had cbc
 
96
            if(m_cbc)
 
97
            {
 
98
                cipherText = (cipherText.mid(0,4) == "+OK ") ? cipherText.mid(4) : cipherText.mid(5);
 
99
                pfx = "ERROR_NONCBC: ";
 
100
                error = true;
 
101
            }
 
102
            //if we don't
 
103
            else
 
104
            {
 
105
                if(cipherText.mid(0,4) == "+OK ")
 
106
                    cipherText = cipherText.mid(4);
 
107
                else
 
108
                    cipherText = cipherText.mid(5);
 
109
            }
 
110
        }
 
111
        //all other cases we fail
 
112
        else
 
113
            return cipherText;
 
114
 
 
115
        QByteArray temp;
 
116
        // (if cbc and no error we parse cbc) || (if ecb and error we parse cbc)
 
117
        if((m_cbc && !error) || (!m_cbc && error))
 
118
        {
 
119
            cipherText = cipherText;
 
120
            temp = blowfishCBC(cipherText, false);
 
121
 
 
122
            if(temp == cipherText)
 
123
            {
 
124
                kDebug() << "Decryption from CBC Failed";
 
125
                return "ERROR: "+cipherText+' '+'\n';
 
126
            }
 
127
            else
 
128
                cipherText = temp;
 
129
        }
 
130
        else
 
131
        {
 
132
            temp = blowfishECB(cipherText, false);
 
133
 
 
134
            if(temp == cipherText)
 
135
            {
 
136
                kDebug() << "Decryption from ECB Failed";
 
137
                return "ERROR: "+cipherText+' '+'\n';
 
138
            }
 
139
            else
 
140
                cipherText = temp;
 
141
        }
 
142
        // If it's a CTCP we don't want to have the (e) interfering with the processing
 
143
        // TODO FIXME the proper fix for this is to show encryption differently e.g. [nick] instead of <nick>
 
144
        // don't hate me for the mircryption reference there.
 
145
        if (cipherText.at(0) == 1)
 
146
            pfx = "\x0";
 
147
        cipherText = pfx+cipherText+' '+'\n'; // FIXME(??) why is there an added space here?
 
148
        return cipherText;
 
149
    }
 
150
 
 
151
    QByteArray Cipher::initKeyExchange()
 
152
    {
 
153
        QCA::Initializer init;
 
154
        m_tempKey = QCA::KeyGenerator().createDH(QCA::DLGroup(m_primeNum, QCA::BigInteger(2))).toDH();
 
155
 
 
156
        if(m_tempKey.isNull())
 
157
            return QByteArray();
 
158
 
 
159
        QByteArray publicKey = m_tempKey.toPublicKey().toDH().y().toArray().toByteArray();
 
160
 
 
161
        //remove leading 0
 
162
        if(publicKey.length() > 135 && publicKey.at(0) == '\0')
 
163
            publicKey = publicKey.mid(1);
 
164
 
 
165
        return publicKey.toBase64().append('A');
 
166
    }
 
167
 
 
168
    QByteArray Cipher::parseInitKeyX(QByteArray key)
 
169
    {
 
170
        QCA::Initializer init;
 
171
 
 
172
        if(key.length() != 181)
 
173
            return QByteArray();
 
174
 
 
175
        QCA::SecureArray remoteKey = QByteArray::fromBase64(key.left(180));
 
176
        QCA::DLGroup group(m_primeNum, QCA::BigInteger(2));
 
177
        QCA::DHPrivateKey privateKey = QCA::KeyGenerator().createDH(group).toDH();
 
178
 
 
179
        if(privateKey.isNull())
 
180
            return QByteArray();
 
181
 
 
182
        QByteArray publicKey = privateKey.y().toArray().toByteArray();
 
183
 
 
184
        //remove leading 0
 
185
        if(publicKey.length() > 135 && publicKey.at(0) == '\0')
 
186
            publicKey = publicKey.mid(1);
 
187
 
 
188
        QCA::DHPublicKey remotePub(group, remoteKey);
 
189
 
 
190
        if(remotePub.isNull())
 
191
            return QByteArray();
 
192
 
 
193
        QByteArray sharedKey = privateKey.deriveKey(remotePub).toByteArray();
 
194
        sharedKey = QCA::Hash("sha256").hash(sharedKey).toByteArray().toBase64();
 
195
 
 
196
        //remove trailing = because mircryption and fish think it's a swell idea.
 
197
        while(sharedKey.endsWith('=')) sharedKey.chop(1);
 
198
 
 
199
        bool success = setKey(sharedKey);
 
200
 
 
201
        if(!success)
 
202
            return QByteArray();
 
203
 
 
204
        return publicKey.toBase64().append('A');
 
205
    }
 
206
 
 
207
    bool Cipher::parseFinishKeyX(QByteArray key)
 
208
    {
 
209
        QCA::Initializer init;
 
210
 
 
211
        if(key.length() != 181)
 
212
            return false;
 
213
 
 
214
        QCA::SecureArray remoteKey = QByteArray::fromBase64(key.left(180));
 
215
        QCA::DLGroup group(m_primeNum, QCA::BigInteger(2));
 
216
 
 
217
        QCA::DHPublicKey remotePub(group, remoteKey);
 
218
 
 
219
        if(remotePub.isNull())
 
220
            return false;
 
221
 
 
222
        if(m_tempKey.isNull())
 
223
            return false;
 
224
 
 
225
        QByteArray sharedKey = m_tempKey.deriveKey(remotePub).toByteArray();
 
226
        sharedKey = QCA::Hash("sha256").hash(sharedKey).toByteArray().toBase64();
 
227
 
 
228
        //remove trailng = because mircryption and fish think it's a swell idea.
 
229
        while(sharedKey.endsWith('=')) sharedKey.chop(1);
 
230
 
 
231
        bool success = setKey(sharedKey);
 
232
 
 
233
        return success;
 
234
    }
 
235
 
 
236
    QByteArray Cipher::decryptTopic(QByteArray cipherText)
 
237
    {
 
238
        if(cipherText.mid(0,4) == "+OK ")// FiSH style topic
 
239
            cipherText = cipherText.mid(4);
 
240
        else if(cipherText.left(5) == "«m«")
 
241
            cipherText = cipherText.mid(5,cipherText.length()-10);
 
242
        else
 
243
            return "ERROR: "+cipherText;
 
244
 
 
245
        QByteArray temp;
 
246
        //TODO currently no backwards sanity checks for topic, it seems to use different standards
 
247
        //if somebody figures them out they can enable it and add "ERROR_NONECB/CBC" warnings
 
248
        if(m_cbc)
 
249
            temp = blowfishCBC(cipherText.mid(1), false);
 
250
        else
 
251
            temp = blowfishECB(cipherText, false);
 
252
 
 
253
        if(temp == cipherText)
 
254
        {
 
255
            return "ERROR: "+cipherText;
 
256
        }
 
257
        else
 
258
            cipherText = temp;
 
259
 
 
260
        if(cipherText.mid(0,2) == "@@")
 
261
            cipherText = cipherText.mid(2);
 
262
 
 
263
        cipherText = "(e) "+cipherText;
 
264
        return cipherText;
 
265
    }
 
266
 
 
267
    bool Cipher::encrypt(QByteArray& cipherText)
 
268
    {
 
269
        if (cipherText.left(3) == "+p ") //don't encode if...?
 
270
            cipherText = cipherText.mid(3);
 
271
        else
 
272
        {
 
273
            if(m_cbc) //encode in ecb or cbc decide how to determine later
 
274
            {
 
275
                QByteArray temp = blowfishCBC(cipherText, true);
 
276
 
 
277
                if(temp == cipherText)
 
278
                {
 
279
                    kDebug() << "CBC Encoding Failed";
 
280
                    return false;
 
281
                }
 
282
 
 
283
                cipherText = "+OK *" + temp;
 
284
            }
 
285
            else
 
286
            {
 
287
                QByteArray temp = blowfishECB(cipherText, true);
 
288
 
 
289
                if(temp == cipherText)
 
290
                {
 
291
                    kDebug() << "ECB Encoding Failed";
 
292
                    return false;
 
293
                }
 
294
 
 
295
                cipherText = "+OK " + temp;
 
296
            }
 
297
        }
 
298
        return true;
 
299
    }
 
300
    //THE BELOW WORKS AKA DO NOT TOUCH UNLESS YOU KNOW WHAT YOU'RE DOING
 
301
    QByteArray Cipher::blowfishCBC(QByteArray cipherText, bool direction)
 
302
    {
 
303
        QCA::Initializer init;
 
304
        QByteArray temp = cipherText;
 
305
        if(direction)
 
306
        {
 
307
            // make sure cipherText is an interval of 8 bits. We do this before so that we
 
308
            // know there's at least 8 bytes to en/decryption this ensures QCA doesn't fail
 
309
            while((temp.length() % 8) != 0) temp.append('\0');
 
310
 
 
311
            QCA::InitializationVector iv(8);
 
312
            temp.prepend(iv.toByteArray()); // prefix with 8bits of IV for mircryptions *CUSTOM* cbc implementation
 
313
        }
 
314
        else
 
315
        {
 
316
            temp = QByteArray::fromBase64(temp);
 
317
            //supposedly nescessary if we get a truncated message also allows for decryption of 'crazy'
 
318
            //en/decoding clients that use STANDARDIZED PADDING TECHNIQUES
 
319
            while((temp.length() % 8) != 0) temp.append('\0');
 
320
        }
 
321
 
 
322
        QCA::Direction dir = (direction) ? QCA::Encode : QCA::Decode;
 
323
        QCA::Cipher cipher(m_type, QCA::Cipher::CBC, QCA::Cipher::NoPadding, dir, m_key, QCA::InitializationVector(QByteArray("0")));
 
324
        QByteArray temp2 = cipher.update(QCA::MemoryRegion(temp)).toByteArray();
 
325
        temp2 += cipher.final().toByteArray();
 
326
 
 
327
        if(!cipher.ok())
 
328
            return cipherText;
 
329
 
 
330
        if(direction) //send in base64
 
331
            temp2 = temp2.toBase64();
 
332
        else //cut off the 8bits of IV
 
333
            temp2 = temp2.remove(0,8);
 
334
 
 
335
        return temp2;
 
336
    }
 
337
 
 
338
    QByteArray Cipher::blowfishECB(QByteArray cipherText, bool direction)
 
339
    {
 
340
        QCA::Initializer init;
 
341
        QByteArray temp = cipherText;
 
342
 
 
343
        //do padding ourselves
 
344
        if(direction)
 
345
        {
 
346
            while((temp.length() % 8) != 0) temp.append('\0');
 
347
        }
 
348
        else
 
349
        {
 
350
            temp = b64ToByte(temp);
 
351
            while((temp.length() % 8) != 0) temp.append('\0');
 
352
        }
 
353
 
 
354
        QCA::Direction dir = (direction) ? QCA::Encode : QCA::Decode;
 
355
        QCA::Cipher cipher(m_type, QCA::Cipher::ECB, QCA::Cipher::NoPadding, dir, m_key);
 
356
        QByteArray temp2 = cipher.update(QCA::MemoryRegion(temp)).toByteArray();
 
357
        temp2 += cipher.final().toByteArray();
 
358
 
 
359
        if(!cipher.ok())
 
360
            return cipherText;
 
361
 
 
362
        if(direction)
 
363
            temp2 = byteToB64(temp2);
 
364
 
 
365
        return temp2;
 
366
    }
 
367
 
 
368
    //Custom non RFC 2045 compliant Base64 enc/dec code for mircryption / FiSH compatibility
 
369
    QByteArray Cipher::byteToB64(QByteArray text)
 
370
    {
 
371
        int left = 0;
 
372
        int right = 0;
 
373
        int k = -1;
 
374
        int v;
 
375
        QString base64 = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
376
        QByteArray encoded;
 
377
        while (k < (text.length() - 1)) {
 
378
            k++;
 
379
            v=text.at(k); if (v<0) v+=256;
 
380
            left = v << 24;
 
381
            k++;
 
382
            v=text.at(k); if (v<0) v+=256;
 
383
            left += v << 16;
 
384
            k++;
 
385
            v=text.at(k); if (v<0) v+=256;
 
386
            left += v << 8;
 
387
            k++;
 
388
            v=text.at(k); if (v<0) v+=256;
 
389
            left += v;
 
390
 
 
391
            k++;
 
392
            v=text.at(k); if (v<0) v+=256;
 
393
            right = v << 24;
 
394
            k++;
 
395
            v=text.at(k); if (v<0) v+=256;
 
396
            right += v << 16;
 
397
            k++;
 
398
            v=text.at(k); if (v<0) v+=256;
 
399
            right += v << 8;
 
400
            k++;
 
401
            v=text.at(k); if (v<0) v+=256;
 
402
            right += v;
 
403
 
 
404
            for (int i = 0; i < 6; i++) {
 
405
                encoded.append(base64.at(right & 0x3F).toAscii());
 
406
                right = right >> 6;
 
407
            }
 
408
            //TODO make sure the .toascii doesn't break anything
 
409
 
 
410
            for (int i = 0; i < 6; i++) {
 
411
                encoded.append(base64.at(left & 0x3F).toAscii());
 
412
                left = left >> 6;
 
413
            }
 
414
        }
 
415
        return encoded;
 
416
    }
 
417
 
 
418
    QByteArray Cipher::b64ToByte(QByteArray text)
 
419
    {
 
420
        QString base64 = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
421
        QByteArray decoded;
 
422
        int k = -1;
 
423
        while (k < (text.length() - 1)) {
 
424
            int right = 0;
 
425
            int left = 0;
 
426
            int v = 0;
 
427
            int w = 0;
 
428
            int z = 0;
 
429
 
 
430
            for (int i = 0; i < 6; i++) {
 
431
                k++;
 
432
                v = base64.indexOf(text.at(k));
 
433
                right |= v << (i * 6);
 
434
            }
 
435
 
 
436
            for (int i = 0; i < 6; i++) {
 
437
                k++;
 
438
                v = base64.indexOf(text.at(k));
 
439
                left |= v << (i * 6);
 
440
            }
 
441
 
 
442
            for (int i = 0; i < 4; i++) {
 
443
                w = ((left & (0xFF << ((3 - i) * 8))));
 
444
                z = w >> ((3 - i) * 8);
 
445
                if(z < 0) {z = z + 256;}
 
446
                decoded.append(z);
 
447
            }
 
448
 
 
449
            for (int i = 0; i < 4; i++) {
 
450
                w = ((right & (0xFF << ((3 - i) * 8))));
 
451
                z = w >> ((3 - i) * 8);
 
452
                if(z < 0) {z = z + 256;}
 
453
                decoded.append(z);
 
454
            }
 
455
        }
 
456
        return decoded;
 
457
    }
 
458
}