1 /*
2     Copyright © 2014-2019 by The qTox Project Contributors
3 
4     This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6     qTox is libre software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     qTox is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with qTox.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "rawdatabase.h"
21 
22 #include <cassert>
23 #include <tox/toxencryptsave.h>
24 
25 #include <QCoreApplication>
26 #include <QDebug>
27 #include <QFile>
28 #include <QMetaObject>
29 #include <QMutexLocker>
30 
31 
32 /**
33  * @class RawDatabase
34  * @brief Implements a low level RAII interface to a SQLCipher (SQlite3) database.
35  *
36  * Thread-safe, does all database operations on a worker thread.
37  * The queries must not contain transaction commands (BEGIN/COMMIT/...) or the behavior is
38  * undefined.
39  *
40  * @var QMutex RawDatabase::transactionsMutex;
41  * @brief Protects pendingTransactions
42  */
43 
44 /**
45  * @class Query
46  * @brief A query to be executed by the database.
47  *
48  * Can be composed of one or more SQL statements in the query,
49  * optional BLOB parameters to be bound, and callbacks fired when the query is executed
50  * Calling any database method from a query callback is undefined behavior.
51  *
52  * @var QByteArray RawDatabase::Query::query
53  * @brief UTF-8 query string
54  *
55  * @var QVector<QByteArray> RawDatabase::Query::blobs
56  * @brief Bound data blobs
57  *
58  * @var std::function<void(int64_t)> RawDatabase::Query::insertCallback
59  * @brief Called after execution with the last insert rowid
60  *
61  * @var std::function<void(const QVector<QVariant>&)> RawDatabase::Query::rowCallback
62  * @brief Called during execution for each row
63  *
64  * @var QVector<sqlite3_stmt*> RawDatabase::Query::statements
65  * @brief Statements to be compiled from the query
66  */
67 
68 /**
69  * @struct Transaction
70  * @brief SQL transactions to be processed.
71  *
72  * A transaction is made of queries, which can have bound BLOBs.
73  *
74  * @var std::atomic_bool* RawDatabase::Transaction::success = nullptr;
75  * @brief If not a nullptr, the result of the transaction will be set
76  *
77  * @var std::atomic_bool* RawDatabase::Transaction::done = nullptr;
78  * @brief If not a nullptr, will be set to true when the transaction has been executed
79  */
80 
81 /**
82  * @brief Tries to open a database.
83  * @param path Path to database.
84  * @param password If empty, the database will be opened unencrypted.
85  * Otherwise we will use toxencryptsave to derive a key and encrypt the database.
86  */
RawDatabase(const QString & path,const QString & password,const QByteArray & salt)87 RawDatabase::RawDatabase(const QString& path, const QString& password, const QByteArray& salt)
88     : workerThread{new QThread}
89     , path{path}
90     , currentSalt{salt} // we need the salt later if a new password should be set
91     , currentHexKey{deriveKey(password, salt)}
92 {
93     workerThread->setObjectName("qTox Database");
94     moveToThread(workerThread.get());
95     workerThread->start();
96 
97     // first try with the new salt
98     if (open(path, currentHexKey)) {
99         return;
100     }
101 
102     // avoid opening the same db twice
103     close();
104 
105     // create a backup before trying to upgrade to new salt
106     bool upgrade = true;
107     if (!QFile::copy(path, path + ".bak")) {
108         qDebug() << "Couldn't create the backup of the database, won't upgrade";
109         upgrade = false;
110     }
111 
112     // fall back to the old salt
113     currentHexKey = deriveKey(password);
114     if (open(path, currentHexKey)) {
115         // upgrade only if backup successful
116         if (upgrade) {
117             // still using old salt, upgrade
118             if (setPassword(password)) {
119                 qDebug() << "Successfully upgraded to dynamic salt";
120             } else {
121                 qWarning() << "Failed to set password with new salt";
122             }
123         }
124     } else {
125         qDebug() << "Failed to open database with old salt";
126     }
127 }
128 
~RawDatabase()129 RawDatabase::~RawDatabase()
130 {
131     close();
132     workerThread->exit(0);
133     while (workerThread->isRunning())
134         workerThread->wait(50);
135 }
136 
137 /**
138  * @brief Tries to open the database with the given (possibly empty) key.
139  * @param path Path to database.
140  * @param hexKey Hex representation of the key in string.
141  * @return True if success, false otherwise.
142  */
open(const QString & path,const QString & hexKey)143 bool RawDatabase::open(const QString& path, const QString& hexKey)
144 {
145     if (QThread::currentThread() != workerThread.get()) {
146         bool ret;
147         QMetaObject::invokeMethod(this, "open", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret),
148                                   Q_ARG(const QString&, path), Q_ARG(const QString&, hexKey));
149         return ret;
150     }
151 
152     if (!QFile::exists(path) && QFile::exists(path + ".tmp")) {
153         qWarning() << "Restoring database from temporary export file! Did we crash while changing "
154                       "the password or upgrading?";
155         QFile::rename(path + ".tmp", path);
156     }
157 
158     if (sqlite3_open_v2(path.toUtf8().data(), &sqlite,
159                         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, nullptr)
160         != SQLITE_OK) {
161         qWarning() << "Failed to open database" << path << "with error:" << sqlite3_errmsg(sqlite);
162         return false;
163     }
164 
165     if (sqlite3_create_function(sqlite, "regexp", 2, SQLITE_UTF8, nullptr, &RawDatabase::regexpInsensitive, nullptr, nullptr)) {
166         qWarning() << "Failed to create function regexp";
167         close();
168         return false;
169     }
170 
171     if (sqlite3_create_function(sqlite, "regexpsensitive", 2, SQLITE_UTF8, nullptr, &RawDatabase::regexpSensitive, nullptr, nullptr)) {
172         qWarning() << "Failed to create function regexpsensitive";
173         close();
174         return false;
175     }
176 
177     if (!hexKey.isEmpty()) {
178         if (!openEncryptedDatabaseAtLatestSupportedVersion(hexKey)) {
179             close();
180             return false;
181         }
182     }
183     return true;
184 }
185 
openEncryptedDatabaseAtLatestSupportedVersion(const QString & hexKey)186 bool RawDatabase::openEncryptedDatabaseAtLatestSupportedVersion(const QString& hexKey)
187 {
188     // old qTox database are saved with SQLCipher 3.x defaults. For a period after 1.16.3 but before 1.17.0, databases
189     // could be partially upgraded to SQLCipher 4.0 defaults, since SQLCipher 3.x isn't capable of setitng all the same
190     // params. If SQLCipher 4.x happened to be used, they would have been fully upgraded to 4.0 default params.
191     // We need to support all three of these cases, so also upgrade to the latest possible params while we're here
192     if (!setKey(hexKey)) {
193         return false;
194     }
195 
196     auto highestSupportedVersion = highestSupportedParams();
197     if (setCipherParameters(highestSupportedVersion)) {
198         if (testUsable()) {
199             qInfo() << "Opened database with SQLCipher" << toString(highestSupportedVersion) << "parameters";
200             return true;
201         } else {
202             return updateSavedCipherParameters(hexKey, highestSupportedVersion);
203         }
204     } else {
205         qCritical() << "Failed to set latest supported SQLCipher params!";
206         return false;
207     }
208 }
209 
testUsable()210 bool RawDatabase::testUsable()
211 {
212     // this will unfortunately log a warning if it fails, even though we may expect failure
213     return execNow("SELECT count(*) FROM sqlite_master;");
214 }
215 
216 /**
217  * @brief Changes stored db encryption from SQLCipher 3.x defaults to 4.x defaults
218  */
updateSavedCipherParameters(const QString & hexKey,SqlCipherParams newParams)219 bool RawDatabase::updateSavedCipherParameters(const QString& hexKey, SqlCipherParams newParams)
220 {
221     auto currentParams = readSavedCipherParams(hexKey, newParams);
222     setKey(hexKey); // setKey again because a SELECT has already been run, causing crypto settings to take effect
223     if (!setCipherParameters(currentParams)) {
224         return false;
225     }
226 
227     const auto user_version = getUserVersion();
228     if (user_version < 0) {
229         return false;
230     }
231     if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS newParams KEY \"x'" + hexKey + "'\";")) {
232         return false;
233     }
234     if (!setCipherParameters(newParams, "newParams")) {
235         return false;
236     }
237     if (!execNow("SELECT sqlcipher_export('newParams');")) {
238         return false;
239     }
240     if (!execNow(QString("PRAGMA newParams.user_version = %1;").arg(user_version))) {
241         return false;
242     }
243     if (!execNow("DETACH DATABASE newParams;")) {
244         return false;
245     }
246     if (!commitDbSwap(hexKey)) {
247         return false;
248     }
249     qInfo() << "Upgraded database from SQLCipher" << toString(currentParams) << "params to" <<
250         toString(newParams) << "params complete";
251     return true;
252 }
253 
setCipherParameters(SqlCipherParams params,const QString & database)254 bool RawDatabase::setCipherParameters(SqlCipherParams params, const QString& database)
255 {
256     QString prefix;
257     if (!database.isNull()) {
258         prefix = database + ".";
259     }
260     // from https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/
261     const QString default3_xParams{"PRAGMA database.cipher_page_size = 1024;"
262                    "PRAGMA database.kdf_iter = 64000;"
263                    "PRAGMA database.cipher_hmac_algorithm = HMAC_SHA1;"
264                    "PRAGMA database.cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"};
265     // cipher_hmac_algorithm and cipher_kdf_algorithm weren't supported in sqlcipher 3.x, so our upgrade to 4 only
266     // applied some of the new params if sqlcipher 3.x was used at the time
267     const QString halfUpgradedTo4Params{"PRAGMA database.cipher_page_size = 4096;"
268                    "PRAGMA database.kdf_iter = 256000;"
269                    "PRAGMA database.cipher_hmac_algorithm = HMAC_SHA1;"
270                    "PRAGMA database.cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"};
271     const QString default4_xParams{"PRAGMA database.cipher_page_size = 4096;"
272                    "PRAGMA database.kdf_iter = 256000;"
273                    "PRAGMA database.cipher_hmac_algorithm = HMAC_SHA512;"
274                    "PRAGMA database.cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;"
275                    "PRAGMA database.cipher_memory_security = ON;"}; // got disabled by default in 4.5.0, so manually enable it
276 
277     QString defaultParams;
278     switch(params) {
279         case SqlCipherParams::p3_0: {
280             defaultParams = default3_xParams;
281             break;
282         }
283         case SqlCipherParams::halfUpgradedTo4: {
284             defaultParams = halfUpgradedTo4Params;
285             break;
286         }
287         case SqlCipherParams::p4_0: {
288             defaultParams = default4_xParams;
289             break;
290         }
291     }
292 
293     qDebug() << "Setting SQLCipher" << toString(params) << "parameters";
294     return execNow(defaultParams.replace("database.", prefix));
295 }
296 
highestSupportedParams()297 RawDatabase::SqlCipherParams RawDatabase::highestSupportedParams()
298 {
299     // Note: This is just calling into the sqlcipher library, not touching the database.
300     QString cipherVersion;
301     if (!execNow(RawDatabase::Query("PRAGMA cipher_version", [&](const QVector<QVariant>& row) {
302             cipherVersion = row[0].toString();
303         }))) {
304         qCritical() << "Failed to read cipher_version";
305         return SqlCipherParams::p3_0;
306     }
307 
308     auto majorVersion = cipherVersion.split('.')[0].toInt();
309 
310     SqlCipherParams highestSupportedParams;
311     switch (majorVersion) {
312         case 3:
313             highestSupportedParams = SqlCipherParams::halfUpgradedTo4;
314             break;
315         case 4:
316             highestSupportedParams = SqlCipherParams::p4_0;
317             break;
318         default:
319             qCritical() << "Unsupported SQLCipher version detected!";
320             return SqlCipherParams::p3_0;
321     }
322     qDebug() << "Highest supported SQLCipher params on this system are" << toString(highestSupportedParams);
323     return highestSupportedParams;
324 }
325 
readSavedCipherParams(const QString & hexKey,SqlCipherParams newParams)326 RawDatabase::SqlCipherParams RawDatabase::readSavedCipherParams(const QString& hexKey, SqlCipherParams newParams)
327 {
328     for (int i = static_cast<int>(SqlCipherParams::p3_0); i < static_cast<int>(newParams); ++i)
329     {
330         if (!setKey(hexKey)) {
331             break;
332         }
333 
334         if (!setCipherParameters(static_cast<SqlCipherParams>(i))) {
335             break;
336         }
337 
338         if (testUsable()) {
339             return static_cast<SqlCipherParams>(i);
340         }
341     }
342     qCritical() << "Failed to check saved SQLCipher params";
343     return SqlCipherParams::p3_0;
344 }
345 
setKey(const QString & hexKey)346 bool RawDatabase::setKey(const QString& hexKey)
347 {
348     // setKey again to clear old bad cipher settings
349     if (!execNow("PRAGMA key = \"x'" + hexKey + "'\"")) {
350         qWarning() << "Failed to set encryption key";
351         return false;
352     }
353     return true;
354 }
355 
getUserVersion()356 int RawDatabase::getUserVersion()
357 {
358     int64_t user_version;
359     if (!execNow(RawDatabase::Query("PRAGMA user_version", [&](const QVector<QVariant>& row) {
360             user_version = row[0].toLongLong();
361         }))) {
362         qCritical() << "Failed to read user_version during cipher upgrade";
363         return -1;
364     }
365     return user_version;
366 }
367 
368 /**
369  * @brief Close the database and free its associated resources.
370  */
close()371 void RawDatabase::close()
372 {
373     if (QThread::currentThread() != workerThread.get())
374         return (void)QMetaObject::invokeMethod(this, "close", Qt::BlockingQueuedConnection);
375 
376     // We assume we're in the ctor or dtor, so we just need to finish processing our transactions
377     process();
378 
379     if (sqlite3_close(sqlite) == SQLITE_OK)
380         sqlite = nullptr;
381     else
382         qWarning() << "Error closing database:" << sqlite3_errmsg(sqlite);
383 }
384 
385 /**
386  * @brief Checks, that the database is open.
387  * @return True if the database was opened successfully.
388  */
isOpen()389 bool RawDatabase::isOpen()
390 {
391     // We don't need thread safety since only the ctor/dtor can write this pointer
392     return sqlite != nullptr;
393 }
394 
395 /**
396  * @brief Executes a SQL transaction synchronously.
397  * @param statement Statement to execute.
398  * @return Whether the transaction was successful.
399  */
execNow(const QString & statement)400 bool RawDatabase::execNow(const QString& statement)
401 {
402     return execNow(Query{statement});
403 }
404 
405 /**
406  * @brief Executes a SQL transaction synchronously.
407  * @param statement Statement to execute.
408  * @return Whether the transaction was successful.
409  */
execNow(const RawDatabase::Query & statement)410 bool RawDatabase::execNow(const RawDatabase::Query& statement)
411 {
412     return execNow(QVector<Query>{statement});
413 }
414 
415 /**
416  * @brief Executes a SQL transaction synchronously.
417  * @param statements List of statements to execute.
418  * @return Whether the transaction was successful.
419  */
execNow(const QVector<RawDatabase::Query> & statements)420 bool RawDatabase::execNow(const QVector<RawDatabase::Query>& statements)
421 {
422     if (!sqlite) {
423         qWarning() << "Trying to exec, but the database is not open";
424         return false;
425     }
426 
427     std::atomic_bool done{false};
428     std::atomic_bool success{false};
429 
430     Transaction trans;
431     trans.queries = statements;
432     trans.done = &done;
433     trans.success = &success;
434     {
435         QMutexLocker locker{&transactionsMutex};
436         pendingTransactions.enqueue(trans);
437     }
438 
439     // We can't use blocking queued here, otherwise we might process future transactions
440     // before returning, but we only want to wait until this transaction is done.
441     QMetaObject::invokeMethod(this, "process");
442     while (!done.load(std::memory_order_acquire))
443         QThread::msleep(10);
444 
445     return success.load(std::memory_order_acquire);
446 }
447 
448 /**
449  * @brief Executes a SQL transaction asynchronously.
450  * @param statement Statement to execute.
451  */
execLater(const QString & statement)452 void RawDatabase::execLater(const QString& statement)
453 {
454     execLater(Query{statement});
455 }
456 
execLater(const RawDatabase::Query & statement)457 void RawDatabase::execLater(const RawDatabase::Query& statement)
458 {
459     execLater(QVector<Query>{statement});
460 }
461 
execLater(const QVector<RawDatabase::Query> & statements)462 void RawDatabase::execLater(const QVector<RawDatabase::Query>& statements)
463 {
464     if (!sqlite) {
465         qWarning() << "Trying to exec, but the database is not open";
466         return;
467     }
468 
469     Transaction trans;
470     trans.queries = statements;
471     {
472         QMutexLocker locker{&transactionsMutex};
473         pendingTransactions.enqueue(trans);
474     }
475 
476     QMetaObject::invokeMethod(this, "process", Qt::QueuedConnection);
477 }
478 
479 /**
480  * @brief Waits until all the pending transactions are executed.
481  */
sync()482 void RawDatabase::sync()
483 {
484     QMetaObject::invokeMethod(this, "process", Qt::BlockingQueuedConnection);
485 }
486 
487 /**
488  * @brief Changes the database password, encrypting or decrypting if necessary.
489  * @param password If password is empty, the database will be decrypted.
490  * @return True if success, false otherwise.
491  * @note Will process all transactions before changing the password.
492  */
setPassword(const QString & password)493 bool RawDatabase::setPassword(const QString& password)
494 {
495     if (!sqlite) {
496         qWarning() << "Trying to change the password, but the database is not open";
497         return false;
498     }
499 
500     if (QThread::currentThread() != workerThread.get()) {
501         bool ret;
502         QMetaObject::invokeMethod(this, "setPassword", Qt::BlockingQueuedConnection,
503                                   Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, password));
504         return ret;
505     }
506 
507     // If we need to decrypt or encrypt, we'll need to sync and close,
508     // so we always process the pending queue before rekeying for consistency
509     process();
510 
511     if (QFile::exists(path + ".tmp")) {
512         qWarning() << "Found old temporary export file while rekeying, deleting it";
513         QFile::remove(path + ".tmp");
514     }
515 
516     if (!password.isEmpty()) {
517         QString newHexKey = deriveKey(password, currentSalt);
518         if (!currentHexKey.isEmpty()) {
519             if (!execNow("PRAGMA rekey = \"x'" + newHexKey + "'\"")) {
520                 qWarning() << "Failed to change encryption key";
521                 close();
522                 return false;
523             }
524         } else {
525             if (!encryptDatabase(newHexKey)) {
526                 close();
527                 return false;
528             }
529             currentHexKey = newHexKey;
530         }
531     } else {
532         if (currentHexKey.isEmpty())
533             return true;
534 
535         if (!decryptDatabase()) {
536             close();
537             return false;
538         }
539     }
540     return true;
541 }
542 
encryptDatabase(const QString & newHexKey)543 bool RawDatabase::encryptDatabase(const QString& newHexKey)
544 {
545     const auto user_version = getUserVersion();
546     if (user_version < 0) {
547         return false;
548     }
549     if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS encrypted KEY \"x'" + newHexKey
550                     + "'\";")) {
551         qWarning() << "Failed to export encrypted database";
552         return false;
553     }
554     if (!setCipherParameters(SqlCipherParams::p4_0, "encrypted")) {
555         return false;
556     }
557     if (!execNow("SELECT sqlcipher_export('encrypted');")) {
558         return false;
559     }
560     if (!execNow(QString("PRAGMA encrypted.user_version = %1;").arg(user_version))) {
561         return false;
562     }
563     if (!execNow("DETACH DATABASE encrypted;")) {
564         return false;
565     }
566     return commitDbSwap(newHexKey);
567 }
568 
decryptDatabase()569 bool RawDatabase::decryptDatabase()
570 {
571     const auto user_version = getUserVersion();
572     if (user_version < 0) {
573         return false;
574     }
575     if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS plaintext KEY '';"
576                                                 "SELECT sqlcipher_export('plaintext');")) {
577         qWarning() << "Failed to export decrypted database";
578         return false;
579     }
580     if (!execNow(QString("PRAGMA plaintext.user_version = %1;").arg(user_version))) {
581         return false;
582     }
583     if (!execNow("DETACH DATABASE plaintext;")) {
584         return false;
585     }
586     return commitDbSwap({});
587 }
588 
commitDbSwap(const QString & hexKey)589 bool RawDatabase::commitDbSwap(const QString& hexKey)
590 {
591     // This is racy as hell, but nobody will race with us since we hold the profile lock
592     // If we crash or die here, the rename should be atomic, so we can recover no matter
593     // what
594     close();
595     QFile::remove(path);
596     QFile::rename(path + ".tmp", path);
597     currentHexKey = hexKey;
598     if (!open(path, currentHexKey)) {
599         qCritical() << "Failed to swap db";
600         return false;
601     }
602     return true;
603 }
604 
605 /**
606  * @brief Moves the database file on disk to match the new path.
607  * @param newPath Path to move database file.
608  * @return True if success, false otherwise.
609  *
610  * @note Will process all transactions before renaming
611  */
rename(const QString & newPath)612 bool RawDatabase::rename(const QString& newPath)
613 {
614     if (!sqlite) {
615         qWarning() << "Trying to change the password, but the database is not open";
616         return false;
617     }
618 
619     if (QThread::currentThread() != workerThread.get()) {
620         bool ret;
621         QMetaObject::invokeMethod(this, "rename", Qt::BlockingQueuedConnection,
622                                   Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, newPath));
623         return ret;
624     }
625 
626     process();
627 
628     if (path == newPath)
629         return true;
630 
631     if (QFile::exists(newPath))
632         return false;
633 
634     close();
635     if (!QFile::rename(path, newPath))
636         return false;
637     path = newPath;
638     return open(path, currentHexKey);
639 }
640 
641 /**
642  * @brief Deletes the on disk database file after closing it.
643  * @note Will process all transactions before deletings.
644  * @return True if success, false otherwise.
645  */
remove()646 bool RawDatabase::remove()
647 {
648     if (!sqlite) {
649         qWarning() << "Trying to remove the database, but it is not open";
650         return false;
651     }
652 
653     if (QThread::currentThread() != workerThread.get()) {
654         bool ret;
655         QMetaObject::invokeMethod(this, "remove", Qt::BlockingQueuedConnection,
656                                   Q_RETURN_ARG(bool, ret));
657         return ret;
658     }
659 
660     qDebug() << "Removing database " << path;
661     close();
662     return QFile::remove(path);
663 }
664 
665 /**
666  * @brief Functor used to free tox_pass_key memory.
667  *
668  * This functor can be used as Deleter for smart pointers.
669  * @note Doesn't take care of overwriting the key.
670  */
671 struct PassKeyDeleter
672 {
operator ()PassKeyDeleter673     void operator()(Tox_Pass_Key* pass_key)
674     {
675         tox_pass_key_free(pass_key);
676     }
677 };
678 
679 /**
680  * @brief Derives a 256bit key from the password and returns it hex-encoded
681  * @param password Password to decrypt database
682  * @return String representation of key
683  * @deprecated deprecated on 2016-11-06, kept for compatibility, replaced by the salted version
684  */
deriveKey(const QString & password)685 QString RawDatabase::deriveKey(const QString& password)
686 {
687     if (password.isEmpty())
688         return {};
689 
690     const QByteArray passData = password.toUtf8();
691 
692     static_assert(TOX_PASS_KEY_LENGTH >= 32, "toxcore must provide 256bit or longer keys");
693 
694     static const uint8_t expandConstant[TOX_PASS_SALT_LENGTH + 1] =
695         "L'ignorance est le pire des maux";
696     const std::unique_ptr<Tox_Pass_Key, PassKeyDeleter> key(tox_pass_key_derive_with_salt(
697         reinterpret_cast<const uint8_t*>(passData.data()),
698         static_cast<std::size_t>(passData.size()), expandConstant, nullptr));
699     return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();
700 }
701 
702 /**
703  * @brief Derives a 256bit key from the password and returns it hex-encoded
704  * @param password Password to decrypt database
705  * @param salt Salt to improve password strength, must be TOX_PASS_SALT_LENGTH bytes
706  * @return String representation of key
707  */
deriveKey(const QString & password,const QByteArray & salt)708 QString RawDatabase::deriveKey(const QString& password, const QByteArray& salt)
709 {
710     if (password.isEmpty()) {
711         return {};
712     }
713 
714     if (salt.length() != TOX_PASS_SALT_LENGTH) {
715         qWarning() << "Salt length doesn't match toxencryptsave expections";
716         return {};
717     }
718 
719     const QByteArray passData = password.toUtf8();
720 
721     static_assert(TOX_PASS_KEY_LENGTH >= 32, "toxcore must provide 256bit or longer keys");
722 
723     const std::unique_ptr<Tox_Pass_Key, PassKeyDeleter> key(tox_pass_key_derive_with_salt(
724         reinterpret_cast<const uint8_t*>(passData.data()),
725         static_cast<std::size_t>(passData.size()),
726         reinterpret_cast<const uint8_t*>(salt.constData()), nullptr));
727     return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();
728 }
729 
730 /**
731  * @brief Implements the actual processing of pending transactions.
732  * Unqueues, compiles, binds and executes queries, then notifies of results
733  *
734  * @warning MUST only be called from the worker thread
735  */
process()736 void RawDatabase::process()
737 {
738     assert(QThread::currentThread() == workerThread.get());
739 
740     if (!sqlite)
741         return;
742 
743     forever
744     {
745         // Fetch the next transaction
746         Transaction trans;
747         {
748             QMutexLocker locker{&transactionsMutex};
749             if (pendingTransactions.isEmpty())
750                 return;
751             trans = pendingTransactions.dequeue();
752         }
753 
754         // In case we exit early, prepare to signal errors
755         if (trans.success != nullptr)
756             trans.success->store(false, std::memory_order_release);
757 
758         // Add transaction commands if necessary
759         if (trans.queries.size() > 1) {
760             trans.queries.prepend({"BEGIN;"});
761             trans.queries.append({"COMMIT;"});
762         }
763 
764         // Compile queries
765         for (Query& query : trans.queries) {
766             assert(query.statements.isEmpty());
767             // sqlite3_prepare_v2 only compiles one statement at a time in the query,
768             // we need to loop over them all
769             int curParam = 0;
770             const char* compileTail = query.query.data();
771             do {
772                 // Compile the next statement
773                 sqlite3_stmt* stmt;
774                 int r;
775                 if ((r = sqlite3_prepare_v2(sqlite, compileTail,
776                                             query.query.size()
777                                                 - static_cast<int>(compileTail - query.query.data()),
778                                             &stmt, &compileTail))
779                     != SQLITE_OK) {
780                     qWarning() << "Failed to prepare statement" << anonymizeQuery(query.query)
781                                << "and returned" << r;
782                     qWarning("The full error is %d: %s", sqlite3_errcode(sqlite), sqlite3_errmsg(sqlite));
783                     goto cleanupStatements;
784                 }
785                 query.statements += stmt;
786 
787                 // Now we can bind our params to this statement
788                 int nParams = sqlite3_bind_parameter_count(stmt);
789                 if (query.blobs.size() < curParam + nParams) {
790                     qWarning() << "Not enough parameters to bind to query "
791                                << anonymizeQuery(query.query);
792                     goto cleanupStatements;
793                 }
794                 for (int i = 0; i < nParams; ++i) {
795                     const QByteArray& blob = query.blobs[curParam + i];
796                     if (sqlite3_bind_blob(stmt, i + 1, blob.data(), blob.size(), SQLITE_STATIC)
797                         != SQLITE_OK) {
798                         qWarning() << "Failed to bind param" << curParam + i << "to query"
799                                    << anonymizeQuery(query.query);
800                         goto cleanupStatements;
801                     }
802                 }
803                 curParam += nParams;
804             } while (compileTail != query.query.data() + query.query.size());
805 
806 
807             // Execute each statement of each query of our transaction
808             for (sqlite3_stmt* stmt : query.statements) {
809                 int column_count = sqlite3_column_count(stmt);
810                 int result;
811                 do {
812                     result = sqlite3_step(stmt);
813 
814                     // Execute our row callback
815                     if (result == SQLITE_ROW && query.rowCallback) {
816                         QVector<QVariant> row;
817                         for (int i = 0; i < column_count; ++i)
818                             row += extractData(stmt, i);
819 
820                         query.rowCallback(row);
821                     }
822                 } while (result == SQLITE_ROW);
823 
824                 if (result == SQLITE_DONE)
825                     continue;
826 
827                 QString anonQuery = anonymizeQuery(query.query);
828                 switch (result) {
829                 case SQLITE_ERROR:
830                     qWarning() << "Error executing query" << anonQuery;
831                     goto cleanupStatements;
832                 case SQLITE_MISUSE:
833                     qWarning() << "Misuse executing query" << anonQuery;
834                     goto cleanupStatements;
835                 case SQLITE_CONSTRAINT:
836                     qWarning() << "Constraint error executing query" << anonQuery;
837                     goto cleanupStatements;
838                 default:
839                     qWarning() << "Unknown error" << result << "executing query" << anonQuery;
840                     goto cleanupStatements;
841                 }
842             }
843 
844             if (query.insertCallback)
845                 query.insertCallback(RowId{sqlite3_last_insert_rowid(sqlite)});
846         }
847 
848         if (trans.success != nullptr)
849             trans.success->store(true, std::memory_order_release);
850 
851     // Free our statements
852     cleanupStatements:
853         for (Query& query : trans.queries) {
854             for (sqlite3_stmt* stmt : query.statements)
855                 sqlite3_finalize(stmt);
856             query.statements.clear();
857         }
858 
859         // Signal transaction results
860         if (trans.done != nullptr)
861             trans.done->store(true, std::memory_order_release);
862     }
863 }
864 
865 /**
866  * @brief Hides public keys and timestamps in query.
867  * @param query Source query, which should be anonymized.
868  * @return Query without timestamps and public keys.
869  */
anonymizeQuery(const QByteArray & query)870 QString RawDatabase::anonymizeQuery(const QByteArray& query)
871 {
872     QString queryString(query);
873     queryString.replace(QRegularExpression("chat.public_key='[A-F0-9]{64}'"),
874                         "char.public_key='<HERE IS PUBLIC KEY>'");
875     queryString.replace(QRegularExpression("timestamp BETWEEN \\d{5,} AND \\d{5,}"),
876                         "timestamp BETWEEN <START HERE> AND <END HERE>");
877 
878     return queryString;
879 }
880 
881 /**
882  * @brief Extracts a variant from one column of a result row depending on the column type.
883  * @param stmt Statement to execute.
884  * @param col Number of column to extract.
885  * @return Extracted data.
886  */
extractData(sqlite3_stmt * stmt,int col)887 QVariant RawDatabase::extractData(sqlite3_stmt* stmt, int col)
888 {
889     int type = sqlite3_column_type(stmt, col);
890     if (type == SQLITE_INTEGER) {
891         return sqlite3_column_int64(stmt, col);
892     } else if (type == SQLITE_TEXT) {
893         const char* str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, col));
894         int len = sqlite3_column_bytes(stmt, col);
895         return QString::fromUtf8(str, len);
896     } else if (type == SQLITE_NULL) {
897         return QVariant{};
898     } else {
899         const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(stmt, col));
900         int len = sqlite3_column_bytes(stmt, col);
901         return QByteArray::fromRawData(data, len);
902     }
903 }
904 
905 /**
906  * @brief Use for create function in db for search data use regular experessions without case sensitive
907  * @param ctx ctx the context in which an SQL function executes
908  * @param argc number of arguments
909  * @param argv arguments
910  */
regexpInsensitive(sqlite3_context * ctx,int argc,sqlite3_value ** argv)911 void RawDatabase::regexpInsensitive(sqlite3_context* ctx, int argc, sqlite3_value** argv)
912 {
913     regexp(ctx, argc, argv, QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption);
914 }
915 
916 /**
917  * @brief Use for create function in db for search data use regular experessions without case sensitive
918  * @param ctx the context in which an SQL function executes
919  * @param argc number of arguments
920  * @param argv arguments
921  */
regexpSensitive(sqlite3_context * ctx,int argc,sqlite3_value ** argv)922 void RawDatabase::regexpSensitive(sqlite3_context* ctx, int argc, sqlite3_value** argv)
923 {
924     regexp(ctx, argc, argv, QRegularExpression::UseUnicodePropertiesOption);
925 }
926 
regexp(sqlite3_context * ctx,int argc,sqlite3_value ** argv,const QRegularExpression::PatternOptions cs)927 void RawDatabase::regexp(sqlite3_context* ctx, int argc, sqlite3_value** argv, const QRegularExpression::PatternOptions cs)
928 {
929     QRegularExpression regex;
930     const QString str1(reinterpret_cast<const char*>(sqlite3_value_text(argv[0])));
931     const QString str2(reinterpret_cast<const char*>(sqlite3_value_text(argv[1])));
932 
933     regex.setPattern(str1);
934     regex.setPatternOptions(cs);
935 
936     const bool b = str2.contains(regex);
937 
938     if (b) {
939         sqlite3_result_int(ctx, 1);
940     } else {
941         sqlite3_result_int(ctx, 0);
942     }
943 }
944