1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

fix(db): Support opening and upgrading to any of three SQLCipher params

Fix #5952
This commit is contained in:
Anthony Bilinski 2020-04-12 19:35:29 -07:00
parent deb7fbb67c
commit 2c59c92030
No known key found for this signature in database
GPG Key ID: 2AA8E0DA1B31FB3C
2 changed files with 77 additions and 27 deletions

View File

@ -175,7 +175,7 @@ bool RawDatabase::open(const QString& path, const QString& hexKey)
}
if (!hexKey.isEmpty()) {
if (!openEncryptedDatabaseAtLatestVersion(hexKey)) {
if (!openEncryptedDatabaseAtLatestSupportedVersion(hexKey)) {
close();
return false;
}
@ -183,31 +183,27 @@ bool RawDatabase::open(const QString& path, const QString& hexKey)
return true;
}
bool RawDatabase::openEncryptedDatabaseAtLatestVersion(const QString& hexKey)
bool RawDatabase::openEncryptedDatabaseAtLatestSupportedVersion(const QString& hexKey)
{
// old qTox database are saved with SQLCipher 3.x defaults. New qTox (and for a period during 1.16.3 master) are stored
// with 4.x defaults. We need to support opening both databases saved with 3.x defaults and 4.x defaults
// so upgrade from 3.x default to 4.x defaults while we're at it
// old qTox database are saved with SQLCipher 3.x defaults. For a period after 1.16.3 but before 1.17.0, databases
// could be partially upgraded to SQLCipher 4.0 defaults, since SQLCipher 3.x isn't capable of setitng all the same
// params. If SQLCipher 4.x happened to be used, they would have been fully upgraded to 4.0 default params.
// We need to support all three of these cases, so also upgrade to the latest possible params while we're here
if (!setKey(hexKey)) {
return false;
}
if (setCipherParameters(SqlCipherParams::p4_0)) {
auto highestSupportedVersion = highestSupportedParams();
if (setCipherParameters(highestSupportedVersion)) {
if (testUsable()) {
qInfo() << "Opened database with SQLCipher 4.x parameters";
qInfo() << "Opened database with SQLCipher" << toString(highestSupportedVersion) << "parameters";
return true;
} else {
return updateSavedCipherParameters(hexKey);
return updateSavedCipherParameters(hexKey, highestSupportedVersion);
}
} else {
// setKey again to clear old bad cipher settings
if (setKey(hexKey) && setCipherParameters(SqlCipherParams::p3_0) && testUsable()) {
qInfo() << "Opened database with SQLCipher 3.x parameters";
return true;
} else {
qCritical() << "Failed to open database with SQLCipher 3.x parameters";
return false;
}
qCritical() << "Failed to set latest supported SQLCipher params!";
return false;
}
}
@ -220,10 +216,11 @@ bool RawDatabase::testUsable()
/**
* @brief Changes stored db encryption from SQLCipher 3.x defaults to 4.x defaults
*/
bool RawDatabase::updateSavedCipherParameters(const QString& hexKey)
bool RawDatabase::updateSavedCipherParameters(const QString& hexKey, SqlCipherParams newParams)
{
auto currentParams = readSavedCipherParams(hexKey, newParams);
setKey(hexKey); // setKey again because a SELECT has already been run, causing crypto settings to take effect
if (!setCipherParameters(SqlCipherParams::p3_0)) {
if (!setCipherParameters(currentParams)) {
return false;
}
@ -231,25 +228,26 @@ bool RawDatabase::updateSavedCipherParameters(const QString& hexKey)
if (user_version < 0) {
return false;
}
if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS sqlcipher4 KEY \"x'" + hexKey + "'\";")) {
if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS newParams KEY \"x'" + hexKey + "'\";")) {
return false;
}
if (!setCipherParameters(SqlCipherParams::p4_0, "sqlcipher4")) {
if (!setCipherParameters(newParams, "newParams")) {
return false;
}
if (!execNow("SELECT sqlcipher_export('sqlcipher4');")) {
if (!execNow("SELECT sqlcipher_export('newParams');")) {
return false;
}
if (!execNow(QString("PRAGMA sqlcipher4.user_version = %1;").arg(user_version))) {
if (!execNow(QString("PRAGMA newParams.user_version = %1;").arg(user_version))) {
return false;
}
if (!execNow("DETACH DATABASE sqlcipher4;")) {
if (!execNow("DETACH DATABASE newParams;")) {
return false;
}
if (!commitDbSwap(hexKey)) {
return false;
}
qInfo() << "Upgraded database from SQLCipher 3.x defaults to SQLCipher 4.x defaults";
qInfo() << "Upgraded database from SQLCipher" << toString(currentParams) << "params to" <<
toString(newParams) << "params complete";
return true;
}
@ -290,10 +288,60 @@ bool RawDatabase::setCipherParameters(SqlCipherParams params, const QString& dat
break;
}
}
qDebug() << "Setting SQLCipher" << static_cast<int>(params) << "parameters";
qDebug() << "Setting SQLCipher" << toString(params) << "parameters";
return execNow(defaultParams.replace("database.", prefix));
}
RawDatabase::SqlCipherParams RawDatabase::highestSupportedParams()
{
// Note: This is just calling into the sqlcipher library, not touching the database.
QString cipherVersion;
if (!execNow(RawDatabase::Query("PRAGMA cipher_version", [&](const QVector<QVariant>& row) {
cipherVersion = row[0].toString();
}))) {
qCritical() << "Failed to read cipher_version";
return SqlCipherParams::p3_0;
}
auto majorVersion = cipherVersion.split('.')[0].toInt();
SqlCipherParams highestSupportedParams;
switch (majorVersion) {
case 3:
highestSupportedParams = SqlCipherParams::halfUpgradedTo4;
break;
case 4:
highestSupportedParams = SqlCipherParams::p4_0;
break;
default:
qCritical() << "Unsupported SQLCipher version detected!";
return SqlCipherParams::p3_0;
}
qDebug() << "Highest supported SQLCipher params on this system are" << toString(highestSupportedParams);
return highestSupportedParams;
}
RawDatabase::SqlCipherParams RawDatabase::readSavedCipherParams(const QString& hexKey, SqlCipherParams newParams)
{
for (int i = static_cast<int>(SqlCipherParams::p3_0); i < static_cast<int>(newParams); ++i)
{
if (!setKey(hexKey)) {
break;
}
if (!setCipherParameters(static_cast<SqlCipherParams>(i))) {
break;
}
if (testUsable()) {
return static_cast<SqlCipherParams>(i);
}
}
qCritical() << "Failed to check saved SQLCipher params";
return SqlCipherParams::p3_0;
}
bool RawDatabase::setKey(const QString& hexKey)
{
// setKey again to clear old bad cipher settings

View File

@ -136,9 +136,11 @@ protected slots:
private:
QString anonymizeQuery(const QByteArray& query);
bool openEncryptedDatabaseAtLatestVersion(const QString& hexKey);
bool updateSavedCipherParameters(const QString& hexKey);
bool openEncryptedDatabaseAtLatestSupportedVersion(const QString& hexKey);
bool updateSavedCipherParameters(const QString& hexKey, SqlCipherParams newParams);
bool setCipherParameters(SqlCipherParams params, const QString& database = {});
SqlCipherParams highestSupportedParams();
SqlCipherParams readSavedCipherParams(const QString& hexKey, SqlCipherParams newParams);
bool setKey(const QString& hexKey);
int getUserVersion();
bool encryptDatabase(const QString& newHexKey);