sqlcipher: Add new encryption settings to improve support for SQLCipher4

This adds three new settings to the cipher dialog: KDF iterations, HMAC
algorithm, and KDF algorithm. To simplify things we also add two presets
for all the encryption settings: SQLCipher3 defaults and SQLCipher4
defaults. The preselected default is chosen depending on the SQLCipher
version which we use.

This should work with any combination of SQLCipher3 and SQLCipher4 and
any database created by either. It should also work with DotEnv files as
expected. Again, the defaults which are used for missing values in the
DotEnv files are chosen depending on the SQLCipher version we use.
This commit is contained in:
Martin Kleusberg
2018-12-12 13:41:47 +01:00
parent 2c8883d540
commit 88ce4acf97
7 changed files with 314 additions and 39 deletions

View File

@@ -1,5 +1,6 @@
#include "CipherDialog.h"
#include "ui_CipherDialog.h"
#include "sqlitedb.h"
#include <QPushButton>
#include <QRegExpValidator>
@@ -41,6 +42,14 @@ CipherDialog::CipherDialog(QWidget* parent, bool encrypt) :
ui->editPassword2->setVisible(false);
ui->labelPassword2->setVisible(false);
}
// Set the default encryption settings depending on the SQLCipher version we use
QString sqlite_version, sqlcipher_version;
DBBrowserDB::getSqliteVersion(sqlite_version, sqlcipher_version);
if(sqlcipher_version.startsWith('4'))
ui->radioEncryptionSqlCipher4->setChecked(true);
else
ui->radioEncryptionSqlCipher3->setChecked(true);
}
CipherDialog::~CipherDialog()
@@ -60,6 +69,9 @@ CipherSettings CipherDialog::getCipherSettings() const
cipherSettings.setKeyFormat(keyFormat);
cipherSettings.setPassword(password);
cipherSettings.setPageSize(pageSize);
cipherSettings.setKdfIterations(ui->spinKdfIterations->value());
cipherSettings.setHmacAlgorithm(ui->comboHmacAlgorithm->currentText());
cipherSettings.setKdfAlgorithm(ui->comboKdfAlgorithm->currentText());
return cipherSettings;
}
@@ -91,3 +103,38 @@ void CipherDialog::checkInputFields()
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
}
void CipherDialog::toggleEncryptionSettings()
{
if(ui->radioEncryptionSqlCipher3->isChecked())
{
// SQLCipher3
ui->comboPageSize->setCurrentText(QLocale().toString(1024));
ui->spinKdfIterations->setValue(64000);
ui->comboHmacAlgorithm->setCurrentText("SHA1");
ui->comboKdfAlgorithm->setCurrentText("SHA1");
ui->comboPageSize->setEnabled(false);
ui->spinKdfIterations->setEnabled(false);
ui->comboHmacAlgorithm->setEnabled(false);
ui->comboKdfAlgorithm->setEnabled(false);
} else if(ui->radioEncryptionSqlCipher4->isChecked()) {
// SQLCipher4
ui->comboPageSize->setCurrentText(QLocale().toString(4096));
ui->spinKdfIterations->setValue(256000);
ui->comboHmacAlgorithm->setCurrentText("SHA512");
ui->comboKdfAlgorithm->setCurrentText("SHA512");
ui->comboPageSize->setEnabled(false);
ui->spinKdfIterations->setEnabled(false);
ui->comboHmacAlgorithm->setEnabled(false);
ui->comboKdfAlgorithm->setEnabled(false);
} else if(ui->radioEncryptionCustom->isChecked()) {
// Custom
ui->comboPageSize->setEnabled(true);
ui->spinKdfIterations->setEnabled(true);
ui->comboHmacAlgorithm->setEnabled(true);
ui->comboKdfAlgorithm->setEnabled(true);
}
}

View File

@@ -30,6 +30,7 @@ private:
private slots:
void checkInputFields();
void toggleEncryptionSettings();
};
#endif

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>712</width>
<height>183</height>
<height>299</height>
</rect>
</property>
<property name="windowTitle">
@@ -18,7 +18,7 @@
<widget class="QLabel" name="labelDialogDescription"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
@@ -55,19 +55,6 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Page si&amp;ze</string>
</property>
<property name="buddy">
<cstring>comboPageSize</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboPageSize"/>
</item>
</layout>
</item>
<item>
@@ -103,6 +90,136 @@
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Encr&amp;yption settings</string>
</property>
<property name="buddy">
<cstring>radioEncryptionSqlCipher3</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="radioEncryptionSqlCipher3">
<property name="text">
<string>SQLCipher &amp;3 defaults</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioEncryptionSqlCipher4">
<property name="text">
<string>SQLCipher &amp;4 defaults</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioEncryptionCustom">
<property name="text">
<string>Custo&amp;m</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Page si&amp;ze</string>
</property>
<property name="buddy">
<cstring>comboPageSize</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboPageSize"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;KDF iterations</string>
</property>
<property name="buddy">
<cstring>spinKdfIterations</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinKdfIterations">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>HMAC algorithm</string>
</property>
<property name="buddy">
<cstring>comboHmacAlgorithm</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboHmacAlgorithm">
<item>
<property name="text">
<string notr="true">SHA512</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA256</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA1</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>KDF algorithm</string>
</property>
<property name="buddy">
<cstring>comboKdfAlgorithm</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboKdfAlgorithm">
<item>
<property name="text">
<string notr="true">SHA512</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA256</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA1</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
@@ -117,9 +234,15 @@
</widget>
<tabstops>
<tabstop>editPassword</tabstop>
<tabstop>comboKeyFormat</tabstop>
<tabstop>editPassword2</tabstop>
<tabstop>comboKeyFormat</tabstop>
<tabstop>radioEncryptionSqlCipher3</tabstop>
<tabstop>radioEncryptionSqlCipher4</tabstop>
<tabstop>radioEncryptionCustom</tabstop>
<tabstop>comboPageSize</tabstop>
<tabstop>spinKdfIterations</tabstop>
<tabstop>comboHmacAlgorithm</tabstop>
<tabstop>comboKdfAlgorithm</tabstop>
</tabstops>
<resources/>
<connections>
@@ -130,8 +253,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>233</x>
<y>174</y>
<x>175</x>
<y>265</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@@ -146,8 +269,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>301</x>
<y>174</y>
<x>175</x>
<y>265</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@@ -178,8 +301,8 @@
<slot>checkInputFields()</slot>
<hints>
<hint type="sourcelabel">
<x>319</x>
<y>96</y>
<x>446</x>
<y>79</y>
</hint>
<hint type="destinationlabel">
<x>206</x>
@@ -203,8 +326,57 @@
</hint>
</hints>
</connection>
<connection>
<sender>radioEncryptionSqlCipher3</sender>
<signal>toggled(bool)</signal>
<receiver>CipherDialog</receiver>
<slot>toggleEncryptionSettings()</slot>
<hints>
<hint type="sourcelabel">
<x>217</x>
<y>114</y>
</hint>
<hint type="destinationlabel">
<x>231</x>
<y>94</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioEncryptionSqlCipher4</sender>
<signal>toggled(bool)</signal>
<receiver>CipherDialog</receiver>
<slot>toggleEncryptionSettings()</slot>
<hints>
<hint type="sourcelabel">
<x>353</x>
<y>117</y>
</hint>
<hint type="destinationlabel">
<x>407</x>
<y>97</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioEncryptionCustom</sender>
<signal>toggled(bool)</signal>
<receiver>CipherDialog</receiver>
<slot>toggleEncryptionSettings()</slot>
<hints>
<hint type="sourcelabel">
<x>552</x>
<y>120</y>
</hint>
<hint type="destinationlabel">
<x>590</x>
<y>99</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>checkInputFields()</slot>
<slot>toggleEncryptionSettings()</slot>
</slots>
</ui>

View File

@@ -32,9 +32,6 @@ void CipherSettings::setPassword(const QString &value)
int CipherSettings::getPageSize() const
{
if (pageSize == 0)
return defaultPageSize;
return pageSize;
}

View File

@@ -12,8 +12,6 @@ public:
RawKey
};
static const int defaultPageSize = 1024;
KeyFormats getKeyFormat() const;
void setKeyFormat(const KeyFormats &value);
@@ -23,12 +21,24 @@ public:
int getPageSize() const;
void setPageSize(int value);
int getKdfIterations() const { return kdfIterations; }
void setKdfIterations(int value) { kdfIterations = value; }
QString getHmacAlgorithm() const { return hmacAlgorithm; }
void setHmacAlgorithm(const QString &value) { hmacAlgorithm = value; }
QString getKdfAlgorithm() const { return kdfAlgorithm; }
void setKdfAlgorithm(const QString &value) { kdfAlgorithm = value; }
static KeyFormats getKeyFormat(int rawKeyFormat);
private:
KeyFormats keyFormat;
QString password;
int pageSize;
int kdfIterations;
QString hmacAlgorithm;
QString kdfAlgorithm;
};
#endif // CIPHERSETTINGS_H

View File

@@ -3044,6 +3044,12 @@ void MainWindow::editEncryption()
qApp->processEvents();
if(ok)
ok = db.executeSQL(QString("PRAGMA sqlitebrowser_edit_encryption.cipher_page_size = %1").arg(cipherSettings.getPageSize()), false, false);
if(ok)
ok = db.executeSQL(QString("PRAGMA sqlitebrowser_edit_encryption.kdf_iter = %1").arg(cipherSettings.getKdfIterations()), false, false);
if(ok)
ok = db.executeSQL(QString("PRAGMA sqlitebrowser_edit_encryption.cipher_hmac_algorithm = %1").arg(cipherSettings.getHmacAlgorithm()), false, false);
if(ok)
ok = db.executeSQL(QString("PRAGMA sqlitebrowser_edit_encryption.cipher_kdf_algorithm = %1").arg(cipherSettings.getKdfAlgorithm()), false, false);
// Export the current database to the new one
qApp->processEvents();

View File

@@ -126,8 +126,10 @@ bool DBBrowserDB::open(const QString& db, bool readOnly)
if(isEncrypted && cipherSettings)
{
executeSQL(QString("PRAGMA key = %1").arg(cipherSettings->getPassword()), false, false);
if(cipherSettings->getPageSize() != CipherSettings::defaultPageSize)
executeSQL(QString("PRAGMA cipher_page_size = %1;").arg(cipherSettings->getPageSize()), false, false);
executeSQL(QString("PRAGMA cipher_page_size = %1;").arg(cipherSettings->getPageSize()), false, false);
executeSQL(QString("PRAGMA kdf_iter = %1;").arg(cipherSettings->getKdfIterations()), false, false);
executeSQL(QString("PRAGMA cipher_hmac_algorithm = HMAC_%1;").arg(cipherSettings->getHmacAlgorithm()), false, false);
executeSQL(QString("PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_%1;").arg(cipherSettings->getKdfAlgorithm()), false, false);
}
#endif
delete cipherSettings;
@@ -233,13 +235,25 @@ bool DBBrowserDB::attach(const QString& filePath, QString attach_as)
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
return false;
}
if(cipherSettings && cipherSettings->getPageSize() != CipherSettings::defaultPageSize)
if(!executeSQL(QString("PRAGMA %1.cipher_page_size = %2").arg(sqlb::escapeIdentifier(attach_as)).arg(cipherSettings->getPageSize()), false))
{
if(!executeSQL(QString("PRAGMA %1.cipher_page_size = %2").arg(sqlb::escapeIdentifier(attach_as)).arg(cipherSettings->getPageSize()), false))
{
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
return false;
}
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
return false;
}
if(!executeSQL(QString("PRAGMA %1.kdf_iter = %2").arg(sqlb::escapeIdentifier(attach_as)).arg(cipherSettings->getKdfIterations()), false))
{
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
return false;
}
if(!executeSQL(QString("PRAGMA %1.cipher_hmac_algorithm = HMAC_%2").arg(sqlb::escapeIdentifier(attach_as)).arg(cipherSettings->getHmacAlgorithm()), false))
{
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
return false;
}
if(!executeSQL(QString("PRAGMA %1.cipher_kdf_algorithm = PBKDF2_HMAC_%2").arg(sqlb::escapeIdentifier(attach_as)).arg(cipherSettings->getKdfAlgorithm()), false))
{
QMessageBox::warning(nullptr, qApp->applicationName(), lastErrorMessage);
return false;
}
delete cipherSettings;
#else
@@ -270,6 +284,24 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted
#ifdef ENABLE_SQLCIPHER
bool isDotenvChecked = false;
// Determine default encryption settings depending on the SQLCipher version we use
QString sqlite_version, sqlcipher_version;
getSqliteVersion(sqlite_version, sqlcipher_version);
int enc_default_page_size, enc_default_kdf_iter;
QString enc_default_hmac_algorithm, enc_default_kdf_algorithm;
if(sqlcipher_version.startsWith('4'))
{
enc_default_page_size = 4096;
enc_default_kdf_iter = 256000;
enc_default_hmac_algorithm = "SHA512";
enc_default_kdf_algorithm = "SHA512";
} else {
enc_default_page_size = 1024;
enc_default_kdf_iter = 64000;
enc_default_hmac_algorithm = "SHA1";
enc_default_kdf_algorithm = "SHA1";
}
#endif
*encrypted = false;
@@ -319,8 +351,10 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted
QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase));
CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt());
QVariant pageSizeValue = dotenv.value(databaseFileName + "_pageSize", QVariant(CipherSettings::defaultPageSize));
int pageSize = pageSizeValue.toInt();
int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt();
int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt();
QString hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", enc_default_hmac_algorithm).toString();
QString kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", enc_default_kdf_algorithm).toString();
delete cipherSettings;
cipherSettings = new CipherSettings();
@@ -328,7 +362,9 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted
cipherSettings->setKeyFormat(keyFormat);
cipherSettings->setPassword(password);
cipherSettings->setPageSize(pageSize);
cipherSettings->setKdfIterations(kdfIterations);
cipherSettings->setHmacAlgorithm(hmacAlgorithm);
cipherSettings->setKdfAlgorithm(kdfAlgorithm);
}
}
@@ -363,8 +399,14 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted
sqlite3_exec(dbHandle, QString("PRAGMA key = %1").arg(cipherSettings->getPassword()).toUtf8(), nullptr, nullptr, nullptr);
// Set the page size if it differs from the default value
if(cipherSettings->getPageSize() != CipherSettings::defaultPageSize)
if(cipherSettings->getPageSize() != enc_default_page_size)
sqlite3_exec(dbHandle, QString("PRAGMA cipher_page_size = %1;").arg(cipherSettings->getPageSize()).toUtf8(), nullptr, nullptr, nullptr);
if(cipherSettings->getKdfIterations() != enc_default_kdf_iter)
sqlite3_exec(dbHandle, QString("PRAGMA kdf_iter = %1;").arg(cipherSettings->getKdfIterations()).toUtf8(), nullptr, nullptr, nullptr);
if(cipherSettings->getHmacAlgorithm() != enc_default_hmac_algorithm)
sqlite3_exec(dbHandle, QString("PRAGMA cipher_hmac_algorithm = HMAC_%1;").arg(cipherSettings->getHmacAlgorithm()).toUtf8(), nullptr, nullptr, nullptr);
if(cipherSettings->getKdfAlgorithm() != enc_default_kdf_algorithm)
sqlite3_exec(dbHandle, QString("PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_%1;").arg(cipherSettings->getKdfAlgorithm()).toUtf8(), nullptr, nullptr, nullptr);
*encrypted = true;
#else