23 #include <tox/toxencryptsave.h> 
   25 #include <QCoreApplication> 
   28 #include <QMetaObject> 
   29 #include <QMutexLocker> 
   88     : workerThread{
new QThread}
 
   91     , currentHexKey{deriveKey(password, salt)}
 
   93     workerThread->setObjectName(
"qTox Database");
 
   94     moveToThread(workerThread.get());
 
   95     workerThread->start();
 
   98     if (open(path, currentHexKey)) {
 
  107     if (!QFile::copy(path, path + 
".bak")) {
 
  108         qDebug() << 
"Couldn't create the backup of the database, won't upgrade";
 
  113     currentHexKey = deriveKey(password);
 
  114     if (open(path, currentHexKey)) {
 
  118             if (setPassword(password)) {
 
  119                 qDebug() << 
"Successfully upgraded to dynamic salt";
 
  121                 qWarning() << 
"Failed to set password with new salt";
 
  125         qDebug() << 
"Failed to open database with old salt";
 
  147         QMetaObject::invokeMethod(
this, 
"open", Qt::BlockingQueuedConnection, Q_RETURN_ARG(
bool, ret),
 
  148                                   Q_ARG(
const QString&, 
path), Q_ARG(
const QString&, hexKey));
 
  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?";
 
  158     if (sqlite3_open_v2(
path.toUtf8().data(), &
sqlite,
 
  159                         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, 
nullptr)
 
  161         qWarning() << 
"Failed to open database" << 
path << 
"with error:" << sqlite3_errmsg(
sqlite);
 
  166         qWarning() << 
"Failed to create function regexp";
 
  172         qWarning() << 
"Failed to create function regexpsensitive";
 
  177     if (!hexKey.isEmpty()) {
 
  199             qInfo() << 
"Opened database with SQLCipher" << 
toString(highestSupportedVersion) << 
"parameters";
 
  205         qCritical() << 
"Failed to set latest supported SQLCipher params!";
 
  213     return execNow(
"SELECT count(*) FROM sqlite_master;");
 
  228     if (user_version < 0) {
 
  231     if (!
execNow(
"ATTACH DATABASE '" + 
path + 
".tmp' AS newParams KEY \"x'" + hexKey + 
"'\";")) {
 
  237     if (!
execNow(
"SELECT sqlcipher_export('newParams');")) {
 
  240     if (!
execNow(QString(
"PRAGMA newParams.user_version = %1;").arg(user_version))) {
 
  243     if (!
execNow(
"DETACH DATABASE newParams;")) {
 
  249     qInfo() << 
"Upgraded database from SQLCipher" << 
toString(currentParams) << 
"params to" <<
 
  250         toString(newParams) << 
"params complete";
 
  257     if (!database.isNull()) {
 
  258         prefix = database + 
".";
 
  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;"};
 
  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;"}; 
 
  277     QString defaultParams;
 
  280             defaultParams = default3_xParams;
 
  284             defaultParams = halfUpgradedTo4Params;
 
  288             defaultParams = default4_xParams;
 
  293     qDebug() << 
"Setting SQLCipher" << 
toString(params) << 
"parameters";
 
  294     return execNow(defaultParams.replace(
"database.", prefix));
 
  300     QString cipherVersion;
 
  302             cipherVersion = row[0].toString();
 
  304         qCritical() << 
"Failed to read cipher_version";
 
  308     auto majorVersion = cipherVersion.split(
'.')[0].toInt();
 
  311     switch (majorVersion) {
 
  319             qCritical() << 
"Unsupported SQLCipher version detected!";
 
  342     qCritical() << 
"Failed to check saved SQLCipher params";
 
  349     if (!
execNow(
"PRAGMA key = \"x'" + hexKey + 
"'\"")) {
 
  350         qWarning() << 
"Failed to set encryption key";
 
  358     int64_t user_version;
 
  360             user_version = row[0].toLongLong();
 
  362         qCritical() << 
"Failed to read user_version during cipher upgrade";
 
  374         return (
void)QMetaObject::invokeMethod(
this, 
"close", Qt::BlockingQueuedConnection);
 
  379     if (sqlite3_close(
sqlite) == SQLITE_OK)
 
  382         qWarning() << 
"Error closing database:" << sqlite3_errmsg(
sqlite);
 
  412     return execNow(QVector<Query>{statement});
 
  423         qWarning() << 
"Trying to exec, but the database is not open";
 
  427     std::atomic_bool done{
false};
 
  428     std::atomic_bool success{
false};
 
  431     trans.queries = statements;
 
  433     trans.success = &success;
 
  441     QMetaObject::invokeMethod(
this, 
"process");
 
  442     while (!done.load(std::memory_order_acquire))
 
  445     return success.load(std::memory_order_acquire);
 
  465         qWarning() << 
"Trying to exec, but the database is not open";
 
  470     trans.queries = statements;
 
  476     QMetaObject::invokeMethod(
this, 
"process", Qt::QueuedConnection);
 
  484     QMetaObject::invokeMethod(
this, 
"process", Qt::BlockingQueuedConnection);
 
  496         qWarning() << 
"Trying to change the password, but the database is not open";
 
  502         QMetaObject::invokeMethod(
this, 
"setPassword", Qt::BlockingQueuedConnection,
 
  503                                   Q_RETURN_ARG(
bool, ret), Q_ARG(
const QString&, password));
 
  511     if (QFile::exists(
path + 
".tmp")) {
 
  512         qWarning() << 
"Found old temporary export file while rekeying, deleting it";
 
  513         QFile::remove(
path + 
".tmp");
 
  516     if (!password.isEmpty()) {
 
  519             if (!
execNow(
"PRAGMA rekey = \"x'" + newHexKey + 
"'\"")) {
 
  520                 qWarning() << 
"Failed to change encryption key";
 
  546     if (user_version < 0) {
 
  549     if (!
execNow(
"ATTACH DATABASE '" + 
path + 
".tmp' AS encrypted KEY \"x'" + newHexKey
 
  551         qWarning() << 
"Failed to export encrypted database";
 
  557     if (!
execNow(
"SELECT sqlcipher_export('encrypted');")) {
 
  560     if (!
execNow(QString(
"PRAGMA encrypted.user_version = %1;").arg(user_version))) {
 
  563     if (!
execNow(
"DETACH DATABASE encrypted;")) {
 
  572     if (user_version < 0) {
 
  575     if (!
execNow(
"ATTACH DATABASE '" + 
path + 
".tmp' AS plaintext KEY '';" 
  576                                                 "SELECT sqlcipher_export('plaintext');")) {
 
  577         qWarning() << 
"Failed to export decrypted database";
 
  580     if (!
execNow(QString(
"PRAGMA plaintext.user_version = %1;").arg(user_version))) {
 
  583     if (!
execNow(
"DETACH DATABASE plaintext;")) {
 
  599         qCritical() << 
"Failed to swap db";
 
  615         qWarning() << 
"Trying to change the password, but the database is not open";
 
  621         QMetaObject::invokeMethod(
this, 
"rename", Qt::BlockingQueuedConnection,
 
  622                                   Q_RETURN_ARG(
bool, ret), Q_ARG(
const QString&, newPath));
 
  631     if (QFile::exists(newPath))
 
  635     if (!QFile::rename(
path, newPath))
 
  649         qWarning() << 
"Trying to remove the database, but it is not open";
 
  655         QMetaObject::invokeMethod(
this, 
"remove", Qt::BlockingQueuedConnection,
 
  656                                   Q_RETURN_ARG(
bool, ret));
 
  660     qDebug() << 
"Removing database " << 
path;
 
  662     return QFile::remove(
path);
 
  675         tox_pass_key_free(pass_key);
 
  687     if (password.isEmpty())
 
  690     const QByteArray passData = password.toUtf8();
 
  692     static_assert(TOX_PASS_KEY_LENGTH >= 32, 
"toxcore must provide 256bit or longer keys");
 
  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();
 
  710     if (password.isEmpty()) {
 
  714     if (salt.length() != TOX_PASS_SALT_LENGTH) {
 
  715         qWarning() << 
"Salt length doesn't match toxencryptsave expections";
 
  719     const QByteArray passData = password.toUtf8();
 
  721     static_assert(TOX_PASS_KEY_LENGTH >= 32, 
"toxcore must provide 256bit or longer keys");
 
  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();
 
  756             trans.
success->store(
false, std::memory_order_release);
 
  759         if (trans.
queries.size() > 1) {
 
  760             trans.
queries.prepend({
"BEGIN;"});
 
  761             trans.
queries.append({
"COMMIT;"});
 
  770             const char* compileTail = query.
query.data();
 
  775                 if ((r = sqlite3_prepare_v2(
sqlite, compileTail,
 
  777                                                 - 
static_cast<int>(compileTail - query.
query.data()),
 
  778                                             &stmt, &compileTail))
 
  781                                << 
"and returned" << r;
 
  782                     qWarning(
"The full error is %d: %s", sqlite3_errcode(
sqlite), sqlite3_errmsg(
sqlite));
 
  783                     goto cleanupStatements;
 
  788                 int nParams = sqlite3_bind_parameter_count(stmt);
 
  789                 if (query.
blobs.size() < curParam + nParams) {
 
  790                     qWarning() << 
"Not enough parameters to bind to query " 
  792                     goto cleanupStatements;
 
  794                 for (
int i = 0; i < nParams; ++i) {
 
  795                     const QByteArray& blob = query.
blobs[curParam + i];
 
  796 #pragma GCC diagnostic push 
  797 #pragma GCC diagnostic ignored "-Wold-style-cast" 
  798 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" 
  801                     auto sqliteDataType = SQLITE_STATIC;
 
  802 #pragma GCC diagnostic pop 
  803                     if (sqlite3_bind_blob(stmt, i + 1, blob.data(), blob.size(), sqliteDataType)
 
  805                         qWarning() << 
"Failed to bind param" << curParam + i << 
"to query" 
  807                         goto cleanupStatements;
 
  811             } 
while (compileTail != query.
query.data() + query.
query.size());
 
  816                 int column_count = sqlite3_column_count(stmt);
 
  819                     result = sqlite3_step(stmt);
 
  823                         QVector<QVariant> row;
 
  824                         for (
int i = 0; i < column_count; ++i)
 
  829                 } 
while (result == SQLITE_ROW);
 
  831                 if (result == SQLITE_DONE)
 
  837                     qWarning() << 
"Error executing query" << anonQuery;
 
  838                     goto cleanupStatements;
 
  840                     qWarning() << 
"Misuse executing query" << anonQuery;
 
  841                     goto cleanupStatements;
 
  842                 case SQLITE_CONSTRAINT:
 
  843                     qWarning() << 
"Constraint error executing query" << anonQuery;
 
  844                     goto cleanupStatements;
 
  846                     qWarning() << 
"Unknown error" << result << 
"executing query" << anonQuery;
 
  847                     goto cleanupStatements;
 
  856             trans.
success->store(
true, std::memory_order_release);
 
  862                 sqlite3_finalize(stmt);
 
  867         if (trans.
done != 
nullptr)
 
  868             trans.
done->store(
true, std::memory_order_release);
 
  879     QString queryString(query);
 
  880     queryString.replace(QRegularExpression(
"chat.public_key='[A-F0-9]{64}'"),
 
  881                         "char.public_key='<HERE IS PUBLIC KEY>'");
 
  882     queryString.replace(QRegularExpression(
"timestamp BETWEEN \\d{5,} AND \\d{5,}"),
 
  883                         "timestamp BETWEEN <START HERE> AND <END HERE>");
 
  896     int type = sqlite3_column_type(stmt, col);
 
  897     if (type == SQLITE_INTEGER) {
 
  898         return sqlite3_column_int64(stmt, col);
 
  899     } 
else if (type == SQLITE_TEXT) {
 
  900         const char* str = 
reinterpret_cast<const char*
>(sqlite3_column_text(stmt, col));
 
  901         int len = sqlite3_column_bytes(stmt, col);
 
  902         return QString::fromUtf8(str, len);
 
  903     } 
else if (type == SQLITE_NULL) {
 
  906         const char* data = 
reinterpret_cast<const char*
>(sqlite3_column_blob(stmt, col));
 
  907         int len = sqlite3_column_bytes(stmt, col);
 
  908         return QByteArray::fromRawData(data, len);
 
  920     regexp(ctx, argc, argv, QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption);
 
  931     regexp(ctx, argc, argv, QRegularExpression::UseUnicodePropertiesOption);
 
  934 void RawDatabase::regexp(sqlite3_context* ctx, 
int argc, sqlite3_value** argv, 
const QRegularExpression::PatternOptions cs)
 
  936     QRegularExpression regex;
 
  937     const QString str1(
reinterpret_cast<const char*
>(sqlite3_value_text(argv[0])));
 
  938     const QString str2(
reinterpret_cast<const char*
>(sqlite3_value_text(argv[1])));
 
  940     regex.setPattern(str1);
 
  941     regex.setPatternOptions(cs);
 
  943     const bool b = str2.contains(regex);
 
  946         sqlite3_result_int(ctx, 1);
 
  948         sqlite3_result_int(ctx, 0);