+
+YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::YubiKeyEditWidget())
+{
+ setComponentName(tr("YubiKey Challenge-Response"));
+ setComponentDescription(tr("If you own a YubiKey, you can use it "
+ "for additional security.
The YubiKey requires one of its slots to be programmed as "
+ ""
+ "HMAC-SHA1 Challenge-Response.
"));
+}
+
+YubiKeyEditWidget::~YubiKeyEditWidget()
+{
+}
+
+bool YubiKeyEditWidget::addToCompositeKey(QSharedPointer key)
+{
+ QSharedPointer keyPtr;
+ if (!createCrKey(keyPtr, false)) {
+ return false;
+ }
+ key->addChallengeResponseKey(keyPtr);
+
+ return true;
+}
+
+bool YubiKeyEditWidget::validate(QString& errorMessage) const
+{
+ QSharedPointer keyPtr;
+ if (!createCrKey(keyPtr)) {
+ errorMessage = tr("No YubiKey detected, please ensure it's plugged in.");
+ return false;
+ }
+
+ return true;
+}
+
+QWidget* YubiKeyEditWidget::componentEditWidget()
+{
+ m_compEditWidget = new QWidget();
+ m_compUi->setupUi(m_compEditWidget);
+
+ QSizePolicy sp = m_compUi->yubikeyProgress->sizePolicy();
+ sp.setRetainSizeWhenHidden(true);
+ m_compUi->yubikeyProgress->setSizePolicy(sp);
+ m_compUi->yubikeyProgress->setVisible(false);
+
+#ifdef WITH_XC_YUBIKEY
+ connect(m_compUi->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey()));
+
+ connect(YubiKey::instance(), SIGNAL(detected(int, bool)), SLOT(yubikeyDetected(int, bool)), Qt::QueuedConnection);
+ connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection);
+
+ pollYubikey();
+#endif
+
+ return m_compEditWidget;
+}
+
+void YubiKeyEditWidget::initComponentEditWidget(QWidget* widget)
+{
+ Q_UNUSED(widget);
+ Q_ASSERT(m_compEditWidget);
+ m_compUi->comboChallengeResponse->setFocus();
+}
+
+void YubiKeyEditWidget::pollYubikey()
+{
+#ifdef WITH_XC_YUBIKEY
+ if (!m_compEditWidget) {
+ return;
+ }
+ m_compUi->buttonRedetectYubikey->setEnabled(false);
+ m_compUi->comboChallengeResponse->setEnabled(false);
+ m_compUi->comboChallengeResponse->clear();
+ m_compUi->yubikeyProgress->setVisible(true);
+
+ // YubiKey init is slow, detect asynchronously to not block the UI
+ QtConcurrent::run(YubiKey::instance(), &YubiKey::detect);
+#endif
+}
+
+void YubiKeyEditWidget::yubikeyDetected(int slot, bool blocking)
+{
+#ifdef WITH_XC_YUBIKEY
+ if (!m_compEditWidget) {
+ return;
+ }
+ YkChallengeResponseKey yk(slot, blocking);
+ m_compUi->comboChallengeResponse->clear();
+ // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB
+ m_compUi->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1u) | blocking));
+ m_compUi->comboChallengeResponse->setEnabled(true);
+ m_compUi->buttonRedetectYubikey->setEnabled(true);
+ m_compUi->yubikeyProgress->setVisible(false);
+ m_isDetected = true;
+#endif
+}
+
+void YubiKeyEditWidget::noYubikeyFound()
+{
+#ifdef WITH_XC_YUBIKEY
+ if (!m_compEditWidget) {
+ return;
+ }
+ m_compUi->comboChallengeResponse->clear();
+ m_compUi->comboChallengeResponse->setEnabled(false);
+ m_compUi->comboChallengeResponse->addItem(tr("No YubiKey inserted."));
+ m_compUi->buttonRedetectYubikey->setEnabled(true);
+ m_compUi->yubikeyProgress->setVisible(false);
+ m_isDetected = false;
+#endif
+}
+
+bool YubiKeyEditWidget::createCrKey(QSharedPointer& key, bool testChallenge) const
+{
+ Q_ASSERT(m_compEditWidget);
+ if (!m_isDetected || !m_compEditWidget) {
+ return false;
+ }
+
+ int selectionIndex = m_compUi->comboChallengeResponse->currentIndex();
+ int comboPayload = m_compUi->comboChallengeResponse->itemData(selectionIndex).toInt();
+
+ if (0 == comboPayload) {
+ return false;
+ }
+
+ auto blocking = static_cast(comboPayload & 1u);
+ int slot = comboPayload >> 1u;
+ key.reset(new YkChallengeResponseKey(slot, blocking));
+ if (testChallenge) {
+ return key->challenge(QByteArray("0000"));
+ }
+ return true;
+}
diff --git a/src/gui/masterkey/YubiKeyEditWidget.h b/src/gui/masterkey/YubiKeyEditWidget.h
new file mode 100644
index 000000000..82fc8b35a
--- /dev/null
+++ b/src/gui/masterkey/YubiKeyEditWidget.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_YUBIKEYEDITWIDGET_H
+#define KEEPASSXC_YUBIKEYEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include
+
+namespace Ui
+{
+class YubiKeyEditWidget;
+}
+
+class YkChallengeResponseKey;
+
+class YubiKeyEditWidget : public KeyComponentWidget
+{
+ Q_OBJECT
+
+public:
+ explicit YubiKeyEditWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(YubiKeyEditWidget);
+ ~YubiKeyEditWidget() override;
+
+ bool addToCompositeKey(QSharedPointer key) override;
+ bool validate(QString& errorMessage) const override;
+
+protected:
+ QWidget* componentEditWidget() override;
+ void initComponentEditWidget(QWidget* widget) override;
+
+private slots:
+ void yubikeyDetected(int slot, bool blocking);
+ void noYubikeyFound();
+ void pollYubikey();
+
+private:
+ bool createCrKey(QSharedPointer& key, bool testChallenge = true) const;
+
+ const QScopedPointer m_compUi;
+ QPointer m_compEditWidget;
+ bool m_isDetected = false;
+};
+
+#endif //KEEPASSXC_YUBIKEYEDITWIDGET_H
diff --git a/src/gui/masterkey/YubiKeyEditWidget.ui b/src/gui/masterkey/YubiKeyEditWidget.ui
new file mode 100644
index 000000000..08508739a
--- /dev/null
+++ b/src/gui/masterkey/YubiKeyEditWidget.ui
@@ -0,0 +1,90 @@
+
+
+ YubiKeyEditWidget
+
+
+
+ 0
+ 0
+ 381
+ 64
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ 0
+
+
-
+
+
+ Refresh
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+
+ 16777215
+ 2
+
+
+
+ 0
+
+
+ -1
+
+
+ false
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 0
+
+
+
+
+
+
+
+ comboChallengeResponse
+ buttonRedetectYubikey
+
+
+
+
diff --git a/src/gui/settings/SettingsWidget.cpp b/src/gui/settings/SettingsWidget.cpp
new file mode 100644
index 000000000..5053bf983
--- /dev/null
+++ b/src/gui/settings/SettingsWidget.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "SettingsWidget.h"
+
+SettingsWidget::SettingsWidget(QWidget* parent)
+ : QWidget(parent)
+{
+}
+
+SettingsWidget::~SettingsWidget()
+{
+}
+
+/**
+ * Switch between simple mode (the default) and advanced mode.
+ * Subclasses which implement an advanced mode, need to override this method,
+ * \link advancedMode() and \link hasAdvancedMode.
+ *
+ * When overriding this method, make sure to also emit the
+ * \link advancedModeChanged() signal.
+ *
+ * @param advanced whether advanced mode is enabled
+ */
+void SettingsWidget::setAdvancedMode(bool advanced)
+{
+ if (hasAdvancedMode() && advanced != advancedMode()) {
+ m_advancedMode = advanced;
+ emit advancedModeChanged(advanced);
+ }
+}
+
+/**
+ * @return true if advanced mode is on (default: false)
+ */
+bool SettingsWidget::advancedMode() const
+{
+ return m_advancedMode;
+}
diff --git a/src/gui/settings/SettingsWidget.h b/src/gui/settings/SettingsWidget.h
new file mode 100644
index 000000000..4630a776e
--- /dev/null
+++ b/src/gui/settings/SettingsWidget.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_SETTINGSWIDGET_H
+#define KEEPASSXC_SETTINGSWIDGET_H
+
+#include
+
+class Database;
+
+/**
+ * Pure-virtual base class for KeePassXC settings widgets.
+ */
+class SettingsWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(SettingsWidget);
+ ~SettingsWidget() override;
+
+ /**
+ * @return true if widget has an advanced mode
+ */
+ virtual bool hasAdvancedMode() const = 0;
+ virtual void setAdvancedMode(bool advanced);
+ virtual bool advancedMode() const;
+
+public slots:
+ /**
+ * Initialize settings widget.
+ */
+ virtual void initialize() = 0;
+
+ /**
+ * Perform needed clean-up operations before widget is destroyed or re-initialized.
+ */
+ virtual void uninitialize() = 0;
+
+ /**
+ * Save settings.
+ *
+ * @return true on success, false on failure
+ */
+ virtual bool save() = 0;
+
+ /**
+ * Discard settings.
+ */
+ virtual void discard() {};
+
+signals:
+ void editFinished(bool saved);
+ void advancedModeChanged(bool advanced);
+
+private:
+ bool m_advancedMode = false;
+};
+
+#endif //KEEPASSXC_SETTINGSWIDGET_H
diff --git a/src/gui/wizard/NewDatabaseWizard.cpp b/src/gui/wizard/NewDatabaseWizard.cpp
new file mode 100644
index 000000000..96aa06629
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizard.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NewDatabaseWizard.h"
+#include "NewDatabaseWizardPageMetaData.h"
+#include "NewDatabaseWizardPageEncryption.h"
+#include "NewDatabaseWizardPageMasterKey.h"
+
+#include "core/Global.h"
+#include "core/Database.h"
+#include "core/Group.h"
+#include "core/FilePath.h"
+#include "format/KeePass2.h"
+
+#include
+
+NewDatabaseWizard::NewDatabaseWizard(QWidget* parent)
+ : QWizard(parent)
+ , m_pages()
+{
+ setWizardStyle(QWizard::MacStyle);
+ setOption(QWizard::WizardOption::HaveHelpButton, false);
+
+ m_pages << new NewDatabaseWizardPageMetaData()
+ << new NewDatabaseWizardPageEncryption()
+ << new NewDatabaseWizardPageMasterKey();
+
+ for (auto const& page: asConst(m_pages)) {
+ addPage(page);
+ }
+
+ setWindowTitle(tr("Create a new KeePassXC database..."));
+
+ setPixmap(QWizard::BackgroundPixmap, QPixmap(filePath()->dataPath("wizard/background-pixmap.png")));
+}
+
+NewDatabaseWizard::~NewDatabaseWizard()
+{
+}
+
+bool NewDatabaseWizard::validateCurrentPage()
+{
+ return m_pages[currentId()]->validatePage();
+}
+
+Database* NewDatabaseWizard::takeDatabase()
+{
+ return m_db.take();
+}
+
+void NewDatabaseWizard::initializePage(int id)
+{
+ if (id == startId()) {
+ m_db.reset(new Database());
+ m_db->rootGroup()->setName(tr("Root", "Root group"));
+ m_db->setKdf({});
+ m_db->setKey({});
+ }
+
+ m_pages[id]->setDatabase(m_db.data());
+ m_pages[id]->initializePage();
+}
diff --git a/src/gui/wizard/NewDatabaseWizard.h b/src/gui/wizard/NewDatabaseWizard.h
new file mode 100644
index 000000000..5c3b49c01
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizard.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_NEWDATABASEWIZARD_H
+#define KEEPASSXC_NEWDATABASEWIZARD_H
+
+#include
+#include
+#include
+
+class Database;
+class NewDatabaseWizardPage;
+
+/**
+ * Setup wizard for creating a new database.
+ */
+class NewDatabaseWizard : public QWizard
+{
+Q_OBJECT
+
+public:
+ explicit NewDatabaseWizard(QWidget* parent = nullptr);
+ ~NewDatabaseWizard() override;
+
+ Database* takeDatabase();
+ bool validateCurrentPage() override;
+
+protected:
+ void initializePage(int id) override;
+
+private:
+ QScopedPointer m_db;
+ QList> m_pages;
+};
+
+#endif //KEEPASSXC_NEWDATABASEWIZARD_H
diff --git a/src/gui/wizard/NewDatabaseWizardPage.cpp b/src/gui/wizard/NewDatabaseWizardPage.cpp
new file mode 100644
index 000000000..e38cb8641
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPage.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NewDatabaseWizardPage.h"
+#include "ui_NewDatabaseWizardPage.h"
+#include "core/Database.h"
+#include "gui/dbsettings/DatabaseSettingsWidget.h"
+
+#include
+
+NewDatabaseWizardPage::NewDatabaseWizardPage(QWidget* parent)
+ : QWizardPage(parent)
+ , m_ui(new Ui::NewDatabaseWizardPage())
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->advancedSettingsButton, SIGNAL(clicked()), SLOT(toggleAdvancedSettings()));
+}
+
+NewDatabaseWizardPage::~NewDatabaseWizardPage()
+{
+}
+
+/**
+ * Set the database settings page widget for this wizard page.
+ * The wizard page will take ownership of the settings page widget.
+ *
+ * @param page database settings page widget
+ */
+void NewDatabaseWizardPage::setPageWidget(DatabaseSettingsWidget* page)
+{
+ m_pageWidget = page;
+ if (!m_ui->pageContentLayout->isEmpty()) {
+ delete m_ui->pageContentLayout->takeAt(0);
+ }
+ m_ui->pageContentLayout->addWidget(m_pageWidget);
+ m_ui->pageContentLayout->setSizeConstraint(QLayout::SetMinimumSize);
+ m_ui->advancedSettingsButton->setVisible(m_pageWidget->hasAdvancedMode());
+}
+
+/**
+ * @return database settings widget of this page widget.
+ */
+DatabaseSettingsWidget* NewDatabaseWizardPage::pageWidget()
+{
+ return m_pageWidget;
+}
+
+/**
+ * Set the database to be configured by the wizard page.
+ * The wizard will NOT take ownership of the database object.
+ *
+ * @param db database object to be configured
+ */
+void NewDatabaseWizardPage::setDatabase(Database* db)
+{
+ m_db = db;
+}
+
+void NewDatabaseWizardPage::initializePage()
+{
+ Q_ASSERT(m_pageWidget && m_db);
+ if (!m_pageWidget || !m_db) {
+ return;
+ }
+
+ m_pageWidget->load(m_db);
+}
+
+bool NewDatabaseWizardPage::validatePage()
+{
+ Q_ASSERT(m_pageWidget && m_db);
+ if (!m_pageWidget || !m_db) {
+ return false;
+ }
+
+ bool valid = m_pageWidget->save();
+ m_pageWidget->uninitialize();
+ return valid;
+}
+
+/**
+ * Toggle settings page widget between simple and advanced mode.
+ */
+void NewDatabaseWizardPage::toggleAdvancedSettings()
+{
+ if (!m_pageWidget || !m_pageWidget->hasAdvancedMode()) {
+ return;
+ }
+
+ if (m_pageWidget->advancedMode()) {
+ m_pageWidget->setAdvancedMode(false);
+ m_ui->advancedSettingsButton->setText(tr("Advanced Settings"));
+ } else {
+ m_pageWidget->setAdvancedMode(true);
+ m_ui->advancedSettingsButton->setText(tr("Simple Settings"));
+ }
+}
diff --git a/src/gui/wizard/NewDatabaseWizardPage.h b/src/gui/wizard/NewDatabaseWizardPage.h
new file mode 100644
index 000000000..39b47940e
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPage.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_NEWDATABASEWIZARDPAGE_H
+#define KEEPASSXC_NEWDATABASEWIZARDPAGE_H
+
+#include
+#include
+#include
+
+class Database;
+class DatabaseSettingsWidget;
+namespace Ui
+{
+class NewDatabaseWizardPage;
+}
+
+/**
+ * Pure-virtual base class for "New Database" setup wizard pages
+ */
+class NewDatabaseWizardPage : public QWizardPage
+{
+Q_OBJECT
+
+public:
+ explicit NewDatabaseWizardPage(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(NewDatabaseWizardPage);
+ ~NewDatabaseWizardPage() override;
+
+ void setPageWidget(DatabaseSettingsWidget* page);
+ DatabaseSettingsWidget* pageWidget();
+ void setDatabase(Database* db);
+
+ void initializePage() override;
+ bool validatePage() override;
+
+public slots:
+ void toggleAdvancedSettings();
+
+protected:
+ QPointer m_pageWidget;
+ QPointer m_db;
+
+ const QScopedPointer m_ui;
+};
+
+#endif //KEEPASSXC_NEWDATABASEWIZARDPAGE_H
diff --git a/src/gui/wizard/NewDatabaseWizardPage.ui b/src/gui/wizard/NewDatabaseWizardPage.ui
new file mode 100644
index 000000000..6b69e85b5
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPage.ui
@@ -0,0 +1,65 @@
+
+
+ NewDatabaseWizardPage
+
+
+
+ 0
+ 0
+
+
+
+ WizardPage
+
+
+ En&cryption Settings
+
+
+ Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings.
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 15
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Advanced Settings
+
+
+
+
+
+
+
+
+
+
diff --git a/src/gui/wizard/NewDatabaseWizardPageEncryption.cpp b/src/gui/wizard/NewDatabaseWizardPageEncryption.cpp
new file mode 100644
index 000000000..743f7bcc2
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPageEncryption.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NewDatabaseWizardPageEncryption.h"
+#include "gui/dbsettings/DatabaseSettingsWidgetEncryption.h"
+
+NewDatabaseWizardPageEncryption::NewDatabaseWizardPageEncryption(QWidget* parent)
+ : NewDatabaseWizardPage(parent)
+{
+ setPageWidget(new DatabaseSettingsWidgetEncryption());
+
+ setTitle(tr("Encryption Settings"));
+ setSubTitle(tr("Here you can adjust the database encryption settings. "
+ "Don't worry, you can change them later in the database settings."));
+}
+
+NewDatabaseWizardPageEncryption::~NewDatabaseWizardPageEncryption()
+{
+}
diff --git a/src/gui/wizard/NewDatabaseWizardPageEncryption.h b/src/gui/wizard/NewDatabaseWizardPageEncryption.h
new file mode 100644
index 000000000..c10e84dba
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPageEncryption.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_NEWDATABASEWIZARDPAGEENCRYPTION_H
+#define KEEPASSXC_NEWDATABASEWIZARDPAGEENCRYPTION_H
+
+#include "NewDatabaseWizardPage.h"
+
+class NewDatabaseWizardPageEncryption : public NewDatabaseWizardPage
+{
+Q_OBJECT
+
+public:
+ explicit NewDatabaseWizardPageEncryption(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(NewDatabaseWizardPageEncryption);
+ ~NewDatabaseWizardPageEncryption() override;
+};
+
+#endif //KEEPASSXC_NEWDATABASEWIZARDPAGEENCRYPTION_H
diff --git a/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp b/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp
new file mode 100644
index 000000000..130560e27
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NewDatabaseWizardPageMasterKey.h"
+#include "gui/dbsettings/DatabaseSettingsWidgetMasterKey.h"
+#include
+
+NewDatabaseWizardPageMasterKey::NewDatabaseWizardPageMasterKey(QWidget* parent)
+ : NewDatabaseWizardPage(parent)
+{
+ setPageWidget(new DatabaseSettingsWidgetMasterKey());
+
+ setTitle(tr("Database Master Key"));
+ setSubTitle(tr("A master key known only to you protects your database."));
+
+ connect(pageWidget(), SIGNAL(sizeChanged()), SLOT(updateWindowSize()));
+}
+
+NewDatabaseWizardPageMasterKey::~NewDatabaseWizardPageMasterKey()
+{
+}
+
+void NewDatabaseWizardPageMasterKey::updateWindowSize()
+{
+ // ugly workaround for QWizard not managing to react to size changes automatically
+ QApplication::activeWindow()->adjustSize();
+}
diff --git a/src/gui/wizard/NewDatabaseWizardPageMasterKey.h b/src/gui/wizard/NewDatabaseWizardPageMasterKey.h
new file mode 100644
index 000000000..c6fa53cea
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPageMasterKey.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_NEWDATABASEWIZARDPAGEMASTERKEY_H
+#define KEEPASSXC_NEWDATABASEWIZARDPAGEMASTERKEY_H
+
+#include "NewDatabaseWizardPage.h"
+
+class NewDatabaseWizardPageMasterKey : public NewDatabaseWizardPage
+{
+Q_OBJECT
+
+public:
+ explicit NewDatabaseWizardPageMasterKey(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(NewDatabaseWizardPageMasterKey);
+ ~NewDatabaseWizardPageMasterKey() override;
+
+private slots:
+ void updateWindowSize();
+};
+
+#endif //KEEPASSXC_NEWDATABASEWIZARDPAGEMASTERKEY_H
diff --git a/src/gui/wizard/NewDatabaseWizardPageMetaData.cpp b/src/gui/wizard/NewDatabaseWizardPageMetaData.cpp
new file mode 100644
index 000000000..f4e2712fb
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPageMetaData.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "NewDatabaseWizardPageMetaData.h"
+#include "gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h"
+
+NewDatabaseWizardPageMetaData::NewDatabaseWizardPageMetaData(QWidget* parent)
+ : NewDatabaseWizardPage(parent)
+{
+ setPageWidget(new DatabaseSettingWidgetMetaData());
+
+ setTitle(tr("General Database Information"));
+ setSubTitle(tr("Please fill in the display name and an optional description for your new database:"));
+}
+
+NewDatabaseWizardPageMetaData::~NewDatabaseWizardPageMetaData()
+{
+}
\ No newline at end of file
diff --git a/src/gui/wizard/NewDatabaseWizardPageMetaData.h b/src/gui/wizard/NewDatabaseWizardPageMetaData.h
new file mode 100644
index 000000000..44e8f1941
--- /dev/null
+++ b/src/gui/wizard/NewDatabaseWizardPageMetaData.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 or (at your option)
+ * version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H
+#define KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H
+
+#include "NewDatabaseWizardPage.h"
+
+#include
+#include
+
+class Database;
+
+class NewDatabaseWizardPageMetaData : public NewDatabaseWizardPage
+{
+Q_OBJECT
+
+public:
+ explicit NewDatabaseWizardPageMetaData(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(NewDatabaseWizardPageMetaData);
+ ~NewDatabaseWizardPageMetaData() override;
+};
+
+#endif //KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H
diff --git a/src/keys/ChallengeResponseKey.h b/src/keys/ChallengeResponseKey.h
index 702ee2511..2f7e7f4ca 100644
--- a/src/keys/ChallengeResponseKey.h
+++ b/src/keys/ChallengeResponseKey.h
@@ -20,15 +20,23 @@
#define KEEPASSX_CHALLENGE_RESPONSE_KEY_H
#include
+#include
class ChallengeResponseKey
{
public:
- virtual ~ChallengeResponseKey()
- {
- }
+ explicit ChallengeResponseKey(const QUuid& uuid) : m_uuid(uuid) {}
+ Q_DISABLE_COPY(ChallengeResponseKey);
+ virtual ~ChallengeResponseKey() {}
virtual QByteArray rawKey() const = 0;
virtual bool challenge(const QByteArray& challenge) = 0;
+ virtual QUuid uuid() const
+ {
+ return m_uuid;
+ }
+
+private:
+ QUuid m_uuid;
};
#endif // KEEPASSX_CHALLENGE_RESPONSE_KEY_H
diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp
index 3bce1559a..4fca7d320 100644
--- a/src/keys/CompositeKey.cpp
+++ b/src/keys/CompositeKey.cpp
@@ -1,6 +1,6 @@
/*
+* Copyright (C) 2018 KeePassXC Team
* Copyright (C) 2010 Felix Geyer
-* Copyright (C) 2017 KeePassXC Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,13 +25,11 @@
#include "crypto/CryptoHash.h"
#include "crypto/kdf/AesKdf.h"
-CompositeKey::CompositeKey()
-{
-}
+QUuid CompositeKey::UUID("76a7ae25-a542-4add-9849-7c06be945b94");
-CompositeKey::CompositeKey(const CompositeKey& key)
+CompositeKey::CompositeKey()
+ : Key(UUID)
{
- *this = key;
}
CompositeKey::~CompositeKey()
@@ -41,7 +39,6 @@ CompositeKey::~CompositeKey()
void CompositeKey::clear()
{
- qDeleteAll(m_keys);
m_keys.clear();
m_challengeResponseKeys.clear();
}
@@ -51,30 +48,6 @@ bool CompositeKey::isEmpty() const
return m_keys.isEmpty() && m_challengeResponseKeys.isEmpty();
}
-CompositeKey* CompositeKey::clone() const
-{
- return new CompositeKey(*this);
-}
-
-CompositeKey& CompositeKey::operator=(const CompositeKey& key)
-{
- // handle self assignment as that would break when calling clear()
- if (this == &key) {
- return *this;
- }
-
- clear();
-
- for (const Key* subKey : asConst(key.m_keys)) {
- addKey(*subKey);
- }
- for (const auto subKey : asConst(key.m_challengeResponseKeys)) {
- addChallengeResponseKey(subKey);
- }
-
- return *this;
-}
-
/**
* Get raw key hash as bytes.
*
@@ -104,7 +77,7 @@ QByteArray CompositeKey::rawKey(const QByteArray* transformSeed, bool* ok) const
{
CryptoHash cryptoHash(CryptoHash::Sha256);
- for (const Key* key : m_keys) {
+ for (auto const& key : m_keys) {
cryptoHash.addData(key->rawKey());
}
@@ -174,12 +147,41 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const
return true;
}
-void CompositeKey::addKey(const Key& key)
+/**
+ * Add a \link Key to this composite key.
+ * Keys will be hashed in the order they were initially added.
+ *
+ * @param key the key
+ */
+void CompositeKey::addKey(QSharedPointer key)
{
- m_keys.append(key.clone());
+ m_keys.append(key);
}
+/**
+ * @return list of Keys which are part of this CompositeKey
+ */
+const QList>& CompositeKey::keys() const
+{
+ return m_keys;
+}
+
+/**
+ * Add a \link ChallengeResponseKey to this composite key.
+ * ChallengeResponseKeys will be hashed in the order they were initially added,
+ * but will always come after normal \link Keys.
+ *
+ * @param key the key
+ */
void CompositeKey::addChallengeResponseKey(QSharedPointer key)
{
m_challengeResponseKeys.append(key);
}
+
+/**
+ * @return list of ChallengeResponseKeys which are part of this CompositeKey
+ */
+const QList>& CompositeKey::challengeResponseKeys() const
+{
+ return m_challengeResponseKeys;
+}
diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h
index 50d9dbd37..d81c46986 100644
--- a/src/keys/CompositeKey.h
+++ b/src/keys/CompositeKey.h
@@ -1,6 +1,6 @@
/*
+* Copyright (C) 2018 KeePassXC Team
* Copyright (C) 2010 Felix Geyer
-* Copyright (C) 2017 KeePassXC Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,24 +30,26 @@
class CompositeKey : public Key
{
public:
+ static QUuid UUID;
+
CompositeKey();
- CompositeKey(const CompositeKey& key);
~CompositeKey() override;
void clear();
bool isEmpty() const;
- CompositeKey* clone() const override;
- CompositeKey& operator=(const CompositeKey& key);
QByteArray rawKey() const override;
QByteArray rawKey(const QByteArray* transformSeed, bool* ok = nullptr) const;
bool transform(const Kdf& kdf, QByteArray& result) const Q_REQUIRED_RESULT;
bool challenge(const QByteArray& seed, QByteArray& result) const;
- void addKey(const Key& key);
- void addChallengeResponseKey(QSharedPointer key);
+ void addKey(QSharedPointer key);
+ const QList>& keys() const;
+
+ void addChallengeResponseKey(QSharedPointer key);\
+ const QList>& challengeResponseKeys() const;
private:
- QList m_keys;
+ QList> m_keys;
QList> m_challengeResponseKeys;
};
diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp
index b491fc51e..6751b7877 100644
--- a/src/keys/FileKey.cpp
+++ b/src/keys/FileKey.cpp
@@ -24,6 +24,13 @@
#include "crypto/CryptoHash.h"
#include "crypto/Random.h"
+QUuid FileKey::UUID("a584cbc4-c9b4-437e-81bb-362ca9709273");
+
+FileKey::FileKey()
+ : Key(UUID)
+{
+}
+
/**
* Read key file from device while trying to detect its file format.
*
@@ -144,14 +151,6 @@ QByteArray FileKey::rawKey() const
return m_key;
}
-/**
- * @return cloned \link FileKey instance
- */
-FileKey* FileKey::clone() const
-{
- return new FileKey(*this);
-}
-
/**
* Generate a new key file from random bytes.
*
diff --git a/src/keys/FileKey.h b/src/keys/FileKey.h
index 3f04edede..295fe5565 100644
--- a/src/keys/FileKey.h
+++ b/src/keys/FileKey.h
@@ -28,6 +28,8 @@ class QIODevice;
class FileKey : public Key
{
public:
+ static QUuid UUID;
+
enum Type
{
None,
@@ -37,10 +39,10 @@ public:
FixedBinaryHex
};
+ FileKey();
bool load(QIODevice* device);
bool load(const QString& fileName, QString* errorMsg = nullptr);
QByteArray rawKey() const override;
- FileKey* clone() const override;
Type type() const;
static void create(QIODevice* device, int size = 128);
static bool create(const QString& fileName, QString* errorMsg = nullptr, int size = 128);
diff --git a/src/keys/Key.h b/src/keys/Key.h
index 4014e3907..db7864ec3 100644
--- a/src/keys/Key.h
+++ b/src/keys/Key.h
@@ -1,4 +1,5 @@
/*
+* Copyright (C) 2018 KeePassXC Team
* Copyright (C) 2010 Felix Geyer
*
* This program is free software: you can redistribute it and/or modify
@@ -19,15 +20,22 @@
#define KEEPASSX_KEY_H
#include
+#include
class Key
{
public:
- virtual ~Key()
- {
- }
+ explicit Key(const QUuid& uuid) : m_uuid(uuid) {};
+ Q_DISABLE_COPY(Key);
+ virtual ~Key() = default;
virtual QByteArray rawKey() const = 0;
- virtual Key* clone() const = 0;
+ inline virtual QUuid uuid() const
+ {
+ return m_uuid;
+ }
+
+private:
+ QUuid m_uuid;
};
#endif // KEEPASSX_KEY_H
diff --git a/src/keys/PasswordKey.cpp b/src/keys/PasswordKey.cpp
index ea1440480..9fecc7637 100644
--- a/src/keys/PasswordKey.cpp
+++ b/src/keys/PasswordKey.cpp
@@ -19,19 +19,23 @@
#include "crypto/CryptoHash.h"
+QUuid PasswordKey::UUID("77e90411-303a-43f2-b773-853b05635ead");
+
PasswordKey::PasswordKey()
+ : Key(UUID)
{
}
PasswordKey::PasswordKey(const QString& password)
+ : Key(UUID)
{
setPassword(password);
}
-PasswordKey PasswordKey::fromRawKey(const QByteArray& rawKey)
+QSharedPointer PasswordKey::fromRawKey(const QByteArray& rawKey)
{
- PasswordKey result;
- result.m_key = rawKey;
+ auto result = QSharedPointer::create();
+ result->m_key = rawKey;
return result;
}
@@ -44,8 +48,3 @@ void PasswordKey::setPassword(const QString& password)
{
m_key = CryptoHash::hash(password.toUtf8(), CryptoHash::Sha256);
}
-
-PasswordKey* PasswordKey::clone() const
-{
- return new PasswordKey(*this);
-}
diff --git a/src/keys/PasswordKey.h b/src/keys/PasswordKey.h
index 821510703..46c47e6d3 100644
--- a/src/keys/PasswordKey.h
+++ b/src/keys/PasswordKey.h
@@ -19,19 +19,21 @@
#define KEEPASSX_PASSWORDKEY_H
#include
+#include
#include "keys/Key.h"
class PasswordKey : public Key
{
public:
+ static QUuid UUID;
+
PasswordKey();
explicit PasswordKey(const QString& password);
- QByteArray rawKey() const;
+ QByteArray rawKey() const override;
void setPassword(const QString& password);
- PasswordKey* clone() const;
- static PasswordKey fromRawKey(const QByteArray& rawKey);
+ static QSharedPointer fromRawKey(const QByteArray& rawKey);
private:
QByteArray m_key;
};
diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp
index 913e2d32a..b2a40bd23 100644
--- a/src/keys/YkChallengeResponseKey.cpp
+++ b/src/keys/YkChallengeResponseKey.cpp
@@ -30,8 +30,11 @@
#include
#include
+QUuid YkChallengeResponseKey::UUID("e092495c-e77d-498b-84a1-05ae0d955508");
+
YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking)
- : m_slot(slot)
+ : ChallengeResponseKey(UUID)
+ , m_slot(slot)
, m_blocking(blocking)
{
if (KEEPASSXC_MAIN_WINDOW) {
diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h
index 912896e9d..0ce915271 100644
--- a/src/keys/YkChallengeResponseKey.h
+++ b/src/keys/YkChallengeResponseKey.h
@@ -29,10 +29,12 @@ class YkChallengeResponseKey : public QObject, public ChallengeResponseKey
Q_OBJECT
public:
- YkChallengeResponseKey(int slot = -1, bool blocking = false);
+ static QUuid UUID;
- QByteArray rawKey() const;
- bool challenge(const QByteArray& challenge);
+ explicit YkChallengeResponseKey(int slot = -1, bool blocking = false);
+
+ QByteArray rawKey() const override;
+ bool challenge(const QByteArray& challenge) override;
bool challenge(const QByteArray& challenge, unsigned retries);
QString getName() const;
bool isBlocking() const;
diff --git a/src/sshagent/AgentSettingsPage.h b/src/sshagent/AgentSettingsPage.h
index 1cd4d9a57..09d898048 100644
--- a/src/sshagent/AgentSettingsPage.h
+++ b/src/sshagent/AgentSettingsPage.h
@@ -20,7 +20,7 @@
#define AGENTSETTINGSPAGE_H
#include "gui/DatabaseTabWidget.h"
-#include "gui/SettingsWidget.h"
+#include "gui/ApplicationSettingsWidget.h"
class AgentSettingsPage : public ISettingsPage
{
diff --git a/src/touchid/TouchID.h b/src/touchid/TouchID.h
index cae108b64..115943a27 100644
--- a/src/touchid/TouchID.h
+++ b/src/touchid/TouchID.h
@@ -12,44 +12,32 @@
class TouchID
{
- public:
- static TouchID& getInstance();
+public:
+ static TouchID& getInstance();
- private:
- TouchID() {} // Constructor? (the {} brackets) are needed here.
+private:
+ TouchID() {}
- // C++ 03
- // ========
- // Don't forget to declare these two. You want to make sure they
- // are unacceptable otherwise you may accidentally get copies of
- // your singleton appearing.
+ // TouchID(TouchID const&); // Don't Implement
+ // void operator=(TouchID const&); // Don't implement
- // TouchID(TouchID const&); // Don't Implement
- // void operator=(TouchID const&); // Don't implement
+ QHash m_encryptedMasterKeys;
+ int m_available = TOUCHID_UNDEFINED;
- QHash m_encryptedMasterKeys;
- int m_available = TOUCHID_UNDEFINED;
+public:
+ TouchID(TouchID const&) = delete;
- public:
- // C++ 11
- // =======
- // We can use the better technique of deleting the methods
- // we don't want.
+ void operator=(TouchID const&) = delete;
- TouchID(TouchID const&) = delete;
- void operator=(TouchID const&) = delete;
+ bool storeKey(const QString& databasePath, const QByteArray& passwordKey);
- // Note: Scott Meyers mentions in his Effective Modern
- // C++ book, that deleted functions should generally
- // be public as it results in better error messages
- // due to the compilers behavior to check accessibility
- // before deleted status
+ QSharedPointer getKey(const QString& databasePath) const;
- bool storeKey(const QString& databasePath, const QByteArray& passwordKey);
- QSharedPointer getKey(const QString& databasePath) const;
- bool isAvailable();
- bool authenticate(const QString& message = "") const;
- void reset(const QString& databasePath = "");
+ bool isAvailable();
+
+ bool authenticate(const QString& message = "") const;
+
+ void reset(const QString& databasePath = "");
};
#endif // KEEPASSX_TOUCHID_H
diff --git a/src/touchid/TouchID.mm b/src/touchid/TouchID.mm
index 45f2de0f7..9ef72189b 100644
--- a/src/touchid/TouchID.mm
+++ b/src/touchid/TouchID.mm
@@ -24,20 +24,28 @@ inline QString hash(const QString& value)
return QString(result);
}
-/* Singleton */
+/**
+ * Singleton
+ */
TouchID& TouchID::getInstance()
{
static TouchID instance; // Guaranteed to be destroyed.
- // Instantiated on first use.
+ // Instantiated on first use.
return instance;
}
-/* Generates a random AES 256bit key and uses it to encrypt the PasswordKey that protects the database. The encrypted PasswordKey is kept in memory while the AES key is stored in the macOS KeyChain protected by TouchID. */
+/**
+ * Generates a random AES 256bit key and uses it to encrypt the PasswordKey that
+ * protects the database. The encrypted PasswordKey is kept in memory while the
+ * AES key is stored in the macOS KeyChain protected by TouchID.
+ */
bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKey)
{
if (databasePath.isEmpty() || passwordKey.isEmpty()) {
// illegal arguments
- debug("TouchID::storeKey - Illegal arguments: databasePath = %s, len(passwordKey) = %d", databasePath.toUtf8().constData(), passwordKey.length());
+ debug("TouchID::storeKey - Illegal arguments: databasePath = %s, len(passwordKey) = %d",
+ databasePath.toUtf8().constData(),
+ passwordKey.length());
return false;
}
@@ -56,7 +64,8 @@ bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKe
SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt);
if (!aes256Encrypt.init(randomKey, randomIV)) {
- debug("TouchID::storeKey - Error initializing encryption: %s", aes256Encrypt.errorString().toUtf8().constData());
+ debug("TouchID::storeKey - Error initializing encryption: %s",
+ aes256Encrypt.errorString().toUtf8().constData());
return false;
}
@@ -73,23 +82,24 @@ bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKe
NSString* accountName = (SECURITY_ACCOUNT_PREFIX + hash(databasePath)).toNSString(); // autoreleased
// try to delete an existing entry
- CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-
+ CFMutableDictionaryRef
+ query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
- CFDictionarySetValue(query, kSecAttrAccount, (__bridge CFStringRef)accountName);
+ CFDictionarySetValue(query, kSecAttrAccount, (__bridge CFStringRef) accountName);
CFDictionarySetValue(query, kSecReturnData, kCFBooleanFalse);
-
+
// get data from the KeyChain
- OSStatus status = SecItemDelete(query);
+ OSStatus status = SecItemDelete(query);
debug("TouchID::storeKey - Status deleting existing entry: %d", status);
// prepare adding secure entry to the macOS KeyChain
CFErrorRef error = NULL;
SecAccessControlRef sacObject = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
- kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
- kSecAccessControlTouchIDCurrentSet, // depr: kSecAccessControlBiometryCurrentSet,
- &error);
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
+ kSecAccessControlTouchIDCurrentSet, // depr: kSecAccessControlBiometryCurrentSet,
+ &error);
if (sacObject == NULL || error != NULL) {
NSError* e = (__bridge NSError*) error;
@@ -97,22 +107,24 @@ bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKe
return false;
}
- CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-
+ CFMutableDictionaryRef attributes =
+ CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
// prepare data (key) to be stored
QByteArray dataBytes = (randomKey + randomIV).toHex();
- CFDataRef valueData = CFDataCreateWithBytesNoCopy(NULL, reinterpret_cast(dataBytes.data()), dataBytes.length(), NULL);
+ CFDataRef valueData =
+ CFDataCreateWithBytesNoCopy(NULL, reinterpret_cast(dataBytes.data()), dataBytes.length(), NULL);
CFDictionarySetValue(attributes, kSecClass, kSecClassGenericPassword);
- CFDictionarySetValue(attributes, kSecAttrAccount, (__bridge CFStringRef)accountName);
+ CFDictionarySetValue(attributes, kSecAttrAccount, (__bridge CFStringRef) accountName);
CFDictionarySetValue(attributes, kSecValueData, valueData);
CFDictionarySetValue(attributes, kSecAttrSynchronizable, kCFBooleanFalse);
CFDictionarySetValue(attributes, kSecUseAuthenticationUI, kSecUseAuthenticationUIAllow);
CFDictionarySetValue(attributes, kSecAttrAccessControl, sacObject);
// add to KeyChain
- status = SecItemAdd(attributes, NULL);
+ status = SecItemAdd(attributes, NULL);
debug("TouchID::storeKey - Status adding new entry: %d", status); // read w/ e.g. "security error -50" in shell
@@ -128,8 +140,11 @@ bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKe
return true;
}
-/* Checks if an encrypted PasswordKey is available for the given database, tries to decrypt it using the KeyChain and if successful, returns it. */
-QSharedPointer TouchID::getKey(const QString& databasePath) const
+/**
+ * Checks if an encrypted PasswordKey is available for the given database, tries to
+ * decrypt it using the KeyChain and if successful, returns it.
+ */
+QSharedPointer TouchID::getKey(const QString& databasePath) const
{
if (databasePath.isEmpty()) {
// illegal arguments
@@ -142,21 +157,24 @@ QSharedPointer TouchID::getKey(const QString& databasePath) const
debug("TouchID::getKey - No stored key found");
return NULL;
}
-
+
// query the KeyChain for the AES key
- CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-
+ CFMutableDictionaryRef
+ query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
NSString* accountName = (SECURITY_ACCOUNT_PREFIX + hash(databasePath)).toNSString(); // autoreleased
- NSString* touchPromptMessage = QCoreApplication::translate("DatabaseOpenWidget", "authenticate to access the database").toNSString(); // autoreleased
+ NSString* touchPromptMessage =
+ QCoreApplication::translate("DatabaseOpenWidget", "authenticate to access the database")
+ .toNSString(); // autoreleased
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
- CFDictionarySetValue(query, kSecAttrAccount, (__bridge CFStringRef)accountName);
+ CFDictionarySetValue(query, kSecAttrAccount, (__bridge CFStringRef) accountName);
CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
- CFDictionarySetValue(query, kSecUseOperationPrompt, (__bridge CFStringRef)touchPromptMessage);
-
+ CFDictionarySetValue(query, kSecUseOperationPrompt, (__bridge CFStringRef) touchPromptMessage);
+
// get data from the KeyChain
CFTypeRef dataTypeRef = NULL;
- OSStatus status = SecItemCopyMatching(query, &dataTypeRef);
+ OSStatus status = SecItemCopyMatching(query, &dataTypeRef);
CFRelease(query);
if (status == errSecUserCanceled) {
@@ -169,9 +187,10 @@ QSharedPointer TouchID::getKey(const QString& databasePath) const
}
CFDataRef valueData = static_cast(dataTypeRef);
- QByteArray dataBytes = QByteArray::fromHex(QByteArray(reinterpret_cast(CFDataGetBytePtr(valueData)), CFDataGetLength(valueData)));
+ QByteArray dataBytes = QByteArray::fromHex(QByteArray(reinterpret_cast(CFDataGetBytePtr(valueData)),
+ CFDataGetLength(valueData)));
CFRelease(valueData);
-
+
// extract AES key and IV from data bytes
QByteArray key = dataBytes.left(32);
QByteArray iv = dataBytes.right(16);
@@ -194,7 +213,9 @@ QSharedPointer TouchID::getKey(const QString& databasePath) const
return QSharedPointer::create(result);
}
-/* Dynamic check if TouchID is available on the current machine. */
+/**
+ * Dynamic check if TouchID is available on the current machine.
+ */
bool TouchID::isAvailable()
{
// cache result
@@ -208,47 +229,53 @@ bool TouchID::isAvailable()
this->m_available = canAuthenticate ? TOUCHID_AVAILABLE : TOUCHID_NOT_AVAILABLE;
return canAuthenticate;
}
- @catch(NSException*) {
+ @catch (NSException*) {
this->m_available = TOUCHID_NOT_AVAILABLE;
return false;
}
}
-typedef enum {
+typedef enum
+{
kTouchIDResultNone,
kTouchIDResultAllowed,
kTouchIDResultFailed
} TouchIDResult;
-/* Performs a simple authentication using TouchID. */
+/**
+ * Performs a simple authentication using TouchID.
+ */
bool TouchID::authenticate(const QString& message) const
{
// message must not be an empty string
QString msg = message;
if (message.length() == 0)
msg = QCoreApplication::translate("DatabaseOpenWidget", "authenticate a privileged operation");
-
+
@try {
LAContext* context = [[LAContext alloc] init];
__block TouchIDResult result = kTouchIDResultNone;
NSString* authMessage = msg.toNSString(); // autoreleased
- [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:authMessage reply:^(BOOL success, NSError* error) {
- result = success ? kTouchIDResultAllowed : kTouchIDResultFailed;
- CFRunLoopWakeUp(CFRunLoopGetCurrent());
- }];
-
+ [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
+ localizedReason:authMessage reply:^(BOOL success, NSError* error) {
+ result = success ? kTouchIDResultAllowed : kTouchIDResultFailed;
+ CFRunLoopWakeUp(CFRunLoopGetCurrent());
+ }];
+
while (result == kTouchIDResultNone)
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
-
+
[context release];
return result == kTouchIDResultAllowed;
}
- @catch(NSException*) {
+ @catch (NSException*) {
return false;
}
}
-/* Resets the inner state either for all or for the given database */
+/**
+ * Resets the inner state either for all or for the given database
+ */
void TouchID::reset(const QString& databasePath)
{
if (databasePath.isEmpty()) {
diff --git a/tests/TestDatabase.cpp b/tests/TestDatabase.cpp
index 51304b1fb..78e1b10a4 100644
--- a/tests/TestDatabase.cpp
+++ b/tests/TestDatabase.cpp
@@ -38,60 +38,54 @@ void TestDatabase::initTestCase()
void TestDatabase::testEmptyRecycleBinOnDisabled()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinDisabled.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("123"));
- Database* db = Database::openDatabaseFile(filename, key);
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("123"));
+ QScopedPointer db(Database::openDatabaseFile(filename, key));
QVERIFY(db);
- QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
db->emptyRecycleBin();
// The database must be unmodified in this test after emptying the recycle bin.
QCOMPARE(spyModified.count(), 0);
-
- delete db;
}
void TestDatabase::testEmptyRecycleBinOnNotCreated()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinNotYetCreated.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("123"));
- Database* db = Database::openDatabaseFile(filename, key);
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("123"));
+ QScopedPointer db(Database::openDatabaseFile(filename, key));
QVERIFY(db);
- QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
db->emptyRecycleBin();
// The database must be unmodified in this test after emptying the recycle bin.
QCOMPARE(spyModified.count(), 0);
-
- delete db;
}
void TestDatabase::testEmptyRecycleBinOnEmpty()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinEmpty.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("123"));
- Database* db = Database::openDatabaseFile(filename, key);
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("123"));
+ QScopedPointer db(Database::openDatabaseFile(filename, key));
QVERIFY(db);
- QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
+ QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
db->emptyRecycleBin();
// The database must be unmodified in this test after emptying the recycle bin.
QCOMPARE(spyModified.count(), 0);
-
- delete db;
}
void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinWithData.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("123"));
- Database* db = Database::openDatabaseFile(filename, key);
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("123"));
+ QScopedPointer db(Database::openDatabaseFile(filename, key));
QVERIFY(db);
QFile originalFile(filename);
@@ -104,8 +98,6 @@ void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
QTemporaryFile afterCleanup;
KeePass2Writer writer;
- writer.writeDatabase(&afterCleanup, db);
+ writer.writeDatabase(&afterCleanup, db.data());
QVERIFY(afterCleanup.size() < initialSize);
-
- delete db;
}
diff --git a/tests/TestKdbx2.cpp b/tests/TestKdbx2.cpp
index 9fe90ae62..ef944f7fd 100644
--- a/tests/TestKdbx2.cpp
+++ b/tests/TestKdbx2.cpp
@@ -65,8 +65,8 @@ void TestKdbx2::verifyKdbx2Db(Database* db)
void TestKdbx2::testFormat200()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("a"));
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("a"));
KeePass2Reader reader;
QScopedPointer db(reader.readDatabase(filename, key));
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_2 & KeePass2::FILE_VERSION_CRITICAL_MASK);
@@ -78,8 +78,8 @@ void TestKdbx2::testFormat200()
void TestKdbx2::testFormat200Upgrade()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("a"));
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("a"));
KeePass2Reader reader;
QScopedPointer db(reader.readDatabase(filename, key));
QVERIFY2(!reader.hasError(), reader.errorString().toStdString().c_str());
diff --git a/tests/TestKdbx3.cpp b/tests/TestKdbx3.cpp
index 6df13d380..1c8526b3e 100644
--- a/tests/TestKdbx3.cpp
+++ b/tests/TestKdbx3.cpp
@@ -63,7 +63,7 @@ void TestKdbx3::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& er
}
void TestKdbx3::readKdbx(QIODevice* device,
- CompositeKey const& key,
+ QSharedPointer key,
QScopedPointer& db,
bool& hasError,
QString& errorString)
@@ -78,7 +78,7 @@ void TestKdbx3::readKdbx(QIODevice* device,
}
void TestKdbx3::readKdbx(const QString& path,
- CompositeKey const& key,
+ QSharedPointer key,
QScopedPointer& db,
bool& hasError,
QString& errorString)
@@ -106,8 +106,8 @@ void TestKdbx3::writeKdbx(QIODevice* device, Database* db, bool& hasError, QStri
void TestKdbx3::testFormat300()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format300.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("a"));
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create("a"));
KeePass2Reader reader;
QScopedPointer db(reader.readDatabase(filename, key));
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3);
@@ -121,8 +121,8 @@ void TestKdbx3::testFormat300()
void TestKdbx3::testNonAscii()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/NonAscii.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer::create(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
KeePass2Reader reader;
QScopedPointer db(reader.readDatabase(filename, key));
QVERIFY(db.data());
@@ -134,8 +134,8 @@ void TestKdbx3::testNonAscii()
void TestKdbx3::testCompressed()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Compressed.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey(""));
+ auto key = QSharedPointer::create();
+ key->addKey(QSharedPointer