diff --git a/CMakeLists.txt b/CMakeLists.txt index 68a346e36..7835b8a08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -494,6 +494,10 @@ if(BOTAN2_VERSION VERSION_LESS "2.11.0") message(FATAL_ERROR "Botan2 2.11.0 or higher is required") endif() include_directories(SYSTEM ${BOTAN2_INCLUDE_DIR}) +# Find Argon2 -- Botan 2.18 and below does not support threaded Argon2 +find_library(ARGON2_LIBRARIES NAMES argon2) +find_path(ARGON2_INCLUDE_DIR NAMES argon2.h PATH_SUFFIXES local/include) +include_directories(SYSTEM ${ARGON2_INCLUDE_DIR}) # Find zlib find_package(ZLIB REQUIRED) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c7291a638..f1695b673 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -336,6 +336,7 @@ target_link_libraries(keepassx_core ${PCSC_LIBRARIES} ${ZXCVBN_LIBRARIES} ${ZLIB_LIBRARIES} + ${ARGON2_LIBRARIES} ${thirdparty_LIBRARIES} ) diff --git a/src/crypto/kdf/Argon2Kdf.cpp b/src/crypto/kdf/Argon2Kdf.cpp index 59919500e..fe2d5aa69 100644 --- a/src/crypto/kdf/Argon2Kdf.cpp +++ b/src/crypto/kdf/Argon2Kdf.cpp @@ -17,8 +17,10 @@ #include "Argon2Kdf.h" +#include #include -#include + +#include #include "format/KeePass2.h" @@ -163,20 +165,27 @@ bool Argon2Kdf::transform(const QByteArray& raw, QByteArray& result) const { result.clear(); result.resize(32); - try { - auto algo = type() == Type::Argon2d ? "Argon2d" : "Argon2id"; - auto pwhash = Botan::PasswordHashFamily::create_or_throw(algo)->from_params(memory(), rounds(), parallelism()); - pwhash->derive_key(reinterpret_cast(result.data()), - result.size(), - raw.constData(), - raw.size(), - reinterpret_cast(seed().constData()), - seed().size()); - return true; - } catch (std::exception& e) { - qWarning("Argon2 error: %s", e.what()); + // Time Cost, Mem Cost, Threads/Lanes, Password, length, Salt, length, out, length + + int rc = argon2_hash(rounds(), + memory(), + parallelism(), + raw.data(), + raw.size(), + seed().data(), + seed().size(), + result.data(), + result.size(), + nullptr, + 0, + type() == Type::Argon2d ? Argon2_d : Argon2_id, + version()); + if (rc != ARGON2_OK) { + qWarning("Argon2 error: %s", argon2_error_message(rc)); return false; } + + return true; } QSharedPointer Argon2Kdf::clone() const @@ -186,14 +195,16 @@ QSharedPointer Argon2Kdf::clone() const int Argon2Kdf::benchmark(int msec) const { - try { - auto algo = type() == Type::Argon2d ? "Argon2d" : "Argon2id"; - auto pwhash = Botan::PasswordHashFamily::create_or_throw(algo)->tune( - 32, std::chrono::milliseconds(msec), memory() / 1024); - return qMax(static_cast(1), pwhash->iterations()); - } catch (std::exception& e) { - return 1; + QByteArray key = QByteArray(16, '\x7E'); + + QElapsedTimer timer; + timer.start(); + + if (transform(key, key)) { + return static_cast(rounds() * (static_cast(msec) / timer.elapsed())); } + + return 1; } QString Argon2Kdf::toString() const diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp index 67f894e44..be13cd188 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp @@ -325,6 +325,8 @@ void DatabaseSettingsWidgetEncryption::benchmarkTransformRounds(int millisecs) kdf->setRounds(m_ui->transformRoundsSpinBox->value()); if (IS_ARGON2(kdf->uuid())) { auto argon2Kdf = kdf.staticCast(); + // Set a small static number of rounds for the benchmark + argon2Kdf->setRounds(4); if (!argon2Kdf->setMemory(static_cast(m_ui->memorySpinBox->value()) * (1 << 10))) { m_ui->memorySpinBox->setValue(static_cast(argon2Kdf->memory() / (1 << 10))); } diff --git a/utils/vcpkg_ports/argon2/CMakeLists.txt b/utils/vcpkg_ports/argon2/CMakeLists.txt new file mode 100644 index 000000000..2b5469fed --- /dev/null +++ b/utils/vcpkg_ports/argon2/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.8) +project(argon2 C) + +set(PROJECT_VERSION 20190702) + +if(MSVC) + add_compile_options(/W3) +endif() + +include_directories(include src) + +set(SRC + src/argon2.c + src/core.c + src/encoding.c + src/ref.c + src/thread.c + src/opt.c + src/blake2/blake2b.c +) + +set(HEADERS + include/argon2.h +) + +add_library(argon2 ${SRC}) + +install( + TARGETS argon2 + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) + +if(NOT DISABLE_INSTALL_HEADERS) + install(FILES ${HEADERS} DESTINATION include) +endif() diff --git a/utils/vcpkg_ports/argon2/portfile.cmake b/utils/vcpkg_ports/argon2/portfile.cmake new file mode 100644 index 000000000..0128b9cd0 --- /dev/null +++ b/utils/vcpkg_ports/argon2/portfile.cmake @@ -0,0 +1,25 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO P-H-C/phc-winner-argon2 + REF 20190702 + SHA512 0a4cb89e8e63399f7df069e2862ccd05308b7652bf4ab74372842f66bcc60776399e0eaf979a7b7e31436b5e6913fe5b0a6949549d8c82ebd06e0629b106e85f + HEAD_REF master +) + +configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt ${SOURCE_PATH}/CMakeLists.txt COPYONLY) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + PREFER_NINJA + OPTIONS + ${FEATURE_OPTIONS} + OPTIONS_DEBUG + -DDISABLE_INSTALL_HEADERS=ON +) + +vcpkg_install_cmake() +vcpkg_copy_pdbs() + +file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) diff --git a/utils/vcpkg_ports/argon2/vcpkg.json b/utils/vcpkg_ports/argon2/vcpkg.json new file mode 100644 index 000000000..4583f89ac --- /dev/null +++ b/utils/vcpkg_ports/argon2/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "argon2", + "version-string": "20190702", + "port-version": 1, + "description": "Reference C implementation of Argon2, the password-hashing function that won the Password Hashing Competition (PHC).", + "homepage": "https://github.com/P-H-C/phc-winner-argon2" +}