mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Add QR code generator for TOTP export (#1167)
* Resolves #764 * Add libqrencode and qtsvg dependencies * Ensure QR code remains square * Auto-close QR code dialog when database is locked * Add databaseLocked() Signal to databaseWidget * Correct otpauth URI output in Totp::writeSettings(...)
This commit is contained in:
parent
80749958b7
commit
bb16dc6d01
@ -294,16 +294,16 @@ endif(WITH_TESTS)
|
||||
include(CLangFormat)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Test LinguistTools DBus REQUIRED)
|
||||
find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools DBus REQUIRED)
|
||||
elseif(APPLE)
|
||||
find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Test LinguistTools REQUIRED
|
||||
find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools REQUIRED
|
||||
HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH
|
||||
)
|
||||
find_package(Qt5 COMPONENTS MacExtras
|
||||
HINTS /usr/local/Cellar/qt/*/lib/cmake ENV PATH
|
||||
)
|
||||
else()
|
||||
find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Test LinguistTools REQUIRED)
|
||||
find_package(Qt5 COMPONENTS Core Network Concurrent Widgets Svg Test LinguistTools REQUIRED)
|
||||
endif()
|
||||
|
||||
if(Qt5Core_VERSION VERSION_LESS "5.2.0")
|
||||
@ -339,6 +339,8 @@ find_package(Argon2 REQUIRED)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
find_package(QREncode REQUIRED)
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
if(ZLIB_VERSION_STRING VERSION_LESS "1.2.0")
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
FROM ubuntu:14.04
|
||||
|
||||
ENV REBUILD_COUNTER=8
|
||||
ENV REBUILD_COUNTER=10
|
||||
|
||||
ENV QT5_VERSION=qt510
|
||||
ENV QT5_PPA_VERSION=qt-5.10.1
|
||||
@ -50,12 +50,14 @@ RUN set -x \
|
||||
${QT5_VERSION}x11extras \
|
||||
${QT5_VERSION}translations \
|
||||
${QT5_VERSION}imageformats \
|
||||
${QT5_VERSION}svg \
|
||||
zlib1g-dev \
|
||||
libxi-dev \
|
||||
libxtst-dev \
|
||||
mesa-common-dev \
|
||||
libyubikey-dev \
|
||||
libykpers-1-dev
|
||||
libykpers-1-dev \
|
||||
libqrencode-dev
|
||||
|
||||
ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}"
|
||||
ENV CMAKE_PREFIX_PATH="/opt/${QT5_VERSION}/lib/cmake"
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
FROM ubuntu:14.04
|
||||
|
||||
ENV REBUILD_COUNTER=4
|
||||
ENV REBUILD_COUNTER=5
|
||||
|
||||
ENV QT5_VERSION=qt53
|
||||
ENV QT5_PPA_VERSION=${QT5_VERSION}2
|
||||
@ -49,11 +49,13 @@ RUN set -x \
|
||||
${QT5_VERSION}tools \
|
||||
${QT5_VERSION}x11extras \
|
||||
${QT5_VERSION}translations \
|
||||
${QT5_VERSION}svg \
|
||||
zlib1g-dev \
|
||||
libyubikey-dev \
|
||||
libykpers-1-dev \
|
||||
libxi-dev \
|
||||
libxtst-dev \
|
||||
libqrencode-dev \
|
||||
xvfb
|
||||
|
||||
ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}"
|
||||
|
22
cmake/FindQREncode.cmake
Normal file
22
cmake/FindQREncode.cmake
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
find_path(QRENCODE_INCLUDE_DIR qrencode.h)
|
||||
find_library(QRENCODE_LIBRARY qrencode)
|
||||
|
||||
mark_as_advanced(QRENCODE_LIBRARY QRENCODE_INCLUDE_DIR)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(QREncode DEFAULT_MSG QRENCODE_LIBRARY QRENCODE_INCLUDE_DIR)
|
@ -39,6 +39,7 @@ parts:
|
||||
- libgcrypt20-dev
|
||||
- libqt5x11extras5-dev
|
||||
- qtbase5-dev
|
||||
- qtsvg5-dev
|
||||
- qttools5-dev
|
||||
- qttools5-dev-tools
|
||||
- zlib1g-dev
|
||||
@ -48,6 +49,7 @@ parts:
|
||||
- libykpers-1-dev
|
||||
- libsodium-dev
|
||||
- libargon2-0-dev
|
||||
- libqrencode-dev
|
||||
stage-packages:
|
||||
- dbus
|
||||
- qttranslations5-l10n # common translations
|
||||
|
@ -127,8 +127,10 @@ set(keepassx_SOURCES
|
||||
gui/ApplicationSettingsWidget.cpp
|
||||
gui/SearchWidget.cpp
|
||||
gui/SortFilterHideProxyModel.cpp
|
||||
gui/SquareSvgWidget.cpp
|
||||
gui/TotpSetupDialog.cpp
|
||||
gui/TotpDialog.cpp
|
||||
gui/TotpExportSettingsDialog.cpp
|
||||
gui/UnlockDatabaseWidget.cpp
|
||||
gui/UnlockDatabaseDialog.cpp
|
||||
gui/WelcomeWidget.cpp
|
||||
@ -225,6 +227,8 @@ endif()
|
||||
|
||||
add_subdirectory(autotype)
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(qrcode)
|
||||
set(qrcode_LIB qrcode)
|
||||
|
||||
add_subdirectory(sshagent)
|
||||
if(WITH_XC_SSHAGENT)
|
||||
@ -270,9 +274,10 @@ target_link_libraries(keepassx_core
|
||||
autotype
|
||||
${keepassxcbrowser_LIB}
|
||||
${sshagent_LIB}
|
||||
${qrcode_LIB}
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
Qt5::Concurrent
|
||||
Qt5::Network
|
||||
Qt5::Widgets
|
||||
${CURL_LIBRARIES}
|
||||
${YUBIKEY_LIBRARIES}
|
||||
@ -280,7 +285,9 @@ target_link_libraries(keepassx_core
|
||||
${ARGON2_LIBRARIES}
|
||||
${GCRYPT_LIBRARIES}
|
||||
${GPGERROR_LIBRARIES}
|
||||
${ZLIB_LIBRARIES})
|
||||
${YUBIKEY_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${ZXCVBN_LIBRARIES})
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(keepassx_core "-framework Foundation")
|
||||
|
@ -369,7 +369,7 @@ void Entry::setTotp(QSharedPointer<Totp::Settings> settings)
|
||||
beginUpdate();
|
||||
m_data.totpSettings = settings;
|
||||
|
||||
auto text = Totp::writeSettings(m_data.totpSettings);
|
||||
auto text = Totp::writeSettings(m_data.totpSettings, title(), username());
|
||||
if (m_attributes->hasKey(Totp::ATTRIBUTE_OTP)) {
|
||||
m_attributes->set(Totp::ATTRIBUTE_OTP, text, true);
|
||||
} else {
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/TotpSetupDialog.h"
|
||||
#include "gui/TotpDialog.h"
|
||||
#include "gui/TotpExportSettingsDialog.h"
|
||||
#include "gui/UnlockDatabaseDialog.h"
|
||||
#include "gui/UnlockDatabaseWidget.h"
|
||||
#include "gui/entry/EditEntryWidget.h"
|
||||
@ -572,6 +573,18 @@ void DatabaseWidget::copyAttribute(QAction* action)
|
||||
currentEntry->resolveMultiplePlaceholders(currentEntry->attributes()->value(action->data().toString())));
|
||||
}
|
||||
|
||||
void DatabaseWidget::showTotpKeyQrCode()
|
||||
{
|
||||
Entry* currentEntry = m_entryView->currentEntry();
|
||||
Q_ASSERT(currentEntry);
|
||||
if (!currentEntry) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto totpDisplayDialog = new TotpExportSettingsDialog(this, currentEntry);
|
||||
totpDisplayDialog->open();
|
||||
}
|
||||
|
||||
void DatabaseWidget::setClipboardTextAndMinimize(const QString& text)
|
||||
{
|
||||
clipboard()->setText(text);
|
||||
@ -1171,6 +1184,7 @@ void DatabaseWidget::lock()
|
||||
Database* newDb = new Database();
|
||||
newDb->metadata()->setName(m_db->metadata()->name());
|
||||
replaceDatabase(newDb);
|
||||
emit lockedDatabase();
|
||||
}
|
||||
|
||||
void DatabaseWidget::updateFilePath(const QString& filePath)
|
||||
|
@ -125,6 +125,7 @@ signals:
|
||||
void pressedEntry(Entry* selectedEntry);
|
||||
void pressedGroup(Group* selectedGroup);
|
||||
void unlockedDatabase();
|
||||
void lockedDatabase();
|
||||
void listModeAboutToActivate();
|
||||
void listModeActivated();
|
||||
void searchModeAboutToActivate();
|
||||
@ -146,6 +147,7 @@ public slots:
|
||||
void copyNotes();
|
||||
void copyAttribute(QAction* action);
|
||||
void showTotp();
|
||||
void showTotpKeyQrCode();
|
||||
void copyTotp();
|
||||
void setupTotp();
|
||||
void performAutoType();
|
||||
|
@ -287,6 +287,7 @@ MainWindow::MainWindow()
|
||||
m_actionMultiplexer.connect(m_ui->actionEntrySetupTotp, SIGNAL(triggered()), SLOT(setupTotp()));
|
||||
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyTotp, SIGNAL(triggered()), SLOT(copyTotp()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryTotpQRCode, SIGNAL(triggered()), SLOT(showTotpKeyQrCode()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyTitle, SIGNAL(triggered()), SLOT(copyTitle()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), SLOT(copyUsername()));
|
||||
m_actionMultiplexer.connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), SLOT(copyPassword()));
|
||||
@ -478,6 +479,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionEntryTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
||||
m_ui->actionEntryCopyTotp->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
||||
m_ui->actionEntrySetupTotp->setEnabled(singleEntrySelected);
|
||||
m_ui->actionEntryTotpQRCode->setEnabled(singleEntrySelected && dbWidget->currentEntryHasTotp());
|
||||
m_ui->actionGroupNew->setEnabled(groupSelected);
|
||||
m_ui->actionGroupEdit->setEnabled(groupSelected);
|
||||
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
|
||||
|
@ -251,6 +251,7 @@
|
||||
</property>
|
||||
<addaction name="actionEntryCopyTotp"/>
|
||||
<addaction name="actionEntryTotp"/>
|
||||
<addaction name="actionEntryTotpQRCode"/>
|
||||
<addaction name="actionEntrySetupTotp"/>
|
||||
</widget>
|
||||
<addaction name="actionEntryCopyUsername"/>
|
||||
@ -577,7 +578,12 @@
|
||||
</action>
|
||||
<action name="actionEntryTotp">
|
||||
<property name="text">
|
||||
<string>Show TOTP</string>
|
||||
<string>Show TOTP...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryTotpQRCode">
|
||||
<property name="text">
|
||||
<string>Show TOTP QR Code...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntrySetupTotp">
|
||||
|
28
src/gui/SquareSvgWidget.cpp
Normal file
28
src/gui/SquareSvgWidget.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SquareSvgWidget.h"
|
||||
|
||||
bool SquareSvgWidget::hasHeightForWidth() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int SquareSvgWidget::heightForWidth(int width) const
|
||||
{
|
||||
return width;
|
||||
}
|
33
src/gui/SquareSvgWidget.h
Normal file
33
src/gui/SquareSvgWidget.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_SquareSvgWidget_H
|
||||
#define KEEPASSX_SquareSvgWidget_H
|
||||
|
||||
#include <QtSvg/QSvgWidget>
|
||||
|
||||
class SquareSvgWidget : public QSvgWidget
|
||||
{
|
||||
public:
|
||||
SquareSvgWidget() = default;
|
||||
~SquareSvgWidget() override = default;
|
||||
|
||||
bool hasHeightForWidth() const override;
|
||||
int heightForWidth(int width) const override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_SquareSvgWidget_H
|
117
src/gui/TotpExportSettingsDialog.cpp
Normal file
117
src/gui/TotpExportSettingsDialog.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TotpExportSettingsDialog.h"
|
||||
#include "core/Config.h"
|
||||
#include "core/Entry.h"
|
||||
#include "gui/Clipboard.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
#include "gui/SquareSvgWidget.h"
|
||||
#include "qrcode/QrCode.h"
|
||||
#include "totp/totp.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QSizePolicy>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
TotpExportSettingsDialog::TotpExportSettingsDialog(DatabaseWidget* parent, Entry* entry)
|
||||
: QDialog(parent)
|
||||
, m_timer(new QTimer(this))
|
||||
, m_verticalLayout(new QVBoxLayout())
|
||||
, m_totpSvgWidget(new SquareSvgWidget())
|
||||
, m_countDown(new QLabel())
|
||||
, m_warningLabel(new QLabel())
|
||||
, m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Close | QDialogButtonBox::Ok))
|
||||
{
|
||||
m_verticalLayout->addWidget(m_warningLabel);
|
||||
m_verticalLayout->addItem(new QSpacerItem(0, 0));
|
||||
|
||||
m_verticalLayout->addStretch(0);
|
||||
m_verticalLayout->addWidget(m_totpSvgWidget);
|
||||
m_verticalLayout->addStretch(0);
|
||||
m_verticalLayout->addWidget(m_countDown);
|
||||
m_verticalLayout->addWidget(m_buttonBox);
|
||||
|
||||
setLayout(m_verticalLayout);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(m_buttonBox, SIGNAL(rejected()), SLOT(close()));
|
||||
connect(m_buttonBox, SIGNAL(accepted()), SLOT(copyToClipboard()));
|
||||
connect(m_timer, SIGNAL(timeout()), this, SLOT(autoClose()));
|
||||
connect(parent, SIGNAL(lockedDatabase()), this, SLOT(close()));
|
||||
|
||||
m_buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Copy"));
|
||||
m_countDown->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_secTillClose = 45;
|
||||
autoClose();
|
||||
m_timer->start(1000);
|
||||
|
||||
const auto totpSettings = entry->totpSettings();
|
||||
if (totpSettings->custom || !totpSettings->encoder.shortName.isEmpty()) {
|
||||
m_warningLabel->setWordWrap(true);
|
||||
m_warningLabel->setMargin(5);
|
||||
m_warningLabel->setText(tr("NOTE: These TOTP settings are custom and may not work with other authenticators.",
|
||||
"TOTP QR code dialog warning"));
|
||||
} else {
|
||||
m_warningLabel->hide();
|
||||
}
|
||||
|
||||
m_totpUri = Totp::writeSettings(entry->totpSettings(), entry->title(), entry->username(), true);
|
||||
const QrCode qrc(m_totpUri);
|
||||
|
||||
if (qrc.isValid()) {
|
||||
QBuffer buffer;
|
||||
qrc.writeSvg(&buffer, logicalDpiX());
|
||||
m_totpSvgWidget->load(buffer.data());
|
||||
const int minsize = static_cast<int>(logicalDpiX() * 2.5);
|
||||
m_totpSvgWidget->setMinimumSize(minsize, minsize);
|
||||
} else {
|
||||
auto errorBox = new QMessageBox(parent);
|
||||
errorBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
errorBox->setIcon(QMessageBox::Warning);
|
||||
errorBox->setText(tr("There was an error creating the QR code."));
|
||||
errorBox->exec();
|
||||
close();
|
||||
}
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
void TotpExportSettingsDialog::copyToClipboard()
|
||||
{
|
||||
clipboard()->setText(m_totpUri);
|
||||
if (config()->get("MinimizeOnCopy").toBool()) {
|
||||
static_cast<DatabaseWidget*>(parent())->window()->showMinimized();
|
||||
}
|
||||
}
|
||||
|
||||
void TotpExportSettingsDialog::autoClose()
|
||||
{
|
||||
if (--m_secTillClose > 0) {
|
||||
m_countDown->setText(tr("Closing in %1 seconds.").arg(m_secTillClose));
|
||||
} else {
|
||||
m_timer->stop();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
TotpExportSettingsDialog::~TotpExportSettingsDialog() = default;
|
57
src/gui/TotpExportSettingsDialog.h
Normal file
57
src/gui/TotpExportSettingsDialog.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TotpExportSettingsDialog_H
|
||||
#define KEEPASSX_TotpExportSettingsDialog_H
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "gui/DatabaseWidget.h"
|
||||
#include <QDialog>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
class QVBoxLayout;
|
||||
class SquareSvgWidget;
|
||||
class QLabel;
|
||||
class QDialogButtonBox;
|
||||
|
||||
class TotpExportSettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TotpExportSettingsDialog(DatabaseWidget* parent = nullptr, Entry* entry = nullptr);
|
||||
~TotpExportSettingsDialog();
|
||||
|
||||
private slots:
|
||||
void copyToClipboard();
|
||||
void autoClose();
|
||||
|
||||
private:
|
||||
int m_secTillClose;
|
||||
QString m_totpUri;
|
||||
QTimer* m_timer;
|
||||
|
||||
QVBoxLayout* m_verticalLayout;
|
||||
SquareSvgWidget* m_totpSvgWidget;
|
||||
QLabel* m_countDown;
|
||||
QLabel* m_warningLabel;
|
||||
QDialogButtonBox* m_buttonBox;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TOTPEXPORTSETTINGSDIALOG_H
|
21
src/qrcode/CMakeLists.txt
Normal file
21
src/qrcode/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(qrcode_SOURCES
|
||||
QrCode.cpp
|
||||
)
|
||||
|
||||
add_library(qrcode STATIC ${qrcode_SOURCES})
|
||||
target_link_libraries(qrcode Qt5::Core Qt5::Widgets Qt5::Svg ${QRENCODE_LIBRARY})
|
132
src/qrcode/QrCode.cpp
Normal file
132
src/qrcode/QrCode.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "QrCode.h"
|
||||
#include "QrCode_p.h"
|
||||
|
||||
#include <QBrush>
|
||||
#include <QByteArray>
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QString>
|
||||
#include <QSvgGenerator>
|
||||
#include <QVariant>
|
||||
|
||||
QrCodePrivate::QrCodePrivate()
|
||||
: m_qrcode(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
QrCodePrivate::~QrCodePrivate()
|
||||
{
|
||||
if (m_qrcode) {
|
||||
QRcode_free(m_qrcode);
|
||||
}
|
||||
}
|
||||
|
||||
QrCode::QrCode()
|
||||
: d_ptr(new QrCodePrivate())
|
||||
{
|
||||
}
|
||||
|
||||
QrCode::QrCode(const QString& data, const Version version, const ErrorCorrectionLevel ecl, const bool caseSensitive)
|
||||
: d_ptr(new QrCodePrivate())
|
||||
{
|
||||
init(data, version, ecl, caseSensitive);
|
||||
}
|
||||
|
||||
QrCode::QrCode(const QByteArray& data, const Version version, const ErrorCorrectionLevel ecl)
|
||||
: d_ptr(new QrCodePrivate())
|
||||
{
|
||||
init(data, version, ecl);
|
||||
}
|
||||
|
||||
QrCode::~QrCode() = default;
|
||||
|
||||
void QrCode::init(const QString& data, const Version version, const ErrorCorrectionLevel ecl, bool caseSensitive)
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d_ptr->m_qrcode = QRcode_encodeString(data.toLocal8Bit().data(),
|
||||
static_cast<int>(version),
|
||||
static_cast<QRecLevel>(ecl),
|
||||
QR_MODE_8,
|
||||
caseSensitive ? 1 : 0);
|
||||
}
|
||||
|
||||
void QrCode::init(const QByteArray& data, const Version version, const ErrorCorrectionLevel ecl)
|
||||
{
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d_ptr->m_qrcode = QRcode_encodeData(data.size(),
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
static_cast<int>(version),
|
||||
static_cast<QRecLevel>(ecl));
|
||||
}
|
||||
|
||||
bool QrCode::isValid() const
|
||||
{
|
||||
return d_ptr->m_qrcode != nullptr;
|
||||
}
|
||||
|
||||
void QrCode::writeSvg(QIODevice* outputDevice, const int dpi, const int margin) const
|
||||
{
|
||||
if (margin < 0 || d_ptr->m_qrcode == nullptr || outputDevice == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int width = d_ptr->m_qrcode->width + margin * 2;
|
||||
|
||||
QSvgGenerator generator;
|
||||
generator.setSize(QSize(width, width));
|
||||
generator.setViewBox(QRect(0, 0, width, width));
|
||||
generator.setResolution(dpi);
|
||||
generator.setOutputDevice(outputDevice);
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(&generator);
|
||||
|
||||
// Background
|
||||
painter.setClipRect(QRect(0, 0, width, width));
|
||||
painter.fillRect(QRect(0, 0, width, width), Qt::white);
|
||||
|
||||
// Foreground
|
||||
// "Dots" are stored in a quint8 x quint8 array using row-major order.
|
||||
// A dot is black if the LSB of its corresponding quint8 is 1.
|
||||
const QPen pen(Qt::black, 0, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
|
||||
const QBrush brush(Qt::black);
|
||||
painter.setPen(pen);
|
||||
painter.setBrush(brush);
|
||||
|
||||
const int rowSize = d_ptr->m_qrcode->width;
|
||||
unsigned char* dot = d_ptr->m_qrcode->data;
|
||||
for (int y = 0; y < rowSize; ++y) {
|
||||
for (int x = 0; x < rowSize; ++x) {
|
||||
if (quint8(0x01) == (static_cast<quint8>(*dot++) & quint8(0x01))) {
|
||||
painter.drawRect(margin + x, margin + y, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
painter.end();
|
||||
}
|
78
src/qrcode/QrCode.h
Normal file
78
src/qrcode/QrCode.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_QRCODE_H
|
||||
#define KEEPASSX_QRCODE_H
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
class QImage;
|
||||
class QIODevice;
|
||||
class QString;
|
||||
class QByteArray;
|
||||
|
||||
struct QrCodePrivate;
|
||||
|
||||
class QrCode
|
||||
{
|
||||
|
||||
public:
|
||||
enum class ErrorCorrectionLevel : int
|
||||
{
|
||||
LOW = 0,
|
||||
MEDIUM,
|
||||
QUARTILE,
|
||||
HIGH
|
||||
};
|
||||
|
||||
// See: http://www.qrcode.com/en/about/version.html
|
||||
// clang-format off
|
||||
enum class Version : int
|
||||
{
|
||||
AUTO = 0,
|
||||
V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19, V20,
|
||||
V21, V22, V23, V24, V25, V26, V27, V28, V29, V30, V31, V32, V33, V34, V35, V36, V37, V38, V39, V40
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Uses QRcode_encodeString (can't contain NUL characters)
|
||||
explicit QrCode(const QString& data,
|
||||
const Version version = Version::AUTO,
|
||||
const ErrorCorrectionLevel ecl = ErrorCorrectionLevel::HIGH,
|
||||
const bool caseSensitive = true);
|
||||
|
||||
// Uses QRcode_encodeData (can contain NUL characters)
|
||||
explicit QrCode(const QByteArray& data,
|
||||
const Version version = Version::AUTO,
|
||||
const ErrorCorrectionLevel ecl = ErrorCorrectionLevel::HIGH);
|
||||
|
||||
QrCode();
|
||||
~QrCode();
|
||||
|
||||
bool isValid() const;
|
||||
void writeSvg(QIODevice* outputDevice, const int dpi, const int margin = 4) const;
|
||||
|
||||
private:
|
||||
void init(const QString& data, const Version version, const ErrorCorrectionLevel ecl, const bool caseSensitive);
|
||||
|
||||
void init(const QByteArray& data, const Version version, const ErrorCorrectionLevel ecl);
|
||||
|
||||
QScopedPointer<QrCodePrivate> d_ptr;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_QRCODE_H
|
33
src/qrcode/QrCode_p.h
Normal file
33
src/qrcode/QrCode_p.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This class exists to isolate <qrencode.h> from the rest of the code base. */
|
||||
|
||||
#ifndef KEEPASSX_QRCODEPRIVATE_H
|
||||
#define KEEPASSX_QRCODEPRIVATE_H
|
||||
|
||||
#include <qrencode.h>
|
||||
|
||||
struct QrCodePrivate
|
||||
{
|
||||
QRcode* m_qrcode;
|
||||
|
||||
QrCodePrivate();
|
||||
~QrCodePrivate();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_QRCODEPRIVATE_H
|
@ -22,7 +22,8 @@
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QMessageAuthenticationCode>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QVariant>
|
||||
@ -79,7 +80,7 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
|
||||
settings->step = qBound(1u, settings->step, 60u);
|
||||
|
||||
// Detect custom settings, used by setup GUI
|
||||
if (settings->encoder.shortName != STEAM_SHORTNAME
|
||||
if (settings->encoder.shortName.isEmpty()
|
||||
&& (settings->digits != DEFAULT_DIGITS || settings->step != DEFAULT_STEP)) {
|
||||
settings->custom = true;
|
||||
}
|
||||
@ -96,15 +97,21 @@ QSharedPointer<Totp::Settings> Totp::createSettings(const QString& key, const ui
|
||||
});
|
||||
}
|
||||
|
||||
QString Totp::writeSettings(const QSharedPointer<Totp::Settings> settings)
|
||||
QString Totp::writeSettings(const QSharedPointer<Totp::Settings> settings, const QString& title, const QString& username, bool forceOtp)
|
||||
{
|
||||
if (settings.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// OTP Url output
|
||||
if (settings->otpUrl) {
|
||||
auto urlstring = QString("key=%1&step=%2&size=%3").arg(settings->key).arg(settings->step).arg(settings->digits);
|
||||
if (settings->otpUrl || forceOtp) {
|
||||
auto urlstring = QString("otpauth://totp/%1:%2?secret=%3&period=%4&digits=%5&issuer=%1")
|
||||
.arg(title.isEmpty() ? "KeePassXC" : QString(QUrl::toPercentEncoding(title)))
|
||||
.arg(username.isEmpty() ? "none" : QString(QUrl::toPercentEncoding(username)))
|
||||
.arg(QString(Base32::sanitizeInput(settings->key.toLatin1())))
|
||||
.arg(settings->step)
|
||||
.arg(settings->digits);
|
||||
|
||||
if (!settings->encoder.name.isEmpty()) {
|
||||
urlstring.append("&encoder=").append(settings->encoder.name);
|
||||
}
|
||||
|
@ -60,7 +60,8 @@ static const QString ATTRIBUTE_SETTINGS = "TOTP Settings";
|
||||
QSharedPointer<Totp::Settings> parseSettings(const QString& rawSettings, const QString& key = {});
|
||||
QSharedPointer<Totp::Settings> createSettings(const QString& key, const uint digits, const uint step,
|
||||
const QString& encoderShortName = {});
|
||||
QString writeSettings(const QSharedPointer<Totp::Settings> settings);
|
||||
QString writeSettings(const QSharedPointer<Totp::Settings> settings, const QString& title = {},
|
||||
const QString& username = {}, bool forceOtp = false);
|
||||
|
||||
QString generateTotp(const QSharedPointer<Totp::Settings> settings, const quint64 time = 0ull);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user