1 #include <openssl/rsa.h>
2 #include <openssl/evp.h>
3 #include <openssl/pem.h>
4 #include <openssl/err.h>
5 #include <openssl/engine.h>
6 #include <openssl/rand.h>
7
8
9 #include "clientsideencryption.h"
10 #include "account.h"
11 #include "capabilities.h"
12 #include "networkjobs.h"
13 #include "clientsideencryptionjobs.h"
14 #include "theme.h"
15 #include "creds/abstractcredentials.h"
16
17 #include <map>
18 #include <string>
19 #include <algorithm>
20
21 #include <cstdio>
22
23 #include <QDebug>
24 #include <QLoggingCategory>
25 #include <QFileInfo>
26 #include <QDir>
27 #include <QJsonObject>
28 #include <QXmlStreamReader>
29 #include <QXmlStreamNamespaceDeclaration>
30 #include <QStack>
31 #include <QInputDialog>
32 #include <QLineEdit>
33 #include <QIODevice>
34 #include <QUuid>
35 #include <QScopeGuard>
36 #include <QRandomGenerator>
37
38 #include <qt5keychain/keychain.h>
39 #include <common/utility.h>
40 #include <common/constants.h>
41
42 #include "wordlist.h"
43
44 #ifdef LIBRESSL_VERSION_NUMBER
45 #ifndef EVP_PKEY_CTX_set_rsa_oaep_md
46 # define EVP_PKEY_CTRL_RSA_OAEP_MD (EVP_PKEY_ALG_CTRL + 9)
47 # define EVP_PKEY_CTRL_GET_RSA_OAEP_MD (EVP_PKEY_ALG_CTRL + 11)
48
49 # define EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) \
50 EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT, \
51 EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void *)(md))
52 #endif
53 #endif
54
operator <<(QDebug out,const std::string & str)55 QDebug operator<<(QDebug out, const std::string& str)
56 {
57 out << QString::fromStdString(str);
58 return out;
59 }
60
61 using namespace QKeychain;
62
63 namespace OCC
64 {
65
66 Q_LOGGING_CATEGORY(lcCse, "nextcloud.sync.clientsideencryption", QtInfoMsg)
67 Q_LOGGING_CATEGORY(lcCseDecryption, "nextcloud.e2e", QtInfoMsg)
68 Q_LOGGING_CATEGORY(lcCseMetadata, "nextcloud.metadata", QtInfoMsg)
69
e2eeBaseUrl()70 QString e2eeBaseUrl()
71 {
72 return QStringLiteral("ocs/v2.php/apps/end_to_end_encryption/api/v1/");
73 }
74
75 namespace {
76 constexpr char accountProperty[] = "account";
77
78 const char e2e_cert[] = "_e2e-certificate";
79 const char e2e_private[] = "_e2e-private";
80 const char e2e_mnemonic[] = "_e2e-mnemonic";
81
82 constexpr qint64 blockSize = 1024;
83
oldCipherFormatSplit(const QByteArray & cipher)84 QList<QByteArray> oldCipherFormatSplit(const QByteArray &cipher)
85 {
86 const auto separator = QByteArrayLiteral("fA=="); // BASE64 encoded '|'
87 auto result = QList<QByteArray>();
88
89 auto data = cipher;
90 auto index = data.indexOf(separator);
91 while (index >=0) {
92 result.append(data.left(index));
93 data = data.mid(index + separator.size());
94 index = data.indexOf(separator);
95 }
96
97 result.append(data);
98 return result;
99 }
100
splitCipherParts(const QByteArray & data)101 QList<QByteArray> splitCipherParts(const QByteArray &data)
102 {
103 const auto isOldFormat = !data.contains('|');
104 const auto parts = isOldFormat ? oldCipherFormatSplit(data) : data.split('|');
105 qCInfo(lcCse()) << "found parts:" << parts << "old format?" << isOldFormat;
106 return parts;
107 }
108 } // ns
109
110 namespace {
unsignedData(QByteArray & array)111 unsigned char* unsignedData(QByteArray& array)
112 {
113 return (unsigned char*)array.data();
114 }
115
116 //
117 // Simple classes for safe (RAII) handling of OpenSSL
118 // data structures
119 //
120
121 class CipherCtx {
122 public:
CipherCtx()123 CipherCtx()
124 : _ctx(EVP_CIPHER_CTX_new())
125 {
126 }
127
~CipherCtx()128 ~CipherCtx()
129 {
130 EVP_CIPHER_CTX_free(_ctx);
131 }
132
operator EVP_CIPHER_CTX*()133 operator EVP_CIPHER_CTX*()
134 {
135 return _ctx;
136 }
137
138 private:
139 Q_DISABLE_COPY(CipherCtx)
140
141 EVP_CIPHER_CTX* _ctx;
142 };
143
144 class Bio {
145 public:
Bio()146 Bio()
147 : _bio(BIO_new(BIO_s_mem()))
148 {
149 }
150
~Bio()151 ~Bio()
152 {
153 BIO_free_all(_bio);
154 }
155
operator BIO*()156 operator BIO*()
157 {
158 return _bio;
159 }
160
161 private:
162 Q_DISABLE_COPY(Bio)
163
164 BIO* _bio;
165 };
166
167 class PKeyCtx {
168 public:
PKeyCtx(int id,ENGINE * e=nullptr)169 explicit PKeyCtx(int id, ENGINE *e = nullptr)
170 : _ctx(EVP_PKEY_CTX_new_id(id, e))
171 {
172 }
173
~PKeyCtx()174 ~PKeyCtx()
175 {
176 EVP_PKEY_CTX_free(_ctx);
177 }
178
179 // The move constructor is needed for pre-C++17 where
180 // return-value optimization (RVO) is not obligatory
181 // and we have a `forKey` static function that returns
182 // an instance of this class
PKeyCtx(PKeyCtx && other)183 PKeyCtx(PKeyCtx&& other)
184 {
185 std::swap(_ctx, other._ctx);
186 }
187
188 PKeyCtx& operator=(PKeyCtx&& other) = delete;
189
forKey(EVP_PKEY * pkey,ENGINE * e=nullptr)190 static PKeyCtx forKey(EVP_PKEY *pkey, ENGINE *e = nullptr)
191 {
192 PKeyCtx ctx;
193 ctx._ctx = EVP_PKEY_CTX_new(pkey, e);
194 return ctx;
195 }
196
operator EVP_PKEY_CTX*()197 operator EVP_PKEY_CTX*()
198 {
199 return _ctx;
200 }
201
202 private:
203 Q_DISABLE_COPY(PKeyCtx)
204
205 PKeyCtx() = default;
206
207 EVP_PKEY_CTX* _ctx = nullptr;
208 };
209
210 class PKey {
211 public:
~PKey()212 ~PKey()
213 {
214 EVP_PKEY_free(_pkey);
215 }
216
217 // The move constructor is needed for pre-C++17 where
218 // return-value optimization (RVO) is not obligatory
219 // and we have a static functions that return
220 // an instance of this class
PKey(PKey && other)221 PKey(PKey&& other)
222 {
223 std::swap(_pkey, other._pkey);
224 }
225
226 PKey& operator=(PKey&& other) = delete;
227
readPublicKey(Bio & bio)228 static PKey readPublicKey(Bio &bio)
229 {
230 PKey result;
231 result._pkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
232 return result;
233 }
234
readPrivateKey(Bio & bio)235 static PKey readPrivateKey(Bio &bio)
236 {
237 PKey result;
238 result._pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
239 return result;
240 }
241
generate(PKeyCtx & ctx)242 static PKey generate(PKeyCtx& ctx)
243 {
244 PKey result;
245 if (EVP_PKEY_keygen(ctx, &result._pkey) <= 0) {
246 result._pkey = nullptr;
247 }
248 return result;
249 }
250
operator EVP_PKEY*()251 operator EVP_PKEY*()
252 {
253 return _pkey;
254 }
255
operator EVP_PKEY*() const256 operator EVP_PKEY*() const
257 {
258 return _pkey;
259 }
260
261 private:
262 Q_DISABLE_COPY(PKey)
263
264 PKey() = default;
265
266 EVP_PKEY* _pkey = nullptr;
267 };
268
269 class X509Certificate {
270 public:
~X509Certificate()271 ~X509Certificate()
272 {
273 X509_free(_certificate);
274 }
275
276 // The move constructor is needed for pre-C++17 where
277 // return-value optimization (RVO) is not obligatory
278 // and we have a static functions that return
279 // an instance of this class
X509Certificate(X509Certificate && other)280 X509Certificate(X509Certificate&& other)
281 {
282 std::swap(_certificate, other._certificate);
283 }
284
285 X509Certificate& operator=(X509Certificate&& other) = delete;
286
readCertificate(Bio & bio)287 static X509Certificate readCertificate(Bio &bio)
288 {
289 X509Certificate result;
290 result._certificate = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
291 return result;
292 }
293
operator X509*()294 operator X509*()
295 {
296 return _certificate;
297 }
298
operator X509*() const299 operator X509*() const
300 {
301 return _certificate;
302 }
303
304 private:
305 Q_DISABLE_COPY(X509Certificate)
306
307 X509Certificate() = default;
308
309 X509* _certificate = nullptr;
310 };
311
BIO2ByteArray(Bio & b)312 QByteArray BIO2ByteArray(Bio &b) {
313 auto pending = static_cast<int>(BIO_ctrl_pending(b));
314 QByteArray res(pending, '\0');
315 BIO_read(b, unsignedData(res), pending);
316 return res;
317 }
318
handleErrors()319 QByteArray handleErrors()
320 {
321 Bio bioErrors;
322 ERR_print_errors(bioErrors); // This line is not printing anything.
323 return BIO2ByteArray(bioErrors);
324 }
325 }
326
327
328 namespace EncryptionHelper {
329
330
331
332
generateRandomFilename()333 QByteArray generateRandomFilename()
334 {
335 return QUuid::createUuid().toRfc4122().toHex();
336 }
337
generateRandom(int size)338 QByteArray generateRandom(int size)
339 {
340 QByteArray result(size, '\0');
341
342 int ret = RAND_bytes(unsignedData(result), size);
343 if (ret != 1) {
344 qCInfo(lcCse()) << "Random byte generation failed!";
345 // Error out?
346 }
347
348 return result;
349 }
350
generatePassword(const QString & wordlist,const QByteArray & salt)351 QByteArray generatePassword(const QString& wordlist, const QByteArray& salt) {
352 qCInfo(lcCse()) << "Start encryption key generation!";
353
354 const int iterationCount = 1024;
355 const int keyStrength = 256;
356 const int keyLength = keyStrength/8;
357
358 QByteArray secretKey(keyLength, '\0');
359
360 int ret = PKCS5_PBKDF2_HMAC_SHA1(
361 wordlist.toLocal8Bit().constData(), // const char *password,
362 wordlist.size(), // int password length,
363 (const unsigned char *)salt.constData(),// const unsigned char *salt,
364 salt.size(), // int saltlen,
365 iterationCount, // int iterations,
366 keyLength, // int keylen,
367 unsignedData(secretKey) // unsigned char *out
368 );
369
370 if (ret != 1) {
371 qCInfo(lcCse()) << "Failed to generate encryption key";
372 // Error out?
373 }
374
375 qCInfo(lcCse()) << "Encryption key generated!";
376
377 return secretKey;
378 }
379
encryptPrivateKey(const QByteArray & key,const QByteArray & privateKey,const QByteArray & salt)380 QByteArray encryptPrivateKey(
381 const QByteArray& key,
382 const QByteArray& privateKey,
383 const QByteArray& salt
384 ) {
385
386 QByteArray iv = generateRandom(12);
387
388 CipherCtx ctx;
389
390 /* Create and initialise the context */
391 if(!ctx) {
392 qCInfo(lcCse()) << "Error creating cipher";
393 handleErrors();
394 }
395
396 /* Initialise the decryption operation. */
397 if(!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr)) {
398 qCInfo(lcCse()) << "Error initializing context with aes_256";
399 handleErrors();
400 }
401
402 // No padding
403 EVP_CIPHER_CTX_set_padding(ctx, 0);
404
405 /* Set IV length. */
406 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
407 qCInfo(lcCse()) << "Error setting iv length";
408 handleErrors();
409 }
410
411 /* Initialise key and IV */
412 if(!EVP_EncryptInit_ex(ctx, nullptr, nullptr, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
413 qCInfo(lcCse()) << "Error initialising key and iv";
414 handleErrors();
415 }
416
417 // We write the base64 encoded private key
418 QByteArray privateKeyB64 = privateKey.toBase64();
419
420 // Make sure we have enough room in the cipher text
421 QByteArray ctext(privateKeyB64.size() + 32, '\0');
422
423 // Do the actual encryption
424 int len = 0;
425 if(!EVP_EncryptUpdate(ctx, unsignedData(ctext), &len, (unsigned char *)privateKeyB64.constData(), privateKeyB64.size())) {
426 qCInfo(lcCse()) << "Error encrypting";
427 handleErrors();
428 }
429
430 int clen = len;
431
432 /* Finalise the encryption. Normally ciphertext bytes may be written at
433 * this stage, but this does not occur in GCM mode
434 */
435 if(1 != EVP_EncryptFinal_ex(ctx, unsignedData(ctext) + len, &len)) {
436 qCInfo(lcCse()) << "Error finalizing encryption";
437 handleErrors();
438 }
439 clen += len;
440
441 /* Get the e2EeTag */
442 QByteArray e2EeTag(OCC::Constants::e2EeTagSize, '\0');
443 if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, OCC::Constants::e2EeTagSize, unsignedData(e2EeTag))) {
444 qCInfo(lcCse()) << "Error getting the e2EeTag";
445 handleErrors();
446 }
447
448 QByteArray cipherTXT;
449 cipherTXT.reserve(clen + OCC::Constants::e2EeTagSize);
450 cipherTXT.append(ctext, clen);
451 cipherTXT.append(e2EeTag);
452
453 QByteArray result = cipherTXT.toBase64();
454 result += '|';
455 result += iv.toBase64();
456 result += '|';
457 result += salt.toBase64();
458
459 return result;
460 }
461
decryptPrivateKey(const QByteArray & key,const QByteArray & data)462 QByteArray decryptPrivateKey(const QByteArray& key, const QByteArray& data) {
463 qCInfo(lcCse()) << "decryptStringSymmetric key: " << key;
464 qCInfo(lcCse()) << "decryptStringSymmetric data: " << data;
465
466 const auto parts = splitCipherParts(data);
467 if (parts.size() < 2) {
468 qCInfo(lcCse()) << "Not enough parts found";
469 return QByteArray();
470 }
471
472 QByteArray cipherTXT64 = parts.at(0);
473 QByteArray ivB64 = parts.at(1);
474
475 qCInfo(lcCse()) << "decryptStringSymmetric cipherTXT: " << cipherTXT64;
476 qCInfo(lcCse()) << "decryptStringSymmetric IV: " << ivB64;
477
478 QByteArray cipherTXT = QByteArray::fromBase64(cipherTXT64);
479 QByteArray iv = QByteArray::fromBase64(ivB64);
480
481 const QByteArray e2EeTag = cipherTXT.right(OCC::Constants::e2EeTagSize);
482 cipherTXT.chop(OCC::Constants::e2EeTagSize);
483
484 // Init
485 CipherCtx ctx;
486
487 /* Create and initialise the context */
488 if(!ctx) {
489 qCInfo(lcCse()) << "Error creating cipher";
490 return QByteArray();
491 }
492
493 /* Initialise the decryption operation. */
494 if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr)) {
495 qCInfo(lcCse()) << "Error initialising context with aes 256";
496 return QByteArray();
497 }
498
499 /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
500 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
501 qCInfo(lcCse()) << "Error setting IV size";
502 return QByteArray();
503 }
504
505 /* Initialise key and IV */
506 if(!EVP_DecryptInit_ex(ctx, nullptr, nullptr, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
507 qCInfo(lcCse()) << "Error initialising key and iv";
508 return QByteArray();
509 }
510
511 QByteArray ptext(cipherTXT.size() + OCC::Constants::e2EeTagSize, '\0');
512 int plen = 0;
513
514 /* Provide the message to be decrypted, and obtain the plaintext output.
515 * EVP_DecryptUpdate can be called multiple times if necessary
516 */
517 if(!EVP_DecryptUpdate(ctx, unsignedData(ptext), &plen, (unsigned char *)cipherTXT.constData(), cipherTXT.size())) {
518 qCInfo(lcCse()) << "Could not decrypt";
519 return QByteArray();
520 }
521
522 /* Set expected e2EeTag value. Works in OpenSSL 1.0.1d and later */
523 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, e2EeTag.size(), (unsigned char *)e2EeTag.constData())) {
524 qCInfo(lcCse()) << "Could not set e2EeTag";
525 return QByteArray();
526 }
527
528 /* Finalise the decryption. A positive return value indicates success,
529 * anything else is a failure - the plaintext is not trustworthy.
530 */
531 int len = plen;
532 if (EVP_DecryptFinal_ex(ctx, unsignedData(ptext) + plen, &len) == 0) {
533 qCInfo(lcCse()) << "Tag did not match!";
534 return QByteArray();
535 }
536
537 QByteArray result(ptext, plen);
538 return QByteArray::fromBase64(result);
539 }
540
extractPrivateKeySalt(const QByteArray & data)541 QByteArray extractPrivateKeySalt(const QByteArray &data)
542 {
543 const auto parts = splitCipherParts(data);
544 if (parts.size() < 3) {
545 qCInfo(lcCse()) << "Not enough parts found";
546 return QByteArray();
547 }
548
549 return QByteArray::fromBase64(parts.at(2));
550 }
551
decryptStringSymmetric(const QByteArray & key,const QByteArray & data)552 QByteArray decryptStringSymmetric(const QByteArray& key, const QByteArray& data) {
553 qCInfo(lcCse()) << "decryptStringSymmetric key: " << key;
554 qCInfo(lcCse()) << "decryptStringSymmetric data: " << data;
555
556 const auto parts = splitCipherParts(data);
557 if (parts.size() < 2) {
558 qCInfo(lcCse()) << "Not enough parts found";
559 return QByteArray();
560 }
561
562 QByteArray cipherTXT64 = parts.at(0);
563 QByteArray ivB64 = parts.at(1);
564
565 qCInfo(lcCse()) << "decryptStringSymmetric cipherTXT: " << cipherTXT64;
566 qCInfo(lcCse()) << "decryptStringSymmetric IV: " << ivB64;
567
568 QByteArray cipherTXT = QByteArray::fromBase64(cipherTXT64);
569 QByteArray iv = QByteArray::fromBase64(ivB64);
570
571 const QByteArray e2EeTag = cipherTXT.right(OCC::Constants::e2EeTagSize);
572 cipherTXT.chop(OCC::Constants::e2EeTagSize);
573
574 // Init
575 CipherCtx ctx;
576
577 /* Create and initialise the context */
578 if(!ctx) {
579 qCInfo(lcCse()) << "Error creating cipher";
580 return QByteArray();
581 }
582
583 /* Initialise the decryption operation. */
584 if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
585 qCInfo(lcCse()) << "Error initialising context with aes 128";
586 return QByteArray();
587 }
588
589 /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
590 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
591 qCInfo(lcCse()) << "Error setting IV size";
592 return QByteArray();
593 }
594
595 /* Initialise key and IV */
596 if(!EVP_DecryptInit_ex(ctx, nullptr, nullptr, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
597 qCInfo(lcCse()) << "Error initialising key and iv";
598 return QByteArray();
599 }
600
601 QByteArray ptext(cipherTXT.size() + OCC::Constants::e2EeTagSize, '\0');
602 int plen = 0;
603
604 /* Provide the message to be decrypted, and obtain the plaintext output.
605 * EVP_DecryptUpdate can be called multiple times if necessary
606 */
607 if(!EVP_DecryptUpdate(ctx, unsignedData(ptext), &plen, (unsigned char *)cipherTXT.constData(), cipherTXT.size())) {
608 qCInfo(lcCse()) << "Could not decrypt";
609 return QByteArray();
610 }
611
612 /* Set expected e2EeTag value. Works in OpenSSL 1.0.1d and later */
613 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, e2EeTag.size(), (unsigned char *)e2EeTag.constData())) {
614 qCInfo(lcCse()) << "Could not set e2EeTag";
615 return QByteArray();
616 }
617
618 /* Finalise the decryption. A positive return value indicates success,
619 * anything else is a failure - the plaintext is not trustworthy.
620 */
621 int len = plen;
622 if (EVP_DecryptFinal_ex(ctx, unsignedData(ptext) + plen, &len) == 0) {
623 qCInfo(lcCse()) << "Tag did not match!";
624 return QByteArray();
625 }
626
627 return QByteArray::fromBase64(QByteArray(ptext, plen));
628 }
629
privateKeyToPem(const QByteArray key)630 QByteArray privateKeyToPem(const QByteArray key) {
631 Bio privateKeyBio;
632 BIO_write(privateKeyBio, key.constData(), key.size());
633 auto pkey = PKey::readPrivateKey(privateKeyBio);
634
635 Bio pemBio;
636 PEM_write_bio_PKCS8PrivateKey(pemBio, pkey, nullptr, nullptr, 0, nullptr, nullptr);
637 QByteArray pem = BIO2ByteArray(pemBio);
638
639 return pem;
640 }
641
encryptStringSymmetric(const QByteArray & key,const QByteArray & data)642 QByteArray encryptStringSymmetric(const QByteArray& key, const QByteArray& data) {
643 QByteArray iv = generateRandom(16);
644
645 CipherCtx ctx;
646
647 /* Create and initialise the context */
648 if(!ctx) {
649 qCInfo(lcCse()) << "Error creating cipher";
650 handleErrors();
651 return {};
652 }
653
654 /* Initialise the decryption operation. */
655 if(!EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
656 qCInfo(lcCse()) << "Error initializing context with aes_128";
657 handleErrors();
658 return {};
659 }
660
661 // No padding
662 EVP_CIPHER_CTX_set_padding(ctx, 0);
663
664 /* Set IV length. */
665 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
666 qCInfo(lcCse()) << "Error setting iv length";
667 handleErrors();
668 return {};
669 }
670
671 /* Initialise key and IV */
672 if(!EVP_EncryptInit_ex(ctx, nullptr, nullptr, (unsigned char *)key.constData(), (unsigned char *)iv.constData())) {
673 qCInfo(lcCse()) << "Error initialising key and iv";
674 handleErrors();
675 return {};
676 }
677
678 // We write the data base64 encoded
679 QByteArray dataB64 = data.toBase64();
680
681 // Make sure we have enough room in the cipher text
682 QByteArray ctext(dataB64.size() + 16, '\0');
683
684 // Do the actual encryption
685 int len = 0;
686 if(!EVP_EncryptUpdate(ctx, unsignedData(ctext), &len, (unsigned char *)dataB64.constData(), dataB64.size())) {
687 qCInfo(lcCse()) << "Error encrypting";
688 handleErrors();
689 return {};
690 }
691
692 int clen = len;
693
694 /* Finalise the encryption. Normally ciphertext bytes may be written at
695 * this stage, but this does not occur in GCM mode
696 */
697 if(1 != EVP_EncryptFinal_ex(ctx, unsignedData(ctext) + len, &len)) {
698 qCInfo(lcCse()) << "Error finalizing encryption";
699 handleErrors();
700 return {};
701 }
702 clen += len;
703
704 /* Get the e2EeTag */
705 QByteArray e2EeTag(OCC::Constants::e2EeTagSize, '\0');
706 if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, OCC::Constants::e2EeTagSize, unsignedData(e2EeTag))) {
707 qCInfo(lcCse()) << "Error getting the e2EeTag";
708 handleErrors();
709 return {};
710 }
711
712 QByteArray cipherTXT;
713 cipherTXT.reserve(clen + OCC::Constants::e2EeTagSize);
714 cipherTXT.append(ctext, clen);
715 cipherTXT.append(e2EeTag);
716
717 QByteArray result = cipherTXT.toBase64();
718 result += '|';
719 result += iv.toBase64();
720
721 return result;
722 }
723
decryptStringAsymmetric(EVP_PKEY * privateKey,const QByteArray & data)724 QByteArray decryptStringAsymmetric(EVP_PKEY *privateKey, const QByteArray& data) {
725 int err = -1;
726
727 qCInfo(lcCseDecryption()) << "Start to work the decryption.";
728 auto ctx = PKeyCtx::forKey(privateKey, ENGINE_get_default_RSA());
729 if (!ctx) {
730 qCInfo(lcCseDecryption()) << "Could not create the PKEY context.";
731 handleErrors();
732 return {};
733 }
734
735 err = EVP_PKEY_decrypt_init(ctx);
736 if (err <= 0) {
737 qCInfo(lcCseDecryption()) << "Could not init the decryption of the metadata";
738 handleErrors();
739 return {};
740 }
741
742 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
743 qCInfo(lcCseDecryption()) << "Error setting the encryption padding.";
744 handleErrors();
745 return {};
746 }
747
748 if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()) <= 0) {
749 qCInfo(lcCseDecryption()) << "Error setting OAEP SHA 256";
750 handleErrors();
751 return {};
752 }
753
754 if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()) <= 0) {
755 qCInfo(lcCseDecryption()) << "Error setting MGF1 padding";
756 handleErrors();
757 return {};
758 }
759
760 size_t outlen = 0;
761 err = EVP_PKEY_decrypt(ctx, nullptr, &outlen, (unsigned char *)data.constData(), data.size());
762 if (err <= 0) {
763 qCInfo(lcCseDecryption()) << "Could not determine the buffer length";
764 handleErrors();
765 return {};
766 } else {
767 qCInfo(lcCseDecryption()) << "Size of output is: " << outlen;
768 qCInfo(lcCseDecryption()) << "Size of data is: " << data.size();
769 }
770
771 QByteArray out(static_cast<int>(outlen), '\0');
772
773 if (EVP_PKEY_decrypt(ctx, unsignedData(out), &outlen, (unsigned char *)data.constData(), data.size()) <= 0) {
774 const auto error = handleErrors();
775 qCCritical(lcCseDecryption()) << "Could not decrypt the data." << error;
776 return {};
777 } else {
778 qCInfo(lcCseDecryption()) << "data decrypted successfully";
779 }
780
781 qCInfo(lcCse()) << out;
782 return out;
783 }
784
encryptStringAsymmetric(EVP_PKEY * publicKey,const QByteArray & data)785 QByteArray encryptStringAsymmetric(EVP_PKEY *publicKey, const QByteArray& data) {
786 int err = -1;
787
788 auto ctx = PKeyCtx::forKey(publicKey, ENGINE_get_default_RSA());
789 if (!ctx) {
790 qCInfo(lcCse()) << "Could not initialize the pkey context.";
791 exit(1);
792 }
793
794 if (EVP_PKEY_encrypt_init(ctx) != 1) {
795 qCInfo(lcCse()) << "Error initilaizing the encryption.";
796 exit(1);
797 }
798
799 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
800 qCInfo(lcCse()) << "Error setting the encryption padding.";
801 exit(1);
802 }
803
804 if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()) <= 0) {
805 qCInfo(lcCse()) << "Error setting OAEP SHA 256";
806 exit(1);
807 }
808
809 if (EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()) <= 0) {
810 qCInfo(lcCse()) << "Error setting MGF1 padding";
811 exit(1);
812 }
813
814 size_t outLen = 0;
815 if (EVP_PKEY_encrypt(ctx, nullptr, &outLen, (unsigned char *)data.constData(), data.size()) != 1) {
816 qCInfo(lcCse()) << "Error retrieving the size of the encrypted data";
817 exit(1);
818 } else {
819 qCInfo(lcCse()) << "Encryption Length:" << outLen;
820 }
821
822 QByteArray out(static_cast<int>(outLen), '\0');
823 if (EVP_PKEY_encrypt(ctx, unsignedData(out), &outLen, (unsigned char *)data.constData(), data.size()) != 1) {
824 qCInfo(lcCse()) << "Could not encrypt key." << err;
825 exit(1);
826 }
827
828 // Transform the encrypted data into base64.
829 qCInfo(lcCse()) << out.toBase64();
830 return out.toBase64();
831 }
832
833 }
834
835
836 ClientSideEncryption::ClientSideEncryption() = default;
837
initialize(const AccountPtr & account)838 void ClientSideEncryption::initialize(const AccountPtr &account)
839 {
840 Q_ASSERT(account);
841
842 qCInfo(lcCse()) << "Initializing";
843 if (!account->capabilities().clientSideEncryptionAvailable()) {
844 qCInfo(lcCse()) << "No Client side encryption available on server.";
845 emit initializationFinished();
846 return;
847 }
848
849 fetchFromKeyChain(account);
850 }
851
fetchFromKeyChain(const AccountPtr & account)852 void ClientSideEncryption::fetchFromKeyChain(const AccountPtr &account)
853 {
854 const QString kck = AbstractCredentials::keychainKey(
855 account->url().toString(),
856 account->credentials()->user() + e2e_cert,
857 account->id()
858 );
859
860 auto *job = new ReadPasswordJob(Theme::instance()->appName());
861 job->setProperty(accountProperty, QVariant::fromValue(account));
862 job->setInsecureFallback(false);
863 job->setKey(kck);
864 connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::publicKeyFetched);
865 job->start();
866 }
867
checkPublicKeyValidity(const AccountPtr & account) const868 bool ClientSideEncryption::checkPublicKeyValidity(const AccountPtr &account) const
869 {
870 QByteArray data = EncryptionHelper::generateRandom(64);
871
872 Bio publicKeyBio;
873 QByteArray publicKeyPem = account->e2e()->_publicKey.toPem();
874 BIO_write(publicKeyBio, publicKeyPem.constData(), publicKeyPem.size());
875 auto publicKey = PKey::readPublicKey(publicKeyBio);
876
877 auto encryptedData = EncryptionHelper::encryptStringAsymmetric(publicKey, data.toBase64());
878
879 Bio privateKeyBio;
880 QByteArray privateKeyPem = account->e2e()->_privateKey;
881 BIO_write(privateKeyBio, privateKeyPem.constData(), privateKeyPem.size());
882 auto key = PKey::readPrivateKey(privateKeyBio);
883
884 QByteArray decryptResult = QByteArray::fromBase64(EncryptionHelper::decryptStringAsymmetric( key, QByteArray::fromBase64(encryptedData)));
885
886 if (data != decryptResult) {
887 qCInfo(lcCse()) << "invalid private key";
888 return false;
889 }
890
891 return true;
892 }
893
checkServerPublicKeyValidity(const QByteArray & serverPublicKeyString) const894 bool ClientSideEncryption::checkServerPublicKeyValidity(const QByteArray &serverPublicKeyString) const
895 {
896 Bio serverPublicKeyBio;
897 BIO_write(serverPublicKeyBio, serverPublicKeyString.constData(), serverPublicKeyString.size());
898 const auto serverPublicKey = PKey::readPrivateKey(serverPublicKeyBio);
899
900 Bio certificateBio;
901 const auto certificatePem = _certificate.toPem();
902 BIO_write(certificateBio, certificatePem.constData(), certificatePem.size());
903 const auto x509Certificate = X509Certificate::readCertificate(certificateBio);
904 if (!x509Certificate) {
905 qCInfo(lcCse()) << "Client certificate is invalid. Could not check it against the server public key";
906 return false;
907 }
908
909 if (X509_verify(x509Certificate, serverPublicKey) == 0) {
910 qCInfo(lcCse()) << "Client certificate is not valid against the server public key";
911 return false;
912 }
913
914 qCDebug(lcCse()) << "Client certificate is valid against server public key";
915 return true;
916 }
917
publicKeyFetched(Job * incoming)918 void ClientSideEncryption::publicKeyFetched(Job *incoming)
919 {
920 auto *readJob = static_cast<ReadPasswordJob *>(incoming);
921 auto account = readJob->property(accountProperty).value<AccountPtr>();
922 Q_ASSERT(account);
923
924 // Error or no valid public key error out
925 if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
926 getPublicKeyFromServer(account);
927 return;
928 }
929
930 _certificate = QSslCertificate(readJob->binaryData(), QSsl::Pem);
931
932 if (_certificate.isNull()) {
933 getPublicKeyFromServer(account);
934 return;
935 }
936
937 _publicKey = _certificate.publicKey();
938
939 qCInfo(lcCse()) << "Public key fetched from keychain";
940
941 const QString kck = AbstractCredentials::keychainKey(
942 account->url().toString(),
943 account->credentials()->user() + e2e_private,
944 account->id()
945 );
946
947 auto *job = new ReadPasswordJob(Theme::instance()->appName());
948 job->setProperty(accountProperty, QVariant::fromValue(account));
949 job->setInsecureFallback(false);
950 job->setKey(kck);
951 connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::privateKeyFetched);
952 job->start();
953 }
954
privateKeyFetched(Job * incoming)955 void ClientSideEncryption::privateKeyFetched(Job *incoming)
956 {
957 auto *readJob = static_cast<ReadPasswordJob *>(incoming);
958 auto account = readJob->property(accountProperty).value<AccountPtr>();
959 Q_ASSERT(account);
960
961 // Error or no valid public key error out
962 if (readJob->error() != NoError || readJob->binaryData().length() == 0) {
963 _certificate = QSslCertificate();
964 _publicKey = QSslKey();
965 getPublicKeyFromServer(account);
966 return;
967 }
968
969 //_privateKey = QSslKey(readJob->binaryData(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
970 _privateKey = readJob->binaryData();
971
972 if (_privateKey.isNull()) {
973 getPrivateKeyFromServer(account);
974 return;
975 }
976
977 qCInfo(lcCse()) << "Private key fetched from keychain";
978
979 const QString kck = AbstractCredentials::keychainKey(
980 account->url().toString(),
981 account->credentials()->user() + e2e_mnemonic,
982 account->id()
983 );
984
985 auto *job = new ReadPasswordJob(Theme::instance()->appName());
986 job->setProperty(accountProperty, QVariant::fromValue(account));
987 job->setInsecureFallback(false);
988 job->setKey(kck);
989 connect(job, &ReadPasswordJob::finished, this, &ClientSideEncryption::mnemonicKeyFetched);
990 job->start();
991 }
992
mnemonicKeyFetched(QKeychain::Job * incoming)993 void ClientSideEncryption::mnemonicKeyFetched(QKeychain::Job *incoming)
994 {
995 auto *readJob = static_cast<ReadPasswordJob *>(incoming);
996 auto account = readJob->property(accountProperty).value<AccountPtr>();
997 Q_ASSERT(account);
998
999 // Error or no valid public key error out
1000 if (readJob->error() != NoError || readJob->textData().length() == 0) {
1001 _certificate = QSslCertificate();
1002 _publicKey = QSslKey();
1003 _privateKey = QByteArray();
1004 getPublicKeyFromServer(account);
1005 return;
1006 }
1007
1008 _mnemonic = readJob->textData();
1009
1010 qCInfo(lcCse()) << "Mnemonic key fetched from keychain: " << _mnemonic;
1011
1012 emit initializationFinished();
1013 }
1014
writePrivateKey(const AccountPtr & account)1015 void ClientSideEncryption::writePrivateKey(const AccountPtr &account)
1016 {
1017 const QString kck = AbstractCredentials::keychainKey(
1018 account->url().toString(),
1019 account->credentials()->user() + e2e_private,
1020 account->id()
1021 );
1022
1023 auto *job = new WritePasswordJob(Theme::instance()->appName());
1024 job->setInsecureFallback(false);
1025 job->setKey(kck);
1026 job->setBinaryData(_privateKey);
1027 connect(job, &WritePasswordJob::finished, [](Job *incoming) {
1028 Q_UNUSED(incoming);
1029 qCInfo(lcCse()) << "Private key stored in keychain";
1030 });
1031 job->start();
1032 }
1033
writeCertificate(const AccountPtr & account)1034 void ClientSideEncryption::writeCertificate(const AccountPtr &account)
1035 {
1036 const QString kck = AbstractCredentials::keychainKey(
1037 account->url().toString(),
1038 account->credentials()->user() + e2e_cert,
1039 account->id()
1040 );
1041
1042 auto *job = new WritePasswordJob(Theme::instance()->appName());
1043 job->setInsecureFallback(false);
1044 job->setKey(kck);
1045 job->setBinaryData(_certificate.toPem());
1046 connect(job, &WritePasswordJob::finished, [](Job *incoming) {
1047 Q_UNUSED(incoming);
1048 qCInfo(lcCse()) << "Certificate stored in keychain";
1049 });
1050 job->start();
1051 }
1052
writeMnemonic(const AccountPtr & account)1053 void ClientSideEncryption::writeMnemonic(const AccountPtr &account)
1054 {
1055 const QString kck = AbstractCredentials::keychainKey(
1056 account->url().toString(),
1057 account->credentials()->user() + e2e_mnemonic,
1058 account->id()
1059 );
1060
1061 auto *job = new WritePasswordJob(Theme::instance()->appName());
1062 job->setInsecureFallback(false);
1063 job->setKey(kck);
1064 job->setTextData(_mnemonic);
1065 connect(job, &WritePasswordJob::finished, [](Job *incoming) {
1066 Q_UNUSED(incoming);
1067 qCInfo(lcCse()) << "Mnemonic stored in keychain";
1068 });
1069 job->start();
1070 }
1071
forgetSensitiveData(const AccountPtr & account)1072 void ClientSideEncryption::forgetSensitiveData(const AccountPtr &account)
1073 {
1074 _privateKey = QByteArray();
1075 _certificate = QSslCertificate();
1076 _publicKey = QSslKey();
1077 _mnemonic = QString();
1078
1079 auto startDeleteJob = [account](QString user) {
1080 auto *job = new DeletePasswordJob(Theme::instance()->appName());
1081 job->setInsecureFallback(false);
1082 job->setKey(AbstractCredentials::keychainKey(account->url().toString(), user, account->id()));
1083 job->start();
1084 };
1085
1086 auto user = account->credentials()->user();
1087 startDeleteJob(user + e2e_private);
1088 startDeleteJob(user + e2e_cert);
1089 startDeleteJob(user + e2e_mnemonic);
1090 }
1091
slotRequestMnemonic()1092 void ClientSideEncryption::slotRequestMnemonic()
1093 {
1094 emit showMnemonic(_mnemonic);
1095 }
1096
generateKeyPair(const AccountPtr & account)1097 void ClientSideEncryption::generateKeyPair(const AccountPtr &account)
1098 {
1099 // AES/GCM/NoPadding,
1100 // metadataKeys with RSA/ECB/OAEPWithSHA-256AndMGF1Padding
1101 qCInfo(lcCse()) << "No public key, generating a pair.";
1102 const int rsaKeyLen = 2048;
1103
1104
1105 // Init RSA
1106 PKeyCtx ctx(EVP_PKEY_RSA);
1107
1108 if(EVP_PKEY_keygen_init(ctx) <= 0) {
1109 qCInfo(lcCse()) << "Couldn't initialize the key generator";
1110 return;
1111 }
1112
1113 if(EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, rsaKeyLen) <= 0) {
1114 qCInfo(lcCse()) << "Couldn't initialize the key generator bits";
1115 return;
1116 }
1117
1118 auto localKeyPair = PKey::generate(ctx);
1119 if(!localKeyPair) {
1120 qCInfo(lcCse()) << "Could not generate the key";
1121 return;
1122 }
1123
1124 qCInfo(lcCse()) << "Key correctly generated";
1125 qCInfo(lcCse()) << "Storing keys locally";
1126
1127 Bio privKey;
1128 if (PEM_write_bio_PrivateKey(privKey, localKeyPair, nullptr, nullptr, 0, nullptr, nullptr) <= 0) {
1129 qCInfo(lcCse()) << "Could not read private key from bio.";
1130 return;
1131 }
1132 QByteArray key = BIO2ByteArray(privKey);
1133 //_privateKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
1134 _privateKey = key;
1135
1136 qCInfo(lcCse()) << "Keys generated correctly, sending to server.";
1137 generateCSR(account, localKeyPair);
1138 }
1139
generateCSR(const AccountPtr & account,EVP_PKEY * keyPair)1140 void ClientSideEncryption::generateCSR(const AccountPtr &account, EVP_PKEY *keyPair)
1141 {
1142 // OpenSSL expects const char.
1143 auto cnArray = account->davUser().toLocal8Bit();
1144 qCInfo(lcCse()) << "Getting the following array for the account Id" << cnArray;
1145
1146 auto certParams = std::map<const char *, const char*>{
1147 {"C", "DE"},
1148 {"ST", "Baden-Wuerttemberg"},
1149 {"L", "Stuttgart"},
1150 {"O","Nextcloud"},
1151 {"CN", cnArray.constData()}
1152 };
1153
1154 int ret = 0;
1155 int nVersion = 1;
1156
1157 // 2. set version of x509 req
1158 X509_REQ *x509_req = X509_REQ_new();
1159 auto release_on_exit_x509_req = qScopeGuard([&] {
1160 X509_REQ_free(x509_req);
1161 });
1162
1163 ret = X509_REQ_set_version(x509_req, nVersion);
1164
1165 // 3. set subject of x509 req
1166 auto x509_name = X509_REQ_get_subject_name(x509_req);
1167
1168 for(const auto& v : certParams) {
1169 ret = X509_NAME_add_entry_by_txt(x509_name, v.first, MBSTRING_ASC, (const unsigned char*) v.second, -1, -1, 0);
1170 if (ret != 1) {
1171 qCInfo(lcCse()) << "Error Generating the Certificate while adding" << v.first << v.second;
1172 return;
1173 }
1174 }
1175
1176 ret = X509_REQ_set_pubkey(x509_req, keyPair);
1177 if (ret != 1){
1178 qCInfo(lcCse()) << "Error setting the public key on the csr";
1179 return;
1180 }
1181
1182 ret = X509_REQ_sign(x509_req, keyPair, EVP_sha1()); // return x509_req->signature->length
1183 if (ret <= 0){
1184 qCInfo(lcCse()) << "Error setting the public key on the csr";
1185 return;
1186 }
1187
1188 Bio out;
1189 ret = PEM_write_bio_X509_REQ(out, x509_req);
1190 QByteArray output = BIO2ByteArray(out);
1191
1192 qCInfo(lcCse()) << "Returning the certificate";
1193 qCInfo(lcCse()) << output;
1194
1195 auto job = new SignPublicKeyApiJob(account, e2eeBaseUrl() + "public-key", this);
1196 job->setCsr(output);
1197
1198 connect(job, &SignPublicKeyApiJob::jsonReceived, [this, account](const QJsonDocument& json, int retCode) {
1199 if (retCode == 200) {
1200 QString cert = json.object().value("ocs").toObject().value("data").toObject().value("public-key").toString();
1201 _certificate = QSslCertificate(cert.toLocal8Bit(), QSsl::Pem);
1202 _publicKey = _certificate.publicKey();
1203 fetchAndValidatePublicKeyFromServer(account);
1204 }
1205 qCInfo(lcCse()) << retCode;
1206 });
1207 job->start();
1208 }
1209
encryptPrivateKey(const AccountPtr & account)1210 void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account)
1211 {
1212 QStringList list = WordList::getRandomWords(12);
1213 _mnemonic = list.join(' ');
1214 _newMnemonicGenerated = true;
1215 qCInfo(lcCse()) << "mnemonic Generated:" << _mnemonic;
1216
1217 emit mnemonicGenerated(_mnemonic);
1218
1219 QString passPhrase = list.join(QString()).toLower();
1220 qCInfo(lcCse()) << "Passphrase Generated:" << passPhrase;
1221
1222 auto salt = EncryptionHelper::generateRandom(40);
1223 auto secretKey = EncryptionHelper::generatePassword(passPhrase, salt);
1224 auto cryptedText = EncryptionHelper::encryptPrivateKey(secretKey, EncryptionHelper::privateKeyToPem(_privateKey), salt);
1225
1226 // Send private key to the server
1227 auto job = new StorePrivateKeyApiJob(account, e2eeBaseUrl() + "private-key", this);
1228 job->setPrivateKey(cryptedText);
1229 connect(job, &StorePrivateKeyApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
1230 Q_UNUSED(doc);
1231 switch(retCode) {
1232 case 200:
1233 qCInfo(lcCse()) << "Private key stored encrypted on server.";
1234 writePrivateKey(account);
1235 writeCertificate(account);
1236 writeMnemonic(account);
1237 emit initializationFinished();
1238 break;
1239 default:
1240 qCInfo(lcCse()) << "Store private key failed, return code:" << retCode;
1241 }
1242 });
1243 job->start();
1244 }
1245
newMnemonicGenerated() const1246 bool ClientSideEncryption::newMnemonicGenerated() const
1247 {
1248 return _newMnemonicGenerated;
1249 }
1250
decryptPrivateKey(const AccountPtr & account,const QByteArray & key)1251 void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QByteArray &key) {
1252 QString msg = tr("Please enter your end to end encryption passphrase:<br>"
1253 "<br>"
1254 "User: %2<br>"
1255 "Account: %3<br>")
1256 .arg(Utility::escape(account->credentials()->user()),
1257 Utility::escape(account->displayName()));
1258
1259 QInputDialog dialog;
1260 dialog.setWindowTitle(tr("Enter E2E passphrase"));
1261 dialog.setLabelText(msg);
1262 dialog.setTextEchoMode(QLineEdit::Normal);
1263
1264 QString prev;
1265
1266 while(true) {
1267 if (!prev.isEmpty()) {
1268 dialog.setTextValue(prev);
1269 }
1270 bool ok = dialog.exec();
1271 if (ok) {
1272 qCInfo(lcCse()) << "Got mnemonic:" << dialog.textValue();
1273 prev = dialog.textValue();
1274
1275 _mnemonic = prev;
1276 QString mnemonic = prev.split(" ").join(QString()).toLower();
1277 qCInfo(lcCse()) << "mnemonic:" << mnemonic;
1278
1279 // split off salt
1280 const auto salt = EncryptionHelper::extractPrivateKeySalt(key);
1281
1282 auto pass = EncryptionHelper::generatePassword(mnemonic, salt);
1283 qCInfo(lcCse()) << "Generated key:" << pass;
1284
1285 QByteArray privateKey = EncryptionHelper::decryptPrivateKey(pass, key);
1286 //_privateKey = QSslKey(privateKey, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
1287 _privateKey = privateKey;
1288
1289 qCInfo(lcCse()) << "Private key: " << _privateKey;
1290
1291 if (!_privateKey.isNull() && checkPublicKeyValidity(account)) {
1292 writePrivateKey(account);
1293 writeCertificate(account);
1294 writeMnemonic(account);
1295 break;
1296 }
1297 } else {
1298 _mnemonic = QString();
1299 _privateKey = QByteArray();
1300 qCInfo(lcCse()) << "Cancelled";
1301 break;
1302 }
1303 }
1304
1305 emit initializationFinished();
1306 }
1307
getPrivateKeyFromServer(const AccountPtr & account)1308 void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account)
1309 {
1310 qCInfo(lcCse()) << "Retrieving private key from server";
1311 auto job = new JsonApiJob(account, e2eeBaseUrl() + "private-key", this);
1312 connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
1313 if (retCode == 200) {
1314 QString key = doc.object()["ocs"].toObject()["data"].toObject()["private-key"].toString();
1315 qCInfo(lcCse()) << key;
1316 qCInfo(lcCse()) << "Found private key, lets decrypt it!";
1317 decryptPrivateKey(account, key.toLocal8Bit());
1318 } else if (retCode == 404) {
1319 qCInfo(lcCse()) << "No private key on the server: setup is incomplete.";
1320 } else {
1321 qCInfo(lcCse()) << "Error while requesting public key: " << retCode;
1322 }
1323 });
1324 job->start();
1325 }
1326
getPublicKeyFromServer(const AccountPtr & account)1327 void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account)
1328 {
1329 qCInfo(lcCse()) << "Retrieving public key from server";
1330 auto job = new JsonApiJob(account, e2eeBaseUrl() + "public-key", this);
1331 connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
1332 if (retCode == 200) {
1333 QString publicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-keys"].toObject()[account->davUser()].toString();
1334 _certificate = QSslCertificate(publicKey.toLocal8Bit(), QSsl::Pem);
1335 _publicKey = _certificate.publicKey();
1336 qCInfo(lcCse()) << "Found Public key, requesting Server Public Key. Public key:" << publicKey;
1337 fetchAndValidatePublicKeyFromServer(account);
1338 } else if (retCode == 404) {
1339 qCInfo(lcCse()) << "No public key on the server";
1340 generateKeyPair(account);
1341 } else {
1342 qCInfo(lcCse()) << "Error while requesting public key: " << retCode;
1343 }
1344 });
1345 job->start();
1346 }
1347
fetchAndValidatePublicKeyFromServer(const AccountPtr & account)1348 void ClientSideEncryption::fetchAndValidatePublicKeyFromServer(const AccountPtr &account)
1349 {
1350 qCInfo(lcCse()) << "Retrieving public key from server";
1351 auto job = new JsonApiJob(account, e2eeBaseUrl() + "server-key", this);
1352 connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
1353 if (retCode == 200) {
1354 const auto serverPublicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-key"].toString().toLatin1();
1355 qCInfo(lcCse()) << "Found Server Public key, checking it. Server public key:" << serverPublicKey;
1356 if (checkServerPublicKeyValidity(serverPublicKey)) {
1357 if (_privateKey.isEmpty()) {
1358 qCInfo(lcCse()) << "Valid Server Public key, requesting Private Key.";
1359 getPrivateKeyFromServer(account);
1360 } else {
1361 qCInfo(lcCse()) << "Certificate saved, Encrypting Private Key.";
1362 encryptPrivateKey(account);
1363 }
1364 } else {
1365 qCInfo(lcCse()) << "Error invalid server public key";
1366 _certificate = QSslCertificate();
1367 _publicKey = QSslKey();
1368 _privateKey = QByteArray();
1369 getPublicKeyFromServer(account);
1370 return;
1371 }
1372 } else {
1373 qCInfo(lcCse()) << "Error while requesting server public key: " << retCode;
1374 }
1375 });
1376 job->start();
1377 }
1378
FolderMetadata(AccountPtr account,const QByteArray & metadata,int statusCode)1379 FolderMetadata::FolderMetadata(AccountPtr account, const QByteArray& metadata, int statusCode) : _account(account)
1380 {
1381 if (metadata.isEmpty() || statusCode == 404) {
1382 qCInfo(lcCseMetadata()) << "Setupping Empty Metadata";
1383 setupEmptyMetadata();
1384 } else {
1385 qCInfo(lcCseMetadata()) << "Setting up existing metadata";
1386 setupExistingMetadata(metadata);
1387 }
1388 }
1389
setupExistingMetadata(const QByteArray & metadata)1390 void FolderMetadata::setupExistingMetadata(const QByteArray& metadata)
1391 {
1392 /* This is the json response from the server, it contains two extra objects that we are *not* interested.
1393 * ocs and data.
1394 */
1395 QJsonDocument doc = QJsonDocument::fromJson(metadata);
1396 qCInfo(lcCseMetadata()) << doc.toJson(QJsonDocument::Compact);
1397
1398 // The metadata is being retrieved as a string stored in a json.
1399 // This *seems* to be broken but the RFC doesn't explicits how it wants.
1400 // I'm currently unsure if this is error on my side or in the server implementation.
1401 // And because inside of the meta-data there's an object called metadata, without '-'
1402 // make it really different.
1403
1404 QString metaDataStr = doc.object()["ocs"]
1405 .toObject()["data"]
1406 .toObject()["meta-data"]
1407 .toString();
1408
1409 QJsonDocument metaDataDoc = QJsonDocument::fromJson(metaDataStr.toLocal8Bit());
1410 QJsonObject metadataObj = metaDataDoc.object()["metadata"].toObject();
1411 QJsonObject metadataKeys = metadataObj["metadataKeys"].toObject();
1412 QByteArray sharing = metadataObj["sharing"].toString().toLocal8Bit();
1413 QJsonObject files = metaDataDoc.object()["files"].toObject();
1414
1415 QJsonDocument debugHelper;
1416 debugHelper.setObject(metadataKeys);
1417 qCDebug(lcCse) << "Keys: " << debugHelper.toJson(QJsonDocument::Compact);
1418
1419 // Iterate over the document to store the keys. I'm unsure that the keys are in order,
1420 // perhaps it's better to store a map instead of a vector, perhaps this just doesn't matter.
1421 for(auto it = metadataKeys.constBegin(), end = metadataKeys.constEnd(); it != end; it++) {
1422 QByteArray currB64Pass = it.value().toString().toLocal8Bit();
1423 /*
1424 * We have to base64 decode the metadatakey here. This was a misunderstanding in the RFC
1425 * Now we should be compatible with Android and IOS. Maybe we can fix it later.
1426 */
1427 QByteArray b64DecryptedKey = decryptMetadataKey(currB64Pass);
1428 if (b64DecryptedKey.isEmpty()) {
1429 qCDebug(lcCse()) << "Could not decrypt metadata for key" << it.key();
1430 continue;
1431 }
1432
1433 QByteArray decryptedKey = QByteArray::fromBase64(b64DecryptedKey);
1434 _metadataKeys.insert(it.key().toInt(), decryptedKey);
1435 }
1436
1437 // Cool, We actually have the key, we can decrypt the rest of the metadata.
1438 qCDebug(lcCse) << "Sharing: " << sharing;
1439 if (sharing.size()) {
1440 auto sharingDecrypted = decryptJsonObject(sharing, _metadataKeys.last());
1441 qCDebug(lcCse) << "Sharing Decrypted" << sharingDecrypted;
1442
1443 //Sharing is also a JSON object, so extract it and populate.
1444 auto sharingDoc = QJsonDocument::fromJson(sharingDecrypted);
1445 auto sharingObj = sharingDoc.object();
1446 for (auto it = sharingObj.constBegin(), end = sharingObj.constEnd(); it != end; it++) {
1447 _sharing.push_back({it.key(), it.value().toString()});
1448 }
1449 } else {
1450 qCDebug(lcCse) << "Skipping sharing section since it is empty";
1451 }
1452
1453 for (auto it = files.constBegin(), end = files.constEnd(); it != end; it++) {
1454 EncryptedFile file;
1455 file.encryptedFilename = it.key();
1456
1457 auto fileObj = it.value().toObject();
1458 file.metadataKey = fileObj["metadataKey"].toInt();
1459 file.authenticationTag = QByteArray::fromBase64(fileObj["authenticationTag"].toString().toLocal8Bit());
1460 file.initializationVector = QByteArray::fromBase64(fileObj["initializationVector"].toString().toLocal8Bit());
1461
1462 //Decrypt encrypted part
1463 QByteArray key = _metadataKeys[file.metadataKey];
1464 auto encryptedFile = fileObj["encrypted"].toString().toLocal8Bit();
1465 auto decryptedFile = decryptJsonObject(encryptedFile, key);
1466 auto decryptedFileDoc = QJsonDocument::fromJson(decryptedFile);
1467 auto decryptedFileObj = decryptedFileDoc.object();
1468
1469 file.originalFilename = decryptedFileObj["filename"].toString();
1470 file.encryptionKey = QByteArray::fromBase64(decryptedFileObj["key"].toString().toLocal8Bit());
1471 file.mimetype = decryptedFileObj["mimetype"].toString().toLocal8Bit();
1472 file.fileVersion = decryptedFileObj["version"].toInt();
1473
1474 // In case we wrongly stored "inode/directory" we try to recover from it
1475 if (file.mimetype == QByteArrayLiteral("inode/directory")) {
1476 file.mimetype = QByteArrayLiteral("httpd/unix-directory");
1477 }
1478
1479 _files.push_back(file);
1480 }
1481 }
1482
1483 // RSA/ECB/OAEPWithSHA-256AndMGF1Padding using private / public key.
encryptMetadataKey(const QByteArray & data) const1484 QByteArray FolderMetadata::encryptMetadataKey(const QByteArray& data) const
1485 {
1486 Bio publicKeyBio;
1487 QByteArray publicKeyPem = _account->e2e()->_publicKey.toPem();
1488 BIO_write(publicKeyBio, publicKeyPem.constData(), publicKeyPem.size());
1489 auto publicKey = PKey::readPublicKey(publicKeyBio);
1490
1491 // The metadata key is binary so base64 encode it first
1492 return EncryptionHelper::encryptStringAsymmetric(publicKey, data.toBase64());
1493 }
1494
decryptMetadataKey(const QByteArray & encryptedMetadata) const1495 QByteArray FolderMetadata::decryptMetadataKey(const QByteArray& encryptedMetadata) const
1496 {
1497 Bio privateKeyBio;
1498 QByteArray privateKeyPem = _account->e2e()->_privateKey;
1499 BIO_write(privateKeyBio, privateKeyPem.constData(), privateKeyPem.size());
1500 auto key = PKey::readPrivateKey(privateKeyBio);
1501
1502 // Also base64 decode the result
1503 QByteArray decryptResult = EncryptionHelper::decryptStringAsymmetric(
1504 key, QByteArray::fromBase64(encryptedMetadata));
1505
1506 if (decryptResult.isEmpty())
1507 {
1508 qCDebug(lcCse()) << "ERROR. Could not decrypt the metadata key";
1509 return {};
1510 }
1511 return QByteArray::fromBase64(decryptResult);
1512 }
1513
1514 // AES/GCM/NoPadding (128 bit key size)
encryptJsonObject(const QByteArray & obj,const QByteArray pass) const1515 QByteArray FolderMetadata::encryptJsonObject(const QByteArray& obj, const QByteArray pass) const
1516 {
1517 return EncryptionHelper::encryptStringSymmetric(pass, obj);
1518 }
1519
decryptJsonObject(const QByteArray & encryptedMetadata,const QByteArray & pass) const1520 QByteArray FolderMetadata::decryptJsonObject(const QByteArray& encryptedMetadata, const QByteArray& pass) const
1521 {
1522 return EncryptionHelper::decryptStringSymmetric(pass, encryptedMetadata);
1523 }
1524
setupEmptyMetadata()1525 void FolderMetadata::setupEmptyMetadata() {
1526 qCDebug(lcCse) << "Settint up empty metadata";
1527 QByteArray newMetadataPass = EncryptionHelper::generateRandom(16);
1528 _metadataKeys.insert(0, newMetadataPass);
1529
1530 QString publicKey = _account->e2e()->_publicKey.toPem().toBase64();
1531 QString displayName = _account->displayName();
1532
1533 _sharing.append({displayName, publicKey});
1534 }
1535
encryptedMetadata()1536 QByteArray FolderMetadata::encryptedMetadata() {
1537 qCDebug(lcCse) << "Generating metadata";
1538
1539 QJsonObject metadataKeys;
1540 for (auto it = _metadataKeys.constBegin(), end = _metadataKeys.constEnd(); it != end; it++) {
1541 /*
1542 * We have to already base64 encode the metadatakey here. This was a misunderstanding in the RFC
1543 * Now we should be compatible with Android and IOS. Maybe we can fix it later.
1544 */
1545 const QByteArray encryptedKey = encryptMetadataKey(it.value().toBase64());
1546 metadataKeys.insert(QString::number(it.key()), QString(encryptedKey));
1547 }
1548
1549 /* NO SHARING IN V1
1550 QJsonObject recepients;
1551 for (auto it = _sharing.constBegin(), end = _sharing.constEnd(); it != end; it++) {
1552 recepients.insert(it->first, it->second);
1553 }
1554 QJsonDocument recepientDoc;
1555 recepientDoc.setObject(recepients);
1556 QString sharingEncrypted = encryptJsonObject(recepientDoc.toJson(QJsonDocument::Compact), _metadataKeys.last());
1557 */
1558
1559 QJsonObject metadata = {
1560 {"metadataKeys", metadataKeys},
1561 // {"sharing", sharingEncrypted},
1562 {"version", 1}
1563 };
1564
1565 QJsonObject files;
1566 for (auto it = _files.constBegin(), end = _files.constEnd(); it != end; it++) {
1567 QJsonObject encrypted;
1568 encrypted.insert("key", QString(it->encryptionKey.toBase64()));
1569 encrypted.insert("filename", it->originalFilename);
1570 encrypted.insert("mimetype", QString(it->mimetype));
1571 encrypted.insert("version", it->fileVersion);
1572 QJsonDocument encryptedDoc;
1573 encryptedDoc.setObject(encrypted);
1574
1575 QString encryptedEncrypted = encryptJsonObject(encryptedDoc.toJson(QJsonDocument::Compact), _metadataKeys.last());
1576 if (encryptedEncrypted.isEmpty()) {
1577 qCDebug(lcCse) << "Metadata generation failed!";
1578 }
1579
1580 QJsonObject file;
1581 file.insert("encrypted", encryptedEncrypted);
1582 file.insert("initializationVector", QString(it->initializationVector.toBase64()));
1583 file.insert("authenticationTag", QString(it->authenticationTag.toBase64()));
1584 file.insert("metadataKey", _metadataKeys.lastKey());
1585
1586 files.insert(it->encryptedFilename, file);
1587 }
1588
1589 QJsonObject metaObject = {
1590 {"metadata", metadata},
1591 {"files", files}
1592 };
1593
1594 QJsonDocument internalMetadata;
1595 internalMetadata.setObject(metaObject);
1596 return internalMetadata.toJson();
1597 }
1598
addEncryptedFile(const EncryptedFile & f)1599 void FolderMetadata::addEncryptedFile(const EncryptedFile &f) {
1600
1601 for (int i = 0; i < _files.size(); i++) {
1602 if (_files.at(i).originalFilename == f.originalFilename) {
1603 _files.removeAt(i);
1604 break;
1605 }
1606 }
1607
1608 _files.append(f);
1609 }
1610
removeEncryptedFile(const EncryptedFile & f)1611 void FolderMetadata::removeEncryptedFile(const EncryptedFile &f)
1612 {
1613 for (int i = 0; i < _files.size(); i++) {
1614 if (_files.at(i).originalFilename == f.originalFilename) {
1615 _files.removeAt(i);
1616 break;
1617 }
1618 }
1619 }
1620
removeAllEncryptedFiles()1621 void FolderMetadata::removeAllEncryptedFiles()
1622 {
1623 _files.clear();
1624 }
1625
files() const1626 QVector<EncryptedFile> FolderMetadata::files() const {
1627 return _files;
1628 }
1629
fileEncryption(const QByteArray & key,const QByteArray & iv,QFile * input,QFile * output,QByteArray & returnTag)1630 bool EncryptionHelper::fileEncryption(const QByteArray &key, const QByteArray &iv, QFile *input, QFile *output, QByteArray& returnTag)
1631 {
1632 if (!input->open(QIODevice::ReadOnly)) {
1633 qCDebug(lcCse) << "Could not open input file for reading" << input->errorString();
1634 }
1635 if (!output->open(QIODevice::WriteOnly)) {
1636 qCDebug(lcCse) << "Could not oppen output file for writing" << output->errorString();
1637 }
1638
1639 // Init
1640 CipherCtx ctx;
1641
1642 /* Create and initialise the context */
1643 if(!ctx) {
1644 qCInfo(lcCse()) << "Could not create context";
1645 return false;
1646 }
1647
1648 /* Initialise the decryption operation. */
1649 if(!EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
1650 qCInfo(lcCse()) << "Could not init cipher";
1651 return false;
1652 }
1653
1654 EVP_CIPHER_CTX_set_padding(ctx, 0);
1655
1656 /* Set IV length. */
1657 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
1658 qCInfo(lcCse()) << "Could not set iv length";
1659 return false;
1660 }
1661
1662 /* Initialise key and IV */
1663 if(!EVP_EncryptInit_ex(ctx, nullptr, nullptr, (const unsigned char *)key.constData(), (const unsigned char *)iv.constData())) {
1664 qCInfo(lcCse()) << "Could not set key and iv";
1665 return false;
1666 }
1667
1668 QByteArray out(blockSize + OCC::Constants::e2EeTagSize - 1, '\0');
1669 int len = 0;
1670 int total_len = 0;
1671
1672 qCDebug(lcCse) << "Starting to encrypt the file" << input->fileName() << input->atEnd();
1673 while(!input->atEnd()) {
1674 const auto data = input->read(blockSize);
1675
1676 if (data.size() == 0) {
1677 qCInfo(lcCse()) << "Could not read data from file";
1678 return false;
1679 }
1680
1681 if(!EVP_EncryptUpdate(ctx, unsignedData(out), &len, (unsigned char *)data.constData(), data.size())) {
1682 qCInfo(lcCse()) << "Could not encrypt";
1683 return false;
1684 }
1685
1686 output->write(out, len);
1687 total_len += len;
1688 }
1689
1690 if(1 != EVP_EncryptFinal_ex(ctx, unsignedData(out), &len)) {
1691 qCInfo(lcCse()) << "Could finalize encryption";
1692 return false;
1693 }
1694 output->write(out, len);
1695 total_len += len;
1696
1697 /* Get the e2EeTag */
1698 QByteArray e2EeTag(OCC::Constants::e2EeTagSize, '\0');
1699 if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, OCC::Constants::e2EeTagSize, unsignedData(e2EeTag))) {
1700 qCInfo(lcCse()) << "Could not get e2EeTag";
1701 return false;
1702 }
1703
1704 returnTag = e2EeTag;
1705 output->write(e2EeTag, OCC::Constants::e2EeTagSize);
1706
1707 input->close();
1708 output->close();
1709 qCDebug(lcCse) << "File Encrypted Successfully";
1710 return true;
1711 }
1712
fileDecryption(const QByteArray & key,const QByteArray & iv,QFile * input,QFile * output)1713 bool EncryptionHelper::fileDecryption(const QByteArray &key, const QByteArray& iv,
1714 QFile *input, QFile *output)
1715 {
1716 input->open(QIODevice::ReadOnly);
1717 output->open(QIODevice::WriteOnly);
1718
1719 // Init
1720 CipherCtx ctx;
1721
1722 /* Create and initialise the context */
1723 if(!ctx) {
1724 qCInfo(lcCse()) << "Could not create context";
1725 return false;
1726 }
1727
1728 /* Initialise the decryption operation. */
1729 if(!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
1730 qCInfo(lcCse()) << "Could not init cipher";
1731 return false;
1732 }
1733
1734 EVP_CIPHER_CTX_set_padding(ctx, 0);
1735
1736 /* Set IV length. */
1737 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
1738 qCInfo(lcCse()) << "Could not set iv length";
1739 return false;
1740 }
1741
1742 /* Initialise key and IV */
1743 if(!EVP_DecryptInit_ex(ctx, nullptr, nullptr, (const unsigned char *) key.constData(), (const unsigned char *) iv.constData())) {
1744 qCInfo(lcCse()) << "Could not set key and iv";
1745 return false;
1746 }
1747
1748 qint64 size = input->size() - OCC::Constants::e2EeTagSize;
1749
1750 QByteArray out(blockSize + OCC::Constants::e2EeTagSize - 1, '\0');
1751 int len = 0;
1752
1753 while(input->pos() < size) {
1754
1755 auto toRead = size - input->pos();
1756 if (toRead > blockSize) {
1757 toRead = blockSize;
1758 }
1759
1760 QByteArray data = input->read(toRead);
1761
1762 if (data.size() == 0) {
1763 qCInfo(lcCse()) << "Could not read data from file";
1764 return false;
1765 }
1766
1767 if(!EVP_DecryptUpdate(ctx, unsignedData(out), &len, (unsigned char *)data.constData(), data.size())) {
1768 qCInfo(lcCse()) << "Could not decrypt";
1769 return false;
1770 }
1771
1772 output->write(out, len);
1773 }
1774
1775 const QByteArray e2EeTag = input->read(OCC::Constants::e2EeTagSize);
1776
1777 /* Set expected e2EeTag value. Works in OpenSSL 1.0.1d and later */
1778 if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, e2EeTag.size(), (unsigned char *)e2EeTag.constData())) {
1779 qCInfo(lcCse()) << "Could not set expected e2EeTag";
1780 return false;
1781 }
1782
1783 if(1 != EVP_DecryptFinal_ex(ctx, unsignedData(out), &len)) {
1784 qCInfo(lcCse()) << "Could finalize decryption";
1785 return false;
1786 }
1787 output->write(out, len);
1788
1789 input->close();
1790 output->close();
1791 return true;
1792 }
1793
StreamingDecryptor(const QByteArray & key,const QByteArray & iv,quint64 totalSize)1794 EncryptionHelper::StreamingDecryptor::StreamingDecryptor(const QByteArray &key, const QByteArray &iv, quint64 totalSize) : _totalSize(totalSize)
1795 {
1796 if (_ctx && !key.isEmpty() && !iv.isEmpty() && totalSize > 0) {
1797 _isInitialized = true;
1798
1799 /* Initialize the decryption operation. */
1800 if(!EVP_DecryptInit_ex(_ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
1801 qCritical(lcCse()) << "Could not init cipher";
1802 _isInitialized = false;
1803 }
1804
1805 EVP_CIPHER_CTX_set_padding(_ctx, 0);
1806
1807 /* Set IV length. */
1808 if(!EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_SET_IVLEN, iv.size(), nullptr)) {
1809 qCritical(lcCse()) << "Could not set iv length";
1810 _isInitialized = false;
1811 }
1812
1813 /* Initialize key and IV */
1814 if(!EVP_DecryptInit_ex(_ctx, nullptr, nullptr, reinterpret_cast<const unsigned char*>(key.constData()), reinterpret_cast<const unsigned char*>(iv.constData()))) {
1815 qCritical(lcCse()) << "Could not set key and iv";
1816 _isInitialized = false;
1817 }
1818 }
1819 }
1820
chunkDecryption(const char * input,quint64 chunkSize)1821 QByteArray EncryptionHelper::StreamingDecryptor::chunkDecryption(const char *input, quint64 chunkSize)
1822 {
1823 QByteArray byteArray;
1824 QBuffer buffer(&byteArray);
1825 buffer.open(QIODevice::WriteOnly);
1826
1827 Q_ASSERT(isInitialized());
1828 if (!isInitialized()) {
1829 qCritical(lcCse()) << "Decryption failed. Decryptor is not initialized!";
1830 return QByteArray();
1831 }
1832
1833 Q_ASSERT(buffer.isOpen() && buffer.isWritable());
1834 if (!buffer.isOpen() || !buffer.isWritable()) {
1835 qCritical(lcCse()) << "Decryption failed. Incorrect output device!";
1836 return QByteArray();
1837 }
1838
1839 Q_ASSERT(input);
1840 if (!input) {
1841 qCritical(lcCse()) << "Decryption failed. Incorrect input!";
1842 return QByteArray();
1843 }
1844
1845 Q_ASSERT(chunkSize > 0);
1846 if (chunkSize <= 0) {
1847 qCritical(lcCse()) << "Decryption failed. Incorrect chunkSize!";
1848 return QByteArray();
1849 }
1850
1851 if (_decryptedSoFar == 0) {
1852 qCDebug(lcCse()) << "Decryption started";
1853 }
1854
1855 Q_ASSERT(_decryptedSoFar + chunkSize <= _totalSize);
1856 if (_decryptedSoFar + chunkSize > _totalSize) {
1857 qCritical(lcCse()) << "Decryption failed. Chunk is out of range!";
1858 return QByteArray();
1859 }
1860
1861 Q_ASSERT(_decryptedSoFar + chunkSize < OCC::Constants::e2EeTagSize || _totalSize - OCC::Constants::e2EeTagSize >= _decryptedSoFar + chunkSize - OCC::Constants::e2EeTagSize);
1862 if (_decryptedSoFar + chunkSize > OCC::Constants::e2EeTagSize && _totalSize - OCC::Constants::e2EeTagSize < _decryptedSoFar + chunkSize - OCC::Constants::e2EeTagSize) {
1863 qCritical(lcCse()) << "Decryption failed. Incorrect chunk!";
1864 return QByteArray();
1865 }
1866
1867 const bool isLastChunk = _decryptedSoFar + chunkSize == _totalSize;
1868
1869 // last OCC::Constants::e2EeTagSize bytes is ALWAYS a e2EeTag!!!
1870 const qint64 size = isLastChunk ? chunkSize - OCC::Constants::e2EeTagSize : chunkSize;
1871
1872 // either the size is more than 0 and an e2EeTag is at the end of chunk, or, chunk is the e2EeTag itself
1873 Q_ASSERT(size > 0 || chunkSize == OCC::Constants::e2EeTagSize);
1874 if (size <= 0 && chunkSize != OCC::Constants::e2EeTagSize) {
1875 qCritical(lcCse()) << "Decryption failed. Invalid input size: " << size << " !";
1876 return QByteArray();
1877 }
1878
1879 qint64 bytesWritten = 0;
1880 qint64 inputPos = 0;
1881
1882 QByteArray decryptedBlock(blockSize + OCC::Constants::e2EeTagSize - 1, '\0');
1883
1884 while(inputPos < size) {
1885 // read blockSize or less bytes
1886 const QByteArray encryptedBlock(input + inputPos, qMin(size - inputPos, blockSize));
1887
1888 if (encryptedBlock.size() == 0) {
1889 qCritical(lcCse()) << "Could not read data from the input buffer.";
1890 return QByteArray();
1891 }
1892
1893 int outLen = 0;
1894
1895 if(!EVP_DecryptUpdate(_ctx, unsignedData(decryptedBlock), &outLen, reinterpret_cast<const unsigned char*>(encryptedBlock.data()), encryptedBlock.size())) {
1896 qCritical(lcCse()) << "Could not decrypt";
1897 return QByteArray();
1898 }
1899
1900 const auto writtenToOutput = buffer.write(decryptedBlock, outLen);
1901
1902 Q_ASSERT(writtenToOutput == outLen);
1903 if (writtenToOutput != outLen) {
1904 qCritical(lcCse()) << "Failed to write decrypted data to device.";
1905 return QByteArray();
1906 }
1907
1908 bytesWritten += writtenToOutput;
1909
1910 // advance input position for further read
1911 inputPos += encryptedBlock.size();
1912
1913 _decryptedSoFar += encryptedBlock.size();
1914 }
1915
1916 if (isLastChunk) {
1917 // if it's a last chunk, we'd need to read a e2EeTag at the end and finalize the decryption
1918
1919 Q_ASSERT(chunkSize - inputPos == OCC::Constants::e2EeTagSize);
1920 if (chunkSize - inputPos != OCC::Constants::e2EeTagSize) {
1921 qCritical(lcCse()) << "Decryption failed. e2EeTag is missing!";
1922 return QByteArray();
1923 }
1924
1925 int outLen = 0;
1926
1927 QByteArray e2EeTag = QByteArray(input + inputPos, OCC::Constants::e2EeTagSize);
1928
1929 /* Set expected e2EeTag value. Works in OpenSSL 1.0.1d and later */
1930 if(!EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_SET_TAG, e2EeTag.size(), reinterpret_cast<unsigned char*>(e2EeTag.data()))) {
1931 qCritical(lcCse()) << "Could not set expected e2EeTag";
1932 return QByteArray();
1933 }
1934
1935 if(1 != EVP_DecryptFinal_ex(_ctx, unsignedData(decryptedBlock), &outLen)) {
1936 qCritical(lcCse()) << "Could finalize decryption";
1937 return QByteArray();
1938 }
1939
1940 const auto writtenToOutput = buffer.write(decryptedBlock, outLen);
1941
1942 Q_ASSERT(writtenToOutput == outLen);
1943 if (writtenToOutput != outLen) {
1944 qCritical(lcCse()) << "Failed to write decrypted data to device.";
1945 return QByteArray();
1946 }
1947
1948 bytesWritten += writtenToOutput;
1949
1950 _decryptedSoFar += OCC::Constants::e2EeTagSize;
1951
1952 _isFinished = true;
1953 }
1954
1955 if (isFinished()) {
1956 qCDebug(lcCse()) << "Decryption complete";
1957 }
1958
1959 return byteArray;
1960 }
1961
isInitialized() const1962 bool EncryptionHelper::StreamingDecryptor::isInitialized() const
1963 {
1964 return _isInitialized;
1965 }
1966
isFinished() const1967 bool EncryptionHelper::StreamingDecryptor::isFinished() const
1968 {
1969 return _isFinished;
1970 }
1971 }
1972