Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJanek Bevendorff <janek@jbev.net>2018-05-14 00:21:43 +0300
committerJanek Bevendorff <janek@jbev.net>2018-09-25 22:12:47 +0300
commite443cde45280512c39b918d60d98a933b9804751 (patch)
treebca157f0775765a4256578ae6d0c74468327254d
parente4ded388b480bdd3b2f02bee39298053eb400acf (diff)
Add a new database settings wizard
This patch implements a new database wizard to guide users through the process of setting up a new database and choosing sane encryption settings. It also reimplements the master key settings to be more user-friendly. Users can now add, change, or remove individual composite key components instead of having to set all components at once. This avoids confusion about a password being reset if the user only wants to add a key file. With these changes comes a major refactor of how database composite keys and key components are handled. Copying of keys is prohibited and each key exists only once in memory and is referenced via shared pointers. GUI components for changing individual keys are encapsulated into separate classes to be more reusable. The password edit and generator widgets have also been refactored to be more reusable.
-rw-r--r--share/CMakeLists.txt2
-rw-r--r--share/wizard/background-pixmap.pngbin0 -> 45338 bytes
-rw-r--r--share/wizard/background-pixmap.svg313
-rw-r--r--src/CMakeLists.txt23
-rw-r--r--src/cli/Extract.cpp16
-rw-r--r--src/cli/Merge.cpp2
-rw-r--r--src/core/Config.cpp1
-rw-r--r--src/core/Database.cpp54
-rw-r--r--src/core/Database.h14
-rw-r--r--src/crypto/kdf/Kdf.cpp2
-rw-r--r--src/format/Kdbx3Reader.cpp2
-rw-r--r--src/format/Kdbx3Reader.h2
-rw-r--r--src/format/Kdbx4Reader.cpp2
-rw-r--r--src/format/Kdbx4Reader.h2
-rw-r--r--src/format/KdbxReader.cpp2
-rw-r--r--src/format/KdbxReader.h7
-rw-r--r--src/format/KeePass1Reader.cpp10
-rw-r--r--src/format/KeePass2.cpp14
-rw-r--r--src/format/KeePass2Reader.cpp4
-rw-r--r--src/format/KeePass2Reader.h4
-rw-r--r--src/format/KeePass2Repair.cpp2
-rw-r--r--src/format/KeePass2Repair.h2
-rw-r--r--src/gui/ApplicationSettingsWidget.cpp (renamed from src/gui/SettingsWidget.cpp)28
-rw-r--r--src/gui/ApplicationSettingsWidget.h (renamed from src/gui/SettingsWidget.h)14
-rw-r--r--src/gui/ApplicationSettingsWidgetGeneral.ui (renamed from src/gui/SettingsWidgetGeneral.ui)8
-rw-r--r--src/gui/ApplicationSettingsWidgetSecurity.ui (renamed from src/gui/SettingsWidgetSecurity.ui)4
-rw-r--r--src/gui/CategoryListWidget.h5
-rw-r--r--src/gui/ChangeMasterKeyWidget.cpp252
-rw-r--r--src/gui/ChangeMasterKeyWidget.h68
-rw-r--r--src/gui/ChangeMasterKeyWidget.ui238
-rw-r--r--src/gui/DatabaseOpenWidget.cpp12
-rw-r--r--src/gui/DatabaseOpenWidget.ui13
-rw-r--r--src/gui/DatabaseRepairWidget.cpp10
-rw-r--r--src/gui/DatabaseSettingsWidget.cpp315
-rw-r--r--src/gui/DatabaseSettingsWidgetEncryption.ui183
-rw-r--r--src/gui/DatabaseTabWidget.cpp145
-rw-r--r--src/gui/DatabaseTabWidget.h1
-rw-r--r--src/gui/DatabaseWidget.cpp70
-rw-r--r--src/gui/DatabaseWidget.h9
-rw-r--r--src/gui/MainWindow.cpp6
-rw-r--r--src/gui/MainWindow.h2
-rw-r--r--src/gui/MainWindow.ui6
-rw-r--r--src/gui/PasswordGeneratorWidget.cpp21
-rw-r--r--src/gui/PasswordGeneratorWidget.h3
-rw-r--r--src/gui/PasswordGeneratorWidget.ui71
-rw-r--r--src/gui/csvImport/CsvImportWizard.cpp16
-rw-r--r--src/gui/csvImport/CsvImportWizard.h6
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.cpp139
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.h (renamed from src/gui/DatabaseSettingsWidget.h)50
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.ui (renamed from src/gui/DatabaseSettingsWidget.ui)21
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidget.cpp43
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidget.h51
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp410
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h75
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui410
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp119
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h54
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui (renamed from src/gui/DatabaseSettingsWidgetGeneral.ui)20
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp247
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h78
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp62
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h55
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui50
-rw-r--r--src/gui/masterkey/KeyComponentWidget.cpp196
-rw-r--r--src/gui/masterkey/KeyComponentWidget.h130
-rw-r--r--src/gui/masterkey/KeyComponentWidget.ui226
-rw-r--r--src/gui/masterkey/KeyFileEditWidget.cpp123
-rw-r--r--src/gui/masterkey/KeyFileEditWidget.h54
-rw-r--r--src/gui/masterkey/KeyFileEditWidget.ui70
-rw-r--r--src/gui/masterkey/PasswordEditWidget.cpp124
-rw-r--r--src/gui/masterkey/PasswordEditWidget.h56
-rw-r--r--src/gui/masterkey/PasswordEditWidget.ui90
-rw-r--r--src/gui/masterkey/YubiKeyEditWidget.cpp163
-rw-r--r--src/gui/masterkey/YubiKeyEditWidget.h60
-rw-r--r--src/gui/masterkey/YubiKeyEditWidget.ui90
-rw-r--r--src/gui/settings/SettingsWidget.cpp53
-rw-r--r--src/gui/settings/SettingsWidget.h75
-rw-r--r--src/gui/wizard/NewDatabaseWizard.cpp76
-rw-r--r--src/gui/wizard/NewDatabaseWizard.h50
-rw-r--r--src/gui/wizard/NewDatabaseWizardPage.cpp112
-rw-r--r--src/gui/wizard/NewDatabaseWizardPage.h61
-rw-r--r--src/gui/wizard/NewDatabaseWizardPage.ui65
-rw-r--r--src/gui/wizard/NewDatabaseWizardPageEncryption.cpp33
-rw-r--r--src/gui/wizard/NewDatabaseWizardPageEncryption.h33
-rw-r--r--src/gui/wizard/NewDatabaseWizardPageMasterKey.cpp41
-rw-r--r--src/gui/wizard/NewDatabaseWizardPageMasterKey.h36
-rw-r--r--src/gui/wizard/NewDatabaseWizardPageMetaData.cpp32
-rw-r--r--src/gui/wizard/NewDatabaseWizardPageMetaData.h38
-rw-r--r--src/keys/ChallengeResponseKey.h14
-rw-r--r--src/keys/CompositeKey.cpp70
-rw-r--r--src/keys/CompositeKey.h16
-rw-r--r--src/keys/FileKey.cpp15
-rw-r--r--src/keys/FileKey.h4
-rw-r--r--src/keys/Key.h14
-rw-r--r--src/keys/PasswordKey.cpp15
-rw-r--r--src/keys/PasswordKey.h8
-rw-r--r--src/keys/YkChallengeResponseKey.cpp5
-rw-r--r--src/keys/YkChallengeResponseKey.h8
-rw-r--r--src/sshagent/AgentSettingsPage.h2
-rw-r--r--src/touchid/TouchID.h64
-rw-r--r--src/touchid/TouchID.mm113
-rw-r--r--tests/TestDatabase.cpp40
-rw-r--r--tests/TestKdbx2.cpp8
-rw-r--r--tests/TestKdbx3.cpp28
-rw-r--r--tests/TestKdbx3.h4
-rw-r--r--tests/TestKdbx4.cpp30
-rw-r--r--tests/TestKdbx4.h4
-rw-r--r--tests/TestKeePass1Reader.cpp13
-rw-r--r--tests/TestKeePass2Format.cpp16
-rw-r--r--tests/TestKeePass2Format.h4
-rw-r--r--tests/TestKeys.cpp108
-rw-r--r--tests/TestModified.cpp2
-rw-r--r--tests/gui/TestGui.cpp180
-rw-r--r--tests/gui/TestGui.h4
-rw-r--r--tests/mock/MockChallengeResponseKey.cpp3
-rw-r--r--tests/mock/MockChallengeResponseKey.h1
116 files changed, 5056 insertions, 1694 deletions
diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt
index 81bb26938..816966036 100644
--- a/share/CMakeLists.txt
+++ b/share/CMakeLists.txt
@@ -39,6 +39,8 @@ if(APPLE)
install(FILES macosx/keepassxc.icns DESTINATION ${DATA_INSTALL_DIR})
endif()
+install(DIRECTORY wizard/ DESTINATION ${DATA_INSTALL_DIR}/wizard FILES_MATCHING PATTERN "*.png")
+
install(DIRECTORY icons/application/ DESTINATION ${DATA_INSTALL_DIR}/icons/application
FILES_MATCHING PATTERN "*.png" PATTERN "*.svgz")
diff --git a/share/wizard/background-pixmap.png b/share/wizard/background-pixmap.png
new file mode 100644
index 000000000..e64d87041
--- /dev/null
+++ b/share/wizard/background-pixmap.png
Binary files differ
diff --git a/share/wizard/background-pixmap.svg b/share/wizard/background-pixmap.svg
new file mode 100644
index 000000000..af48dcbc5
--- /dev/null
+++ b/share/wizard/background-pixmap.svg
@@ -0,0 +1,313 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="400"
+ height="400"
+ viewBox="0 0 399.99998 400"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.2 2405546, 2018-03-11"
+ sodipodi:docname="background-pixmap.svg"
+ inkscape:export-filename="/home/janek/keepassxc/keepassxc/share/wizard/background-pixmap.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="2560"
+ inkscape:window-height="1379"
+ id="namedview4155"
+ showgrid="false"
+ inkscape:zoom="1.1540412"
+ inkscape:cx="-353.8167"
+ inkscape:cy="-16.000182"
+ inkscape:window-x="2560"
+ inkscape:window-y="32"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient848"
+ inkscape:collect="always">
+ <stop
+ id="stop844"
+ offset="0"
+ style="stop-color:#ffffff;stop-opacity:1" />
+ <stop
+ id="stop846"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4551">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop4547" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1"
+ id="stop4549" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4316">
+ <stop
+ style="stop-color:#226e23;stop-opacity:1"
+ offset="0"
+ id="stop4318" />
+ <stop
+ style="stop-color:#63ab3a;stop-opacity:1"
+ offset="1"
+ id="stop4320" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4153"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#b3b3b3;stop-opacity:1;"
+ offset="0"
+ id="stop4155" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#linearGradient4316"
+ id="linearGradient4324"
+ x1="50.757614"
+ y1="964.83679"
+ x2="50.757614"
+ y2="1042.2632"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0561225,0,0,1.0561225,-2.8061215,-1008.6172)" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ r="43.571938"
+ fy="41.189114"
+ fx="-82.91127"
+ cy="41.189114"
+ cx="-82.91127"
+ id="radialGradient5106"
+ xlink:href="#linearGradient4316" />
+ <linearGradient
+ xlink:href="#linearGradient4316"
+ id="linearGradient4324-3"
+ x1="50.757614"
+ y1="964.83679"
+ x2="50.757614"
+ y2="1042.2632"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0561225,0,0,1.0561225,118.96071,-1109.1994)" />
+ <linearGradient
+ xlink:href="#linearGradient4316"
+ id="linearGradient4324-3-6"
+ x1="50.757614"
+ y1="964.83679"
+ x2="50.757614"
+ y2="1042.2632"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0561225,0,0,1.0561225,-2.8061235,-1008.6171)" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="86.356995"
+ x2="53.238865"
+ y1="12.753036"
+ x1="53.238865"
+ id="linearGradient5199"
+ xlink:href="#linearGradient4316"
+ gradientTransform="matrix(3.7956531,0,0,3.7956531,9.2052011,7.1809209)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4551"
+ id="linearGradient4553"
+ x1="-326.6781"
+ y1="-45.824631"
+ x2="-48.525116"
+ y2="-45.824631"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.94081,0,0,1.8710802,248.41865,279.07651)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="linearGradient842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1339564,0,0,1.6084199,370.43874,273.70526)"
+ x1="-270.92221"
+ y1="-81.950562"
+ x2="-48.525116"
+ y2="-45.824631" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="linearGradient856"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1339564,0,0,1.6084199,370.43874,273.70526)"
+ x1="-270.92221"
+ y1="-81.950562"
+ x2="-122.6483"
+ y2="-52.828251" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient864"
+ cx="46.960838"
+ cy="168.3365"
+ fx="46.960838"
+ fy="168.3365"
+ r="157.70668"
+ gradientTransform="matrix(1.2164529,1.1254339,-0.89269349,0.96488966,132.14067,-53.404357)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.96274844,1.8215723,-1.4639086,0.77371372,199.30904,-23.641878)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.00717113,2.8325787,-1.5239128,-0.00385817,197.56997,80.438211)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.02197802,4.8325787,-1.5942812,0.00725037,206.40768,97.733628)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,3.6622493,-1.4325805,-1.8197637e-7,186.51898,87.704285)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,3.1237879,-1.4325805,-1.5522034e-7,186.51898,82.68376)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient849"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,2.5743375,-1.4325805,-1.2791827e-7,186.51898,77.560775)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient857"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,2.3710409,-1.4325805,-1.178165e-7,186.51898,75.665271)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient848"
+ id="radialGradient863"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0,2.3710409,-1.4325805,-1.178165e-7,299.06959,72.426405)"
+ cx="-9.3238287"
+ cy="121.72976"
+ fx="-9.3238287"
+ fy="121.72976"
+ r="157.70668" />
+ <mask
+ maskUnits="userSpaceOnUse"
+ id="mask859">
+ <rect
+ id="rect861"
+ width="315.41336"
+ height="400"
+ x="112.55062"
+ y="-3.2388663"
+ style="fill:url(#radialGradient863);fill-opacity:1;stroke-width:1.35051024"
+ mask="none" />
+ </mask>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="g24"
+ transform="translate(-112.55061,3.2388664)"
+ mask="url(#mask859)">
+ <circle
+ r="174.34979"
+ style="opacity:0.87099998;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:11.88748455;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.78431373"
+ id="path5201"
+ cx="198.98785"
+ cy="196.96356" />
+ <circle
+ style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.69348001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3336"
+ cx="198.98785"
+ cy="196.96367"
+ r="160.63747" />
+ <path
+ inkscape:connector-curvature="0"
+ style="opacity:1;fill:#f9f9f9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.48913288;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 198.98785,31.185462 A 165.78074,165.78074 0 0 0 33.209743,196.96356 165.78074,165.78074 0 0 0 198.98785,362.74167 165.78074,165.78074 0 0 0 364.76596,196.96356 165.78074,165.78074 0 0 0 198.98785,31.185462 Z m 0,25.472401 c 8.59424,0.03031 17.16925,0.853638 25.61325,2.453814 0.40674,1.835873 0.63755,3.73499 0.63755,5.69348 0,14.509453 -11.75616,26.258395 -26.25821,26.250812 -14.50202,0.0083 -26.25079,-11.73385 -26.25079,-26.243413 0,-1.966534 0.23547,-3.872865 0.64495,-5.715722 8.44454,-1.594777 17.01951,-2.412896 25.61325,-2.438971 z m -72.09516,20.112482 c -0.91297,4.62751 -1.40945,9.391006 -1.40854,14.285624 0,29.301141 17.1882,54.535791 41.99682,66.349761 v 133.18888 l 31.49947,31.49948 31.50688,-31.50687 -1.80886,-27.89657 14.85643,-14.84901 -14.85643,-14.84901 22.28462,-22.26983 -22.28462,-22.28461 1.80886,-31.03987 c 24.81606,-11.80664 42.00424,-37.04121 42.00424,-66.342351 0,-4.88486 -0.50632,-9.644717 -1.41595,-14.263382 42.25494,25.307703 68.14884,70.916983 68.21796,120.170973 -0.002,77.4867 -62.81902,140.30363 -140.30572,140.30571 C 121.50116,337.26743 58.6842,274.45026 58.682132,196.96356 58.741155,147.70264 84.633476,102.08443 126.89269,76.770345 Z m 51.08563,98.301485 h 10.50476 v 105.01061 h -10.49735 l -0.008,-105.00321 v -0.008 z"
+ id="path4264" />
+ <path
+ inkscape:connector-curvature="0"
+ style="opacity:0.93999999;fill:url(#linearGradient5199);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.03652239;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 224.59926,59.112359 c 0.40674,1.835882 0.63911,3.737385 0.63911,5.695839 0,14.50948 -11.75647,26.258053 -26.25853,26.250461 -14.50201,0.0083 -26.2505,-11.732937 -26.2505,-26.242538 0,-1.966524 0.23761,-3.876946 0.64708,-5.719822 8.44454,-1.594776 17.01769,-2.41038 25.61143,-2.436473 8.59424,0.03031 17.16741,0.85232 25.61141,2.452533 z m -99.11441,31.9463 c 0,29.301141 17.18739,54.531271 41.99601,66.345281 v 133.1939 l 31.49898,31.49903 31.50704,-31.507 -1.80544,-27.89615 14.8508,-14.85088 -14.8508,-14.85086 22.28018,-22.2721 -22.28018,-22.2801 1.8054,-31.04381 c 24.81605,-11.80664 42.00402,-37.03617 42.00402,-66.337311 0,-4.884851 -0.50436,-9.648891 -1.41399,-14.267556 42.25495,25.307677 68.14544,70.918577 68.21456,120.172567 -0.002,77.4867 -62.81687,140.30154 -140.30358,140.30367 -77.48669,-0.002 -140.301528,-62.81697 -140.303577,-140.30367 0.05883,-49.26092 25.947343,-94.88251 68.206567,-120.19655 -0.91297,4.62751 -1.40689,9.396922 -1.40599,14.291539 z m 52.493,84.015711 h 10.50501 v 105.00989 h -10.49704 l -0.008,-105.00195 v -0.008 z"
+ id="path3336-3-2-7" />
+ </g>
+</svg>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index faeb669b9..0246d1075 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -99,12 +99,10 @@ set(keepassx_SOURCES
gui/AboutDialog.cpp
gui/Application.cpp
gui/CategoryListWidget.cpp
- gui/ChangeMasterKeyWidget.cpp
gui/Clipboard.cpp
gui/CloneDialog.cpp
gui/DatabaseOpenWidget.cpp
gui/DatabaseRepairWidget.cpp
- gui/DatabaseSettingsWidget.cpp
gui/DatabaseTabWidget.cpp
gui/DatabaseWidget.cpp
gui/DatabaseWidgetStateSync.cpp
@@ -115,6 +113,7 @@ set(keepassx_SOURCES
gui/EditWidgetIcons.cpp
gui/EditWidgetProperties.cpp
gui/FileDialog.cpp
+ gui/masterkey/KeyComponentWidget.cpp
gui/Font.cpp
gui/IconModels.cpp
gui/KeePass1OpenWidget.cpp
@@ -125,7 +124,7 @@ set(keepassx_SOURCES
gui/MessageWidget.cpp
gui/PasswordEdit.cpp
gui/PasswordGeneratorWidget.cpp
- gui/SettingsWidget.cpp
+ gui/ApplicationSettingsWidget.cpp
gui/SearchWidget.cpp
gui/SortFilterHideProxyModel.cpp
gui/TotpSetupDialog.cpp
@@ -151,6 +150,21 @@ set(keepassx_SOURCES
gui/group/EditGroupWidget.cpp
gui/group/GroupModel.cpp
gui/group/GroupView.cpp
+ gui/masterkey/PasswordEditWidget.cpp
+ gui/masterkey/YubiKeyEditWidget.cpp
+ gui/masterkey/KeyFileEditWidget.cpp
+ gui/settings/SettingsWidget.cpp
+ gui/dbsettings/DatabaseSettingsWidget.cpp
+ gui/dbsettings/DatabaseSettingsDialog.cpp
+ gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
+ gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp
+ gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp
+ gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
+ gui/wizard/NewDatabaseWizard.cpp
+ gui/wizard/NewDatabaseWizardPage.cpp
+ gui/wizard/NewDatabaseWizardPageMetaData.cpp
+ gui/wizard/NewDatabaseWizardPageEncryption.cpp
+ gui/wizard/NewDatabaseWizardPageMasterKey.cpp
keys/ChallengeResponseKey.h
keys/CompositeKey.cpp
keys/drivers/YubiKey.h
@@ -165,8 +179,7 @@ set(keepassx_SOURCES
streams/StoreDataStream.cpp
streams/SymmetricCipherStream.cpp
totp/totp.h
- totp/totp.cpp
-)
+ totp/totp.cpp)
if(APPLE)
set(keepassx_SOURCES ${keepassx_SOURCES}
core/ScreenLockListenerMac.h
diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp
index 089a784d2..f0004e688 100644
--- a/src/cli/Extract.cpp
+++ b/src/cli/Extract.cpp
@@ -65,31 +65,31 @@ int Extract::execute(const QStringList& arguments)
out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0));
out.flush();
- CompositeKey compositeKey;
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
QString line = Utils::getPassword();
- PasswordKey passwordKey;
- passwordKey.setPassword(line);
- compositeKey.addKey(passwordKey);
+ auto passwordKey = QSharedPointer<PasswordKey>::create();
+ passwordKey->setPassword(line);
+ compositeKey->addKey(passwordKey);
QString keyFilePath = parser.value(keyFile);
if (!keyFilePath.isEmpty()) {
- FileKey fileKey;
+ auto fileKey = QSharedPointer<FileKey>::create();
QString errorMsg;
- if (!fileKey.load(keyFilePath, &errorMsg)) {
+ if (!fileKey->load(keyFilePath, &errorMsg)) {
errorTextStream << QObject::tr("Failed to load key file %1 : %2").arg(keyFilePath).arg(errorMsg);
errorTextStream << endl;
return EXIT_FAILURE;
}
- if (fileKey.type() != FileKey::Hashed) {
+ if (fileKey->type() != FileKey::Hashed) {
errorTextStream << QObject::tr("WARNING: You are using a legacy key file format which may become\n"
"unsupported in the future.\n\n"
"Please consider generating a new key file.");
errorTextStream << endl;
}
- compositeKey.addKey(fileKey);
+ compositeKey->addKey(fileKey);
}
QString databaseFilename = args.at(0);
diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp
index 6b114bff3..8248c45a0 100644
--- a/src/cli/Merge.cpp
+++ b/src/cli/Merge.cpp
@@ -76,7 +76,7 @@ int Merge::execute(const QStringList& arguments)
if (!parser.isSet("same-credentials")) {
db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom));
} else {
- db2 = Database::openDatabaseFile(args.at(1), *(db1->key().clone()));
+ db2 = Database::openDatabaseFile(args.at(1), db1->key());
}
if (db2 == nullptr) {
return EXIT_FAILURE;
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
index 187878d9f..1a0f7b43a 100644
--- a/src/core/Config.cpp
+++ b/src/core/Config.cpp
@@ -160,6 +160,7 @@ void Config::init(const QString& fileName)
m_defaults.insert("GUI/MinimizeOnClose", false);
m_defaults.insert("GUI/HideUsernames", false);
m_defaults.insert("GUI/HidePasswords", true);
+ m_defaults.insert("GUI/AdvancedSettings", false);
}
Config* Config::instance()
diff --git a/src/core/Database.cpp b/src/core/Database.cpp
index 21108f41a..bc0a1b302 100644
--- a/src/core/Database.cpp
+++ b/src/core/Database.cpp
@@ -98,6 +98,16 @@ const Metadata* Database::metadata() const
return m_metadata;
}
+QString Database::filePath() const
+{
+ return m_filePath;
+}
+
+void Database::setFilePath(const QString& filePath)
+{
+ m_filePath = filePath;
+}
+
Entry* Database::resolveEntry(const QUuid& uuid)
{
return findEntryRecursive(uuid, m_rootGroup);
@@ -244,7 +254,7 @@ QByteArray Database::challengeResponseKey() const
bool Database::challengeMasterSeed(const QByteArray& masterSeed)
{
m_data.masterSeed = masterSeed;
- return m_data.key.challenge(masterSeed, m_data.challengeResponseKey);
+ return m_data.key->challenge(masterSeed, m_data.challengeResponseKey);
}
void Database::setCipher(const QUuid& cipher)
@@ -264,13 +274,20 @@ void Database::setCompressionAlgo(Database::CompressionAlgorithm algo)
/**
* Set and transform a new encryption key.
*
- * @param key key to set and transform
+ * @param key key to set and transform or nullptr to reset the key
* @param updateChangedTime true to update database change time
* @param updateTransformSalt true to update the transform salt
* @return true on success
*/
-bool Database::setKey(const CompositeKey& key, bool updateChangedTime, bool updateTransformSalt)
+bool Database::setKey(QSharedPointer<const CompositeKey> key, bool updateChangedTime, bool updateTransformSalt)
{
+ if (!key) {
+ m_data.key.reset();
+ m_data.transformedMasterKey = {};
+ m_data.hasKey = false;
+ return true;
+ }
+
if (updateTransformSalt) {
m_data.kdf->randomizeSeed();
Q_ASSERT(!m_data.kdf->seed().isEmpty());
@@ -278,7 +295,7 @@ bool Database::setKey(const CompositeKey& key, bool updateChangedTime, bool upda
QByteArray oldTransformedMasterKey = m_data.transformedMasterKey;
QByteArray transformedMasterKey;
- if (!key.transform(*m_data.kdf, transformedMasterKey)) {
+ if (!key->transform(*m_data.kdf, transformedMasterKey)) {
return false;
}
@@ -301,14 +318,14 @@ bool Database::hasKey() const
return m_data.hasKey;
}
-bool Database::verifyKey(const CompositeKey& key) const
+bool Database::verifyKey(QSharedPointer<CompositeKey> key) const
{
Q_ASSERT(hasKey());
if (!m_data.challengeResponseKey.isEmpty()) {
QByteArray result;
- if (!key.challenge(m_data.masterSeed, result)) {
+ if (!key->challenge(m_data.masterSeed, result)) {
// challenge failed, (YubiKey?) removed?
return false;
}
@@ -319,7 +336,7 @@ bool Database::verifyKey(const CompositeKey& key) const
}
}
- return (m_data.key.rawKey() == key.rawKey());
+ return (m_data.key->rawKey() == key->rawKey());
}
QVariantMap& Database::publicCustomData()
@@ -430,12 +447,12 @@ void Database::startModifiedTimer()
m_timer->start(150);
}
-const CompositeKey& Database::key() const
+QSharedPointer<const CompositeKey> Database::key() const
{
return m_data.key;
}
-Database* Database::openDatabaseFile(QString fileName, CompositeKey key)
+Database* Database::openDatabaseFile(const QString& fileName, QSharedPointer<const CompositeKey> key)
{
QFile dbFile(fileName);
@@ -461,7 +478,7 @@ Database* Database::openDatabaseFile(QString fileName, CompositeKey key)
Database* Database::unlockFromStdin(QString databaseFilename, QString keyFilename)
{
- CompositeKey compositeKey;
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
QTextStream outputTextStream(stdout);
QTextStream errorTextStream(stderr);
@@ -469,19 +486,19 @@ Database* Database::unlockFromStdin(QString databaseFilename, QString keyFilenam
outputTextStream.flush();
QString line = Utils::getPassword();
- PasswordKey passwordKey;
- passwordKey.setPassword(line);
- compositeKey.addKey(passwordKey);
+ auto passwordKey = QSharedPointer<PasswordKey>::create();
+ passwordKey->setPassword(line);
+ compositeKey->addKey(passwordKey);
if (!keyFilename.isEmpty()) {
- FileKey fileKey;
+ auto fileKey = QSharedPointer<FileKey>::create();
QString errorMessage;
- if (!fileKey.load(keyFilename, &errorMessage)) {
+ if (!fileKey->load(keyFilename, &errorMessage)) {
errorTextStream << QObject::tr("Failed to load key file %1: %2").arg(keyFilename, errorMessage);
errorTextStream << endl;
return nullptr;
}
- compositeKey.addKey(fileKey);
+ compositeKey->addKey(fileKey);
}
return Database::openDatabaseFile(databaseFilename, compositeKey);
@@ -607,7 +624,10 @@ bool Database::changeKdf(QSharedPointer<Kdf> kdf)
{
kdf->randomizeSeed();
QByteArray transformedMasterKey;
- if (!m_data.key.transform(*kdf, transformedMasterKey)) {
+ if (!m_data.key) {
+ m_data.key = QSharedPointer<CompositeKey>::create();
+ }
+ if (!m_data.key->transform(*kdf, transformedMasterKey)) {
return false;
}
diff --git a/src/core/Database.h b/src/core/Database.h
index c712faa38..912f95073 100644
--- a/src/core/Database.h
+++ b/src/core/Database.h
@@ -59,7 +59,7 @@ public:
CompressionAlgorithm compressionAlgo;
QByteArray transformedMasterKey;
QSharedPointer<Kdf> kdf;
- CompositeKey key;
+ QSharedPointer<const CompositeKey> key;
bool hasKey;
QByteArray masterSeed;
QByteArray challengeResponseKey;
@@ -82,6 +82,8 @@ public:
Metadata* metadata();
const Metadata* metadata() const;
+ QString filePath() const;
+ void setFilePath(const QString& filePath);
Entry* resolveEntry(const QUuid& uuid);
Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
Group* resolveGroup(const QUuid& uuid);
@@ -93,16 +95,16 @@ public:
Database::CompressionAlgorithm compressionAlgo() const;
QSharedPointer<Kdf> kdf() const;
QByteArray transformedMasterKey() const;
- const CompositeKey& key() const;
+ QSharedPointer<const CompositeKey> key() const;
QByteArray challengeResponseKey() const;
bool challengeMasterSeed(const QByteArray& masterSeed);
void setCipher(const QUuid& cipher);
void setCompressionAlgo(Database::CompressionAlgorithm algo);
void setKdf(QSharedPointer<Kdf> kdf);
- bool setKey(const CompositeKey& key, bool updateChangedTime = true, bool updateTransformSalt = false);
+ bool setKey(QSharedPointer<const CompositeKey> key, bool updateChangedTime = true, bool updateTransformSalt = false);
bool hasKey() const;
- bool verifyKey(const CompositeKey& key) const;
+ bool verifyKey(QSharedPointer<CompositeKey> key) const;
QVariantMap& publicCustomData();
const QVariantMap& publicCustomData() const;
void setPublicCustomData(const QVariantMap& customData);
@@ -120,7 +122,7 @@ public:
bool changeKdf(QSharedPointer<Kdf> kdf);
static Database* databaseByUuid(const QUuid& uuid);
- static Database* openDatabaseFile(QString fileName, CompositeKey key);
+ static Database* openDatabaseFile(const QString& fileName, QSharedPointer<const CompositeKey> key);
static Database* unlockFromStdin(QString databaseFilename, QString keyFilename = QString(""));
signals:
@@ -154,6 +156,8 @@ private:
DatabaseData m_data;
bool m_emitModified;
+ QString m_filePath;
+
QUuid m_uuid;
static QHash<QUuid, Database*> m_uuidMap;
};
diff --git a/src/crypto/kdf/Kdf.cpp b/src/crypto/kdf/Kdf.cpp
index 2e1ad2ec9..fcedc488f 100644
--- a/src/crypto/kdf/Kdf.cpp
+++ b/src/crypto/kdf/Kdf.cpp
@@ -80,7 +80,7 @@ int Kdf::benchmark(int msec) const
thread1.wait();
thread2.wait();
- return qMax(1, qMin(thread1.rounds(), thread2.rounds()));
+ return qMax(1, (thread1.rounds() + thread2.rounds()) / 2);
}
Kdf::BenchmarkThread::BenchmarkThread(int msec, const Kdf* kdf)
diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp
index 00b0adebe..aeacaad3d 100644
--- a/src/format/Kdbx3Reader.cpp
+++ b/src/format/Kdbx3Reader.cpp
@@ -31,7 +31,7 @@
Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device,
const QByteArray& headerData,
- const CompositeKey& key,
+ QSharedPointer<const CompositeKey> key,
bool keepDatabase)
{
Q_ASSERT(m_kdbxVersion <= KeePass2::FILE_VERSION_3_1);
diff --git a/src/format/Kdbx3Reader.h b/src/format/Kdbx3Reader.h
index 41916d0e5..d0dd4c1b7 100644
--- a/src/format/Kdbx3Reader.h
+++ b/src/format/Kdbx3Reader.h
@@ -31,7 +31,7 @@ class Kdbx3Reader : public KdbxReader
public:
Database* readDatabaseImpl(QIODevice* device,
const QByteArray& headerData,
- const CompositeKey& key,
+ QSharedPointer<const CompositeKey> key,
bool keepDatabase) override;
protected:
diff --git a/src/format/Kdbx4Reader.cpp b/src/format/Kdbx4Reader.cpp
index 92c8187cd..7b94d34f8 100644
--- a/src/format/Kdbx4Reader.cpp
+++ b/src/format/Kdbx4Reader.cpp
@@ -30,7 +30,7 @@
Database* Kdbx4Reader::readDatabaseImpl(QIODevice* device,
const QByteArray& headerData,
- const CompositeKey& key,
+ QSharedPointer<const CompositeKey> key,
bool keepDatabase)
{
Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4);
diff --git a/src/format/Kdbx4Reader.h b/src/format/Kdbx4Reader.h
index ce965a7bb..f415543e7 100644
--- a/src/format/Kdbx4Reader.h
+++ b/src/format/Kdbx4Reader.h
@@ -32,7 +32,7 @@ class Kdbx4Reader : public KdbxReader
public:
Database* readDatabaseImpl(QIODevice* device,
const QByteArray& headerData,
- const CompositeKey& key,
+ QSharedPointer<const CompositeKey> key,
bool keepDatabase) override;
QHash<QByteArray, QString> binaryPoolInverse() const;
QHash<QString, QByteArray> binaryPool() const;
diff --git a/src/format/KdbxReader.cpp b/src/format/KdbxReader.cpp
index a8127509b..72f0f1375 100644
--- a/src/format/KdbxReader.cpp
+++ b/src/format/KdbxReader.cpp
@@ -57,7 +57,7 @@ bool KdbxReader::readMagicNumbers(QIODevice* device, quint32& sig1, quint32& sig
* @param keepDatabase keep database in case of read failure
* @return pointer to the read database, nullptr on failure
*/
-Database* KdbxReader::readDatabase(QIODevice* device, const CompositeKey& key, bool keepDatabase)
+Database* KdbxReader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase)
{
device->seek(0);
diff --git a/src/format/KdbxReader.h b/src/format/KdbxReader.h
index 23050d28f..d42927c0f 100644
--- a/src/format/KdbxReader.h
+++ b/src/format/KdbxReader.h
@@ -40,7 +40,7 @@ public:
virtual ~KdbxReader() = default;
static bool readMagicNumbers(QIODevice* device, quint32& sig1, quint32& sig2, quint32& version);
- Database* readDatabase(QIODevice* device, const CompositeKey& key, bool keepDatabase = false);
+ Database* readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase = false);
bool hasError() const;
QString errorString() const;
@@ -62,7 +62,10 @@ protected:
* @return pointer to the read database, nullptr on failure
*/
virtual Database*
- readDatabaseImpl(QIODevice* device, const QByteArray& headerData, const CompositeKey& key, bool keepDatabase) = 0;
+ readDatabaseImpl(QIODevice* device,
+ const QByteArray& headerData,
+ QSharedPointer<const CompositeKey> key,
+ bool keepDatabase) = 0;
/**
* Read next header field from stream.
diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp
index 1db6e5d16..7c0ea652e 100644
--- a/src/format/KeePass1Reader.cpp
+++ b/src/format/KeePass1Reader.cpp
@@ -63,7 +63,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
m_errorStr.clear();
QByteArray keyfileData;
- FileKey newFileKey;
+ auto newFileKey = QSharedPointer<FileKey>::create();
if (keyfileDevice) {
keyfileData = readKeyfile(keyfileDevice);
@@ -77,7 +77,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
return nullptr;
}
- if (!newFileKey.load(keyfileDevice)) {
+ if (!newFileKey->load(keyfileDevice)) {
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
return nullptr;
}
@@ -233,12 +233,12 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
entry->setUpdateTimeinfo(true);
}
- CompositeKey key;
+ auto key = QSharedPointer<CompositeKey>::create();
if (!password.isEmpty()) {
- key.addKey(PasswordKey(password));
+ key->addKey(QSharedPointer<PasswordKey>::create(password));
}
if (keyfileDevice) {
- key.addKey(newFileKey);
+ key->addKey(newFileKey);
}
if (!db->setKey(key)) {
diff --git a/src/format/KeePass2.cpp b/src/format/KeePass2.cpp
index dd9ffc0c6..5aad1f7f2 100644
--- a/src/format/KeePass2.cpp
+++ b/src/format/KeePass2.cpp
@@ -23,15 +23,15 @@
#define UUID_LENGTH 16
-const QUuid KeePass2::CIPHER_AES = QUuid::fromRfc4122(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff"));
-const QUuid KeePass2::CIPHER_TWOFISH = QUuid::fromRfc4122(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c"));
-const QUuid KeePass2::CIPHER_CHACHA20 = QUuid::fromRfc4122(QByteArray::fromHex("D6038A2B8B6F4CB5A524339A31DBB59A"));
+const QUuid KeePass2::CIPHER_AES = QUuid("31c1f2e6-bf71-4350-be58-05216afc5aff");
+const QUuid KeePass2::CIPHER_TWOFISH = QUuid("ad68f29f-576f-4bb9-a36a-d47af965346c");
+const QUuid KeePass2::CIPHER_CHACHA20 = QUuid("d6038a2b-8b6f-4cb5-a524-339a31dbb59a");
-const QUuid KeePass2::KDF_AES_KDBX3 = QUuid::fromRfc4122(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
-const QUuid KeePass2::KDF_AES_KDBX4 = QUuid::fromRfc4122(QByteArray::fromHex("7C02BB8279A74AC0927D114A00648238"));
-const QUuid KeePass2::KDF_ARGON2 = QUuid::fromRfc4122(QByteArray::fromHex("EF636DDF8C29444B91F7A9A403E30A0C"));
+const QUuid KeePass2::KDF_AES_KDBX3 = QUuid("c9d9f39a-628a-4460-bf74-0d08c18a4fea");
+const QUuid KeePass2::KDF_AES_KDBX4 = QUuid("7c02bb82-79a7-4ac0-927d-114a00648238");
+const QUuid KeePass2::KDF_ARGON2 = QUuid("ef636ddf-8c29-444b-91f7-a9a403e30a0c");
-const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xE8\x30\x09\x4B\x97\x20\x5D\x2A");
+const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xe8\x30\x09\x4b\x97\x20\x5d\x2a");
const QString KeePass2::KDFPARAM_UUID("$UUID");
// AES parameters
diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp
index 1c16e76d6..2a6d61118 100644
--- a/src/format/KeePass2Reader.cpp
+++ b/src/format/KeePass2Reader.cpp
@@ -29,7 +29,7 @@
* @param key database encryption composite key
* @return pointer to the read database, nullptr on failure
*/
-Database* KeePass2Reader::readDatabase(const QString& filename, const CompositeKey& key)
+Database* KeePass2Reader::readDatabase(const QString& filename, QSharedPointer<const CompositeKey> key)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
@@ -55,7 +55,7 @@ Database* KeePass2Reader::readDatabase(const QString& filename, const CompositeK
* @param keepDatabase keep database in case of read failure
* @return pointer to the read database, nullptr on failure
*/
-Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key, bool keepDatabase)
+Database* KeePass2Reader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase)
{
m_error = false;
m_errorStr.clear();
diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h
index c4947c732..5ed720d4a 100644
--- a/src/format/KeePass2Reader.h
+++ b/src/format/KeePass2Reader.h
@@ -35,8 +35,8 @@ class KeePass2Reader
Q_DECLARE_TR_FUNCTIONS(KdbxReader)
public:
- Database* readDatabase(const QString& filename, const CompositeKey& key);
- Database* readDatabase(QIODevice* device, const CompositeKey& key, bool keepDatabase = false);
+ Database* readDatabase(const QString& filename, QSharedPointer<const CompositeKey> key);
+ Database* readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, bool keepDatabase = false);
bool hasError() const;
QString errorString() const;
diff --git a/src/format/KeePass2Repair.cpp b/src/format/KeePass2Repair.cpp
index 508a9517b..2e0bc53cd 100644
--- a/src/format/KeePass2Repair.cpp
+++ b/src/format/KeePass2Repair.cpp
@@ -27,7 +27,7 @@
#include "format/KeePass2RandomStream.h"
#include "format/KeePass2Reader.h"
-KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device, const CompositeKey& key)
+KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key)
{
m_errorStr.clear();
diff --git a/src/format/KeePass2Repair.h b/src/format/KeePass2Repair.h
index e7f2c8435..2f822dd0f 100644
--- a/src/format/KeePass2Repair.h
+++ b/src/format/KeePass2Repair.h
@@ -40,7 +40,7 @@ public:
};
using RepairOutcome = QPair<RepairResult, Database*>;
- RepairOutcome repairDatabase(QIODevice* device, const CompositeKey& key);
+ RepairOutcome repairDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key);
QString errorString() const;
private:
diff --git a/src/gui/SettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp
index 8c84917b5..c46804571 100644
--- a/src/gui/SettingsWidget.cpp
+++ b/src/gui/ApplicationSettingsWidget.cpp
@@ -16,9 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "SettingsWidget.h"
-#include "ui_SettingsWidgetGeneral.h"
-#include "ui_SettingsWidgetSecurity.h"
+#include "ApplicationSettingsWidget.h"
+#include "ui_ApplicationSettingsWidgetGeneral.h"
+#include "ui_ApplicationSettingsWidgetSecurity.h"
#include "autotype/AutoType.h"
#include "config-keepassx.h"
@@ -29,7 +29,7 @@
#include "touchid/TouchID.h"
-class SettingsWidget::ExtraPage
+class ApplicationSettingsWidget::ExtraPage
{
public:
ExtraPage(ISettingsPage* page, QWidget* widget)
@@ -53,12 +53,12 @@ private:
QWidget* widget;
};
-SettingsWidget::SettingsWidget(QWidget* parent)
+ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
: EditWidget(parent)
, m_secWidget(new QWidget())
, m_generalWidget(new QWidget())
- , m_secUi(new Ui::SettingsWidgetSecurity())
- , m_generalUi(new Ui::SettingsWidgetGeneral())
+ , m_secUi(new Ui::ApplicationSettingsWidgetSecurity())
+ , m_generalUi(new Ui::ApplicationSettingsWidgetGeneral())
, m_globalAutoTypeKey(static_cast<Qt::Key>(0))
, m_globalAutoTypeModifiers(Qt::NoModifier)
{
@@ -109,11 +109,11 @@ SettingsWidget::SettingsWidget(QWidget* parent)
}
}
-SettingsWidget::~SettingsWidget()
+ApplicationSettingsWidget::~ApplicationSettingsWidget()
{
}
-void SettingsWidget::addSettingsPage(ISettingsPage* page)
+void ApplicationSettingsWidget::addSettingsPage(ISettingsPage* page)
{
QWidget* widget = page->createWidget();
widget->setParent(this);
@@ -121,7 +121,7 @@ void SettingsWidget::addSettingsPage(ISettingsPage* page)
addPage(page->name(), page->icon(), widget);
}
-void SettingsWidget::loadSettings()
+void ApplicationSettingsWidget::loadSettings()
{
if (config()->hasAccessError()) {
@@ -204,7 +204,7 @@ void SettingsWidget::loadSettings()
setCurrentPage(0);
}
-void SettingsWidget::saveSettings()
+void ApplicationSettingsWidget::saveSettings()
{
if (config()->hasAccessError()) {
@@ -282,7 +282,7 @@ void SettingsWidget::saveSettings()
}
}
-void SettingsWidget::reject()
+void ApplicationSettingsWidget::reject()
{
// register the old key again as it might have changed
if (m_globalAutoTypeKey > 0 && m_globalAutoTypeModifiers > 0) {
@@ -290,12 +290,12 @@ void SettingsWidget::reject()
}
}
-void SettingsWidget::enableAutoSaveOnExit(bool checked)
+void ApplicationSettingsWidget::enableAutoSaveOnExit(bool checked)
{
m_generalUi->autoSaveOnExitCheckBox->setEnabled(!checked);
}
-void SettingsWidget::enableSystray(bool checked)
+void ApplicationSettingsWidget::enableSystray(bool checked)
{
m_generalUi->systrayDarkIconCheckBox->setEnabled(checked);
m_generalUi->systrayMinimizeToTrayCheckBox->setEnabled(checked);
diff --git a/src/gui/SettingsWidget.h b/src/gui/ApplicationSettingsWidget.h
index d6a1a7e63..5fedabfcd 100644
--- a/src/gui/SettingsWidget.h
+++ b/src/gui/ApplicationSettingsWidget.h
@@ -23,8 +23,8 @@
namespace Ui
{
- class SettingsWidgetGeneral;
- class SettingsWidgetSecurity;
+ class ApplicationSettingsWidgetGeneral;
+ class ApplicationSettingsWidgetSecurity;
}
class ISettingsPage
@@ -40,13 +40,13 @@ public:
virtual void saveSettings(QWidget* widget) = 0;
};
-class SettingsWidget : public EditWidget
+class ApplicationSettingsWidget : public EditWidget
{
Q_OBJECT
public:
- explicit SettingsWidget(QWidget* parent = nullptr);
- ~SettingsWidget();
+ explicit ApplicationSettingsWidget(QWidget* parent = nullptr);
+ ~ApplicationSettingsWidget();
void addSettingsPage(ISettingsPage* page);
void loadSettings();
@@ -59,8 +59,8 @@ private slots:
private:
QWidget* const m_secWidget;
QWidget* const m_generalWidget;
- const QScopedPointer<Ui::SettingsWidgetSecurity> m_secUi;
- const QScopedPointer<Ui::SettingsWidgetGeneral> m_generalUi;
+ const QScopedPointer<Ui::ApplicationSettingsWidgetSecurity> m_secUi;
+ const QScopedPointer<Ui::ApplicationSettingsWidgetGeneral> m_generalUi;
Qt::Key m_globalAutoTypeKey;
Qt::KeyboardModifiers m_globalAutoTypeModifiers;
class ExtraPage;
diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui
index 34c7bc9b1..f88206d60 100644
--- a/src/gui/SettingsWidgetGeneral.ui
+++ b/src/gui/ApplicationSettingsWidgetGeneral.ui
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>SettingsWidgetGeneral</class>
- <widget class="QWidget" name="SettingsWidgetGeneral">
+ <class>ApplicationSettingsWidgetGeneral</class>
+ <widget class="QWidget" name="ApplicationSettingsWidgetGeneral">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>684</width>
- <height>794</height>
+ <height>842</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
@@ -26,7 +26,7 @@
<item>
<widget class="QTabWidget" name="generalSettingsTabWidget">
<property name="currentIndex">
- <number>1</number>
+ <number>0</number>
</property>
<widget class="QWidget" name="tabGeneral">
<attribute name="title">
diff --git a/src/gui/SettingsWidgetSecurity.ui b/src/gui/ApplicationSettingsWidgetSecurity.ui
index f90fb4c3d..bef64dc77 100644
--- a/src/gui/SettingsWidgetSecurity.ui
+++ b/src/gui/ApplicationSettingsWidgetSecurity.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>SettingsWidgetSecurity</class>
- <widget class="QWidget" name="SettingsWidgetSecurity">
+ <class>ApplicationSettingsWidgetSecurity</class>
+ <widget class="QWidget" name="ApplicationSettingsWidgetSecurity">
<property name="geometry">
<rect>
<x>0</x>
diff --git a/src/gui/CategoryListWidget.h b/src/gui/CategoryListWidget.h
index 911b5cc98..3f08fe384 100644
--- a/src/gui/CategoryListWidget.h
+++ b/src/gui/CategoryListWidget.h
@@ -15,6 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#ifndef KEEPASSXC_GUI_CATEGORYLISTWIDGET_H
+#define KEEPASSXC_GUI_CATEGORYLISTWIDGET_H
+
#include <QPointer>
#include <QStyledItemDelegate>
#include <QWidget>
@@ -86,3 +89,5 @@ private:
Q_DISABLE_COPY(CategoryListWidgetDelegate)
};
+
+#endif \ No newline at end of file
diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp
deleted file mode 100644
index b91495f78..000000000
--- a/src/gui/ChangeMasterKeyWidget.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
- * 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 "ChangeMasterKeyWidget.h"
-#include "ui_ChangeMasterKeyWidget.h"
-
-#include "MainWindow.h"
-#include "core/FilePath.h"
-#include "crypto/Random.h"
-#include "gui/FileDialog.h"
-#include "gui/MessageBox.h"
-#include "keys/FileKey.h"
-#include "keys/PasswordKey.h"
-#include "keys/YkChallengeResponseKey.h"
-
-#include "config-keepassx.h"
-
-#include <QSharedPointer>
-#include <QtConcurrentRun>
-
-ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent)
- : DialogyWidget(parent)
- , m_ui(new Ui::ChangeMasterKeyWidget())
-{
- m_ui->setupUi(this);
-
- m_ui->messageWidget->setHidden(true);
-
- m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show"));
- m_ui->repeatPasswordEdit->enableVerifyMode(m_ui->enterPasswordEdit);
-
- connect(m_ui->passwordGroup, SIGNAL(clicked(bool)), SLOT(setOkEnabled()));
- connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), m_ui->enterPasswordEdit, SLOT(setShowPassword(bool)));
-
- connect(m_ui->keyFileGroup, SIGNAL(clicked(bool)), SLOT(setOkEnabled()));
- connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile()));
- connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile()));
- connect(m_ui->keyFileCombo, SIGNAL(editTextChanged(QString)), SLOT(setOkEnabled()));
-
- connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(generateKey()));
- connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
-
-#ifdef WITH_XC_YUBIKEY
- m_ui->yubikeyProgress->setVisible(false);
- QSizePolicy sp = m_ui->yubikeyProgress->sizePolicy();
- sp.setRetainSizeWhenHidden(true);
- m_ui->yubikeyProgress->setSizePolicy(sp);
-
- connect(m_ui->challengeResponseGroup, SIGNAL(clicked(bool)), SLOT(challengeResponseGroupToggled(bool)));
- connect(m_ui->challengeResponseGroup, SIGNAL(clicked(bool)), SLOT(setOkEnabled()));
- connect(m_ui->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);
-#else
- m_ui->challengeResponseGroup->setVisible(false);
-#endif
-}
-
-ChangeMasterKeyWidget::~ChangeMasterKeyWidget()
-{
-}
-
-void ChangeMasterKeyWidget::createKeyFile()
-{
- QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
- QString fileName = fileDialog()->getSaveFileName(this, tr("Create Key File..."), QString(), filters);
-
- if (!fileName.isEmpty()) {
- QString errorMsg;
- bool created = FileKey::create(fileName, &errorMsg);
- if (!created) {
- m_ui->messageWidget->showMessage(tr("Unable to create key file: %1").arg(errorMsg), MessageWidget::Error);
- } else {
- m_ui->keyFileCombo->setEditText(fileName);
- }
- }
-}
-
-void ChangeMasterKeyWidget::browseKeyFile()
-{
- QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
- QString fileName = fileDialog()->getOpenFileName(this, tr("Select a key file"), QString(), filters);
-
- if (!fileName.isEmpty()) {
- m_ui->keyFileCombo->setEditText(fileName);
- }
-}
-
-void ChangeMasterKeyWidget::clearForms()
-{
- m_key.clear();
-
- m_ui->passwordGroup->setChecked(true);
- m_ui->enterPasswordEdit->setText("");
- m_ui->repeatPasswordEdit->setText("");
- m_ui->keyFileGroup->setChecked(false);
- m_ui->togglePasswordButton->setChecked(false);
-
-#ifdef WITH_XC_YUBIKEY
- m_ui->challengeResponseGroup->setChecked(false);
- m_ui->comboChallengeResponse->clear();
-#endif
-
- m_ui->enterPasswordEdit->setFocus();
-}
-
-CompositeKey ChangeMasterKeyWidget::newMasterKey()
-{
- return m_key;
-}
-
-QLabel* ChangeMasterKeyWidget::headlineLabel()
-{
- return m_ui->headlineLabel;
-}
-
-void ChangeMasterKeyWidget::generateKey()
-{
- m_key.clear();
-
- if (m_ui->passwordGroup->isChecked()) {
- if (m_ui->enterPasswordEdit->text() == m_ui->repeatPasswordEdit->text()) {
- if (m_ui->enterPasswordEdit->text().isEmpty()) {
- if (MessageBox::warning(this,
- tr("Empty password"),
- tr("Do you really want to use an empty string as password?"),
- QMessageBox::Yes | QMessageBox::No)
- != QMessageBox::Yes) {
- return;
- }
- }
- m_key.addKey(PasswordKey(m_ui->enterPasswordEdit->text()));
- } else {
- m_ui->messageWidget->showMessage(tr("Different passwords supplied."), MessageWidget::Error);
- m_ui->enterPasswordEdit->setText("");
- m_ui->repeatPasswordEdit->setText("");
- return;
- }
- }
- if (m_ui->keyFileGroup->isChecked()) {
- FileKey fileKey;
- QString errorMsg;
- QString fileKeyName = m_ui->keyFileCombo->currentText();
- if (!fileKey.load(fileKeyName, &errorMsg)) {
- m_ui->messageWidget->showMessage(tr("Failed to set %1 as the key file:\n%2").arg(fileKeyName, errorMsg),
- MessageWidget::Error);
- return;
- }
- if (fileKey.type() != FileKey::Hashed) {
- QMessageBox::warning(this,
- tr("Legacy key file format"),
- tr("You are using a legacy key file format which may become\n"
- "unsupported in the future.\n\n"
- "Please consider generating a new key file."),
- QMessageBox::Ok);
- }
- m_key.addKey(fileKey);
- }
-
-#ifdef WITH_XC_YUBIKEY
- if (m_ui->challengeResponseGroup->isChecked()) {
- int selectionIndex = m_ui->comboChallengeResponse->currentIndex();
- int comboPayload = m_ui->comboChallengeResponse->itemData(selectionIndex).toInt();
-
- if (0 == comboPayload) {
- m_ui->messageWidget->showMessage(tr("Changing master key failed: no YubiKey inserted."),
- MessageWidget::Error);
- return;
- }
-
- // read blocking mode from LSB and slot index number from second LSB
- bool blocking = comboPayload & 1;
- int slot = comboPayload >> 1;
- auto key = QSharedPointer<YkChallengeResponseKey>(new YkChallengeResponseKey(slot, blocking));
- m_key.addChallengeResponseKey(key);
- }
-#endif
-
- m_ui->messageWidget->hideMessage();
- emit editFinished(true);
-}
-
-void ChangeMasterKeyWidget::reject()
-{
- emit editFinished(false);
-}
-
-void ChangeMasterKeyWidget::challengeResponseGroupToggled(bool checked)
-{
- if (checked)
- pollYubikey();
-}
-
-void ChangeMasterKeyWidget::pollYubikey()
-{
- m_ui->buttonRedetectYubikey->setEnabled(false);
- m_ui->comboChallengeResponse->setEnabled(false);
- m_ui->comboChallengeResponse->clear();
- m_ui->yubikeyProgress->setVisible(true);
- setOkEnabled();
-
- // YubiKey init is slow, detect asynchronously to not block the UI
- QtConcurrent::run(YubiKey::instance(), &YubiKey::detect);
-}
-
-void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking)
-{
- YkChallengeResponseKey yk(slot, blocking);
- // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB
- m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking));
- m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked());
- m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked());
- m_ui->yubikeyProgress->setVisible(false);
- setOkEnabled();
-}
-
-void ChangeMasterKeyWidget::noYubikeyFound()
-{
- m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked());
- m_ui->yubikeyProgress->setVisible(false);
- setOkEnabled();
-}
-
-void ChangeMasterKeyWidget::setOkEnabled()
-{
- bool ok = m_ui->passwordGroup->isChecked()
- || (m_ui->challengeResponseGroup->isChecked() && !m_ui->comboChallengeResponse->currentText().isEmpty())
- || (m_ui->keyFileGroup->isChecked() && !m_ui->keyFileCombo->currentText().isEmpty());
-
- m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
-}
-
-void ChangeMasterKeyWidget::setCancelEnabled(bool enabled)
-{
- m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(enabled);
-}
diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h
deleted file mode 100644
index 2a4e0cc4a..000000000
--- a/src/gui/ChangeMasterKeyWidget.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
- * 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_CHANGEMASTERKEYWIDGET_H
-#define KEEPASSX_CHANGEMASTERKEYWIDGET_H
-
-#include <QScopedPointer>
-
-#include "gui/DialogyWidget.h"
-#include "keys/CompositeKey.h"
-
-class QLabel;
-namespace Ui
-{
- class ChangeMasterKeyWidget;
-}
-
-class ChangeMasterKeyWidget : public DialogyWidget
-{
- Q_OBJECT
-
-public:
- explicit ChangeMasterKeyWidget(QWidget* parent = nullptr);
- ~ChangeMasterKeyWidget();
- void clearForms();
- CompositeKey newMasterKey();
- QLabel* headlineLabel();
-
-public slots:
- void setOkEnabled();
- void setCancelEnabled(bool enabled);
-
-signals:
- void editFinished(bool accepted);
-
-private slots:
- void generateKey();
- void reject();
- void createKeyFile();
- void browseKeyFile();
- void yubikeyDetected(int slot, bool blocking);
- void noYubikeyFound();
- void challengeResponseGroupToggled(bool checked);
- void pollYubikey();
-
-private:
- const QScopedPointer<Ui::ChangeMasterKeyWidget> m_ui;
- CompositeKey m_key;
-
- Q_DISABLE_COPY(ChangeMasterKeyWidget)
-};
-
-#endif // KEEPASSX_CHANGEMASTERKEYWIDGET_H
diff --git a/src/gui/ChangeMasterKeyWidget.ui b/src/gui/ChangeMasterKeyWidget.ui
deleted file mode 100644
index 693d8ac1d..000000000
--- a/src/gui/ChangeMasterKeyWidget.ui
+++ /dev/null
@@ -1,238 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ChangeMasterKeyWidget</class>
- <widget class="QWidget" name="ChangeMasterKeyWidget">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>818</width>
- <height>471</height>
- </rect>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="MessageWidget" name="messageWidget" native="true"/>
- </item>
- <item>
- <widget class="QLabel" name="headlineLabel"/>
- </item>
- <item>
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>1</width>
- <height>3</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QGroupBox" name="passwordGroup">
- <property name="title">
- <string>Password</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0" alignment="Qt::AlignRight">
- <widget class="QLabel" name="enterPasswordLabel">
- <property name="text">
- <string>Enter password:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="PasswordEdit" name="enterPasswordEdit">
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="togglePasswordButton">
- <property name="checkable">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0" alignment="Qt::AlignRight">
- <widget class="QLabel" name="repeatPasswordLabel">
- <property name="text">
- <string>Repeat password:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="PasswordEdit" name="repeatPasswordEdit">
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="keyFileGroup">
- <property name="title">
- <string>&amp;Key file</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="2">
- <widget class="QPushButton" name="browseKeyFileButton">
- <property name="text">
- <string>Browse</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QPushButton" name="createKeyFileButton">
- <property name="text">
- <string>Create</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="keyFileCombo">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="editable">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="challengeResponseGroup">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="title">
- <string>Cha&amp;llenge Response</string>
- </property>
- <property name="checkable">
- <bool>true</bool>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <layout class="QGridLayout" name="gridLayout_4">
- <property name="verticalSpacing">
- <number>0</number>
- </property>
- <item row="0" column="1">
- <widget class="QPushButton" name="buttonRedetectYubikey">
- <property name="text">
- <string>Refresh</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QComboBox" name="comboChallengeResponse">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QProgressBar" name="yubikeyProgress">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>2</height>
- </size>
- </property>
- <property name="maximum">
- <number>0</number>
- </property>
- <property name="value">
- <number>-1</number>
- </property>
- <property name="textVisible">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>PasswordEdit</class>
- <extends>QLineEdit</extends>
- <header>gui/PasswordEdit.h</header>
- </customwidget>
- <customwidget>
- <class>MessageWidget</class>
- <extends>QWidget</extends>
- <header>gui/MessageWidget.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
- <tabstops>
- <tabstop>passwordGroup</tabstop>
- <tabstop>enterPasswordEdit</tabstop>
- <tabstop>repeatPasswordEdit</tabstop>
- <tabstop>togglePasswordButton</tabstop>
- <tabstop>keyFileGroup</tabstop>
- <tabstop>keyFileCombo</tabstop>
- <tabstop>browseKeyFileButton</tabstop>
- <tabstop>createKeyFileButton</tabstop>
- <tabstop>buttonBox</tabstop>
- </tabstops>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp
index ebb07bf12..d1ae52780 100644
--- a/src/gui/DatabaseOpenWidget.cpp
+++ b/src/gui/DatabaseOpenWidget.cpp
@@ -197,7 +197,7 @@ void DatabaseOpenWidget::openDatabase()
delete m_db;
}
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- m_db = reader.readDatabase(&file, *masterKey);
+ m_db = reader.readDatabase(&file, masterKey);
QApplication::restoreOverrideCursor();
if (m_db) {
@@ -240,7 +240,7 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::databaseKey()
auto masterKey = QSharedPointer<CompositeKey>::create();
if (m_ui->checkPassword->isChecked()) {
- masterKey->addKey(PasswordKey(m_ui->editPassword->text()));
+ masterKey->addKey(QSharedPointer<PasswordKey>::create(m_ui->editPassword->text()));
}
#ifdef WITH_XC_TOUCHID
@@ -262,14 +262,14 @@ QSharedPointer<CompositeKey> DatabaseOpenWidget::databaseKey()
QHash<QString, QVariant> lastChallengeResponse = config()->get("LastChallengeResponse").toHash();
if (m_ui->checkKeyFile->isChecked()) {
- FileKey key;
+ auto key = QSharedPointer<FileKey>::create();
QString keyFilename = m_ui->comboKeyFile->currentText();
QString errorMsg;
- if (!key.load(keyFilename, &errorMsg)) {
+ if (!key->load(keyFilename, &errorMsg)) {
m_ui->messageWidget->showMessage(tr("Can't open key file:\n%1").arg(errorMsg), MessageWidget::Error);
- return QSharedPointer<CompositeKey>();
+ return {};
}
- if (key.type() != FileKey::Hashed && !config()->get("Messages/NoLegacyKeyFileWarning").toBool()) {
+ if (key->type() != FileKey::Hashed && !config()->get("Messages/NoLegacyKeyFileWarning").toBool()) {
QMessageBox legacyWarning;
legacyWarning.setWindowTitle(tr("Legacy key file format"));
legacyWarning.setText(tr("You are using a legacy key file format which may become\n"
diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui
index b603ed32a..641d67da0 100644
--- a/src/gui/DatabaseOpenWidget.ui
+++ b/src/gui/DatabaseOpenWidget.ui
@@ -6,11 +6,11 @@
<rect>
<x>0</x>
<y>0</y>
- <width>596</width>
- <height>302</height>
+ <width>841</width>
+ <height>467</height>
</rect>
</property>
- <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,1,0,0,0">
+ <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>8</number>
</property>
@@ -55,10 +55,13 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
- <height>40</height>
+ <height>20</height>
</size>
</property>
</spacer>
@@ -216,7 +219,7 @@
<item row="3" column="0">
<widget class="QCheckBox" name="checkTouchID">
<property name="text">
- <string>Use TouchID for quick unlock</string>
+ <string>TouchID for quick unlock</string>
</property>
</widget>
</item>
diff --git a/src/gui/DatabaseRepairWidget.cpp b/src/gui/DatabaseRepairWidget.cpp
index b4be71247..7a90cf7dd 100644
--- a/src/gui/DatabaseRepairWidget.cpp
+++ b/src/gui/DatabaseRepairWidget.cpp
@@ -39,22 +39,22 @@ DatabaseRepairWidget::DatabaseRepairWidget(QWidget* parent)
void DatabaseRepairWidget::openDatabase()
{
- CompositeKey masterKey;
+ auto masterKey = QSharedPointer<CompositeKey>::create();
if (m_ui->checkPassword->isChecked()) {
- masterKey.addKey(PasswordKey(m_ui->editPassword->text()));
+ masterKey->addKey(QSharedPointer<PasswordKey>::create(m_ui->editPassword->text()));
}
if (m_ui->checkKeyFile->isChecked()) {
- FileKey key;
+ auto key = QSharedPointer<FileKey>::create();
QString keyFilename = m_ui->comboKeyFile->currentText();
QString errorMsg;
- if (!key.load(keyFilename, &errorMsg)) {
+ if (!key->load(keyFilename, &errorMsg)) {
MessageBox::warning(this, tr("Error"), tr("Can't open key file:\n%1").arg(errorMsg));
emit editFinished(false);
return;
}
- masterKey.addKey(key);
+ masterKey->addKey(key);
}
KeePass2Repair repair;
diff --git a/src/gui/DatabaseSettingsWidget.cpp b/src/gui/DatabaseSettingsWidget.cpp
deleted file mode 100644
index 8aea47266..000000000
--- a/src/gui/DatabaseSettingsWidget.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
- * Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
- *
- * 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 "DatabaseSettingsWidget.h"
-#include "ui_DatabaseSettingsWidget.h"
-#include "ui_DatabaseSettingsWidgetEncryption.h"
-#include "ui_DatabaseSettingsWidgetGeneral.h"
-
-#include <QMessageBox>
-#include <QPushButton>
-#include <QThread>
-
-#include "MessageBox.h"
-#include "core/AsyncTask.h"
-#include "core/Database.h"
-#include "core/FilePath.h"
-#include "core/Global.h"
-#include "core/Group.h"
-#include "core/Metadata.h"
-#include "crypto/SymmetricCipher.h"
-#include "crypto/kdf/Argon2Kdf.h"
-
-DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent)
- : DialogyWidget(parent)
- , m_ui(new Ui::DatabaseSettingsWidget())
- , m_uiGeneral(new Ui::DatabaseSettingsWidgetGeneral())
- , m_uiEncryption(new Ui::DatabaseSettingsWidgetEncryption())
- , m_uiGeneralPage(new QWidget())
- , m_uiEncryptionPage(new QWidget())
- , m_db(nullptr)
-{
- m_ui->setupUi(this);
- m_uiGeneral->setupUi(m_uiGeneralPage);
- m_uiEncryption->setupUi(m_uiEncryptionPage);
-
- connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(save()));
- connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
- connect(m_uiGeneral->historyMaxItemsCheckBox,
- SIGNAL(toggled(bool)),
- m_uiGeneral->historyMaxItemsSpinBox,
- SLOT(setEnabled(bool)));
- connect(m_uiGeneral->historyMaxSizeCheckBox,
- SIGNAL(toggled(bool)),
- m_uiGeneral->historyMaxSizeSpinBox,
- SLOT(setEnabled(bool)));
- connect(m_uiEncryption->transformBenchmarkButton, SIGNAL(clicked()), SLOT(transformRoundsBenchmark()));
- connect(m_uiEncryption->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(kdfChanged(int)));
-
- connect(m_uiEncryption->memorySpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryChanged(int)));
- connect(m_uiEncryption->parallelismSpinBox, SIGNAL(valueChanged(int)), this, SLOT(parallelismChanged(int)));
-
- m_ui->categoryList->addCategory(tr("General"), FilePath::instance()->icon("categories", "preferences-other"));
- m_ui->categoryList->addCategory(tr("Encryption"), FilePath::instance()->icon("actions", "document-encrypt"));
- m_ui->stackedWidget->addWidget(m_uiGeneralPage);
- m_ui->stackedWidget->addWidget(m_uiEncryptionPage);
-
- connect(m_ui->categoryList, SIGNAL(categoryChanged(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int)));
-}
-
-DatabaseSettingsWidget::~DatabaseSettingsWidget()
-{
-}
-
-void DatabaseSettingsWidget::load(Database* db)
-{
- m_db = db;
-
- Metadata* meta = m_db->metadata();
-
- m_uiGeneral->dbNameEdit->setText(meta->name());
- m_uiGeneral->dbDescriptionEdit->setText(meta->description());
- m_uiGeneral->recycleBinEnabledCheckBox->setChecked(meta->recycleBinEnabled());
- m_uiGeneral->defaultUsernameEdit->setText(meta->defaultUserName());
- m_uiGeneral->compressionCheckbox->setChecked(m_db->compressionAlgo() != Database::CompressionNone);
-
- if (meta->historyMaxItems() > -1) {
- m_uiGeneral->historyMaxItemsSpinBox->setValue(meta->historyMaxItems());
- m_uiGeneral->historyMaxItemsCheckBox->setChecked(true);
- } else {
- m_uiGeneral->historyMaxItemsSpinBox->setValue(Metadata::DefaultHistoryMaxItems);
- m_uiGeneral->historyMaxItemsCheckBox->setChecked(false);
- }
- int historyMaxSizeMiB = qRound(meta->historyMaxSize() / qreal(1048576));
- if (historyMaxSizeMiB > 0) {
- m_uiGeneral->historyMaxSizeSpinBox->setValue(historyMaxSizeMiB);
- m_uiGeneral->historyMaxSizeCheckBox->setChecked(true);
- } else {
- m_uiGeneral->historyMaxSizeSpinBox->setValue(Metadata::DefaultHistoryMaxSize);
- m_uiGeneral->historyMaxSizeCheckBox->setChecked(false);
- }
-
- m_uiEncryption->algorithmComboBox->clear();
- for (auto& cipher: asConst(KeePass2::CIPHERS)) {
- m_uiEncryption->algorithmComboBox->addItem(QCoreApplication::translate("KeePass2", cipher.second.toUtf8()), cipher.first);
- }
- int cipherIndex = m_uiEncryption->algorithmComboBox->findData(m_db->cipher().toRfc4122());
- if (cipherIndex > -1) {
- m_uiEncryption->algorithmComboBox->setCurrentIndex(cipherIndex);
- }
-
- // Setup kdf combo box
- m_uiEncryption->kdfComboBox->blockSignals(true);
- m_uiEncryption->kdfComboBox->clear();
- for (auto& kdf: asConst(KeePass2::KDFS)) {
- m_uiEncryption->kdfComboBox->addItem(QCoreApplication::translate("KeePass2", kdf.second.toUtf8()), kdf.first);
- }
- m_uiEncryption->kdfComboBox->blockSignals(false);
-
- auto kdfUuid = m_db->kdf()->uuid();
- int kdfIndex = m_uiEncryption->kdfComboBox->findData(kdfUuid);
- if (kdfIndex > -1) {
- m_uiEncryption->kdfComboBox->setCurrentIndex(kdfIndex);
- kdfChanged(kdfIndex);
- }
-
- m_uiEncryption->memorySpinBox->setValue(64);
- m_uiEncryption->parallelismSpinBox->setValue(QThread::idealThreadCount());
-
- // Setup kdf parameters
- auto kdf = m_db->kdf();
- m_uiEncryption->transformRoundsSpinBox->setValue(kdf->rounds());
- if (kdfUuid == KeePass2::KDF_ARGON2) {
- auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
- m_uiEncryption->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory()) / (1 << 10));
- m_uiEncryption->parallelismSpinBox->setValue(argon2Kdf->parallelism());
- }
-
- m_uiGeneral->dbNameEdit->setFocus();
- m_ui->categoryList->setCurrentCategory(0);
-}
-
-void DatabaseSettingsWidget::save()
-{
- // first perform safety check for KDF rounds
- auto kdf = KeePass2::uuidToKdf(m_uiEncryption->kdfComboBox->currentData().value<QUuid>());
- if (kdf->uuid() == KeePass2::KDF_ARGON2 && m_uiEncryption->transformRoundsSpinBox->value() > 10000) {
- QMessageBox warning;
- warning.setIcon(QMessageBox::Warning);
- warning.setWindowTitle(tr("Number of rounds too high", "Key transformation rounds"));
- warning.setText(tr("You are using a very high number of key transform rounds with Argon2.\n\n"
- "If you keep this number, your database may take hours or days (or even longer) to open!"));
- auto ok = warning.addButton(tr("Understood, keep number"), QMessageBox::ButtonRole::AcceptRole);
- auto cancel = warning.addButton(tr("Cancel"), QMessageBox::ButtonRole::RejectRole);
- warning.setDefaultButton(cancel);
- warning.exec();
- if (warning.clickedButton() != ok) {
- return;
- }
- } else if ((kdf->uuid() == KeePass2::KDF_AES_KDBX3 || kdf->uuid() == KeePass2::KDF_AES_KDBX4)
- && m_uiEncryption->transformRoundsSpinBox->value() < 100000) {
- QMessageBox warning;
- warning.setIcon(QMessageBox::Warning);
- warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds"));
- warning.setText(tr("You are using a very low number of key transform rounds with AES-KDF.\n\n"
- "If you keep this number, your database may be too easy to crack!"));
- auto ok = warning.addButton(tr("Understood, keep number"), QMessageBox::ButtonRole::AcceptRole);
- auto cancel = warning.addButton(tr("Cancel"), QMessageBox::ButtonRole::RejectRole);
- warning.setDefaultButton(cancel);
- warning.exec();
- if (warning.clickedButton() != ok) {
- return;
- }
- }
-
- m_db->setCompressionAlgo(m_uiGeneral->compressionCheckbox->isChecked() ? Database::CompressionGZip
- : Database::CompressionNone);
-
- Metadata* meta = m_db->metadata();
-
- meta->setName(m_uiGeneral->dbNameEdit->text());
- meta->setDescription(m_uiGeneral->dbDescriptionEdit->text());
- meta->setDefaultUserName(m_uiGeneral->defaultUsernameEdit->text());
- meta->setRecycleBinEnabled(m_uiGeneral->recycleBinEnabledCheckBox->isChecked());
- meta->setSettingsChanged(QDateTime::currentDateTimeUtc());
-
- bool truncate = false;
-
- int historyMaxItems;
- if (m_uiGeneral->historyMaxItemsCheckBox->isChecked()) {
- historyMaxItems = m_uiGeneral->historyMaxItemsSpinBox->value();
- } else {
- historyMaxItems = -1;
- }
- if (historyMaxItems != meta->historyMaxItems()) {
- meta->setHistoryMaxItems(historyMaxItems);
- truncate = true;
- }
-
- int historyMaxSize;
- if (m_uiGeneral->historyMaxSizeCheckBox->isChecked()) {
- historyMaxSize = m_uiGeneral->historyMaxSizeSpinBox->value() * 1048576;
- } else {
- historyMaxSize = -1;
- }
- if (historyMaxSize != meta->historyMaxSize()) {
- meta->setHistoryMaxSize(historyMaxSize);
- truncate = true;
- }
-
- if (truncate) {
- truncateHistories();
- }
-
- m_db->setCipher(m_uiEncryption->algorithmComboBox->currentData().value<QUuid>());
-
- // Save kdf parameters
- kdf->setRounds(m_uiEncryption->transformRoundsSpinBox->value());
- if (kdf->uuid() == KeePass2::KDF_ARGON2) {
- auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
- argon2Kdf->setMemory(static_cast<quint64>(m_uiEncryption->memorySpinBox->value()) * (1 << 10));
- argon2Kdf->setParallelism(static_cast<quint32>(m_uiEncryption->parallelismSpinBox->value()));
- }
-
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- // TODO: we should probably use AsyncTask::runAndWaitForFuture() here,
- // but not without making Database thread-safe
- bool ok = m_db->changeKdf(kdf);
- QApplication::restoreOverrideCursor();
-
- if (!ok) {
- MessageBox::warning(this,
- tr("KDF unchanged"),
- tr("Failed to transform key with new KDF parameters; KDF unchanged."),
- QMessageBox::Ok);
- }
-
- emit editFinished(true);
-}
-
-void DatabaseSettingsWidget::reject()
-{
- emit editFinished(false);
-}
-
-void DatabaseSettingsWidget::transformRoundsBenchmark()
-{
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- m_uiEncryption->transformBenchmarkButton->setEnabled(false);
- m_uiEncryption->transformRoundsSpinBox->setFocus();
-
- // Create a new kdf with the current parameters
- auto kdf = KeePass2::uuidToKdf(m_uiEncryption->kdfComboBox->currentData().value<QUuid>());
- kdf->setRounds(m_uiEncryption->transformRoundsSpinBox->value());
- if (kdf->uuid() == KeePass2::KDF_ARGON2) {
- auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
- if (!argon2Kdf->setMemory(static_cast<quint64>(m_uiEncryption->memorySpinBox->value()) * (1 << 10))) {
- m_uiEncryption->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory() / (1 << 10)));
- }
- if (!argon2Kdf->setParallelism(static_cast<quint32>(m_uiEncryption->parallelismSpinBox->value()))) {
- m_uiEncryption->parallelismSpinBox->setValue(argon2Kdf->parallelism());
- }
- }
-
- // Determine the number of rounds required to meet 1 second delay
- int rounds = AsyncTask::runAndWaitForFuture([&kdf]() { return kdf->benchmark(1000); });
-
- m_uiEncryption->transformRoundsSpinBox->setValue(rounds);
- m_uiEncryption->transformBenchmarkButton->setEnabled(true);
- QApplication::restoreOverrideCursor();
-}
-
-void DatabaseSettingsWidget::truncateHistories()
-{
- const QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(false);
- for (Entry* entry : allEntries) {
- entry->truncateHistory();
- }
-}
-
-void DatabaseSettingsWidget::kdfChanged(int index)
-{
- QUuid id(m_uiEncryption->kdfComboBox->itemData(index).value<QUuid>());
-
- bool memoryEnabled = id == KeePass2::KDF_ARGON2;
- m_uiEncryption->memoryUsageLabel->setEnabled(memoryEnabled);
- m_uiEncryption->memorySpinBox->setEnabled(memoryEnabled);
-
- bool parallelismEnabled = id == KeePass2::KDF_ARGON2;
- m_uiEncryption->parallelismLabel->setEnabled(parallelismEnabled);
- m_uiEncryption->parallelismSpinBox->setEnabled(parallelismEnabled);
-
- transformRoundsBenchmark();
-}
-
-/**
- * Update memory spin box suffix on value change.
- */
-void DatabaseSettingsWidget::memoryChanged(int value)
-{
- m_uiEncryption->memorySpinBox->setSuffix(tr(" MiB", "Abbreviation for Mebibytes (KDF settings)", value));
-}
-
-/**
- * Update parallelism spin box suffix on value change.
- */
-void DatabaseSettingsWidget::parallelismChanged(int value)
-{
- m_uiEncryption->parallelismSpinBox->setSuffix(
- tr(" thread(s)", "Threads for parallel execution (KDF settings)", value));
-}
diff --git a/src/gui/DatabaseSettingsWidgetEncryption.ui b/src/gui/DatabaseSettingsWidgetEncryption.ui
deleted file mode 100644
index 502bc012c..000000000
--- a/src/gui/DatabaseSettingsWidgetEncryption.ui
+++ /dev/null
@@ -1,183 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>DatabaseSettingsWidgetEncryption</class>
- <widget class="QWidget" name="DatabaseSettingsWidgetEncryption">
- <layout class="QFormLayout" name="formLayout">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item row="0" column="0">
- <widget class="QLabel" name="algorithmLabel">
- <property name="text">
- <string>Encryption Algorithm:</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="algorithmComboBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <item>
- <property name="text">
- <string>AES: 256 Bit (default)</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Twofish: 256 Bit</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="kdfLabel">
- <property name="text">
- <string>Key Derivation Function:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="kdfComboBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="transformRoundsLabel">
- <property name="text">
- <string>Transform rounds:</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <layout class="QHBoxLayout" name="horizontalLayout" stretch="40,40,0">
- <item>
- <widget class="QSpinBox" name="transformRoundsSpinBox">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>1000000000</number>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="transformBenchmarkButton">
- <property name="focusPolicy">
- <enum>Qt::WheelFocus</enum>
- </property>
- <property name="text">
- <string>Benchmark 1-second delay</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- <item row="3" column="0">
- <widget class="QLabel" name="memoryUsageLabel">
- <property name="text">
- <string>Memory Usage:</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QSpinBox" name="memorySpinBox">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>1048576</number>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="parallelismLabel">
- <property name="text">
- <string>Parallelism:</string>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QSpinBox" name="parallelismSpinBox">
- <property name="minimumSize">
- <size>
- <width>150</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>150</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="minimum">
- <number>1</number>
- </property>
- <property name="maximum">
- <number>128</number>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp
index ce90a0d57..71bd74814 100644
--- a/src/gui/DatabaseTabWidget.cpp
+++ b/src/gui/DatabaseTabWidget.cpp
@@ -39,6 +39,7 @@
#include "gui/UnlockDatabaseDialog.h"
#include "gui/entry/EntryView.h"
#include "gui/group/GroupView.h"
+#include "gui/wizard/NewDatabaseWizard.h"
DatabaseManagerStruct::DatabaseManagerStruct()
: dbWidget(nullptr)
@@ -85,24 +86,53 @@ void DatabaseTabWidget::toggleTabbar()
}
}
+/**
+ * Helper method for invoking the new database wizard.
+ * The user of this method MUST take ownership of the returned pointer.
+ *
+ * @return pointer to the configured new database, nullptr on failure
+ */
+Database* DatabaseTabWidget::execNewDatabaseWizard()
+{
+ // use QScopedPointer to ensure deletion after scope ends, but still parent
+ // it to this to make it modal and allow easier access in unit tests
+ QScopedPointer<NewDatabaseWizard> wizard(new NewDatabaseWizard(this));
+ if (!wizard->exec()) {
+ return nullptr;
+ }
+
+ auto* db = wizard->takeDatabase();
+ if (!db) {
+ return nullptr;
+ }
+ Q_ASSERT(db->key());
+ Q_ASSERT(db->kdf());
+ if (!db->key() || !db->kdf()) {
+ MessageBox::critical(this, tr("Database creation error"),
+ tr("The created database has no key or KDF, refusing to save it.\n"
+ "This is definitely a bug, please report it to the developers."),
+ QMessageBox::Ok, QMessageBox::Ok);
+ return nullptr;
+ }
+
+ return db;
+}
+
void DatabaseTabWidget::newDatabase()
{
+ auto* db = execNewDatabaseWizard();
+ if (!db) {
+ return;
+ }
+
DatabaseManagerStruct dbStruct;
- Database* db = new Database();
- db->rootGroup()->setName(tr("Root", "Root group"));
dbStruct.dbWidget = new DatabaseWidget(db, this);
-
- CompositeKey emptyKey;
- db->setKey(emptyKey);
-
insertDatabase(db, dbStruct);
if (!saveDatabaseAs(db)) {
- closeDatabase(db);
- return;
+ // mark database as dirty if user canceled save dialog
+ emit db->modifiedImmediate();
}
-
- dbStruct.dbWidget->switchToMasterKeyChange(true);
}
void DatabaseTabWidget::openDatabase()
@@ -178,18 +208,21 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw,
void DatabaseTabWidget::importCsv()
{
QString filter = QString("%1 (*.csv);;%2 (*)").arg(tr("CSV file"), tr("All files"));
- QString fileName = fileDialog()->getOpenFileName(this, tr("Open CSV file"), QString(), filter);
+ QString fileName = fileDialog()->getOpenFileName(this, tr("Select CSV file"), {}, filter);
if (fileName.isEmpty()) {
return;
}
- Database* db = new Database();
+ auto* db = execNewDatabaseWizard();
+ if (!db) {
+ return;
+ }
+
DatabaseManagerStruct dbStruct;
dbStruct.dbWidget = new DatabaseWidget(db, this);
-
insertDatabase(db, dbStruct);
- dbStruct.dbWidget->switchToImportCsv(fileName);
+ dbStruct.dbWidget->switchToCsvImport(fileName);
}
void DatabaseTabWidget::mergeDatabase()
@@ -322,52 +355,52 @@ bool DatabaseTabWidget::saveDatabase(Database* db, QString filePath)
return true;
}
- if (!dbStruct.readOnly) {
- if (filePath.isEmpty()) {
- filePath = dbStruct.fileInfo.canonicalFilePath();
- }
+ if (filePath.isEmpty()) {
+ filePath = dbStruct.fileInfo.canonicalFilePath();
+ }
- dbStruct.dbWidget->blockAutoReload(true);
- // TODO: Make this async, but lock out the database widget to prevent re-entrance
- bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
- QString errorMessage = db->saveToFile(filePath, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
- dbStruct.dbWidget->blockAutoReload(false);
+ if (dbStruct.readOnly || filePath.isEmpty()) {
+ return saveDatabaseAs(db);
+ }
- if (errorMessage.isEmpty()) {
- // successfully saved database file
- dbStruct.modified = false;
- dbStruct.saveAttempts = 0;
- dbStruct.fileInfo = QFileInfo(filePath);
- dbStruct.dbWidget->databaseSaved();
- updateTabName(db);
- emit messageDismissTab();
- return true;
- } else {
- dbStruct.modified = true;
- updateTabName(db);
-
- if (++dbStruct.saveAttempts > 2 && useAtomicSaves) {
- // Saving failed 3 times, issue a warning and attempt to resolve
- auto choice = MessageBox::question(this,
- tr("Disable safe saves?"),
- tr("KeePassXC has failed to save the database multiple times. "
- "This is likely caused by file sync services holding a lock on "
- "the save file.\nDisable safe saves and try again?"),
- QMessageBox::Yes | QMessageBox::No,
- QMessageBox::Yes);
- if (choice == QMessageBox::Yes) {
- config()->set("UseAtomicSaves", false);
- return saveDatabase(db, filePath);
- }
- // Reset save attempts without changing anything
- dbStruct.saveAttempts = 0;
- }
+ dbStruct.dbWidget->blockAutoReload(true);
+ // TODO: Make this async, but lock out the database widget to prevent re-entrance
+ bool useAtomicSaves = config()->get("UseAtomicSaves", true).toBool();
+ QString errorMessage = db->saveToFile(filePath, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
+ dbStruct.dbWidget->blockAutoReload(false);
- emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage), MessageWidget::Error);
- return false;
- }
+ if (errorMessage.isEmpty()) {
+ // successfully saved database file
+ dbStruct.modified = false;
+ dbStruct.saveAttempts = 0;
+ dbStruct.fileInfo = QFileInfo(filePath);
+ dbStruct.dbWidget->databaseSaved();
+ updateTabName(db);
+ emit messageDismissTab();
+ return true;
} else {
- return saveDatabaseAs(db);
+ dbStruct.modified = true;
+ updateTabName(db);
+
+ if (++dbStruct.saveAttempts > 2 && useAtomicSaves) {
+ // Saving failed 3 times, issue a warning and attempt to resolve
+ auto choice = MessageBox::question(this,
+ tr("Disable safe saves?"),
+ tr("KeePassXC has failed to save the database multiple times. "
+ "This is likely caused by file sync services holding a lock on "
+ "the save file.\nDisable safe saves and try again?"),
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::Yes);
+ if (choice == QMessageBox::Yes) {
+ config()->set("UseAtomicSaves", false);
+ return saveDatabase(db, filePath);
+ }
+ // Reset save attempts without changing anything
+ dbStruct.saveAttempts = 0;
+ }
+
+ emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage), MessageWidget::Error);
+ return false;
}
}
diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h
index f4cdf2953..dcb4a62da 100644
--- a/src/gui/DatabaseTabWidget.h
+++ b/src/gui/DatabaseTabWidget.h
@@ -103,6 +103,7 @@ private slots:
void emitDatabaseUnlockedFromDbWidgetSender();
private:
+ Database* execNewDatabaseWizard();
bool saveDatabase(Database* db, QString filePath = "");
bool saveDatabaseAs(Database* db);
bool closeDatabase(Database* db);
diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp
index f83211323..45fc058eb 100644
--- a/src/gui/DatabaseWidget.cpp
+++ b/src/gui/DatabaseWidget.cpp
@@ -39,11 +39,10 @@
#include "core/Metadata.h"
#include "core/Tools.h"
#include "format/KeePass2Reader.h"
-#include "gui/ChangeMasterKeyWidget.h"
#include "gui/Clipboard.h"
#include "gui/CloneDialog.h"
#include "gui/DatabaseOpenWidget.h"
-#include "gui/DatabaseSettingsWidget.h"
+#include "gui/dbsettings/DatabaseSettingsDialog.h"
#include "gui/DetailsWidget.h"
#include "gui/KeePass1OpenWidget.h"
#include "gui/MessageBox.h"
@@ -69,7 +68,6 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
, m_newGroup(nullptr)
, m_newEntry(nullptr)
, m_newParent(nullptr)
- , m_importingCsv(false)
{
m_mainWidget = new QWidget(this);
@@ -150,17 +148,10 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
m_historyEditEntryWidget = new EditEntryWidget();
m_editGroupWidget = new EditGroupWidget();
m_editGroupWidget->setObjectName("editGroupWidget");
- m_changeMasterKeyWidget = new ChangeMasterKeyWidget();
- m_changeMasterKeyWidget->setObjectName("changeMasterKeyWidget");
- m_changeMasterKeyWidget->headlineLabel()->setText(tr("Change master key"));
- QFont headlineLabelFont = m_changeMasterKeyWidget->headlineLabel()->font();
- headlineLabelFont.setBold(true);
- headlineLabelFont.setPointSize(headlineLabelFont.pointSize() + 2);
- m_changeMasterKeyWidget->headlineLabel()->setFont(headlineLabelFont);
m_csvImportWizard = new CsvImportWizard();
m_csvImportWizard->setObjectName("csvImportWizard");
- m_databaseSettingsWidget = new DatabaseSettingsWidget();
- m_databaseSettingsWidget->setObjectName("databaseSettingsWidget");
+ m_databaseSettingDialog = new DatabaseSettingsDialog();
+ m_databaseSettingDialog->setObjectName("databaseSettingsDialog");
m_databaseOpenWidget = new DatabaseOpenWidget();
m_databaseOpenWidget->setObjectName("databaseOpenWidget");
m_databaseOpenMergeWidget = new DatabaseOpenWidget();
@@ -174,8 +165,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
addWidget(m_mainWidget);
addWidget(m_editEntryWidget);
addWidget(m_editGroupWidget);
- addWidget(m_changeMasterKeyWidget);
- addWidget(m_databaseSettingsWidget);
+ addWidget(m_databaseSettingDialog);
addWidget(m_historyEditEntryWidget);
addWidget(m_databaseOpenWidget);
addWidget(m_csvImportWizard);
@@ -196,8 +186,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
connect(m_editEntryWidget, SIGNAL(historyEntryActivated(Entry*)), SLOT(switchToHistoryView(Entry*)));
connect(m_historyEditEntryWidget, SIGNAL(editFinished(bool)), SLOT(switchBackToEntryEdit()));
connect(m_editGroupWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
- connect(m_changeMasterKeyWidget, SIGNAL(editFinished(bool)), SLOT(updateMasterKey(bool)));
- connect(m_databaseSettingsWidget, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
+ connect(m_databaseSettingDialog, SIGNAL(editFinished(bool)), SLOT(switchToView(bool)));
connect(m_databaseOpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
connect(m_databaseOpenMergeWidget, SIGNAL(editFinished(bool)), SLOT(mergeDatabase(bool)));
connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool)));
@@ -810,34 +799,6 @@ void DatabaseWidget::switchToGroupEdit(Group* group, bool create)
setCurrentWidget(m_editGroupWidget);
}
-void DatabaseWidget::updateMasterKey(bool accepted)
-{
- if (m_importingCsv) {
- setCurrentWidget(m_csvImportWizard);
- m_csvImportWizard->keyFinished(accepted, m_changeMasterKeyWidget->newMasterKey());
- return;
- }
-
- if (accepted) {
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- bool result = m_db->setKey(m_changeMasterKeyWidget->newMasterKey(), true, true);
-#ifdef WITH_XC_TOUCHID
- TouchID::getInstance().reset(m_filePath);
-#endif
- QApplication::restoreOverrideCursor();
-
- if (!result) {
- m_messageWidget->showMessage(tr("Unable to calculate master key"), MessageWidget::Error);
- return;
- }
- } else if (!m_db->hasKey()) {
- emit closeRequest();
- return;
- }
-
- setCurrentWidget(m_mainWidget);
-}
-
void DatabaseWidget::openDatabase(bool accepted)
{
if (accepted) {
@@ -975,18 +936,16 @@ void DatabaseWidget::switchToGroupEdit()
switchToGroupEdit(group, false);
}
-void DatabaseWidget::switchToMasterKeyChange(bool disableCancel)
+void DatabaseWidget::switchToMasterKeyChange()
{
- m_changeMasterKeyWidget->clearForms();
- m_changeMasterKeyWidget->setCancelEnabled(!disableCancel);
- setCurrentWidget(m_changeMasterKeyWidget);
- m_importingCsv = false;
+ switchToDatabaseSettings();
+ m_databaseSettingDialog->showMasterKeySettings();
}
void DatabaseWidget::switchToDatabaseSettings()
{
- m_databaseSettingsWidget->load(m_db);
- setCurrentWidget(m_databaseSettingsWidget);
+ m_databaseSettingDialog->load(m_db);
+ setCurrentWidget(m_databaseSettingDialog);
}
void DatabaseWidget::switchToOpenDatabase(const QString& filePath)
@@ -1012,14 +971,10 @@ void DatabaseWidget::switchToOpenDatabase(const QString& filePath, const QString
}
}
-void DatabaseWidget::switchToImportCsv(const QString& filePath)
+void DatabaseWidget::switchToCsvImport(const QString& filePath)
{
- updateFilePath(filePath);
+ setCurrentWidget(m_csvImportWizard);
m_csvImportWizard->load(filePath, m_db);
- m_changeMasterKeyWidget->clearForms();
- m_changeMasterKeyWidget->setCancelEnabled(false);
- setCurrentWidget(m_changeMasterKeyWidget);
- m_importingCsv = true;
}
void DatabaseWidget::switchToOpenMergeDatabase(const QString& filePath)
@@ -1220,6 +1175,7 @@ void DatabaseWidget::updateFilePath(const QString& filePath)
m_fileWatcher.addPath(filePath);
m_filePath = filePath;
+ m_db->setFilePath(filePath);
}
void DatabaseWidget::blockAutoReload(bool block)
diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h
index 78b162c3a..896703eb6 100644
--- a/src/gui/DatabaseWidget.h
+++ b/src/gui/DatabaseWidget.h
@@ -31,7 +31,7 @@
class ChangeMasterKeyWidget;
class DatabaseOpenWidget;
-class DatabaseSettingsWidget;
+class DatabaseSettingsDialog;
class Database;
class EditEntryWidget;
class EditGroupWidget;
@@ -157,11 +157,11 @@ public slots:
void switchToView(bool accepted);
void switchToEntryEdit();
void switchToGroupEdit();
- void switchToMasterKeyChange(bool disableCancel = false);
+ void switchToMasterKeyChange();
void switchToDatabaseSettings();
void switchToOpenDatabase(const QString& filePath);
void switchToOpenDatabase(const QString& filePath, const QString& password, const QString& keyFile);
- void switchToImportCsv(const QString& filePath);
+ void switchToCsvImport(const QString& filePath);
void csvImportFinished(bool accepted);
void switchToOpenMergeDatabase(const QString& filePath);
void switchToOpenMergeDatabase(const QString& filePath, const QString& password, const QString& keyFile);
@@ -195,7 +195,6 @@ private slots:
void emitPressedEntry();
void emitPressedEntry(Entry* currentEntry);
void emitPressedGroup(Group* currentGroup);
- void updateMasterKey(bool accepted);
void openDatabase(bool accepted);
void mergeDatabase(bool accepted);
void unlockDatabase(bool accepted);
@@ -218,7 +217,7 @@ private:
EditGroupWidget* m_editGroupWidget;
ChangeMasterKeyWidget* m_changeMasterKeyWidget;
CsvImportWizard* m_csvImportWizard;
- DatabaseSettingsWidget* m_databaseSettingsWidget;
+ DatabaseSettingsDialog* m_databaseSettingDialog;
DatabaseOpenWidget* m_databaseOpenWidget;
DatabaseOpenWidget* m_databaseOpenMergeWidget;
KeePass1OpenWidget* m_keepass1OpenWidget;
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index 2072fb585..f3e974724 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -58,7 +58,7 @@
#endif
#include "gui/PasswordGeneratorWidget.h"
-#include "gui/SettingsWidget.h"
+#include "gui/ApplicationSettingsWidget.h"
#include "touchid/TouchID.h"
@@ -313,7 +313,7 @@ MainWindow::MainWindow()
connect(m_ui->welcomeWidget, SIGNAL(openDatabase()), SLOT(switchToOpenDatabase()));
connect(m_ui->welcomeWidget, SIGNAL(openDatabaseFile(QString)), SLOT(switchToDatabaseFile(QString)));
connect(m_ui->welcomeWidget, SIGNAL(importKeePass1Database()), SLOT(switchToKeePass1Database()));
- connect(m_ui->welcomeWidget, SIGNAL(importCsv()), SLOT(switchToImportCsv()));
+ connect(m_ui->welcomeWidget, SIGNAL(importCsv()), SLOT(switchToCsvImport()));
connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog()));
connect(m_ui->actionDonate, SIGNAL(triggered()), SLOT(openDonateUrl()));
@@ -690,7 +690,7 @@ void MainWindow::switchToKeePass1Database()
switchToDatabases();
}
-void MainWindow::switchToImportCsv()
+void MainWindow::switchToCsvImport()
{
m_ui->tabWidget->importCsv();
switchToDatabases();
diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h
index 79fb311c0..c47700726 100644
--- a/src/gui/MainWindow.h
+++ b/src/gui/MainWindow.h
@@ -90,7 +90,7 @@ private slots:
void switchToOpenDatabase();
void switchToDatabaseFile(QString file);
void switchToKeePass1Database();
- void switchToImportCsv();
+ void switchToCsvImport();
void closePasswordGen();
void databaseStatusChanged(DatabaseWidget* dbWidget);
void databaseTabChanged(int tabIndex);
diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui
index 4acb0a534..78d5178ba 100644
--- a/src/gui/MainWindow.ui
+++ b/src/gui/MainWindow.ui
@@ -116,7 +116,7 @@
<number>0</number>
</property>
<item>
- <widget class="SettingsWidget" name="settingsWidget" native="true"/>
+ <widget class="ApplicationSettingsWidget" name="settingsWidget" native="true"/>
</item>
</layout>
</widget>
@@ -629,9 +629,9 @@
<container>1</container>
</customwidget>
<customwidget>
- <class>SettingsWidget</class>
+ <class>ApplicationSettingsWidget</class>
<extends>QWidget</extends>
- <header>gui/SettingsWidget.h</header>
+ <header>gui/ApplicationSettingsWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
diff --git a/src/gui/PasswordGeneratorWidget.cpp b/src/gui/PasswordGeneratorWidget.cpp
index fed34754b..d594cba8e 100644
--- a/src/gui/PasswordGeneratorWidget.cpp
+++ b/src/gui/PasswordGeneratorWidget.cpp
@@ -41,7 +41,7 @@ PasswordGeneratorWidget::PasswordGeneratorWidget(QWidget* parent)
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updateButtonsEnabled(QString)));
connect(m_ui->editNewPassword, SIGNAL(textChanged(QString)), SLOT(updatePasswordStrength(QString)));
- connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(togglePasswordShown(bool)));
+ connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), SLOT(setPasswordVisible(bool)));
connect(m_ui->buttonSimpleMode, SIGNAL(clicked()), SLOT(selectSimpleMode()));
connect(m_ui->buttonAdvancedMode, SIGNAL(clicked()), SLOT(selectAdvancedMode()));
connect(m_ui->buttonAddHex, SIGNAL(clicked()), SLOT(excludeHexChars()));
@@ -183,7 +183,7 @@ void PasswordGeneratorWidget::reset()
{
m_ui->editNewPassword->setText("");
setStandaloneMode(false);
- togglePasswordShown(config()->get("security/passwordscleartext").toBool());
+ setPasswordVisible(config()->get("security/passwordscleartext").toBool());
updateGenerator();
}
@@ -192,9 +192,9 @@ void PasswordGeneratorWidget::setStandaloneMode(bool standalone)
m_standalone = standalone;
if (standalone) {
m_ui->buttonApply->setText(tr("Close"));
- togglePasswordShown(true);
+ setPasswordVisible(true);
} else {
- m_ui->buttonApply->setText(tr("Apply"));
+ m_ui->buttonApply->setText(tr("Accept"));
}
}
@@ -205,7 +205,7 @@ QString PasswordGeneratorWidget::getGeneratedPassword()
void PasswordGeneratorWidget::keyPressEvent(QKeyEvent* e)
{
- if (e->key() == Qt::Key_Escape && m_standalone == true) {
+ if (e->key() == Qt::Key_Escape && m_standalone) {
emit dialogTerminated();
} else {
e->ignore();
@@ -309,14 +309,19 @@ void PasswordGeneratorWidget::dicewareSpinBoxChanged()
updateGenerator();
}
-void PasswordGeneratorWidget::togglePasswordShown(bool showing)
+void PasswordGeneratorWidget::setPasswordVisible(bool visible)
{
- m_ui->editNewPassword->setShowPassword(showing);
+ m_ui->editNewPassword->setShowPassword(visible);
bool blockSignals = m_ui->togglePasswordButton->blockSignals(true);
- m_ui->togglePasswordButton->setChecked(showing);
+ m_ui->togglePasswordButton->setChecked(visible);
m_ui->togglePasswordButton->blockSignals(blockSignals);
}
+bool PasswordGeneratorWidget::isPasswordVisible() const
+{
+ return m_ui->togglePasswordButton->isChecked();
+}
+
void PasswordGeneratorWidget::selectSimpleMode()
{
m_ui->advancedBar->hide();
diff --git a/src/gui/PasswordGeneratorWidget.h b/src/gui/PasswordGeneratorWidget.h
index 5b65d7d22..0607f0fe8 100644
--- a/src/gui/PasswordGeneratorWidget.h
+++ b/src/gui/PasswordGeneratorWidget.h
@@ -51,11 +51,13 @@ public:
void reset();
void setStandaloneMode(bool standalone);
QString getGeneratedPassword();
+ bool isPasswordVisible() const;
public slots:
void regeneratePassword();
void applyPassword();
void copyPassword();
+ void setPasswordVisible(bool visible);
signals:
void appliedPassword(const QString& password);
@@ -64,7 +66,6 @@ signals:
private slots:
void updateButtonsEnabled(const QString& password);
void updatePasswordStrength(const QString& password);
- void togglePasswordShown(bool hidden);
void selectSimpleMode();
void selectAdvancedMode();
void excludeHexChars();
diff --git a/src/gui/PasswordGeneratorWidget.ui b/src/gui/PasswordGeneratorWidget.ui
index e196ff89f..ada94d653 100644
--- a/src/gui/PasswordGeneratorWidget.ui
+++ b/src/gui/PasswordGeneratorWidget.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>571</width>
- <height>394</height>
+ <width>716</width>
+ <height>468</height>
</rect>
</property>
<property name="sizePolicy">
@@ -19,12 +19,15 @@
<property name="windowTitle">
<string/>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<layout class="QGridLayout" name="passwordFieldLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<property name="bottomMargin">
<number>0</number>
</property>
@@ -173,8 +176,17 @@ QProgressBar::chunk {
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QTabWidget" name="tabWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
<property name="tabPosition">
<enum>QTabWidget::North</enum>
</property>
@@ -191,12 +203,24 @@ QProgressBar::chunk {
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<layout class="QHBoxLayout" name="optionsLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QGroupBox" name="groupBox">
+ <property name="minimumSize">
+ <size>
+ <width>580</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="title">
<string>Character Types</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QWidget" name="simpleBar" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
@@ -214,6 +238,9 @@ QProgressBar::chunk {
</property>
<item>
<layout class="QHBoxLayout" name="alphabetLayout" stretch="0,0,0,0,0,0,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QToolButton" name="checkBoxUpper">
<property name="minimumSize">
@@ -405,6 +432,9 @@ QProgressBar::chunk {
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QToolButton" name="checkBoxUpperAdv">
<property name="minimumSize">
@@ -459,6 +489,9 @@ QProgressBar::chunk {
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QToolButton" name="checkBoxNumbersAdv">
<property name="minimumSize">
@@ -513,6 +546,9 @@ QProgressBar::chunk {
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QToolButton" name="checkBoxPunctuation">
<property name="minimumSize">
@@ -567,6 +603,9 @@ QProgressBar::chunk {
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QToolButton" name="checkBoxMath">
<property name="minimumSize">
@@ -621,6 +660,9 @@ QProgressBar::chunk {
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_9">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item>
<widget class="QToolButton" name="checkBoxLogograms">
<property name="minimumSize">
@@ -688,6 +730,9 @@ QProgressBar::chunk {
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_10">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<item alignment="Qt::AlignTop">
<widget class="QPushButton" name="buttonSimpleMode">
<property name="minimumSize">
@@ -818,6 +863,9 @@ QProgressBar::chunk {
<property name="spacing">
<number>15</number>
</property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
<property name="topMargin">
<number>6</number>
</property>
@@ -905,7 +953,7 @@ QProgressBar::chunk {
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="labelWordCount">
<property name="text">
- <string>Word Count:</string>
+ <string>Word Co&amp;unt:</string>
</property>
<property name="buddy">
<cstring>spinBoxLength</cstring>
@@ -1010,7 +1058,7 @@ QProgressBar::chunk {
<item>
<widget class="QPushButton" name="buttonGenerate">
<property name="text">
- <string>Generate</string>
+ <string>Regenerate</string>
</property>
</widget>
</item>
@@ -1035,6 +1083,19 @@ QProgressBar::chunk {
</item>
</layout>
</item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<customwidgets>
diff --git a/src/gui/csvImport/CsvImportWizard.cpp b/src/gui/csvImport/CsvImportWizard.cpp
index d1d152154..9b255f941 100644
--- a/src/gui/csvImport/CsvImportWizard.cpp
+++ b/src/gui/csvImport/CsvImportWizard.cpp
@@ -42,22 +42,6 @@ void CsvImportWizard::load(const QString& filename, Database* database)
m_parse->load(filename, database);
}
-void CsvImportWizard::keyFinished(bool accepted, CompositeKey key)
-{
- if (!accepted) {
- emit importFinished(false);
- return;
- }
-
- QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
- bool result = m_db->setKey(key);
- QApplication::restoreOverrideCursor();
-
- if (!result) {
- MessageBox::critical(this, tr("Error"), tr("Unable to calculate master key"));
- emit importFinished(false);
- }
-}
void CsvImportWizard::parseFinished(bool accepted)
{
diff --git a/src/gui/csvImport/CsvImportWizard.h b/src/gui/csvImport/CsvImportWizard.h
index 9c11e9d11..9f7bb720c 100644
--- a/src/gui/csvImport/CsvImportWizard.h
+++ b/src/gui/csvImport/CsvImportWizard.h
@@ -25,7 +25,6 @@
#include <QStackedWidget>
#include "core/Database.h"
-#include "gui/ChangeMasterKeyWidget.h"
#include "gui/DialogyWidget.h"
class CsvImportWidget;
@@ -36,9 +35,8 @@ class CsvImportWizard : public DialogyWidget
public:
explicit CsvImportWizard(QWidget* parent = nullptr);
- ~CsvImportWizard();
+ ~CsvImportWizard() override;
void load(const QString& filename, Database* database);
- void keyFinished(bool accepted, CompositeKey key);
signals:
void importFinished(bool accepted);
@@ -47,7 +45,7 @@ private slots:
void parseFinished(bool accepted);
private:
- Database* m_db;
+ QPointer<Database> m_db;
CsvImportWidget* m_parse;
QGridLayout* m_layout;
};
diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
new file mode 100644
index 000000000..b2624a425
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
+ * Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
+ *
+ * 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 "DatabaseSettingsDialog.h"
+#include "ui_DatabaseSettingsDialog.h"
+#include "DatabaseSettingsWidgetGeneral.h"
+#include "DatabaseSettingsWidgetEncryption.h"
+#include "DatabaseSettingsWidgetMasterKey.h"
+
+#include "core/Config.h"
+#include "core/FilePath.h"
+#include "core/Database.h"
+
+DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent)
+ : DialogyWidget(parent)
+ , m_ui(new Ui::DatabaseSettingsDialog())
+ , m_generalWidget(new DatabaseSettingsWidgetGeneral(this))
+ , m_securityTabWidget(new QTabWidget(this))
+ , m_masterKeyWidget(new DatabaseSettingsWidgetMasterKey(this))
+ , m_encryptionWidget(new DatabaseSettingsWidgetEncryption(this))
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(save()));
+ connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));
+
+ m_ui->categoryList->addCategory(tr("General"), FilePath::instance()->icon("categories", "preferences-other"));
+ m_ui->categoryList->addCategory(tr("Security"), FilePath::instance()->icon("actions", "document-encrypt"));
+ m_ui->stackedWidget->addWidget(m_generalWidget);
+
+ m_ui->stackedWidget->addWidget(m_securityTabWidget);
+ m_securityTabWidget->addTab(m_masterKeyWidget, tr("Master Key"));
+ m_securityTabWidget->addTab(m_encryptionWidget, tr("Encryption Settings"));
+
+ m_ui->stackedWidget->setCurrentIndex(0);
+ m_securityTabWidget->setCurrentIndex(0);
+
+ connect(m_securityTabWidget, SIGNAL(currentChanged(int)), SLOT(pageChanged()));
+ connect(m_ui->categoryList, SIGNAL(categoryChanged(int)), m_ui->stackedWidget, SLOT(setCurrentIndex(int)));
+ connect(m_ui->advancedSettingsToggle, SIGNAL(toggled(bool)), SLOT(toggleAdvancedMode(bool)));
+
+ pageChanged();
+}
+
+DatabaseSettingsDialog::~DatabaseSettingsDialog()
+{
+}
+
+void DatabaseSettingsDialog::load(Database* db)
+{
+ m_ui->categoryList->setCurrentCategory(0);
+ m_generalWidget->load(db);
+ m_masterKeyWidget->load(db);
+ m_encryptionWidget->load(db);
+ m_ui->advancedSettingsToggle->setChecked(config()->get("GUI/AdvancedSettings", false).toBool());
+ m_db = db;
+}
+
+/**
+ * Show page and tab with database master key settings.
+ */
+void DatabaseSettingsDialog::showMasterKeySettings()
+{
+ m_ui->categoryList->setCurrentCategory(1);
+ m_securityTabWidget->setCurrentIndex(0);
+}
+
+void DatabaseSettingsDialog::save()
+{
+ if (!m_generalWidget->save()) {
+ return;
+ }
+
+ if (!m_masterKeyWidget->save()) {
+ return;
+ }
+
+ if (!m_encryptionWidget->save()) {
+ return;
+ }
+
+#ifdef WITH_XC_TOUCHID
+ TouchID::getInstance().reset(m_db ? m_db->filePath() : "");
+#endif
+
+ emit editFinished(true);
+}
+
+void DatabaseSettingsDialog::reject()
+{
+ emit editFinished(false);
+}
+
+void DatabaseSettingsDialog::pageChanged()
+{
+ int pageIndex = m_ui->stackedWidget->currentIndex();
+
+ bool enabled = (pageIndex == Page::General && m_generalWidget->hasAdvancedMode());
+
+ if (Page::Security == pageIndex) {
+ int tabIndex = m_securityTabWidget->currentIndex();
+ enabled = (tabIndex == 0 && m_masterKeyWidget->hasAdvancedMode());
+ enabled |= (tabIndex == 1 && m_encryptionWidget->hasAdvancedMode());
+ }
+
+ m_ui->advancedSettingsToggle->setEnabled(enabled);
+}
+
+void DatabaseSettingsDialog::toggleAdvancedMode(bool advanced)
+{
+ if (m_generalWidget->hasAdvancedMode()) {
+ m_generalWidget->setAdvancedMode(advanced);
+ }
+
+ if (m_masterKeyWidget->hasAdvancedMode()) {
+ m_masterKeyWidget->setAdvancedMode(advanced);
+ }
+
+ if (m_encryptionWidget->hasAdvancedMode()) {
+ m_encryptionWidget->setAdvancedMode(advanced);
+ }
+
+ config()->set("GUI/AdvancedSettings", advanced);
+}
diff --git a/src/gui/DatabaseSettingsWidget.h b/src/gui/dbsettings/DatabaseSettingsDialog.h
index 5abad8e05..50fec32d6 100644
--- a/src/gui/DatabaseSettingsWidget.h
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.h
@@ -18,33 +18,33 @@
#ifndef KEEPASSX_DATABASESETTINGSWIDGET_H
#define KEEPASSX_DATABASESETTINGSWIDGET_H
-#include <QLayout>
-#include <QScopedPointer>
-#include <QSpinBox>
-#include <QWidget>
-
-#include "crypto/kdf/Kdf.h"
#include "gui/DialogyWidget.h"
+#include <QScopedPointer>
+#include <QPointer>
+
class Database;
+class DatabaseSettingsWidgetGeneral;
+class DatabaseSettingsWidgetEncryption;
+class DatabaseSettingsWidgetMasterKey;
+class QTabWidget;
namespace Ui
{
- class DatabaseSettingsWidget;
- class DatabaseSettingsWidgetGeneral;
- class DatabaseSettingsWidgetEncryption;
+ class DatabaseSettingsDialog;
}
-class DatabaseSettingsWidget : public DialogyWidget
+class DatabaseSettingsDialog : public DialogyWidget
{
Q_OBJECT
public:
- explicit DatabaseSettingsWidget(QWidget* parent = nullptr);
- ~DatabaseSettingsWidget();
- Q_DISABLE_COPY(DatabaseSettingsWidget)
+ explicit DatabaseSettingsDialog(QWidget* parent = nullptr);
+ ~DatabaseSettingsDialog() override;
+ Q_DISABLE_COPY(DatabaseSettingsDialog);
void load(Database* db);
+ void showMasterKeySettings();
signals:
void editFinished(bool accepted);
@@ -52,20 +52,22 @@ signals:
private slots:
void save();
void reject();
- void transformRoundsBenchmark();
- void kdfChanged(int index);
- void memoryChanged(int value);
- void parallelismChanged(int value);
+ void pageChanged();
+ void toggleAdvancedMode(bool advanced);
private:
- void truncateHistories();
+ enum Page
+ {
+ General = 0,
+ Security = 1
+ };
- const QScopedPointer<Ui::DatabaseSettingsWidget> m_ui;
- const QScopedPointer<Ui::DatabaseSettingsWidgetGeneral> m_uiGeneral;
- const QScopedPointer<Ui::DatabaseSettingsWidgetEncryption> m_uiEncryption;
- QWidget* m_uiGeneralPage;
- QWidget* m_uiEncryptionPage;
- Database* m_db;
+ QPointer<Database> m_db;
+ const QScopedPointer<Ui::DatabaseSettingsDialog> m_ui;
+ QPointer<DatabaseSettingsWidgetGeneral> m_generalWidget;
+ QPointer<QTabWidget> m_securityTabWidget;
+ QPointer<DatabaseSettingsWidgetMasterKey> m_masterKeyWidget;
+ QPointer<DatabaseSettingsWidgetEncryption> m_encryptionWidget;
};
#endif // KEEPASSX_DATABASESETTINGSWIDGET_H
diff --git a/src/gui/DatabaseSettingsWidget.ui b/src/gui/dbsettings/DatabaseSettingsDialog.ui
index 9b45feaa7..1e10e4f1d 100644
--- a/src/gui/DatabaseSettingsWidget.ui
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.ui
@@ -1,15 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>DatabaseSettingsWidget</class>
- <widget class="QWidget" name="DatabaseSettingsWidget">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>1082</width>
- <height>578</height>
- </rect>
- </property>
+ <class>DatabaseSettingsDialog</class>
+ <widget class="QWidget" name="DatabaseSettingsDialog">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
@@ -28,6 +20,13 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
+ <widget class="QCheckBox" name="advancedSettingsToggle">
+ <property name="text">
+ <string>Advanced Settings</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
@@ -48,4 +47,4 @@
</customwidgets>
<resources/>
<connections/>
-</ui> \ No newline at end of file
+</ui>
diff --git a/src/gui/dbsettings/DatabaseSettingsWidget.cpp b/src/gui/dbsettings/DatabaseSettingsWidget.cpp
new file mode 100644
index 000000000..992101fd4
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidget.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "DatabaseSettingsWidget.h"
+#include "core/Database.h"
+
+#include <QWidget>
+#include <QTimer>
+
+DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* parent)
+ : SettingsWidget(parent)
+{
+}
+
+DatabaseSettingsWidget::~DatabaseSettingsWidget()
+{
+}
+
+/**
+ * Load the database to be configured by this page and initialize the page.
+ * The page will NOT take ownership of the database.
+ *
+ * @param db database object to be configured
+ */
+void DatabaseSettingsWidget::load(Database* db)
+{
+ m_db = db;
+ initialize();
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidget.h b/src/gui/dbsettings/DatabaseSettingsWidget.h
new file mode 100644
index 000000000..61082d5c7
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidget.h
@@ -0,0 +1,51 @@
+/*
+ * 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 KEEPASSXC_DATABASESETTINGSWIDGET_H
+#define KEEPASSXC_DATABASESETTINGSWIDGET_H
+
+#include "gui/settings/SettingsWidget.h"
+
+#include <QPointer>
+
+class Database;
+
+/**
+ * Pure-virtual base class for KeePassXC database settings widgets.
+ */
+class DatabaseSettingsWidget : public SettingsWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DatabaseSettingsWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(DatabaseSettingsWidget);
+ ~DatabaseSettingsWidget() override;
+
+ virtual void load(Database* db);
+
+signals:
+ /**
+ * Can be emitted to indicate size changes and allow parents widgets to adjust properly.
+ */
+ void sizeChanged();
+
+protected:
+ QPointer<Database> m_db;
+};
+
+#endif //KEEPASSXC_DATABASESETTINGSWIDGET_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp
new file mode 100644
index 000000000..2a3cf7cbb
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp
@@ -0,0 +1,410 @@
+/*
+ * 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 "DatabaseSettingsWidgetEncryption.h"
+#include "ui_DatabaseSettingsWidgetEncryption.h"
+#include "core/Database.h"
+#include "core/Metadata.h"
+#include "core/Global.h"
+#include "core/AsyncTask.h"
+#include "gui/MessageBox.h"
+#include "crypto/kdf/Argon2Kdf.h"
+#include "format/KeePass2.h"
+
+#include <QApplication>
+#include <QPushButton>
+
+const char* DatabaseSettingsWidgetEncryption::CD_DECRYPTION_TIME_PREFERENCE_KEY = "KPXC_DECRYPTION_TIME_PREFERENCE";
+
+DatabaseSettingsWidgetEncryption::DatabaseSettingsWidgetEncryption(QWidget* parent)
+ : DatabaseSettingsWidget(parent)
+ , m_ui(new Ui::DatabaseSettingsWidgetEncryption())
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->transformBenchmarkButton, SIGNAL(clicked()), SLOT(benchmarkTransformRounds()));
+ connect(m_ui->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(changeKdf(int)));
+
+ connect(m_ui->memorySpinBox, SIGNAL(valueChanged(int)), this, SLOT(memoryChanged(int)));
+ connect(m_ui->parallelismSpinBox, SIGNAL(valueChanged(int)), this, SLOT(parallelismChanged(int)));
+
+ m_ui->compatibilitySelection->addItem(tr("KDBX 4.0 (recommended)"), KeePass2::KDF_ARGON2.toByteArray());
+ m_ui->compatibilitySelection->addItem(tr("KDBX 3.1"), KeePass2::KDF_AES_KDBX3.toByteArray());
+ m_ui->decryptionTimeSlider->setValue(10);
+ updateDecryptionTime(m_ui->decryptionTimeSlider->value());
+
+ connect(m_ui->activateChangeDecryptionTimeButton, SIGNAL(clicked()), SLOT(activateChangeDecryptionTime()));
+ connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(updateDecryptionTime(int)));
+ connect(m_ui->compatibilitySelection, SIGNAL(currentIndexChanged(int)), SLOT(updateFormatCompatibility(int)));
+
+ // conditions under which a key re-transformation is needed
+ connect(m_ui->decryptionTimeSlider, SIGNAL(valueChanged(int)), SLOT(markDirty()));
+ connect(m_ui->compatibilitySelection, SIGNAL(currentIndexChanged(int)), SLOT(markDirty()));
+ connect(m_ui->activateChangeDecryptionTimeButton, SIGNAL(clicked()), SLOT(markDirty()));
+ connect(m_ui->algorithmComboBox, SIGNAL(currentIndexChanged(int)), SLOT(markDirty()));
+ connect(m_ui->kdfComboBox, SIGNAL(currentIndexChanged(int)), SLOT(markDirty()));
+ connect(m_ui->transformRoundsSpinBox, SIGNAL(valueChanged(int)), SLOT(markDirty()));
+ connect(m_ui->memorySpinBox, SIGNAL(valueChanged(int)), SLOT(markDirty()));
+ connect(m_ui->parallelismSpinBox, SIGNAL(valueChanged(int)), SLOT(markDirty()));
+}
+
+DatabaseSettingsWidgetEncryption::~DatabaseSettingsWidgetEncryption()
+{
+}
+
+void DatabaseSettingsWidgetEncryption::initialize()
+{
+ Q_ASSERT(m_db);
+ if (!m_db) {
+ return;
+ }
+
+ bool isDirty = false;
+
+ if (!m_db->kdf()) {
+ m_db->setKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
+ isDirty = true;
+ }
+ if (!m_db->key()) {
+ m_db->setKey(QSharedPointer<CompositeKey>::create());
+ m_db->setCipher(KeePass2::CIPHER_AES);
+ isDirty = true;
+ }
+
+ // check if the DB's custom data has a decryption time setting stored
+ // and set the slider to it, otherwise just state that the time is unchanged
+ // (we cannot infer the time from the raw KDF settings)
+ auto* cd = m_db->metadata()->customData();
+ if (cd->hasKey(CD_DECRYPTION_TIME_PREFERENCE_KEY)) {
+ int decryptionTime = qMax(100, cd->value(CD_DECRYPTION_TIME_PREFERENCE_KEY).toInt());
+ bool block = m_ui->decryptionTimeSlider->blockSignals(true);
+ m_ui->decryptionTimeSlider->setValue(decryptionTime / 100);
+ updateDecryptionTime(decryptionTime / 100);
+ m_ui->decryptionTimeSlider->blockSignals(block);
+ m_ui->activateChangeDecryptionTimeButton->setVisible(false);
+ } else {
+ m_ui->decryptionTimeSettings->setVisible(isDirty);
+ m_ui->activateChangeDecryptionTimeButton->setVisible(!isDirty);
+ if (!isDirty) {
+ m_ui->decryptionTimeValueLabel->setText(tr("unchanged", "Database decryption time is unchanged"));
+ }
+ }
+
+ updateFormatCompatibility(m_db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 ? KDBX3 : KDBX4, isDirty);
+ setupAlgorithmComboBox();
+ setupKdfComboBox();
+ loadKdfParameters();
+
+ m_isDirty = isDirty;
+}
+
+void DatabaseSettingsWidgetEncryption::uninitialize()
+{
+}
+
+void DatabaseSettingsWidgetEncryption::showEvent(QShowEvent* event)
+{
+ QWidget::showEvent(event);
+ m_ui->decryptionTimeSlider->setFocus();
+}
+
+void DatabaseSettingsWidgetEncryption::setupAlgorithmComboBox()
+{
+ m_ui->algorithmComboBox->clear();
+ for (auto& cipher : asConst(KeePass2::CIPHERS)) {
+ m_ui->algorithmComboBox->addItem(QCoreApplication::translate("KeePass2", cipher.second.toUtf8()),
+ cipher.first.toByteArray());
+ }
+ int cipherIndex = m_ui->algorithmComboBox->findData(m_db->cipher().toByteArray());
+ if (cipherIndex > -1) {
+ m_ui->algorithmComboBox->setCurrentIndex(cipherIndex);
+ }
+}
+
+void DatabaseSettingsWidgetEncryption::setupKdfComboBox()
+{
+ // Setup kdf combo box
+ bool block = m_ui->kdfComboBox->blockSignals(true);
+ m_ui->kdfComboBox->clear();
+ for (auto& kdf : asConst(KeePass2::KDFS)) {
+ m_ui->kdfComboBox->addItem(QCoreApplication::translate("KeePass2", kdf.second.toUtf8()),
+ kdf.first.toByteArray());
+ }
+ m_ui->kdfComboBox->blockSignals(block);
+}
+
+void DatabaseSettingsWidgetEncryption::loadKdfParameters()
+{
+ Q_ASSERT(m_db);
+ if (!m_db) {
+ return;
+ }
+
+ auto kdf = m_db->kdf();
+ Q_ASSERT(kdf);
+ if (!kdf) {
+ return;
+ }
+
+ int kdfIndex = m_ui->kdfComboBox->findData(m_db->kdf()->uuid().toByteArray());
+ if (kdfIndex > -1) {
+ bool block = m_ui->kdfComboBox->blockSignals(true);
+ m_ui->kdfComboBox->setCurrentIndex(kdfIndex);
+ m_ui->kdfComboBox->blockSignals(block);
+ }
+
+ m_ui->transformRoundsSpinBox->setValue(kdf->rounds());
+ if (m_db->kdf()->uuid() == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory()) / (1 << 10));
+ m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
+ }
+
+ updateKdfFields();
+}
+
+void DatabaseSettingsWidgetEncryption::updateKdfFields()
+{
+ QUuid id = m_db->kdf()->uuid();
+
+ bool memoryVisible = (id == KeePass2::KDF_ARGON2);
+ m_ui->memoryUsageLabel->setVisible(memoryVisible);
+ m_ui->memorySpinBox->setVisible(memoryVisible);
+
+ bool parallelismVisible = (id == KeePass2::KDF_ARGON2);
+ m_ui->parallelismLabel->setVisible(parallelismVisible);
+ m_ui->parallelismSpinBox->setVisible(parallelismVisible);
+}
+
+void DatabaseSettingsWidgetEncryption::activateChangeDecryptionTime()
+{
+ m_ui->decryptionTimeSettings->setVisible(true);
+ m_ui->activateChangeDecryptionTimeButton->setVisible(false);
+ updateDecryptionTime(m_ui->decryptionTimeSlider->value());
+}
+
+void DatabaseSettingsWidgetEncryption::markDirty()
+{
+ m_isDirty = true;
+}
+
+bool DatabaseSettingsWidgetEncryption::save()
+{
+ Q_ASSERT(m_db);
+ if (!m_db) {
+ return false;
+ }
+
+ if (m_db->key() && !m_db->key()->keys().isEmpty() && !m_isDirty) {
+ // nothing has changed, don't re-transform
+ return true;
+ }
+
+ auto kdf = m_db->kdf();
+ Q_ASSERT(kdf);
+
+ if (!advancedMode()) {
+ if (kdf && !m_isDirty && !m_ui->decryptionTimeSettings->isVisible()) {
+ return true;
+ }
+
+ int time = m_ui->decryptionTimeSlider->value() * 100;
+ updateFormatCompatibility(m_ui->compatibilitySelection->currentIndex(), false);
+
+ QApplication::setOverrideCursor(Qt::BusyCursor);
+
+ int rounds = AsyncTask::runAndWaitForFuture([&kdf, time]() { return kdf->benchmark(time); });
+ kdf->setRounds(rounds);
+
+ // TODO: we should probably use AsyncTask::runAndWaitForFuture() here,
+ // but not without making Database thread-safe
+ bool ok = m_db->changeKdf(kdf);
+
+ QApplication::restoreOverrideCursor();
+
+ m_db->metadata()->customData()->set(CD_DECRYPTION_TIME_PREFERENCE_KEY, QString("%1").arg(time));
+
+ return ok;
+ }
+
+ // remove a stored decryption time from custom data when advanced settings are used
+ // we don't know it until we actually run the KDF
+ m_db->metadata()->customData()->remove(CD_DECRYPTION_TIME_PREFERENCE_KEY);
+
+ // first perform safety check for KDF rounds
+ if (kdf->uuid() == KeePass2::KDF_ARGON2 && m_ui->transformRoundsSpinBox->value() > 10000) {
+ QMessageBox warning;
+ warning.setIcon(QMessageBox::Warning);
+ warning.setWindowTitle(tr("Number of rounds too high", "Key transformation rounds"));
+ warning.setText(tr("You are using a very high number of key transform rounds with Argon2.\n\n"
+ "If you keep this number, your database may take hours or days (or even longer) to open!"));
+ auto ok = warning.addButton(tr("Understood, keep number"), QMessageBox::ButtonRole::AcceptRole);
+ auto cancel = warning.addButton(tr("Cancel"), QMessageBox::ButtonRole::RejectRole);
+ warning.setDefaultButton(cancel);
+ warning.exec();
+ if (warning.clickedButton() != ok) {
+ return false;
+ }
+ } else if ((kdf->uuid() == KeePass2::KDF_AES_KDBX3 || kdf->uuid() == KeePass2::KDF_AES_KDBX4)
+ && m_ui->transformRoundsSpinBox->value() < 100000) {
+ QMessageBox warning;
+ warning.setIcon(QMessageBox::Warning);
+ warning.setWindowTitle(tr("Number of rounds too low", "Key transformation rounds"));
+ warning.setText(tr("You are using a very low number of key transform rounds with AES-KDF.\n\n"
+ "If you keep this number, your database may be too easy to crack!"));
+ auto ok = warning.addButton(tr("Understood, keep number"), QMessageBox::ButtonRole::AcceptRole);
+ auto cancel = warning.addButton(tr("Cancel"), QMessageBox::ButtonRole::RejectRole);
+ warning.setDefaultButton(cancel);
+ warning.exec();
+ if (warning.clickedButton() != ok) {
+ return false;
+ }
+ }
+
+ m_db->setCipher(QUuid(m_ui->algorithmComboBox->currentData().toByteArray()));
+
+ // Save kdf parameters
+ kdf->setRounds(m_ui->transformRoundsSpinBox->value());
+ if (kdf->uuid() == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ argon2Kdf->setMemory(static_cast<quint64>(m_ui->memorySpinBox->value()) * (1 << 10));
+ argon2Kdf->setParallelism(static_cast<quint32>(m_ui->parallelismSpinBox->value()));
+ }
+
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ // TODO: we should probably use AsyncTask::runAndWaitForFuture() here,
+ // but not without making Database thread-safe
+ bool ok = m_db->changeKdf(kdf);
+ QApplication::restoreOverrideCursor();
+
+ if (!ok) {
+ MessageBox::warning(this,
+ tr("KDF unchanged"),
+ tr("Failed to transform key with new KDF parameters; KDF unchanged."),
+ QMessageBox::Ok);
+ }
+
+ return ok;
+}
+
+void DatabaseSettingsWidgetEncryption::benchmarkTransformRounds(int millisecs)
+{
+ QApplication::setOverrideCursor(Qt::BusyCursor);
+ m_ui->transformBenchmarkButton->setEnabled(false);
+ m_ui->transformRoundsSpinBox->setFocus();
+
+ // Create a new kdf with the current parameters
+ auto kdf = KeePass2::uuidToKdf(QUuid(m_ui->kdfComboBox->currentData().toByteArray()));
+ kdf->setRounds(m_ui->transformRoundsSpinBox->value());
+ if (kdf->uuid() == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ if (!argon2Kdf->setMemory(static_cast<quint64>(m_ui->memorySpinBox->value()) * (1 << 10))) {
+ m_ui->memorySpinBox->setValue(static_cast<int>(argon2Kdf->memory() / (1 << 10)));
+ }
+ if (!argon2Kdf->setParallelism(static_cast<quint32>(m_ui->parallelismSpinBox->value()))) {
+ m_ui->parallelismSpinBox->setValue(argon2Kdf->parallelism());
+ }
+ }
+
+ // Determine the number of rounds required to meet 1 second delay
+ int rounds = AsyncTask::runAndWaitForFuture([&kdf, millisecs]() { return kdf->benchmark(millisecs); });
+
+ m_ui->transformRoundsSpinBox->setValue(rounds);
+ m_ui->transformBenchmarkButton->setEnabled(true);
+ m_ui->decryptionTimeSlider->setValue(millisecs / 100);
+ QApplication::restoreOverrideCursor();
+}
+
+void DatabaseSettingsWidgetEncryption::changeKdf(int index)
+{
+ Q_ASSERT(m_db);
+ if (!m_db) {
+ return;
+ }
+
+ QUuid id(m_ui->kdfComboBox->itemData(index).toByteArray());
+ m_db->setKdf(KeePass2::uuidToKdf(id));
+ updateKdfFields();
+ activateChangeDecryptionTime();
+ benchmarkTransformRounds();
+}
+
+/**
+ * Update memory spin box suffix on value change.
+ */
+void DatabaseSettingsWidgetEncryption::memoryChanged(int value)
+{
+ m_ui->memorySpinBox->setSuffix(tr(" MiB", "Abbreviation for Mebibytes (KDF settings)", value));
+}
+
+/**
+ * Update parallelism spin box suffix on value change.
+ */
+void DatabaseSettingsWidgetEncryption::parallelismChanged(int value)
+{
+ m_ui->parallelismSpinBox->setSuffix(tr(" thread(s)", "Threads for parallel execution (KDF settings)", value));
+}
+
+void DatabaseSettingsWidgetEncryption::setAdvancedMode(bool advanced)
+{
+ DatabaseSettingsWidget::setAdvancedMode(advanced);
+
+ if (advanced) {
+ loadKdfParameters();
+ m_ui->stackedWidget->setCurrentIndex(1);
+ } else {
+ m_ui->compatibilitySelection->setCurrentIndex(m_db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 ? KDBX3 : KDBX4);
+ m_ui->stackedWidget->setCurrentIndex(0);
+ }
+}
+
+void DatabaseSettingsWidgetEncryption::updateDecryptionTime(int value)
+{
+ if (value < 10) {
+ m_ui->decryptionTimeValueLabel->setText(tr("%1 ms", "milliseconds", value * 100).arg(value * 100));
+ } else {
+ m_ui->decryptionTimeValueLabel->setText(tr("%1 s", "seconds", value / 10).arg(value / 10.0, 0, 'f', 1));
+ }
+}
+
+void DatabaseSettingsWidgetEncryption::updateFormatCompatibility(int index, bool retransform)
+{
+ Q_ASSERT(m_db);
+ if (!m_db) {
+ return;
+ }
+
+ if (m_ui->compatibilitySelection->currentIndex() != index) {
+ bool block = m_ui->compatibilitySelection->blockSignals(true);
+ m_ui->compatibilitySelection->setCurrentIndex(index);
+ m_ui->compatibilitySelection->blockSignals(block);
+ }
+
+ if (retransform) {
+ QUuid kdfUuid(m_ui->compatibilitySelection->itemData(index).toByteArray());
+ auto kdf = KeePass2::uuidToKdf(kdfUuid);
+ m_db->setKdf(kdf);
+
+ if (kdf->uuid() == KeePass2::KDF_ARGON2) {
+ auto argon2Kdf = kdf.staticCast<Argon2Kdf>();
+ argon2Kdf->setMemory(128 * 1024);
+ argon2Kdf->setParallelism(static_cast<quint32>(QThread::idealThreadCount()));
+ }
+
+ activateChangeDecryptionTime();
+ }
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h
new file mode 100644
index 000000000..8b1e42e13
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.h
@@ -0,0 +1,75 @@
+/*
+ * 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 KEEPASSXC_DATABASESETTINGSWIDGETENCRYPTION_H
+#define KEEPASSXC_DATABASESETTINGSWIDGETENCRYPTION_H
+
+#include "DatabaseSettingsWidget.h"
+
+#include <QPointer>
+#include <QScopedPointer>
+
+class Database;
+namespace Ui
+{
+class DatabaseSettingsWidgetEncryption;
+}
+
+class DatabaseSettingsWidgetEncryption: public DatabaseSettingsWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DatabaseSettingsWidgetEncryption(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(DatabaseSettingsWidgetEncryption);
+ ~DatabaseSettingsWidgetEncryption() override;
+
+ inline bool hasAdvancedMode() const override { return true; }
+ void setAdvancedMode(bool advanced) override;
+
+public slots:
+ void initialize() override;
+ void uninitialize() override;
+ bool save() override;
+
+protected:
+ void showEvent(QShowEvent* event) override;
+
+private slots:
+ void benchmarkTransformRounds(int millisecs = 1000);
+ void changeKdf(int index);
+ void memoryChanged(int value);
+ void parallelismChanged(int value);
+ void updateDecryptionTime(int value);
+ void updateFormatCompatibility(int index, bool retransform = true);
+ void setupAlgorithmComboBox();
+ void setupKdfComboBox();
+ void loadKdfParameters();
+ void updateKdfFields();
+ void activateChangeDecryptionTime();
+ void markDirty();
+
+private:
+ enum FormatSelection { KDBX4, KDBX3 };
+ static const char* CD_DECRYPTION_TIME_PREFERENCE_KEY;
+
+ bool m_isDirty = false;
+ bool m_formatCompatibilityDirty = false;
+ const QScopedPointer<Ui::DatabaseSettingsWidgetEncryption> m_ui;
+};
+
+#endif //KEEPASSXC_DATABASESETTINGSWIDGETENCRYPTION_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui
new file mode 100644
index 000000000..1de060e9a
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.ui
@@ -0,0 +1,410 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DatabaseSettingsWidgetEncryption</class>
+ <widget class="QWidget" name="DatabaseSettingsWidgetEncryption">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>585</width>
+ <height>339</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="_2">
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="simpleSettings">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="decryptionTimeLabel">
+ <property name="text">
+ <string>Decryption Time:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="decryptionTimeValueLabel">
+ <property name="text">
+ <string>?? s</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="activateChangeDecryptionTimeButton">
+ <property name="text">
+ <string>Change</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="decryptionTimeSettings" native="true">
+ <layout class="QVBoxLayout" name="decryptionTimeSliderLayout">
+ <item>
+ <widget class="QSlider" name="decryptionTimeSlider">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>50</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBelow</enum>
+ </property>
+ <property name="tickInterval">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>100 ms</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>5 s</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Higher values offer more protection, but opening the database will take longer.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="compatibilityLabel">
+ <property name="text">
+ <string>Database format:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="compatibilitySelection">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>This is only important if you need to use your database with other programs.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="advancedSettings">
+ <layout class="QFormLayout" name="formLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="algorithmLabel">
+ <property name="text">
+ <string>Encryption Algorithm:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="algorithmComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>AES: 256 Bit (default)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Twofish: 256 Bit</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="kdfLabel">
+ <property name="text">
+ <string>Key Derivation Function:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="kdfComboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="transformRoundsLabel">
+ <property name="text">
+ <string>Transform rounds:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="40,40,0">
+ <item>
+ <widget class="QSpinBox" name="transformRoundsSpinBox">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>1000000000</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="transformBenchmarkButton">
+ <property name="focusPolicy">
+ <enum>Qt::WheelFocus</enum>
+ </property>
+ <property name="text">
+ <string>Benchmark 1-second delay</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="memoryUsageLabel">
+ <property name="text">
+ <string>Memory Usage:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="memorySpinBox">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>1048576</number>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="parallelismLabel">
+ <property name="text">
+ <string>Parallelism:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QSpinBox" name="parallelismSpinBox">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>128</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
new file mode 100644
index 000000000..187dccc50
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "DatabaseSettingsWidgetGeneral.h"
+#include "ui_DatabaseSettingsWidgetGeneral.h"
+#include "core/Database.h"
+#include "core/Entry.h"
+#include "core/Group.h"
+#include "core/Metadata.h"
+
+DatabaseSettingsWidgetGeneral::DatabaseSettingsWidgetGeneral(QWidget* parent)
+ : DatabaseSettingsWidget(parent), m_ui(new Ui::DatabaseSettingsWidgetGeneral())
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->historyMaxItemsCheckBox, SIGNAL(toggled(bool)), m_ui->historyMaxItemsSpinBox, SLOT(setEnabled(bool)));
+ connect(m_ui->historyMaxSizeCheckBox, SIGNAL(toggled(bool)), m_ui->historyMaxSizeSpinBox, SLOT(setEnabled(bool)));
+}
+
+DatabaseSettingsWidgetGeneral::~DatabaseSettingsWidgetGeneral()
+{
+}
+
+void DatabaseSettingsWidgetGeneral::initialize()
+{
+ Metadata* meta = m_db->metadata();
+
+ m_ui->dbNameEdit->setText(meta->name());
+ m_ui->dbDescriptionEdit->setText(meta->description());
+ m_ui->recycleBinEnabledCheckBox->setChecked(meta->recycleBinEnabled());
+ m_ui->defaultUsernameEdit->setText(meta->defaultUserName());
+ m_ui->compressionCheckbox->setChecked(m_db->compressionAlgo() != Database::CompressionNone);
+
+ if (meta->historyMaxItems() > -1) {
+ m_ui->historyMaxItemsSpinBox->setValue(meta->historyMaxItems());
+ m_ui->historyMaxItemsCheckBox->setChecked(true);
+ } else {
+ m_ui->historyMaxItemsSpinBox->setValue(Metadata::DefaultHistoryMaxItems);
+ m_ui->historyMaxItemsCheckBox->setChecked(false);
+ }
+ int historyMaxSizeMiB = qRound(meta->historyMaxSize()/qreal(1048576));
+ if (historyMaxSizeMiB > 0) {
+ m_ui->historyMaxSizeSpinBox->setValue(historyMaxSizeMiB);
+ m_ui->historyMaxSizeCheckBox->setChecked(true);
+ } else {
+ m_ui->historyMaxSizeSpinBox->setValue(Metadata::DefaultHistoryMaxSize);
+ m_ui->historyMaxSizeCheckBox->setChecked(false);
+ }
+}
+
+void DatabaseSettingsWidgetGeneral::uninitialize()
+{
+}
+
+void DatabaseSettingsWidgetGeneral::showEvent(QShowEvent* event)
+{
+ QWidget::showEvent(event);
+ m_ui->dbNameEdit->setFocus();
+}
+
+bool DatabaseSettingsWidgetGeneral::save()
+{
+ m_db->setCompressionAlgo(m_ui->compressionCheckbox->isChecked() ? Database::CompressionGZip
+ : Database::CompressionNone);
+ Metadata* meta = m_db->metadata();
+
+ meta->setName(m_ui->dbNameEdit->text());
+ meta->setDescription(m_ui->dbDescriptionEdit->text());
+ meta->setDefaultUserName(m_ui->defaultUsernameEdit->text());
+ meta->setRecycleBinEnabled(m_ui->recycleBinEnabledCheckBox->isChecked());
+ meta->setSettingsChanged(QDateTime::currentDateTimeUtc());
+
+ bool truncate = false;
+
+ int historyMaxItems;
+ if (m_ui->historyMaxItemsCheckBox->isChecked()) {
+ historyMaxItems = m_ui->historyMaxItemsSpinBox->value();
+ } else {
+ historyMaxItems = -1;
+ }
+ if (historyMaxItems != meta->historyMaxItems()) {
+ meta->setHistoryMaxItems(historyMaxItems);
+ truncate = true;
+ }
+
+ int historyMaxSize;
+ if (m_ui->historyMaxSizeCheckBox->isChecked()) {
+ historyMaxSize = m_ui->historyMaxSizeSpinBox->value()*1048576;
+ } else {
+ historyMaxSize = -1;
+ }
+ if (historyMaxSize != meta->historyMaxSize()) {
+ meta->setHistoryMaxSize(historyMaxSize);
+ truncate = true;
+ }
+
+ if (truncate) {
+ const QList<Entry*> allEntries = m_db->rootGroup()->entriesRecursive(false);
+ for (Entry* entry : allEntries) {
+ entry->truncateHistory();
+ }
+ }
+
+ return true;
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h
new file mode 100644
index 000000000..009b6b643
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.h
@@ -0,0 +1,54 @@
+/*
+ * 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 KEEPASSXC_DATABASESETTINGSWIDGETGENERAL_H
+#define KEEPASSXC_DATABASESETTINGSWIDGETGENERAL_H
+
+#include "DatabaseSettingsWidget.h"
+
+#include <QPointer>
+#include <QScopedPointer>
+
+class Database;
+namespace Ui
+{
+class DatabaseSettingsWidgetGeneral;
+}
+
+class DatabaseSettingsWidgetGeneral : public DatabaseSettingsWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DatabaseSettingsWidgetGeneral(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(DatabaseSettingsWidgetGeneral);
+ ~DatabaseSettingsWidgetGeneral() override;
+
+ inline bool hasAdvancedMode() const override { return false; }
+
+public slots:
+ void initialize() override;
+ void uninitialize() override;
+ bool save() override;
+
+protected:
+ void showEvent(QShowEvent* event) override;
+
+ const QScopedPointer<Ui::DatabaseSettingsWidgetGeneral> m_ui;
+};
+
+#endif //KEEPASSXC_DATABASESETTINGSWIDGETGENERAL_H
diff --git a/src/gui/DatabaseSettingsWidgetGeneral.ui b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui
index d3baf6132..00c1437bb 100644
--- a/src/gui/DatabaseSettingsWidgetGeneral.ui
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetGeneral.ui
@@ -2,6 +2,26 @@
<ui version="4.0">
<class>DatabaseSettingsWidgetGeneral</class>
<widget class="QWidget" name="DatabaseSettingsWidgetGeneral">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>453</width>
+ <height>374</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>0</height>
+ </size>
+ </property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
new file mode 100644
index 000000000..d64a89787
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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 "DatabaseSettingsWidgetMasterKey.h"
+#include "core/Database.h"
+#include "keys/PasswordKey.h"
+#include "keys/FileKey.h"
+#include "keys/YkChallengeResponseKey.h"
+#include "gui/MessageBox.h"
+#include "gui/masterkey/PasswordEditWidget.h"
+#include "gui/masterkey/KeyFileEditWidget.h"
+#include "gui/masterkey/YubiKeyEditWidget.h"
+
+#include <QVBoxLayout>
+#include <QSpacerItem>
+#include <QPushButton>
+
+DatabaseSettingsWidgetMasterKey::DatabaseSettingsWidgetMasterKey(QWidget* parent)
+ : DatabaseSettingsWidget(parent)
+ , m_additionalKeyOptionsToggle(new QPushButton(tr("Add additional protection..."), this))
+ , m_additionalKeyOptions(new QWidget(this))
+ , m_passwordEditWidget(new PasswordEditWidget(this))
+ , m_keyFileEditWidget(new KeyFileEditWidget(this))
+#ifdef WITH_XC_YUBIKEY
+ , m_yubiKeyEditWidget(new YubiKeyEditWidget(this))
+#endif
+{
+ auto* vbox = new QVBoxLayout(this);
+ vbox->setSizeConstraint(QLayout::SetMinimumSize);
+
+ // primary password option
+ vbox->addWidget(m_passwordEditWidget);
+
+ // additional key options
+ m_additionalKeyOptionsToggle->setObjectName("additionalKeyOptionsToggle");
+ vbox->addWidget(m_additionalKeyOptionsToggle);
+ vbox->addWidget(m_additionalKeyOptions);
+ vbox->setSizeConstraint(QLayout::SetMinimumSize);
+ m_additionalKeyOptions->setLayout(new QVBoxLayout());
+ m_additionalKeyOptions->layout()->setMargin(0);
+ m_additionalKeyOptions->layout()->addWidget(m_keyFileEditWidget);
+#ifdef WITH_XC_YUBIKEY
+ m_additionalKeyOptions->layout()->addWidget(m_yubiKeyEditWidget);
+#endif
+ m_additionalKeyOptions->setVisible(false);
+
+ connect(m_additionalKeyOptionsToggle, SIGNAL(clicked()), SLOT(showAdditionalKeyOptions()));
+
+ vbox->addStretch();
+ setLayout(vbox);
+}
+
+DatabaseSettingsWidgetMasterKey::~DatabaseSettingsWidgetMasterKey()
+{
+}
+
+void DatabaseSettingsWidgetMasterKey::load(Database* db)
+{
+ DatabaseSettingsWidget::load(db);
+
+ if (!m_db->key() || m_db->key()->keys().isEmpty()) {
+ // database has no key, we are about to add a new one
+ m_passwordEditWidget->changeVisiblePage(KeyComponentWidget::Page::Edit);
+ m_passwordEditWidget->setPasswordVisible(true);
+ m_isDirty = true;
+ return;
+ }
+
+ bool isDirty = false;
+ bool hasAdditionalKeys = false;
+ for (const auto& key: m_db->key()->keys()) {
+ if (key->uuid() == PasswordKey::UUID) {
+ m_passwordEditWidget->setComponentAdded(true);
+ } else if (key->uuid() == FileKey::UUID) {
+ m_keyFileEditWidget->setComponentAdded(true);
+ hasAdditionalKeys = true;
+ }
+ }
+
+#ifdef WITH_XC_YUBIKEY
+ for (const auto& key: m_db->key()->challengeResponseKeys()) {
+ if (key->uuid() == YkChallengeResponseKey::UUID) {
+ m_yubiKeyEditWidget->setComponentAdded(true);
+ hasAdditionalKeys = true;
+ }
+ }
+#endif
+
+ setAdditionalKeyOptionsVisible(hasAdditionalKeys);
+
+ m_isDirty = isDirty;
+}
+
+void DatabaseSettingsWidgetMasterKey::initialize()
+{
+ bool blocked = blockSignals(true);
+ m_passwordEditWidget->setComponentAdded(false);
+ m_keyFileEditWidget->setComponentAdded(false);
+#ifdef WITH_XC_YUBIKEY
+ m_yubiKeyEditWidget->setComponentAdded(false);
+#endif
+ blockSignals(blocked);
+}
+
+void DatabaseSettingsWidgetMasterKey::uninitialize()
+{
+}
+
+bool DatabaseSettingsWidgetMasterKey::save()
+{
+ m_isDirty |= (m_passwordEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
+ m_isDirty |= (m_keyFileEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
+#ifdef WITH_XC_YUBIKEY
+ m_isDirty |= (m_yubiKeyEditWidget->visiblePage() == KeyComponentWidget::Page::Edit);
+#endif
+
+ if (m_db->key() && ! m_db->key()->keys().isEmpty() && !m_isDirty) {
+ // key unchanged
+ return true;
+ }
+
+ auto newKey = QSharedPointer<CompositeKey>::create();
+
+ QSharedPointer<Key> passwordKey;
+ QSharedPointer<Key> fileKey;
+ QSharedPointer<ChallengeResponseKey> ykCrKey;
+
+ for (const auto& key: m_db->key()->keys()) {
+ if (key->uuid() == PasswordKey::UUID) {
+ passwordKey = key;
+ } else if (key->uuid() == FileKey::UUID) {
+ fileKey = key;
+ }
+ }
+
+ for (const auto& key: m_db->key()->challengeResponseKeys()) {
+ if (key->uuid() == YkChallengeResponseKey::UUID) {
+ ykCrKey = key;
+ }
+ }
+
+ if (!addToCompositeKey(m_passwordEditWidget, newKey, passwordKey)) {
+ return false;
+ }
+
+ if (!addToCompositeKey(m_keyFileEditWidget, newKey, fileKey)) {
+ return false;
+ }
+
+#ifdef WITH_XC_YUBIKEY
+ if (!addToCompositeKey(m_yubiKeyEditWidget, newKey, ykCrKey)) {
+ return false;
+ }
+#endif
+
+ if (newKey->keys().isEmpty() && newKey->challengeResponseKeys().isEmpty()) {
+ MessageBox::critical(this, tr("No encryption key added"),
+ tr("You must add at least one encryption key to secure your database!"),
+ QMessageBox::Ok, QMessageBox::Ok);
+ return false;
+ }
+
+ if (m_passwordEditWidget->visiblePage() == KeyComponentWidget::AddNew) {
+ auto answer = MessageBox::warning(this, tr("No password set"),
+ tr("WARNING! You have not set a password. Using a database without "
+ "a password is strongly discouraged!\n\n"
+ "Are you sure you want to continue without a password?"),
+ QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
+ if (answer != QMessageBox::Yes) {
+ return false;
+ }
+ }
+
+ m_db->setKey(newKey);
+
+ emit editFinished(true);
+ return true;
+}
+
+void DatabaseSettingsWidgetMasterKey::discard()
+{
+ emit editFinished(false);
+}
+
+void DatabaseSettingsWidgetMasterKey::showAdditionalKeyOptions()
+{
+ setAdditionalKeyOptionsVisible(true);
+}
+
+void DatabaseSettingsWidgetMasterKey::setAdditionalKeyOptionsVisible(bool show)
+{
+ m_additionalKeyOptionsToggle->setVisible(!show);
+ m_additionalKeyOptions->setVisible(show);
+ m_additionalKeyOptions->layout()->setSizeConstraint(QLayout::SetMinimumSize);
+ emit sizeChanged();
+}
+
+bool DatabaseSettingsWidgetMasterKey::addToCompositeKey(KeyComponentWidget* widget,
+ QSharedPointer<CompositeKey>& newKey, QSharedPointer<Key>& oldKey)
+{
+ if (widget->visiblePage() == KeyComponentWidget::Edit) {
+ QString error = tr("Unknown error");
+ if (!widget->validate(error) || !widget->addToCompositeKey(newKey)) {
+ QMessageBox::critical(this, tr("Failed to change master key"), error, QMessageBox::Ok);
+ return false;
+ }
+ } else if (widget->visiblePage() == KeyComponentWidget::LeaveOrRemove) {
+ Q_ASSERT(oldKey);
+ newKey->addKey(oldKey);
+ }
+ return true;
+}
+
+bool DatabaseSettingsWidgetMasterKey::addToCompositeKey(KeyComponentWidget* widget,
+ QSharedPointer<CompositeKey>& newKey, QSharedPointer<ChallengeResponseKey>& oldKey)
+{
+ if (widget->visiblePage() == KeyComponentWidget::Edit) {
+ QString error = tr("Unknown error");
+ if (!widget->validate(error) || !widget->addToCompositeKey(newKey)) {
+ QMessageBox::critical(this, tr("Failed to change master key"), error, QMessageBox::Ok);
+ return false;
+ }
+ } else if (widget->visiblePage() == KeyComponentWidget::LeaveOrRemove) {
+ Q_ASSERT(oldKey);
+ newKey->addChallengeResponseKey(oldKey);
+ }
+ return true;
+}
+
+void DatabaseSettingsWidgetMasterKey::markDirty()
+{
+ m_isDirty = true;
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h
new file mode 100644
index 000000000..89e077895
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMasterKey.h
@@ -0,0 +1,78 @@
+/*
+ * 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 KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H
+#define KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H
+
+#include "config-keepassx.h"
+#include "DatabaseSettingsWidget.h"
+#include <QPointer>
+
+class Database;
+class Key;
+class CompositeKey;
+class ChallengeResponseKey;
+class KeyComponentWidget;
+class PasswordEditWidget;
+class KeyFileEditWidget;
+class YubiKeyEditWidget;
+class QPushButton;
+
+class DatabaseSettingsWidgetMasterKey: public DatabaseSettingsWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DatabaseSettingsWidgetMasterKey(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(DatabaseSettingsWidgetMasterKey);
+ ~DatabaseSettingsWidgetMasterKey() override;
+
+ void load(Database* db) override;
+
+ inline bool hasAdvancedMode() const override { return false; }
+
+public slots:
+ void initialize() override;
+ void uninitialize() override;
+ bool save() override;
+ void discard() override;
+
+private slots:
+ void showAdditionalKeyOptions();
+ void markDirty();
+
+private:
+ void setAdditionalKeyOptionsVisible(bool show);
+
+ bool addToCompositeKey(KeyComponentWidget* widget,
+ QSharedPointer<CompositeKey>& newKey,
+ QSharedPointer<Key>& oldKey);
+ bool addToCompositeKey(KeyComponentWidget* widget,
+ QSharedPointer<CompositeKey>& newKey,
+ QSharedPointer<ChallengeResponseKey>& oldKey);
+
+ bool m_isDirty = false;
+ const QPointer<QPushButton> m_additionalKeyOptionsToggle;
+ const QPointer<QWidget> m_additionalKeyOptions;
+ const QPointer<PasswordEditWidget> m_passwordEditWidget;
+ const QPointer<KeyFileEditWidget> m_keyFileEditWidget;
+#ifdef WITH_XC_YUBIKEY
+ const QPointer<YubiKeyEditWidget> m_yubiKeyEditWidget;
+#endif
+};
+
+#endif //KEEPASSXC_DATABASESETTINGSPAGECHANGEMASTERKEY_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp
new file mode 100644
index 000000000..6805d7b65
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "DatabaseSettingsWidgetMetaDataSimple.h"
+#include "ui_DatabaseSettingsWidgetMetaDataSimple.h"
+#include "core/Database.h"
+#include "core/Metadata.h"
+
+DatabaseSettingWidgetMetaData::DatabaseSettingWidgetMetaData(QWidget* parent)
+ : DatabaseSettingsWidget(parent)
+ , m_ui(new Ui::DatabaseSettingsWidgetMetaDataSimple())
+{
+ m_ui->setupUi(this);
+}
+
+DatabaseSettingWidgetMetaData::~DatabaseSettingWidgetMetaData()
+{
+}
+
+void DatabaseSettingWidgetMetaData::initialize()
+{
+ Metadata* meta = m_db->metadata();
+ auto name = meta->name();
+ m_ui->databaseName->setText(name.isEmpty() ? tr("Passwords") : name);
+ m_ui->databaseDescription->setText(meta->description());
+
+ m_ui->databaseName->setFocus();
+ m_ui->databaseName->selectAll();
+}
+
+void DatabaseSettingWidgetMetaData::showEvent(QShowEvent* event)
+{
+ QWidget::showEvent(event);
+ m_ui->databaseName->setFocus();
+}
+
+void DatabaseSettingWidgetMetaData::uninitialize()
+{
+}
+
+bool DatabaseSettingWidgetMetaData::save()
+{
+ Metadata* meta = m_db->metadata();
+ meta->setName(m_ui->databaseName->text());
+ meta->setDescription(m_ui->databaseDescription->text());
+
+ return true;
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h
new file mode 100644
index 000000000..bc32c1ee0
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.h
@@ -0,0 +1,55 @@
+/*
+ * 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 KEEPASSXC_DATABASESETTINGSWIDGEMETADATA_H
+#define KEEPASSXC_DATABASESETTINGSWIDGEMETADATA_H
+
+#include "DatabaseSettingsWidget.h"
+
+#include <QPointer>
+#include <QScopedPointer>
+
+class Database;
+namespace Ui
+{
+class DatabaseSettingsWidgetMetaDataSimple;
+}
+
+class DatabaseSettingWidgetMetaData : public DatabaseSettingsWidget
+{
+Q_OBJECT
+
+public:
+ explicit DatabaseSettingWidgetMetaData(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(DatabaseSettingWidgetMetaData);
+ ~DatabaseSettingWidgetMetaData() override;
+
+ inline bool hasAdvancedMode() const override { return false; }
+
+public slots:
+ void initialize() override;
+ void uninitialize() override;
+ bool save() override;
+
+protected:
+ void showEvent(QShowEvent* event) override;
+
+private:
+ const QScopedPointer<Ui::DatabaseSettingsWidgetMetaDataSimple> m_ui;
+};
+
+#endif //KEEPASSXC_DATABASESETTINGSWIDGEMETADATA_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui
new file mode 100644
index 000000000..b4ac30b9b
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.ui
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DatabaseSettingsWidgetMetaDataSimple</class>
+ <widget class="QWidget" name="DatabaseSettingsWidgetMetaDataSimple">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>86</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QFormLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="databaseNameLabel">
+ <property name="text">
+ <string>Database Name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="databaseName"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="databaseDescriptionLabel">
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="databaseDescription"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/masterkey/KeyComponentWidget.cpp b/src/gui/masterkey/KeyComponentWidget.cpp
new file mode 100644
index 000000000..45f133b44
--- /dev/null
+++ b/src/gui/masterkey/KeyComponentWidget.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 "KeyComponentWidget.h"
+#include "ui_KeyComponentWidget.h"
+#include <QStackedWidget>
+#include <QTimer>
+
+KeyComponentWidget::KeyComponentWidget(QWidget* parent)
+ : KeyComponentWidget({}, parent)
+{
+}
+
+KeyComponentWidget::KeyComponentWidget(const QString& name, QWidget* parent)
+ : QWidget(parent)
+ , m_ui(new Ui::KeyComponentWidget())
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->addButton, SIGNAL(clicked(bool)), SIGNAL(componentAddRequested()));
+ connect(m_ui->changeButton, SIGNAL(clicked(bool)), SIGNAL(componentEditRequested()));
+ connect(m_ui->removeButton, SIGNAL(clicked(bool)), SIGNAL(componentRemovalRequested()));
+ connect(m_ui->cancelButton, SIGNAL(clicked(bool)), SLOT(cancelEdit()));
+
+ connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(reset()));
+
+ connect(this, SIGNAL(nameChanged(const QString&)), SLOT(updateComponentName(const QString&)));
+ connect(this, SIGNAL(descriptionChanged(const QString&)), SLOT(updateComponentDescription(const QString&)));
+ connect(this, SIGNAL(componentAddRequested()), SLOT(doAdd()));
+ connect(this, SIGNAL(componentEditRequested()), SLOT(doEdit()));
+ connect(this, SIGNAL(componentRemovalRequested()), SLOT(doRemove()));
+ connect(this, SIGNAL(componentAddChanged(bool)), SLOT(updateAddStatus(bool)));
+
+ blockSignals(true);
+ setComponentName(name);
+ m_ui->stackedWidget->setCurrentIndex(Page::AddNew);
+ updateSize();
+ blockSignals(false);
+}
+
+KeyComponentWidget::~KeyComponentWidget()
+{
+}
+
+/**
+ * @param name display name for the key component
+ */
+void KeyComponentWidget::setComponentName(const QString& name)
+{
+ if (name == m_componentName) {
+ return;
+ }
+
+ m_componentName = name;
+ emit nameChanged(name);
+}
+
+/**
+ * @return The key component's display name
+ */
+QString KeyComponentWidget::componentName() const
+{
+ return m_componentName;
+}
+
+void KeyComponentWidget::setComponentDescription(const QString& description)
+{
+ if (description == m_componentDescription) {
+ return;
+ }
+
+ m_componentDescription = description;
+ emit descriptionChanged(description);
+}
+
+QString KeyComponentWidget::componentDescription() const
+{
+ return m_componentDescription;
+}
+
+void KeyComponentWidget::setComponentAdded(bool added)
+{
+ if (m_isComponentAdded == added) {
+ return;
+ }
+
+ m_isComponentAdded = added;
+ emit componentAddChanged(added);
+}
+
+bool KeyComponentWidget::componentAdded() const
+{
+ return m_isComponentAdded;
+}
+
+void KeyComponentWidget::changeVisiblePage(KeyComponentWidget::Page page)
+{
+ m_previousPage = static_cast<Page>(m_ui->stackedWidget->currentIndex());
+ m_ui->stackedWidget->setCurrentIndex(page);
+}
+
+KeyComponentWidget::Page KeyComponentWidget::visiblePage() const
+{
+ return static_cast<Page>(m_ui->stackedWidget->currentIndex());
+}
+
+void KeyComponentWidget::updateComponentName(const QString& name)
+{
+ m_ui->groupBox->setTitle(name);
+ m_ui->addButton->setText(tr("Add %1", "Add a key component").arg(name));
+ m_ui->changeButton->setText(tr("Change %1", "Change a key component").arg(name));
+ m_ui->removeButton->setText(tr("Remove %1", "Remove a key component").arg(name));
+ m_ui->changeOrRemoveLabel->setText(tr("%1 set, click to change or remove", "Change or remove a key component").arg(name));
+}
+
+void KeyComponentWidget::updateComponentDescription(const QString& description)
+{
+ m_ui->componentDescription->setText(description);
+}
+
+void KeyComponentWidget::updateAddStatus(bool added)
+{
+ if (added) {
+ m_ui->stackedWidget->setCurrentIndex(Page::LeaveOrRemove);
+ } else {
+ m_ui->stackedWidget->setCurrentIndex(Page::AddNew);
+ }
+}
+
+void KeyComponentWidget::doAdd()
+{
+ changeVisiblePage(Page::Edit);
+}
+
+void KeyComponentWidget::doEdit()
+{
+ changeVisiblePage(Page::Edit);
+}
+
+void KeyComponentWidget::doRemove()
+{
+ changeVisiblePage(Page::AddNew);
+}
+
+void KeyComponentWidget::cancelEdit()
+{
+ m_ui->stackedWidget->setCurrentIndex(m_previousPage);
+ emit editCanceled();
+}
+
+void KeyComponentWidget::reset()
+{
+ if (static_cast<Page>(m_ui->stackedWidget->currentIndex()) == Page::Edit) {
+ if (!m_ui->componentWidgetLayout->isEmpty()) {
+ auto* item = m_ui->componentWidgetLayout->takeAt(0);
+ if (item->widget()) {
+ delete item->widget();
+ }
+ delete item;
+ }
+
+ QWidget* widget = componentEditWidget();
+ m_ui->componentWidgetLayout->addWidget(widget);
+
+ initComponentEditWidget(widget);
+ }
+
+ QTimer::singleShot(0, this, SLOT(updateSize()));
+}
+
+void KeyComponentWidget::updateSize()
+{
+ for (int i = 0; i < m_ui->stackedWidget->count(); ++i) {
+ if (m_ui->stackedWidget->currentIndex() == i) {
+ m_ui->stackedWidget->widget(i)->setSizePolicy(
+ m_ui->stackedWidget->widget(i)->sizePolicy().horizontalPolicy(), QSizePolicy::Preferred);
+ } else {
+ m_ui->stackedWidget->widget(i)->setSizePolicy(
+ m_ui->stackedWidget->widget(i)->sizePolicy().horizontalPolicy(), QSizePolicy::Ignored);
+ }
+ }
+}
diff --git a/src/gui/masterkey/KeyComponentWidget.h b/src/gui/masterkey/KeyComponentWidget.h
new file mode 100644
index 000000000..5d5188d6e
--- /dev/null
+++ b/src/gui/masterkey/KeyComponentWidget.h
@@ -0,0 +1,130 @@
+/*
+ * 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 KEEPASSXC_KEYCOMPONENTWIDGET_H
+#define KEEPASSXC_KEYCOMPONENTWIDGET_H
+
+#include <QWidget>
+#include <QScopedPointer>
+
+namespace Ui
+{
+class KeyComponentWidget;
+}
+class CompositeKey;
+class QStackedWidget;
+
+class KeyComponentWidget : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QString componentName READ m_componentName READ componentName
+ WRITE setComponentName NOTIFY nameChanged)
+ Q_PROPERTY(QString componentDescription READ m_componentDescription READ componentDescription
+ WRITE setComponentDescription NOTIFY descriptionChanged)
+ Q_PROPERTY(bool componentAdded READ m_isComponentAdded READ componentAdded
+ WRITE setComponentAdded NOTIFY componentAddChanged)
+
+public:
+ enum Page
+ {
+ AddNew = 0,
+ Edit = 1,
+ LeaveOrRemove = 2
+ };
+
+ explicit KeyComponentWidget(QWidget* parent = nullptr);
+ explicit KeyComponentWidget(const QString& name, QWidget* parent = nullptr);
+ Q_DISABLE_COPY(KeyComponentWidget);
+ ~KeyComponentWidget() override;
+
+ /**
+ * Add the new key component to the given \link CompositeKey.
+ * A caller should always check first with \link validate() if
+ * the new key data is actually valid before adding it to a CompositeKey.
+ *
+ * @param key CompositeKey to add new key to
+ * @return true if added successfully
+ */
+ virtual bool addToCompositeKey(QSharedPointer<CompositeKey> key) = 0;
+
+ /**
+ * Validate key component data to check if key component
+ * may be added to a CompositeKey.
+ *
+ * @param errorMessage error message in case data is not valid
+ * @return true if data is valid
+ */
+ virtual bool validate(QString& errorMessage) const = 0;
+
+ void setComponentName(const QString& name);
+ QString componentName() const;
+ void setComponentDescription(const QString& name);
+ QString componentDescription() const;
+ void setComponentAdded(bool added);
+ bool componentAdded() const;
+ void changeVisiblePage(Page page);
+ Page visiblePage() const;
+
+protected:
+ /**
+ * Construct and return an instance of the key component edit widget
+ * which is to be inserted into the component management UI.
+ * Since previous widgets will be destroyed, every successive call to
+ * this function must return a new widget.
+ *
+ * @return edit widget instance
+ */
+ virtual QWidget* componentEditWidget() = 0;
+
+ /**
+ * Initialize the key component widget created by \link componentEditWidget().
+ * This method is called every time the component edit widget is shown.
+ *
+ * @param widget pointer to the widget
+ */
+ virtual void initComponentEditWidget(QWidget* widget) = 0;
+
+signals:
+ void nameChanged(const QString& newName);
+ void descriptionChanged(const QString& newDescription);
+ void componentAddChanged(bool added);
+ void componentAddRequested();
+ void componentEditRequested();
+ void editCanceled();
+ void componentRemovalRequested();
+
+private slots:
+ void updateComponentName(const QString& name);
+ void updateComponentDescription(const QString& decription);
+ void updateAddStatus(bool added);
+ void doAdd();
+ void doEdit();
+ void doRemove();
+ void cancelEdit();
+ void reset();
+ void updateSize();
+
+private:
+ bool m_isComponentAdded = false;
+ Page m_previousPage = Page::AddNew;
+ QString m_componentName;
+ QString m_componentDescription;
+
+ const QScopedPointer<Ui::KeyComponentWidget> m_ui;
+};
+
+#endif //KEEPASSXC_KEYCOMPONENTWIDGET_H
diff --git a/src/gui/masterkey/KeyComponentWidget.ui b/src/gui/masterkey/KeyComponentWidget.ui
new file mode 100644
index 000000000..5036655e0
--- /dev/null
+++ b/src/gui/masterkey/KeyComponentWidget.ui
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>KeyComponentWidget</class>
+ <widget class="QWidget" name="KeyComponentWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>354</width>
+ <height>106</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">QGroupBox { font-weight: bold; }</string>
+ </property>
+ <property name="title">
+ <string>Key Component</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget">
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <widget class="QWidget" name="addPage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="componentDescription">
+ <property name="text">
+ <string>Key Component Description</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addButton">
+ <property name="text">
+ <string notr="true">Add Key Component</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="editPage">
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="componentWidgetContainer" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="componentWidgetLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="changeOrRemovePage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="changeOrRemoveLabel">
+ <property name="text">
+ <string>Key Component set, click to change or remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="changeButton">
+ <property name="text">
+ <string notr="true">Change Key Component</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="removeButton">
+ <property name="text">
+ <string notr="true">Remove Key Component</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/masterkey/KeyFileEditWidget.cpp b/src/gui/masterkey/KeyFileEditWidget.cpp
new file mode 100644
index 000000000..14ac4879c
--- /dev/null
+++ b/src/gui/masterkey/KeyFileEditWidget.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "KeyFileEditWidget.h"
+#include "ui_KeyFileEditWidget.h"
+#include "gui/MainWindow.h"
+#include "gui/FileDialog.h"
+#include "gui/MessageBox.h"
+#include "keys/CompositeKey.h"
+#include "keys/FileKey.h"
+
+KeyFileEditWidget::KeyFileEditWidget(QWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::KeyFileEditWidget())
+{
+ setComponentName(tr("Key File"));
+ setComponentDescription(tr("<p>You can add a key file containing random bytes for additional security.</p>"
+ "<p>You must keep it secret and never lose it or you will be locked out!</p>"));
+}
+
+KeyFileEditWidget::~KeyFileEditWidget()
+{
+}
+
+bool KeyFileEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
+{
+ auto fileKey = QSharedPointer<FileKey>::create();
+ QString fileKeyName = m_compUi->keyFileCombo->currentText();
+ if (!fileKey->load(fileKeyName, nullptr)) {
+ return false;
+ }
+
+ if (fileKey->type() != FileKey::Hashed) {
+ QMessageBox::warning(KEEPASSXC_MAIN_WINDOW,
+ tr("Legacy key file format"),
+ tr("You are using a legacy key file format which may become\n"
+ "unsupported in the future.\n\n"
+ "Please go to the master key settings and generate a new key file."),
+ QMessageBox::Ok);
+ }
+
+ key->addKey(fileKey);
+ return true;
+}
+
+bool KeyFileEditWidget::validate(QString& errorMessage) const
+{
+ FileKey fileKey;
+ QString fileKeyError;
+ QString fileKeyName = m_compUi->keyFileCombo->currentText();
+ if (!fileKey.load(fileKeyName, &fileKeyError)) {
+ errorMessage = tr("Error loading the key file '%1'\nMessage: %2").arg(fileKeyName, fileKeyError);
+ return false;
+ }
+ return true;
+}
+
+QWidget* KeyFileEditWidget::componentEditWidget()
+{
+ m_compEditWidget = new QWidget();
+ m_compUi->setupUi(m_compEditWidget);
+
+ connect(m_compUi->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile()));
+ connect(m_compUi->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile()));
+
+ return m_compEditWidget;
+}
+
+void KeyFileEditWidget::initComponentEditWidget(QWidget* widget)
+{
+ Q_UNUSED(widget);
+ Q_ASSERT(m_compEditWidget);
+ m_compUi->keyFileCombo->setFocus();
+}
+
+void KeyFileEditWidget::createKeyFile()
+{
+ Q_ASSERT(m_compEditWidget);
+ if (!m_compEditWidget) {
+ return;
+ }
+ QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
+ QString fileName = fileDialog()->getSaveFileName(this, tr("Create Key File..."), QString(), filters);
+
+ if (!fileName.isEmpty()) {
+ QString errorMsg;
+ bool created = FileKey::create(fileName, &errorMsg);
+ if (!created) {
+ MessageBox::critical(KEEPASSXC_MAIN_WINDOW, tr("Error creating key file"),
+ tr("Unable to create key file: %1").arg(errorMsg), QMessageBox::Button::Ok);
+ } else {
+ m_compUi->keyFileCombo->setEditText(fileName);
+ }
+ }
+}
+
+void KeyFileEditWidget::browseKeyFile()
+{
+ Q_ASSERT(m_compEditWidget);
+ if (!m_compEditWidget) {
+ return;
+ }
+ QString filters = QString("%1 (*.key);;%2 (*)").arg(tr("Key files"), tr("All files"));
+ QString fileName = fileDialog()->getOpenFileName(this, tr("Select a key file"), QString(), filters);
+
+ if (!fileName.isEmpty()) {
+ m_compUi->keyFileCombo->setEditText(fileName);
+ }
+}
diff --git a/src/gui/masterkey/KeyFileEditWidget.h b/src/gui/masterkey/KeyFileEditWidget.h
new file mode 100644
index 000000000..aa6c71a58
--- /dev/null
+++ b/src/gui/masterkey/KeyFileEditWidget.h
@@ -0,0 +1,54 @@
+/*
+ * 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 KEEPASSXC_KEYFILEEDITWIDGET_H
+#define KEEPASSXC_KEYFILEEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include <QPointer>
+
+namespace Ui
+{
+class KeyFileEditWidget;
+}
+
+class KeyFileEditWidget : public KeyComponentWidget
+{
+Q_OBJECT
+
+public:
+ explicit KeyFileEditWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(KeyFileEditWidget);
+ ~KeyFileEditWidget() override;
+
+ bool addToCompositeKey(QSharedPointer<CompositeKey> key) override;
+ bool validate(QString& errorMessage) const override;
+
+protected:
+ QWidget* componentEditWidget() override;
+ void initComponentEditWidget(QWidget* widget) override;
+
+private slots:
+ void createKeyFile();
+ void browseKeyFile();
+
+private:
+ const QScopedPointer<Ui::KeyFileEditWidget> m_compUi;
+ QPointer<QWidget> m_compEditWidget;
+};
+
+#endif //KEEPASSXC_KEYFILEEDITWIDGET_H
diff --git a/src/gui/masterkey/KeyFileEditWidget.ui b/src/gui/masterkey/KeyFileEditWidget.ui
new file mode 100644
index 000000000..a267935b5
--- /dev/null
+++ b/src/gui/masterkey/KeyFileEditWidget.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>KeyFileEditWidget</class>
+ <widget class="QWidget" name="KeyFileEditWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>364</width>
+ <height>76</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="keyFileCombo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="browseKeyFileButton">
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="createKeyFileButton">
+ <property name="text">
+ <string>Generate</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/masterkey/PasswordEditWidget.cpp b/src/gui/masterkey/PasswordEditWidget.cpp
new file mode 100644
index 000000000..dc43f9159
--- /dev/null
+++ b/src/gui/masterkey/PasswordEditWidget.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "PasswordEditWidget.h"
+#include "ui_PasswordEditWidget.h"
+#include "core/FilePath.h"
+#include "gui/PasswordGeneratorWidget.h"
+#include "keys/PasswordKey.h"
+#include "keys/CompositeKey.h"
+
+#include <QDialog>
+
+PasswordEditWidget::PasswordEditWidget(QWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::PasswordEditWidget())
+{
+ setComponentName(tr("Password"));
+ setComponentDescription(tr("<p>A password is the primary method for securing your database.</p>"
+ "<p>Good passwords are long and unique. KeePassXC can generate one for you.</p>"));
+}
+
+PasswordEditWidget::~PasswordEditWidget()
+{
+}
+
+bool PasswordEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
+{
+ key->addKey(QSharedPointer<PasswordKey>::create(m_compUi->enterPasswordEdit->text()));
+ return true;
+}
+
+/**
+ * @param visible changed password visibility state
+ */
+void PasswordEditWidget::setPasswordVisible(bool visible)
+{
+ m_compUi->togglePasswordButton->setChecked(visible);
+}
+
+/**
+ * @return password visibility state
+ */
+bool PasswordEditWidget::isPasswordVisible() const
+{
+ return m_compUi->togglePasswordButton->isChecked();
+}
+
+QWidget* PasswordEditWidget::componentEditWidget()
+{
+ m_compEditWidget = new QWidget();
+ m_compUi->setupUi(m_compEditWidget);
+ m_compUi->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show"));
+ m_compUi->passwordGeneratorButton->setIcon(filePath()->icon("actions", "password-generator", false));
+ m_compUi->repeatPasswordEdit->enableVerifyMode(m_compUi->enterPasswordEdit);
+
+ connect(m_compUi->togglePasswordButton, SIGNAL(toggled(bool)), m_compUi->enterPasswordEdit, SLOT(setShowPassword(bool)));
+ connect(m_compUi->passwordGeneratorButton, SIGNAL(clicked(bool)), SLOT(showPasswordGenerator()));
+
+ return m_compEditWidget;
+}
+
+void PasswordEditWidget::initComponentEditWidget(QWidget* widget)
+{
+ Q_UNUSED(widget);
+ Q_ASSERT(m_compEditWidget);
+ m_compUi->enterPasswordEdit->setFocus();
+}
+
+bool PasswordEditWidget::validate(QString& errorMessage) const
+{
+ if (m_compUi->enterPasswordEdit->text().isEmpty()) {
+ errorMessage = tr("Password cannot be empty.");
+ return false;
+ }
+
+ if (m_compUi->enterPasswordEdit->text() != m_compUi->repeatPasswordEdit->text()) {
+ errorMessage = tr("Passwords do not match.");
+ return false;
+ }
+
+ return true;
+}
+
+void PasswordEditWidget::showPasswordGenerator()
+{
+ QDialog pwDialog;
+ pwDialog.setWindowTitle(tr("Generate master password"));
+
+ auto layout = new QVBoxLayout();
+ pwDialog.setLayout(layout);
+
+ auto pwGenerator = new PasswordGeneratorWidget(&pwDialog);
+ layout->addWidget(pwGenerator);
+
+ pwGenerator->setStandaloneMode(false);
+ connect(pwGenerator, SIGNAL(appliedPassword(const QString&)), SLOT(setPassword(const QString&)));
+ connect(pwGenerator, SIGNAL(dialogTerminated()), &pwDialog, SLOT(close()));
+
+ pwGenerator->setPasswordVisible(isPasswordVisible());
+
+ pwDialog.exec();
+}
+
+void PasswordEditWidget::setPassword(const QString& password)
+{
+ Q_ASSERT(m_compEditWidget);
+
+ m_compUi->enterPasswordEdit->setText(password);
+ m_compUi->repeatPasswordEdit->setText(password);
+}
diff --git a/src/gui/masterkey/PasswordEditWidget.h b/src/gui/masterkey/PasswordEditWidget.h
new file mode 100644
index 000000000..ac10c5122
--- /dev/null
+++ b/src/gui/masterkey/PasswordEditWidget.h
@@ -0,0 +1,56 @@
+/*
+ * 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 KEEPASSXC_PASSWORDEDITWIDGET_H
+#define KEEPASSXC_PASSWORDEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include <QPointer>
+
+namespace Ui
+{
+class PasswordEditWidget;
+}
+
+class PasswordEditWidget : public KeyComponentWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PasswordEditWidget(QWidget* parent = nullptr);
+ Q_DISABLE_COPY(PasswordEditWidget);
+ ~PasswordEditWidget() override;
+
+ bool addToCompositeKey(QSharedPointer<CompositeKey> key) override;
+ void setPasswordVisible(bool visible);
+ bool isPasswordVisible() const;
+ bool validate(QString& errorMessage) const override;
+
+protected:
+ QWidget* componentEditWidget() override;
+ void initComponentEditWidget(QWidget* widget) override;
+
+private slots:
+ void showPasswordGenerator();
+ void setPassword(const QString& password);
+
+private:
+ const QScopedPointer<Ui::PasswordEditWidget> m_compUi;
+ QPointer<QWidget> m_compEditWidget;
+};
+
+#endif //KEEPASSXC_PASSWORDEDITWIDGET_H
diff --git a/src/gui/masterkey/PasswordEditWidget.ui b/src/gui/masterkey/PasswordEditWidget.ui
new file mode 100644
index 000000000..e435e9901
--- /dev/null
+++ b/src/gui/masterkey/PasswordEditWidget.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PasswordEditWidget</class>
+ <widget class="QWidget" name="PasswordEditWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>571</width>
+ <height>78</height>
+ </rect>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="enterPasswordLabel">
+ <property name="text">
+ <string>Enter password:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="PasswordEdit" name="enterPasswordEdit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="togglePasswordButton">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="PasswordEdit" name="repeatPasswordEdit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="passwordGeneratorButton"/>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="repeatPasswordLabel">
+ <property name="text">
+ <string>Confirm password:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>PasswordEdit</class>
+ <extends>QLineEdit</extends>
+ <header>gui/PasswordEdit.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>enterPasswordEdit</tabstop>
+ <tabstop>repeatPasswordEdit</tabstop>
+ <tabstop>togglePasswordButton</tabstop>
+ <tabstop>passwordGeneratorButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/masterkey/YubiKeyEditWidget.cpp b/src/gui/masterkey/YubiKeyEditWidget.cpp
new file mode 100644
index 000000000..6991af427
--- /dev/null
+++ b/src/gui/masterkey/YubiKeyEditWidget.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "YubiKeyEditWidget.h"
+#include "ui_YubiKeyEditWidget.h"
+#include "gui/MessageBox.h"
+#include "gui/MainWindow.h"
+#include "keys/CompositeKey.h"
+#include "keys/YkChallengeResponseKey.h"
+#include "config-keepassx.h"
+
+#include <QtConcurrent>
+
+YubiKeyEditWidget::YubiKeyEditWidget(QWidget* parent)
+ : KeyComponentWidget(parent)
+ , m_compUi(new Ui::YubiKeyEditWidget())
+{
+ setComponentName(tr("YubiKey Challenge-Response"));
+ setComponentDescription(tr("<p>If you own a <a href=\"https://www.yubico.com/\">YubiKey</a>, you can use it "
+ "for additional security.</p><p>The YubiKey requires one of its slots to be programmed as "
+ "<a href=\"https://www.yubico.com/products/services-software/personalization-tools/challenge-response/\">"
+ "HMAC-SHA1 Challenge-Response</a>.</p>"));
+}
+
+YubiKeyEditWidget::~YubiKeyEditWidget()
+{
+}
+
+bool YubiKeyEditWidget::addToCompositeKey(QSharedPointer<CompositeKey> key)
+{
+ QSharedPointer<YkChallengeResponseKey> keyPtr;
+ if (!createCrKey(keyPtr, false)) {
+ return false;
+ }
+ key->addChallengeResponseKey(keyPtr);
+
+ return true;
+}
+
+bool YubiKeyEditWidget::validate(QString& errorMessage) const
+{
+ QSharedPointer<YkChallengeResponseKey> 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<YkChallengeResponseKey>& 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<bool>(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 <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 KEEPASSXC_YUBIKEYEDITWIDGET_H
+#define KEEPASSXC_YUBIKEYEDITWIDGET_H
+
+#include "KeyComponentWidget.h"
+#include <QPointer>
+
+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<CompositeKey> 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<YkChallengeResponseKey>& key, bool testChallenge = true) const;
+
+ const QScopedPointer<Ui::YubiKeyEditWidget> m_compUi;
+ QPointer<QWidget> 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>YubiKeyEditWidget</class>
+ <widget class="QWidget" name="YubiKeyEditWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>381</width>
+ <height>64</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="buttonRedetectYubikey">
+ <property name="text">
+ <string>Refresh</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="comboChallengeResponse">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QProgressBar" name="yubikeyProgress">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>2</height>
+ </size>
+ </property>
+ <property name="maximum">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>-1</number>
+ </property>
+ <property name="textVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>comboChallengeResponse</tabstop>
+ <tabstop>buttonRedetectYubikey</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
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 <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 "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 <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 KEEPASSXC_SETTINGSWIDGET_H
+#define KEEPASSXC_SETTINGSWIDGET_H
+
+#include <QWidget>
+
+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 <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 "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 <QVBoxLayout>
+
+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 <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 KEEPASSXC_NEWDATABASEWIZARD_H
+#define KEEPASSXC_NEWDATABASEWIZARD_H
+
+#include <QPointer>
+#include <QScopedPointer>
+#include <QWizard>
+
+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<Database> m_db;
+ QList<QPointer<NewDatabaseWizardPage>> 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 <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 "NewDatabaseWizardPage.h"
+#include "ui_NewDatabaseWizardPage.h"
+#include "core/Database.h"
+#include "gui/dbsettings/DatabaseSettingsWidget.h"
+
+#include <QVBoxLayout>
+
+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 <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 KEEPASSXC_NEWDATABASEWIZARDPAGE_H
+#define KEEPASSXC_NEWDATABASEWIZARDPAGE_H
+
+#include <QPointer>
+#include <QScopedPointer>
+#include <QWizardPage>
+
+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<DatabaseSettingsWidget> m_pageWidget;
+ QPointer<Database> m_db;
+
+ const QScopedPointer<Ui::NewDatabaseWizardPage> 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NewDatabaseWizardPage</class>
+ <widget class="QWizardPage" name="NewDatabaseWizardPage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>WizardPage</string>
+ </property>
+ <property name="title">
+ <string>En&amp;cryption Settings</string>
+ </property>
+ <property name="subTitle">
+ <string>Here you can adjust the database encryption settings. Don't worry, you can change them later in the database settings.</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="pageContentLayout"/>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>15</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="advancedSettings">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="advancedSettingsButton">
+ <property name="text">
+ <string>Advanced Settings</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
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 <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 "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 <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 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 <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 "NewDatabaseWizardPageMasterKey.h"
+#include "gui/dbsettings/DatabaseSettingsWidgetMasterKey.h"
+#include <QApplication>
+
+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 <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 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 <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 "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 <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 KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H
+#define KEEPASSXC_NEWDATABASEWIZARDPAGEMETADATA_H
+
+#include "NewDatabaseWizardPage.h"
+
+#include <QPointer>
+#include <QScopedPointer>
+
+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 <QByteArray>
+#include <QUuid>
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 <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
-* 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
@@ -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> key)
+{
+ m_keys.append(key);
+}
+
+/**
+ * @return list of Keys which are part of this CompositeKey
+ */
+const QList<QSharedPointer<Key>>& CompositeKey::keys() const
{
- m_keys.append(key.clone());
+ 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<ChallengeResponseKey> key)
{
m_challengeResponseKeys.append(key);
}
+
+/**
+ * @return list of ChallengeResponseKeys which are part of this CompositeKey
+ */
+const QList<QSharedPointer<ChallengeResponseKey>>& 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 <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
-* 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
@@ -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<ChallengeResponseKey> key);
+ void addKey(QSharedPointer<Key> key);
+ const QList<QSharedPointer<Key>>& keys() const;
+
+ void addChallengeResponseKey(QSharedPointer<ChallengeResponseKey> key);\
+ const QList<QSharedPointer<ChallengeResponseKey>>& challengeResponseKeys() const;
private:
- QList<Key*> m_keys;
+ QList<QSharedPointer<Key>> m_keys;
QList<QSharedPointer<ChallengeResponseKey>> 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.
*
@@ -145,14 +152,6 @@ QByteArray FileKey::rawKey() const
}
/**
- * @return cloned \link FileKey instance
- */
-FileKey* FileKey::clone() const
-{
- return new FileKey(*this);
-}
-
-/**
* Generate a new key file from random bytes.
*
* @param device output device
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 <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
@@ -19,15 +20,22 @@
#define KEEPASSX_KEY_H
#include <QByteArray>
+#include <QUuid>
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;
+ inline virtual QUuid uuid() const
{
+ return m_uuid;
}
- virtual QByteArray rawKey() const = 0;
- virtual Key* clone() const = 0;
+
+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> PasswordKey::fromRawKey(const QByteArray& rawKey)
{
- PasswordKey result;
- result.m_key = rawKey;
+ auto result = QSharedPointer<PasswordKey>::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 <QString>
+#include <QSharedPointer>
#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<PasswordKey> 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 <QXmlStreamReader>
#include <QtConcurrent>
+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();
-
- private:
- TouchID() {} // Constructor? (the {} brackets) are needed here.
-
- // 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
-
- QHash<QString, QByteArray> m_encryptedMasterKeys;
- int m_available = TOUCHID_UNDEFINED;
-
- public:
- // C++ 11
- // =======
- // We can use the better technique of deleting the methods
- // we don't want.
-
- TouchID(TouchID const&) = delete;
- void operator=(TouchID const&) = delete;
-
- // 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
-
- bool storeKey(const QString& databasePath, const QByteArray& passwordKey);
- QSharedPointer<QByteArray> getKey(const QString& databasePath) const;
- bool isAvailable();
- bool authenticate(const QString& message = "") const;
- void reset(const QString& databasePath = "");
+public:
+ static TouchID& getInstance();
+
+private:
+ TouchID() {}
+
+ // TouchID(TouchID const&); // Don't Implement
+ // void operator=(TouchID const&); // Don't implement
+
+ QHash<QString, QByteArray> m_encryptedMasterKeys;
+ int m_available = TOUCHID_UNDEFINED;
+
+public:
+ TouchID(TouchID const&) = delete;
+
+ void operator=(TouchID const&) = delete;
+
+ bool storeKey(const QString& databasePath, const QByteArray& passwordKey);
+
+ QSharedPointer<QByteArray> getKey(const QString& databasePath) const;
+
+ 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<UInt8*>(dataBytes.data()), dataBytes.length(), NULL);
+ CFDataRef valueData =
+ CFDataCreateWithBytesNoCopy(NULL, reinterpret_cast<UInt8*>(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<QByteArray> 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 <QByteArray> TouchID::getKey(const QString& databasePath) const
{
if (databasePath.isEmpty()) {
// illegal arguments
@@ -142,21 +157,24 @@ QSharedPointer<QByteArray> 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<QByteArray> TouchID::getKey(const QString& databasePath) const
}
CFDataRef valueData = static_cast<CFDataRef>(dataTypeRef);
- QByteArray dataBytes = QByteArray::fromHex(QByteArray(reinterpret_cast<const char*>(CFDataGetBytePtr(valueData)), CFDataGetLength(valueData)));
+ QByteArray dataBytes = QByteArray::fromHex(QByteArray(reinterpret_cast<const char*>(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<QByteArray> TouchID::getKey(const QString& databasePath) const
return QSharedPointer<QByteArray>::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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("123"));
+ QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("123"));
+ QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("123"));
+ QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("123"));
+ QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("a"));
KeePass2Reader reader;
QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("a"));
KeePass2Reader reader;
QScopedPointer<Database> 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<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString)
@@ -78,7 +78,7 @@ void TestKdbx3::readKdbx(QIODevice* device,
}
void TestKdbx3::readKdbx(const QString& path,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("a"));
KeePass2Reader reader;
QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
KeePass2Reader reader;
QScopedPointer<Database> 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<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create(""));
KeePass2Reader reader;
QScopedPointer<Database> db(reader.readDatabase(filename, key));
QVERIFY(db.data());
@@ -147,8 +147,8 @@ void TestKdbx3::testCompressed()
void TestKdbx3::testProtectedStrings()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/ProtectedStrings.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("masterpw"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("masterpw"));
KeePass2Reader reader;
QScopedPointer<Database> db(reader.readDatabase(filename, key));
QVERIFY(db.data());
@@ -174,8 +174,8 @@ void TestKdbx3::testBrokenHeaderHash()
// Make sure the database won't open.
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/BrokenHeaderHash.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey(""));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create(""));
KeePass2Reader reader;
QScopedPointer<Database> db(reader.readDatabase(filename, key));
QVERIFY(!db.data());
@@ -188,8 +188,8 @@ void TestKdbx3::testKdbxRepair()
// master password = test
// entry username: testuser\x10\x20AC
// entry password: testpw
- CompositeKey key;
- key.addKey(PasswordKey("test"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("test"));
// test that we can't open the broken database
bool hasError;
diff --git a/tests/TestKdbx3.h b/tests/TestKdbx3.h
index 298cefc6b..f96e16061 100644
--- a/tests/TestKdbx3.h
+++ b/tests/TestKdbx3.h
@@ -40,12 +40,12 @@ protected:
void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
void readKdbx(QIODevice* device,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString) override;
void readKdbx(const QString& path,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString) override;
diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp
index 9758ac13f..72c2d4c83 100644
--- a/tests/TestKdbx4.cpp
+++ b/tests/TestKdbx4.cpp
@@ -66,7 +66,7 @@ void TestKdbx4::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& er
}
void TestKdbx4::readKdbx(QIODevice* device,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString)
@@ -81,7 +81,7 @@ void TestKdbx4::readKdbx(QIODevice* device,
}
void TestKdbx4::readKdbx(const QString& path,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString)
@@ -113,8 +113,8 @@ Q_DECLARE_METATYPE(QUuid)
void TestKdbx4::testFormat400()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format400.kdbx");
- CompositeKey key;
- key.addKey(PasswordKey("t"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("t"));
KeePass2Reader reader;
QScopedPointer<Database> db(reader.readDatabase(filename, key));
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
@@ -146,8 +146,8 @@ void TestKdbx4::testFormat400Upgrade()
sourceDb->metadata()->setName("Wubba lubba dub dub");
QCOMPARE(sourceDb->kdf()->uuid(), KeePass2::KDF_AES_KDBX3); // default is legacy AES-KDF
- CompositeKey key;
- key.addKey(PasswordKey("I am in great pain, please help me!"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("I am in great pain, please help me!"));
sourceDb->setKey(key, true, true);
QBuffer buffer;
@@ -228,20 +228,20 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity()
QFETCH(quint32, expectedVersion);
// prepare composite key
- PasswordKey passwordKey("turXpGMQiUE6CkPvWacydAKsnp4cxz");
+ auto passwordKey = QSharedPointer<PasswordKey>::create("turXpGMQiUE6CkPvWacydAKsnp4cxz");
QByteArray fileKeyBytes("Ma6hHov98FbPeyAL22XhcgmpJk8xjQ");
QBuffer fileKeyBuffer(&fileKeyBytes);
fileKeyBuffer.open(QBuffer::ReadOnly);
- FileKey fileKey;
- fileKey.load(&fileKeyBuffer);
+ auto fileKey = QSharedPointer<FileKey>::create();
+ fileKey->load(&fileKeyBuffer);
auto crKey = QSharedPointer<MockChallengeResponseKey>::create(QByteArray("azdJnbVCFE76vV6t9RJ2DS6xvSS93k"));
- CompositeKey compositeKey;
- compositeKey.addKey(passwordKey);
- compositeKey.addKey(fileKey);
- compositeKey.addChallengeResponseKey(crKey);
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
+ compositeKey->addKey(passwordKey);
+ compositeKey->addKey(fileKey);
+ compositeKey->addChallengeResponseKey(crKey);
QScopedPointer<Database> db(new Database());
db->changeKdf(fastKdf(db->kdf()));
@@ -293,7 +293,7 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity()
buffer.seek(0);
KeePass2Reader reader;
QScopedPointer<Database> db2;
- db2.reset(reader.readDatabase(&buffer, CompositeKey()));
+ db2.reset(reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create()));
QVERIFY(reader.hasError());
// check that we can read back the database with the original composite key,
@@ -396,7 +396,7 @@ void TestKdbx4::testCustomData()
// read buffer back
buffer.seek(0);
KeePass2Reader reader;
- QSharedPointer<Database> newDb(reader.readDatabase(&buffer, CompositeKey()));
+ QSharedPointer<Database> newDb(reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create()));
// test all custom data are read back successfully from KDBX
QCOMPARE(newDb->publicCustomData(), publicCustomData);
diff --git a/tests/TestKdbx4.h b/tests/TestKdbx4.h
index 1b677b4ab..edf319a96 100644
--- a/tests/TestKdbx4.h
+++ b/tests/TestKdbx4.h
@@ -40,12 +40,12 @@ protected:
void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
void readKdbx(const QString& path,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString) override;
void readKdbx(QIODevice* device,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString) override;
diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp
index 3cf95286d..a3c148324 100644
--- a/tests/TestKeePass1Reader.cpp
+++ b/tests/TestKeePass1Reader.cpp
@@ -273,19 +273,18 @@ void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, c
QVERIFY(!writer.hasError());
QVERIFY(buffer.seek(0));
- CompositeKey key;
+ auto key = QSharedPointer<CompositeKey>::create();
if (!password.isNull()) {
- key.addKey(PasswordKey(password));
+ key->addKey(QSharedPointer<PasswordKey>::create(password));
}
if (!keyfileName.isEmpty()) {
- FileKey fileKey;
- QVERIFY(fileKey.load(keyfileName));
- key.addKey(fileKey);
+ auto fileKey = QSharedPointer<FileKey>::create();
+ QVERIFY(fileKey->load(keyfileName));
+ key->addKey(fileKey);
}
KeePass2Reader reader;
- Database* newDb = reader.readDatabase(&buffer, key);
+ QScopedPointer<Database> newDb(reader.readDatabase(&buffer, key));
QVERIFY(newDb);
QVERIFY(!reader.hasError());
- delete newDb;
}
diff --git a/tests/TestKeePass2Format.cpp b/tests/TestKeePass2Format.cpp
index c5f22ef6d..37b5b7838 100644
--- a/tests/TestKeePass2Format.cpp
+++ b/tests/TestKeePass2Format.cpp
@@ -40,8 +40,8 @@ void TestKeePass2Format::initTestCase()
QVERIFY(m_xmlDb.data());
// construct and write KDBX to buffer
- CompositeKey key;
- key.addKey(PasswordKey("test"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("test"));
m_kdbxSourceDb.reset(new Database());
m_kdbxSourceDb->setKey(key);
@@ -495,8 +495,8 @@ void TestKeePass2Format::testXmlRepairUuidHistoryItem()
void TestKeePass2Format::testReadBackTargetDb()
{
// read back previously constructed KDBX
- CompositeKey key;
- key.addKey(PasswordKey("test"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("test"));
bool hasError;
QString errorString;
@@ -542,8 +542,8 @@ void TestKeePass2Format::testKdbxNonAsciiPasswords()
void TestKeePass2Format::testKdbxDeviceFailure()
{
- CompositeKey key;
- key.addKey(PasswordKey("test"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("test"));
QScopedPointer<Database> db(new Database());
db->setKey(key);
// Disable compression so we write a predictable number of bytes.
@@ -569,7 +569,7 @@ void TestKeePass2Format::testKdbxDeviceFailure()
void TestKeePass2Format::testDuplicateAttachments()
{
QScopedPointer<Database> db(new Database());
- db->setKey(CompositeKey());
+ db->setKey(QSharedPointer<CompositeKey>::create());
const QByteArray attachment1("abc");
const QByteArray attachment2("def");
@@ -612,7 +612,7 @@ void TestKeePass2Format::testDuplicateAttachments()
}
buffer.seek(0);
- readKdbx(&buffer, CompositeKey(), db, hasError, errorString);
+ readKdbx(&buffer, QSharedPointer<CompositeKey>::create(), db, hasError, errorString);
if (hasError) {
QFAIL(qPrintable(QString("Error while reading database: %1").arg(errorString)));
}
diff --git a/tests/TestKeePass2Format.h b/tests/TestKeePass2Format.h
index d29fa2c13..19b0fb649 100644
--- a/tests/TestKeePass2Format.h
+++ b/tests/TestKeePass2Format.h
@@ -72,12 +72,12 @@ protected:
virtual void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) = 0;
virtual void readKdbx(QIODevice* device,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString) = 0;
virtual void readKdbx(const QString& path,
- CompositeKey const& key,
+ QSharedPointer<const CompositeKey> key,
QScopedPointer<Database>& db,
bool& hasError,
QString& errorString) = 0;
diff --git a/tests/TestKeys.cpp b/tests/TestKeys.cpp
index f04aabcb5..84c202914 100644
--- a/tests/TestKeys.cpp
+++ b/tests/TestKeys.cpp
@@ -43,13 +43,13 @@ void TestKeys::initTestCase()
void TestKeys::testComposite()
{
- QScopedPointer<CompositeKey> compositeKey1(new CompositeKey());
- QScopedPointer<PasswordKey> passwordKey1(new PasswordKey());
- QScopedPointer<PasswordKey> passwordKey2(new PasswordKey("test"));
+ auto compositeKey1 = QSharedPointer<CompositeKey>::create();
+ auto passwordKey1 = QSharedPointer<PasswordKey>::create();
+ auto passwordKey2 = QSharedPointer<PasswordKey>::create("test");
// make sure that addKey() creates a copy of the keys
- compositeKey1->addKey(*passwordKey1);
- compositeKey1->addKey(*passwordKey2);
+ compositeKey1->addKey(passwordKey1);
+ compositeKey1->addKey(passwordKey2);
AesKdf kdf;
kdf.setRounds(1);
@@ -57,29 +57,13 @@ void TestKeys::testComposite()
QVERIFY(compositeKey1->transform(kdf, transformed1));
QCOMPARE(transformed1.size(), 32);
- // make sure the subkeys are copied
- QScopedPointer<CompositeKey> compositeKey2(compositeKey1->clone());
- QByteArray transformed2;
- QVERIFY(compositeKey2->transform(kdf, transformed2));
- QCOMPARE(transformed2.size(), 32);
- QCOMPARE(transformed1, transformed2);
-
QScopedPointer<CompositeKey> compositeKey3(new CompositeKey());
QScopedPointer<CompositeKey> compositeKey4(new CompositeKey());
// test clear()
- compositeKey3->addKey(PasswordKey("test"));
+ compositeKey3->addKey(QSharedPointer<PasswordKey>::create("test"));
compositeKey3->clear();
QCOMPARE(compositeKey3->rawKey(), compositeKey4->rawKey());
-
- // test assignment operator
- compositeKey3->addKey(PasswordKey("123"));
- *compositeKey4 = *compositeKey3;
- QCOMPARE(compositeKey3->rawKey(), compositeKey4->rawKey());
-
- // test self-assignment
- *compositeKey3 = *compositeKey3;
- QCOMPARE(compositeKey3->rawKey(), compositeKey4->rawKey());
}
void TestKeys::testFileKey()
@@ -94,14 +78,14 @@ void TestKeys::testFileKey()
QString dbFilename = QString("%1/%2.kdbx").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
QString keyFilename = QString("%1/%2.key").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
- CompositeKey compositeKey;
- FileKey fileKey;
- QVERIFY(fileKey.load(keyFilename));
- QCOMPARE(fileKey.rawKey().size(), 32);
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
+ auto fileKey = QSharedPointer<FileKey>::create();
+ QVERIFY(fileKey->load(keyFilename));
+ QCOMPARE(fileKey->rawKey().size(), 32);
- QCOMPARE(fileKey.type(), type);
+ QCOMPARE(fileKey->type(), type);
- compositeKey.addKey(fileKey);
+ compositeKey->addKey(fileKey);
QScopedPointer<Database> db(reader.readDatabase(dbFilename, compositeKey));
QVERIFY(db);
@@ -146,10 +130,10 @@ void TestKeys::testCreateAndOpenFileKey()
FileKey::create(&keyBuffer);
keyBuffer.reset();
- FileKey fileKey;
- QVERIFY(fileKey.load(&keyBuffer));
- CompositeKey compositeKey;
- compositeKey.addKey(fileKey);
+ auto fileKey = QSharedPointer<FileKey>::create();
+ QVERIFY(fileKey->load(&keyBuffer));
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
+ compositeKey->addKey(fileKey);
QScopedPointer<Database> dbOrg(new Database());
QVERIFY(dbOrg->setKey(compositeKey));
@@ -218,10 +202,10 @@ void TestKeys::benchmarkTransformKey()
QSKIP("Benchmark skipped. Set env variable BENCHMARK=1 to enable.");
}
- PasswordKey pwKey;
- pwKey.setPassword("password");
- CompositeKey compositeKey;
- compositeKey.addKey(pwKey);
+ auto pwKey = QSharedPointer<PasswordKey>::create();
+ pwKey->setPassword("password");
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
+ compositeKey->addKey(pwKey);
QByteArray seed(32, '\x4B');
@@ -232,25 +216,25 @@ void TestKeys::benchmarkTransformKey()
QBENCHMARK
{
- Q_UNUSED(compositeKey.transform(kdf, result));
+ Q_UNUSED(compositeKey->transform(kdf, result));
};
}
void TestKeys::testCompositeKeyComponents()
{
- PasswordKey passwordKeyEnc("password");
- FileKey fileKeyEnc;
+ auto passwordKeyEnc = QSharedPointer<PasswordKey>::create("password");
+ auto fileKeyEnc = QSharedPointer<FileKey>::create();
QString error;
- fileKeyEnc.load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"), &error);
+ fileKeyEnc->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"), &error);
if (!error.isNull()) {
QFAIL(qPrintable(error));
}
auto challengeResponseKeyEnc = QSharedPointer<MockChallengeResponseKey>::create(QByteArray(16, 0x10));
- CompositeKey compositeKeyEnc;
- compositeKeyEnc.addKey(passwordKeyEnc);
- compositeKeyEnc.addKey(fileKeyEnc);
- compositeKeyEnc.addChallengeResponseKey(challengeResponseKeyEnc);
+ auto compositeKeyEnc = QSharedPointer<CompositeKey>::create();
+ compositeKeyEnc->addKey(passwordKeyEnc);
+ compositeKeyEnc->addKey(fileKeyEnc);
+ compositeKeyEnc->addChallengeResponseKey(challengeResponseKeyEnc);
QScopedPointer<Database> db1(new Database());
db1->setKey(compositeKeyEnc);
@@ -263,23 +247,23 @@ void TestKeys::testCompositeKeyComponents()
buffer.seek(0);
QScopedPointer<Database> db2;
KeePass2Reader reader;
- CompositeKey compositeKeyDec1;
+ auto compositeKeyDec1 = QSharedPointer<CompositeKey>::create();
// try decryption and subsequently add key components until decryption is successful
db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
QVERIFY(reader.hasError());
- compositeKeyDec1.addKey(passwordKeyEnc);
+ compositeKeyDec1->addKey(passwordKeyEnc);
buffer.seek(0);
db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
QVERIFY(reader.hasError());
- compositeKeyDec1.addKey(fileKeyEnc);
+ compositeKeyDec1->addKey(fileKeyEnc);
buffer.seek(0);
db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
QVERIFY(reader.hasError());
- compositeKeyDec1.addChallengeResponseKey(challengeResponseKeyEnc);
+ compositeKeyDec1->addChallengeResponseKey(challengeResponseKeyEnc);
buffer.seek(0);
db2.reset(reader.readDatabase(&buffer, compositeKeyDec1));
// now we should be able to open the database
@@ -288,31 +272,31 @@ void TestKeys::testCompositeKeyComponents()
}
// try the same again, but this time with one wrong key component each time
- CompositeKey compositeKeyDec2;
- compositeKeyDec2.addKey(PasswordKey("wrong password"));
- compositeKeyDec2.addKey(fileKeyEnc);
- compositeKeyDec2.addChallengeResponseKey(challengeResponseKeyEnc);
+ auto compositeKeyDec2 = QSharedPointer<CompositeKey>::create();
+ compositeKeyDec2->addKey(QSharedPointer<PasswordKey>::create("wrong password"));
+ compositeKeyDec2->addKey(fileKeyEnc);
+ compositeKeyDec2->addChallengeResponseKey(challengeResponseKeyEnc);
buffer.seek(0);
db2.reset(reader.readDatabase(&buffer, compositeKeyDec2));
QVERIFY(reader.hasError());
- CompositeKey compositeKeyDec3;
- compositeKeyDec3.addKey(passwordKeyEnc);
- FileKey fileKeyWrong;
- fileKeyWrong.load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed2.key"), &error);
+ auto compositeKeyDec3 = QSharedPointer<CompositeKey>::create();
+ compositeKeyDec3->addKey(passwordKeyEnc);
+ auto fileKeyWrong = QSharedPointer<FileKey>::create();
+ fileKeyWrong->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed2.key"), &error);
if (!error.isNull()) {
QFAIL(qPrintable(error));
}
- compositeKeyDec3.addKey(fileKeyWrong);
- compositeKeyDec3.addChallengeResponseKey(challengeResponseKeyEnc);
+ compositeKeyDec3->addKey(fileKeyWrong);
+ compositeKeyDec3->addChallengeResponseKey(challengeResponseKeyEnc);
buffer.seek(0);
db2.reset(reader.readDatabase(&buffer, compositeKeyDec3));
QVERIFY(reader.hasError());
- CompositeKey compositeKeyDec4;
- compositeKeyDec4.addKey(passwordKeyEnc);
- compositeKeyDec4.addKey(fileKeyEnc);
- compositeKeyDec4.addChallengeResponseKey(QSharedPointer<MockChallengeResponseKey>::create(QByteArray(16, 0x20)));
+ auto compositeKeyDec4 = QSharedPointer<CompositeKey>::create();
+ compositeKeyDec4->addKey(passwordKeyEnc);
+ compositeKeyDec4->addKey(fileKeyEnc);
+ compositeKeyDec4->addChallengeResponseKey(QSharedPointer<MockChallengeResponseKey>::create(QByteArray(16, 0x20)));
buffer.seek(0);
db2.reset(reader.readDatabase(&buffer, compositeKeyDec4));
QVERIFY(reader.hasError());
diff --git a/tests/TestModified.cpp b/tests/TestModified.cpp
index 66227e3ad..6e033f25e 100644
--- a/tests/TestModified.cpp
+++ b/tests/TestModified.cpp
@@ -37,7 +37,7 @@ void TestModified::testSignals()
int spyCount = 0;
int spyCount2 = 0;
- CompositeKey compositeKey;
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
QScopedPointer<Database> db(new Database());
auto* root = db->rootGroup();
diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp
index 930dac561..731eadcaf 100644
--- a/tests/gui/TestGui.cpp
+++ b/tests/gui/TestGui.cpp
@@ -48,6 +48,13 @@
#include "crypto/Crypto.h"
#include "crypto/kdf/AesKdf.h"
#include "format/KeePass2Reader.h"
+#include "keys/PasswordKey.h"
+#include "keys/FileKey.h"
+#include "gui/ApplicationSettingsWidget.h"
+#include "gui/dbsettings/DatabaseSettingsDialog.h"
+#include "gui/masterkey/PasswordEditWidget.h"
+#include "gui/masterkey/KeyFileEditWidget.h"
+#include "gui/CategoryListWidget.h"
#include "gui/CloneDialog.h"
#include "gui/DatabaseTabWidget.h"
#include "gui/DatabaseWidget.h"
@@ -63,6 +70,8 @@
#include "gui/group/EditGroupWidget.h"
#include "gui/group/GroupModel.h"
#include "gui/group/GroupView.h"
+#include "gui/wizard/NewDatabaseWizard.h"
+#include "gui/masterkey/KeyComponentWidget.h"
#include "keys/PasswordKey.h"
void TestGui::initTestCase()
@@ -70,9 +79,11 @@ void TestGui::initTestCase()
QVERIFY(Crypto::init());
Config::createTempFileInstance();
// Disable autosave so we can test the modified file indicator
- Config::instance()->set("AutoSaveAfterEveryChange", false);
+ config()->set("AutoSaveAfterEveryChange", false);
// Enable the tray icon so we can test hiding/restoring the window
- Config::instance()->set("GUI/ShowTrayIcon", true);
+ config()->set("GUI/ShowTrayIcon", true);
+ // Disable advanced settings mode (activate within individual tests to test advanced settings)
+ config()->set("GUI/AdvancedSettings", false);
m_mainWindow = new MainWindow();
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
@@ -135,43 +146,114 @@ void TestGui::cleanup()
m_dbWidget = nullptr;
}
-void TestGui::testCreateDatabase()
+void TestGui::testSettingsDefaultTabOrder()
{
- QTemporaryFile tmpFile;
- QVERIFY(tmpFile.open());
- QString tmpFileName = tmpFile.fileName();
- tmpFile.remove();
-
- fileDialog()->setNextFileName(tmpFileName);
- triggerAction("actionDatabaseNew");
-
- DatabaseWidget* dbWidget = m_tabWidget->currentDatabaseWidget();
-
- QWidget* databaseNewWidget = dbWidget->findChild<QWidget*>("changeMasterKeyWidget");
- PasswordEdit* editPassword = databaseNewWidget->findChild<PasswordEdit*>("enterPasswordEdit");
- QVERIFY(editPassword->isVisible());
-
- QLineEdit* editPasswordRepeat = databaseNewWidget->findChild<QLineEdit*>("repeatPasswordEdit");
- QVERIFY(editPasswordRepeat->isVisible());
-
- m_tabWidget->currentDatabaseWidget()->setCurrentWidget(databaseNewWidget);
-
- QTest::keyClicks(editPassword, "test");
- QTest::keyClicks(editPasswordRepeat, "test");
- QTest::keyClick(editPasswordRepeat, Qt::Key_Enter);
+ // check application settings default tab order
+ triggerAction("actionSettings");
+ auto* settingsWidget = m_mainWindow->findChild<ApplicationSettingsWidget*>();
+ QVERIFY(settingsWidget->isVisible());
+ QCOMPARE(settingsWidget->findChild<CategoryListWidget*>("categoryList")->currentCategory(), 0);
+ for (auto* w: settingsWidget->findChildren<QTabWidget*>()) {
+ if (w->currentIndex() != 0) {
+ QFAIL("Application settings contain QTabWidgets whose default index is not 0");
+ }
+ }
+ QTest::keyClick(settingsWidget, Qt::Key::Key_Escape);
- // Auto-save after every change is enabled by default, ensure the db saves right away
- QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).contains("*"));
+ // check database settings default tab order
+ triggerAction("actionChangeDatabaseSettings");
+ auto* dbSettingsWidget = m_mainWindow->findChild<DatabaseSettingsDialog*>();
+ QVERIFY(dbSettingsWidget->isVisible());
+ QCOMPARE(dbSettingsWidget->findChild<CategoryListWidget*>("categoryList")->currentCategory(), 0);
+ for (auto* w: dbSettingsWidget->findChildren<QTabWidget*>()) {
+ if (w->currentIndex() != 0) {
+ QFAIL("Database settings contain QTabWidgets whose default index is not 0");
+ }
+ }
+ QTest::keyClick(dbSettingsWidget, Qt::Key::Key_Escape);
+}
- m_db = m_tabWidget->currentDatabaseWidget()->database();
+void TestGui::testCreateDatabase()
+{
+ QTimer::singleShot(0, this, SLOT(createDatabaseCallback()));
+ triggerAction("actionDatabaseNew");
// there is a new empty db
+ m_db = m_tabWidget->currentDatabaseWidget()->database();
QCOMPARE(m_db->rootGroup()->children().size(), 0);
+ // check meta data
+ QCOMPARE(m_db->metadata()->name(), QString("Test Name"));
+ QCOMPARE(m_db->metadata()->description(), QString("Test Description"));
+
+ // check key and encryption
+ QCOMPARE(m_db->key()->keys().size(), 2);
+ QCOMPARE(m_db->kdf()->uuid(), KeePass2::KDF_ARGON2);
+ QCOMPARE(m_db->cipher(), KeePass2::CIPHER_AES);
+ auto compositeKey = QSharedPointer<CompositeKey>::create();
+ compositeKey->addKey(QSharedPointer<PasswordKey>::create("test"));
+ auto fileKey = QSharedPointer<FileKey>::create();
+ fileKey->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"));
+ compositeKey->addKey(fileKey);
+ QCOMPARE(m_db->key()->rawKey(), compositeKey->rawKey());
+
// close the new database
MessageBox::setNextAnswer(QMessageBox::No);
triggerAction("actionDatabaseClose");
- Tools::wait(100);
+}
+
+void TestGui::createDatabaseCallback()
+{
+ auto* wizard = m_tabWidget->findChild<NewDatabaseWizard*>();
+ QVERIFY(wizard);
+
+ QTest::keyClicks(wizard->currentPage()->findChild<QLineEdit*>("databaseName"), "Test Name");
+ QTest::keyClicks(wizard->currentPage()->findChild<QLineEdit*>("databaseDescription"), "Test Description");
+ QCOMPARE(wizard->currentId(), 0);
+
+ QTest::keyClick(wizard, Qt::Key_Enter);
+ QCOMPARE(wizard->currentId(), 1);
+
+ QTest::keyClick(wizard, Qt::Key_Enter);
+ QCOMPARE(wizard->currentId(), 2);
+
+ // enter password
+ auto* passwordWidget = wizard->currentPage()->findChild<PasswordEditWidget*>();
+ QCOMPARE(passwordWidget->visiblePage(), KeyFileEditWidget::Page::Edit);
+ auto* passwordEdit = passwordWidget->findChild<QLineEdit*>("enterPasswordEdit");
+ auto* passwordRepeatEdit = passwordWidget->findChild<QLineEdit*>("repeatPasswordEdit");
+ QTRY_VERIFY(passwordEdit->isVisible());
+ QVERIFY(passwordEdit->hasFocus());
+ QTest::keyClicks(passwordEdit, "test");
+ QTest::keyClick(passwordEdit, Qt::Key::Key_Tab);
+ QTest::keyClicks(passwordRepeatEdit, "test");
+
+ // add key file
+ auto* additionalOptionsButton = wizard->currentPage()->findChild<QPushButton*>("additionalKeyOptionsToggle");
+ auto* keyFileWidget = wizard->currentPage()->findChild<KeyFileEditWidget*>();
+ QVERIFY(additionalOptionsButton->isVisible());
+ QTest::mouseClick(additionalOptionsButton, Qt::MouseButton::LeftButton);
+ QTRY_VERIFY(keyFileWidget->isVisible());
+ QTRY_VERIFY(!additionalOptionsButton->isVisible());
+ QCOMPARE(passwordWidget->visiblePage(), KeyFileEditWidget::Page::Edit);
+ QTest::mouseClick(keyFileWidget->findChild<QPushButton*>("addButton"), Qt::MouseButton::LeftButton);
+ auto* fileCombo = keyFileWidget->findChild<QComboBox*>("keyFileCombo");
+ QTRY_VERIFY(fileCombo);
+ QTRY_VERIFY(fileCombo->isVisible());
+ fileDialog()->setNextFileName(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"));
+ QTest::keyClick(keyFileWidget->findChild<QPushButton*>("addButton"), Qt::Key::Key_Enter);
+ QVERIFY(fileCombo->hasFocus());
+ auto* browseButton = keyFileWidget->findChild<QPushButton*>("browseKeyFileButton");
+ QTest::keyClick(browseButton, Qt::Key::Key_Enter);
+ QCOMPARE(fileCombo->currentText(), QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"));
+
+ // save database to temporary file
+ TemporaryFile tmpFile;
+ QVERIFY(tmpFile.open());
+ tmpFile.close();
+ fileDialog()->setNextFileName(tmpFile.filePath());
+
+ QTest::keyClick(fileCombo, Qt::Key::Key_Enter);
}
void TestGui::testMergeDatabase()
@@ -390,7 +472,7 @@ void TestGui::testSearchEditEntry()
// Find buttons for group creation
EditGroupWidget* editGroupWidget = m_dbWidget->findChild<EditGroupWidget*>("editGroupWidget");
- QLineEdit* nameEdit = editGroupWidget->findChild<QLineEdit*>("nameEdit");
+ QLineEdit* nameEdit = editGroupWidget->findChild<QLineEdit*>("editName");
QDialogButtonBox* editGroupWidgetButtonBox = editGroupWidget->findChild<QDialogButtonBox*>("buttonBox");
// Add groups "Good" and "Bad"
@@ -965,7 +1047,7 @@ void TestGui::testSaveAs()
QFileInfo fileInfo(m_dbFilePath);
QDateTime lastModified = fileInfo.lastModified();
- m_db->metadata()->setName("SaveAs");
+ m_db->metadata()->setName("testSaveAs");
// open temporary file so it creates a filename
QTemporaryFile tmpFile;
@@ -977,7 +1059,7 @@ void TestGui::testSaveAs()
triggerAction("actionDatabaseSaveAs");
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("SaveAs"));
+ QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSaveAs"));
checkDatabase(tmpFileName);
@@ -987,31 +1069,40 @@ void TestGui::testSaveAs()
void TestGui::testSave()
{
- m_db->metadata()->setName("Save");
+ m_db->metadata()->setName("testSave");
+
// wait for modified timer
- QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save*"));
+ QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSave*"));
triggerAction("actionDatabaseSave");
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save"));
+ QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSave"));
checkDatabase();
}
void TestGui::testDatabaseSettings()
{
- m_db->metadata()->setName("Save");
+ m_db->metadata()->setName("testDatabaseSettings");
triggerAction("actionChangeDatabaseSettings");
- QWidget* dbSettingsWidget = m_dbWidget->findChild<QWidget*>("databaseSettingsWidget");
- QSpinBox* transformRoundsSpinBox = dbSettingsWidget->findChild<QSpinBox*>("transformRoundsSpinBox");
+ auto* dbSettingsDialog = m_dbWidget->findChild<QWidget*>("databaseSettingsDialog");
+ auto* transformRoundsSpinBox = dbSettingsDialog->findChild<QSpinBox*>("transformRoundsSpinBox");
+ auto advancedToggle = dbSettingsDialog->findChild<QCheckBox*>("advancedSettingsToggle");
+
+ advancedToggle->setChecked(true);
+ QApplication::processEvents();
+
QVERIFY(transformRoundsSpinBox != nullptr);
transformRoundsSpinBox->setValue(123456);
QTest::keyClick(transformRoundsSpinBox, Qt::Key_Enter);
// wait for modified timer
- QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save*"));
+ QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testDatabaseSettings*"));
QCOMPARE(m_db->kdf()->rounds(), 123456);
triggerAction("actionDatabaseSave");
- QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("Save"));
+ QCOMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testDatabaseSettings"));
+
+ advancedToggle->setChecked(false);
+ QApplication::processEvents();
checkDatabase();
}
@@ -1021,8 +1112,8 @@ void TestGui::testKeePass1Import()
fileDialog()->setNextFileName(QString(KEEPASSX_TEST_DATA_DIR).append("/basic.kdb"));
triggerAction("actionImportKeePass1");
- QWidget* keepass1OpenWidget = m_mainWindow->findChild<QWidget*>("keepass1OpenWidget");
- QLineEdit* editPassword = keepass1OpenWidget->findChild<QLineEdit*>("editPassword");
+ auto* keepass1OpenWidget = m_mainWindow->findChild<QWidget*>("keepass1OpenWidget");
+ auto* editPassword = keepass1OpenWidget->findChild<QLineEdit*>("editPassword");
QVERIFY(editPassword);
QTest::keyClicks(editPassword, "masterpw");
@@ -1174,8 +1265,8 @@ void TestGui::checkDatabase(QString dbFileName)
if (dbFileName.isEmpty())
dbFileName = m_dbFilePath;
- CompositeKey key;
- key.addKey(PasswordKey("a"));
+ auto key = QSharedPointer<CompositeKey>::create();
+ key->addKey(QSharedPointer<PasswordKey>::create("a"));
KeePass2Reader reader;
QScopedPointer<Database> dbSaved(reader.readDatabase(dbFileName, key));
QVERIFY(dbSaved);
@@ -1189,6 +1280,7 @@ void TestGui::triggerAction(const QString& name)
QVERIFY(action);
QVERIFY(action->isEnabled());
action->trigger();
+ QApplication::processEvents();
}
void TestGui::dragAndDropGroup(const QModelIndex& sourceIndex,
diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h
index 2a3708c42..dc8a05e9b 100644
--- a/tests/gui/TestGui.h
+++ b/tests/gui/TestGui.h
@@ -35,12 +35,16 @@ class TestGui : public QObject
{
Q_OBJECT
+protected slots:
+ void createDatabaseCallback();
+
private slots:
void initTestCase();
void init();
void cleanup();
void cleanupTestCase();
+ void testSettingsDefaultTabOrder();
void testCreateDatabase();
void testMergeDatabase();
void testAutoreloadDatabase();
diff --git a/tests/mock/MockChallengeResponseKey.cpp b/tests/mock/MockChallengeResponseKey.cpp
index 9cfdc0501..628504d85 100644
--- a/tests/mock/MockChallengeResponseKey.cpp
+++ b/tests/mock/MockChallengeResponseKey.cpp
@@ -18,7 +18,8 @@
#include "MockChallengeResponseKey.h"
MockChallengeResponseKey::MockChallengeResponseKey(const QByteArray& secret)
- : m_secret(secret)
+ : ChallengeResponseKey(QUuid("aac5b480-cdc0-411e-9cb8-962062dcc1fd"))
+ , m_secret(secret)
{
}
diff --git a/tests/mock/MockChallengeResponseKey.h b/tests/mock/MockChallengeResponseKey.h
index 011b06e5d..1ef11bf60 100644
--- a/tests/mock/MockChallengeResponseKey.h
+++ b/tests/mock/MockChallengeResponseKey.h
@@ -28,6 +28,7 @@ class MockChallengeResponseKey : public ChallengeResponseKey
{
public:
explicit MockChallengeResponseKey(const QByteArray& secret);
+ Q_DISABLE_COPY(MockChallengeResponseKey);
~MockChallengeResponseKey() override;
QByteArray rawKey() const override;
bool challenge(const QByteArray& challenge) override;