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.
This commit is contained in:
Janek Bevendorff 2018-05-13 23:21:43 +02:00
parent e4ded388b4
commit e443cde452
116 changed files with 5054 additions and 1692 deletions

View File

@ -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")

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -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>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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()

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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)

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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.

View File

@ -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)) {

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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();

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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">

View File

@ -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>

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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;

View File

@ -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));
}

View File

@ -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>

View File

@ -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()
{
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);
auto* db = execNewDatabaseWizard();
if (!db) {
return;
}
dbStruct.dbWidget->switchToMasterKeyChange(true);
DatabaseManagerStruct dbStruct;
dbStruct.dbWidget = new DatabaseWidget(db, this);
insertDatabase(db, dbStruct);
if (!saveDatabaseAs(db)) {
// mark database as dirty if user canceled save dialog
emit db->modifiedImmediate();
}
}
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,53 +355,53 @@ 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 (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;
}
emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage), MessageWidget::Error);
return false;
}
} else {
if (dbStruct.readOnly || filePath.isEmpty()) {
return saveDatabaseAs(db);
}
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 (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;
}
emit messageTab(tr("Writing the database failed.").append("\n").append(errorMessage), MessageWidget::Error);
return false;
}
}
bool DatabaseTabWidget::saveDatabaseAs(Database* db)

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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>

View File

@ -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();

View File

@ -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();

View File

@ -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>

View File

@ -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)
{

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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

View File

@ -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">
@ -27,6 +19,13 @@
</item>
<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">
@ -48,4 +47,4 @@
</customwidgets>
<resources/>
<connections/>
</ui>
</ui>

View File

@ -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();
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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

View File

@ -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>

View File

@ -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);
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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"));
}
}

View File

@ -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

View File

@ -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>

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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()
{
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef 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

View File

@ -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();
}

View File

@ -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

View File

@ -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()
{
}

View File

@ -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

View File

@ -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

View File

@ -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.clone());
m_keys.append(key);
}
/**
* @return list of Keys which are part of this CompositeKey
*/
const QList<QSharedPointer<Key>>& CompositeKey::keys() const
{
return m_keys;
}
/**
* Add a \link ChallengeResponseKey to this composite key.
* ChallengeResponseKeys will be hashed in the order they were initially added,
* but will always come after normal \link Keys.
*
* @param key the key
*/
void CompositeKey::addChallengeResponseKey(QSharedPointer<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;
}

View File

@ -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;
};

View File

@ -24,6 +24,13 @@
#include "crypto/CryptoHash.h"
#include "crypto/Random.h"
QUuid FileKey::UUID("a584cbc4-c9b4-437e-81bb-362ca9709273");
FileKey::FileKey()
: Key(UUID)
{
}
/**
* Read key file from device while trying to detect its file format.
*
@ -144,14 +151,6 @@ QByteArray FileKey::rawKey() const
return m_key;
}
/**
* @return cloned \link FileKey instance
*/
FileKey* FileKey::clone() const
{
return new FileKey(*this);
}
/**
* Generate a new key file from random bytes.
*

View File

@ -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);

View File

@ -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;
virtual Key* clone() const = 0;
inline virtual QUuid uuid() const
{
return m_uuid;
}
private:
QUuid m_uuid;
};
#endif // KEEPASSX_KEY_H

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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) {

View File

@ -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;

View File

@ -20,7 +20,7 @@
#define AGENTSETTINGSPAGE_H
#include "gui/DatabaseTabWidget.h"
#include "gui/SettingsWidget.h"
#include "gui/ApplicationSettingsWidget.h"
class AgentSettingsPage : public ISettingsPage
{

View File

@ -12,44 +12,32 @@
class TouchID
{
public:
static TouchID& getInstance();
public:
static TouchID& getInstance();
private:
TouchID() {} // Constructor? (the {} brackets) are needed here.
private:
TouchID() {}
// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
// TouchID(TouchID const&); // Don't Implement
// void operator=(TouchID const&); // Don't implement
// TouchID(TouchID const&); // Don't Implement
// void operator=(TouchID const&); // Don't implement
QHash<QString, QByteArray> m_encryptedMasterKeys;
int m_available = TOUCHID_UNDEFINED;
QHash<QString, QByteArray> m_encryptedMasterKeys;
int m_available = TOUCHID_UNDEFINED;
public:
TouchID(TouchID const&) = delete;
public:
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
void operator=(TouchID const&) = delete;
TouchID(TouchID const&) = delete;
void operator=(TouchID const&) = delete;
bool storeKey(const QString& databasePath, const QByteArray& passwordKey);
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
QSharedPointer<QByteArray> getKey(const QString& databasePath) const;
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 = "");
bool isAvailable();
bool authenticate(const QString& message = "") const;
void reset(const QString& databasePath = "");
};
#endif // KEEPASSX_TOUCHID_H

Some files were not shown because too many files have changed in this diff Show More