diff options
Diffstat (limited to 'src/crypto')
-rw-r--r-- | src/crypto/kdf/AesKdf.cpp | 63 | ||||
-rw-r--r-- | src/crypto/kdf/AesKdf.h | 12 | ||||
-rw-r--r-- | src/crypto/kdf/Argon2Kdf.cpp | 116 | ||||
-rw-r--r-- | src/crypto/kdf/Argon2Kdf.h | 50 | ||||
-rw-r--r-- | src/crypto/kdf/Kdf.cpp | 39 | ||||
-rw-r--r-- | src/crypto/kdf/Kdf.h | 17 |
6 files changed, 229 insertions, 68 deletions
diff --git a/src/crypto/kdf/AesKdf.cpp b/src/crypto/kdf/AesKdf.cpp index 7056698d7..3177506fc 100644 --- a/src/crypto/kdf/AesKdf.cpp +++ b/src/crypto/kdf/AesKdf.cpp @@ -15,12 +15,17 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "AesKdf.h" + #include <QtConcurrent> #include "format/KeePass2.h" #include "crypto/CryptoHash.h" -#include "crypto/Random.h" -#include "AesKdf.h" + +AesKdf::AesKdf() + : Kdf::Kdf(KeePass2::KDF_AES) +{ +} bool AesKdf::transform(const QByteArray& raw, QByteArray& result) const { @@ -44,7 +49,7 @@ bool AesKdf::transform(const QByteArray& raw, QByteArray& result) const return true; } -bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, quint64 rounds, QByteArray* result) +bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds, QByteArray* result) { QByteArray iv(16, 0); SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb, @@ -65,44 +70,6 @@ bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, quin return true; } -AesKdf::AesKdf() - : Kdf::Kdf(KeePass2::KDF_AES) - , m_rounds(100000ull) - , m_seed(QByteArray(32, 0)) -{ -} - -quint64 AesKdf::rounds() const -{ - return m_rounds; -} - -QByteArray AesKdf::seed() const -{ - return m_seed; -} - -bool AesKdf::setRounds(quint64 rounds) -{ - m_rounds = rounds; - return true; -} - -bool AesKdf::setSeed(const QByteArray& seed) -{ - if (seed.size() != 32) { - return false; - } - - m_seed = seed; - return true; -} - -void AesKdf::randomizeTransformSalt() -{ - setSeed(randomGen()->randomArray(32)); -} - QSharedPointer<Kdf> AesKdf::clone() const { return QSharedPointer<AesKdf>::create(*this); @@ -117,17 +84,13 @@ int AesKdf::benchmarkImpl(int msec) const SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); cipher.init(seed, iv); - int rounds = 0; + quint64 rounds = 1000000; QElapsedTimer timer; timer.start(); - do { - if (!cipher.processInPlace(key, 10000)) { - rounds = -1; - break; - } - rounds += 10000; + + if (!cipher.processInPlace(key, rounds)) { + return -1; } - while (!timer.hasExpired(msec)); - return rounds; + return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed())); } diff --git a/src/crypto/kdf/AesKdf.h b/src/crypto/kdf/AesKdf.h index 28206a8d6..3e2c8ada6 100644 --- a/src/crypto/kdf/AesKdf.h +++ b/src/crypto/kdf/AesKdf.h @@ -26,25 +26,15 @@ public: AesKdf(); bool transform(const QByteArray& raw, QByteArray& result) const override; - void randomizeTransformSalt() override; QSharedPointer<Kdf> clone() const override; - quint64 rounds() const override; - QByteArray seed() const override; - - bool setRounds(quint64 rounds) override; - bool setSeed(const QByteArray& seed) override; - protected: int benchmarkImpl(int msec) const override; private: - quint64 m_rounds; - QByteArray m_seed; - static bool transformKeyRaw(const QByteArray& key, const QByteArray& seed, - quint64 rounds, + int rounds, QByteArray* result) Q_REQUIRED_RESULT; }; diff --git a/src/crypto/kdf/Argon2Kdf.cpp b/src/crypto/kdf/Argon2Kdf.cpp new file mode 100644 index 000000000..fa410dc93 --- /dev/null +++ b/src/crypto/kdf/Argon2Kdf.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 "Argon2Kdf.h" + +#include <QtConcurrent> +#include <argon2.h> + +#include "format/KeePass2.h" +#include "crypto/CryptoHash.h" + +/** + * KeePass' Argon2 implementation supports all parameters that are defined in the official specification, + * but only the number of iterations, the memory size and the degree of parallelism can be configured by + * the user in the database settings dialog. For the other parameters, KeePass chooses reasonable defaults: + * a 256-bit salt is generated each time the database is saved, the tag length is 256 bits, no secret key + * or associated data. KeePass uses the latest version of Argon2, v1.3. + */ +Argon2Kdf::Argon2Kdf() + : Kdf::Kdf(KeePass2::KDF_ARGON2) + , m_memory(1<<16) + , m_parallelism(2) +{ + m_rounds = 1; +} + +quint32 Argon2Kdf::memory() const +{ + // Convert to Megabytes + return m_memory / (1<<10); +} + +bool Argon2Kdf::setMemory(quint32 memoryMegabytes) +{ + // TODO: add bounds check + // Convert to Kibibytes + m_memory = (1<<10) * memoryMegabytes; + return true; +} + +quint32 Argon2Kdf::parallelism() const +{ + return m_parallelism; +} + +bool Argon2Kdf::setParallelism(quint32 threads) +{ + // TODO: add bounds check + m_parallelism = threads; + return true; +} + +bool Argon2Kdf::transform(const QByteArray& raw, QByteArray& result) const +{ + result.clear(); + result.resize(32); + + if (!transformKeyRaw(raw, seed(), rounds(), memory(), parallelism(), result)) { + return false; + } + + result = CryptoHash::hash(result, CryptoHash::Sha256); + return true; +} + +bool Argon2Kdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds, + quint32 memory, quint32 parallelism, QByteArray& result) +{ + // Time Cost, Mem Cost, Threads/Lanes, Password, length, Salt, length, out, length + int rc = argon2d_hash_raw(rounds, memory, parallelism, key.data(), key.size(), + seed.data(), seed.size(), result.data(), result.size()); + if (rc != ARGON2_OK) { + qWarning("Argon2 error: %s", argon2_error_message(rc)); + return false; + } + + return true; +} + +QSharedPointer<Kdf> Argon2Kdf::clone() const +{ + return QSharedPointer<Argon2Kdf>::create(*this); +} + +int Argon2Kdf::benchmarkImpl(int msec) const +{ + QByteArray key = QByteArray(16, '\x7E'); + QByteArray seed = QByteArray(32, '\x4B'); + + QElapsedTimer timer; + timer.start(); + + int rounds = 4; + + int rc = argon2d_hash_raw(rounds, m_memory, m_parallelism, key.data(), key.size(), seed.data(), seed.size(), key.data(), key.size()); + if (rc != ARGON2_OK) { + qWarning("Argon2 error: %s", argon2_error_message(rc)); + return -1; + } + + return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed())); +}
\ No newline at end of file diff --git a/src/crypto/kdf/Argon2Kdf.h b/src/crypto/kdf/Argon2Kdf.h new file mode 100644 index 000000000..c01698120 --- /dev/null +++ b/src/crypto/kdf/Argon2Kdf.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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_ARGON2KDF_H +#define KEEPASSX_ARGON2KDF_H + +#include "Kdf.h" + +class Argon2Kdf : public Kdf { +public: + Argon2Kdf(); + + bool transform(const QByteArray& raw, QByteArray& result) const override; + QSharedPointer<Kdf> clone() const override; + + quint32 memory() const; + bool setMemory(quint32 memory_kb); + quint32 parallelism() const; + bool setParallelism(quint32 threads); + +protected: + int benchmarkImpl(int msec) const override; + + quint32 m_memory; + quint32 m_parallelism; + +private: + static bool transformKeyRaw(const QByteArray& key, + const QByteArray& seed, + int rounds, + quint32 memory, + quint32 parallelism, + QByteArray& result) Q_REQUIRED_RESULT; +}; + +#endif // KEEPASSX_ARGON2KDF_H diff --git a/src/crypto/kdf/Kdf.cpp b/src/crypto/kdf/Kdf.cpp index cfc3025c1..5134adc5f 100644 --- a/src/crypto/kdf/Kdf.cpp +++ b/src/crypto/kdf/Kdf.cpp @@ -20,8 +20,12 @@ #include <QtConcurrent> +#include "crypto/Random.h" + Kdf::Kdf(Uuid uuid) - : m_uuid(uuid) + : m_rounds(KDF_DEFAULT_ROUNDS) + , m_seed(QByteArray(KDF_DEFAULT_SEED_SIZE, 0)) + , m_uuid(uuid) { } @@ -30,6 +34,37 @@ Uuid Kdf::uuid() const return m_uuid; } +int Kdf::rounds() const +{ + return m_rounds; +} + +QByteArray Kdf::seed() const +{ + return m_seed; +} + +bool Kdf::setRounds(int rounds) +{ + m_rounds = rounds; + return true; +} + +bool Kdf::setSeed(const QByteArray& seed) +{ + if (seed.size() != m_seed.size()) { + return false; + } + + m_seed = seed; + return true; +} + +void Kdf::randomizeSeed() +{ + setSeed(randomGen()->randomArray(m_seed.size())); +} + int Kdf::benchmark(int msec) const { BenchmarkThread thread1(msec, this); @@ -41,7 +76,7 @@ int Kdf::benchmark(int msec) const thread1.wait(); thread2.wait(); - return qMin(thread1.rounds(), thread2.rounds()); + return qMax(1, qMin(thread1.rounds(), thread2.rounds())); } Kdf::BenchmarkThread::BenchmarkThread(int msec, const Kdf* kdf) diff --git a/src/crypto/kdf/Kdf.h b/src/crypto/kdf/Kdf.h index 5330e71d0..cb0bcc364 100644 --- a/src/crypto/kdf/Kdf.h +++ b/src/crypto/kdf/Kdf.h @@ -22,6 +22,9 @@ #include "core/Uuid.h" +#define KDF_DEFAULT_SEED_SIZE 32 +#define KDF_DEFAULT_ROUNDS 100000ull + class Kdf { public: @@ -30,12 +33,13 @@ public: Uuid uuid() const; - virtual quint64 rounds() const = 0; - virtual bool setRounds(quint64 rounds) = 0; - virtual QByteArray seed() const = 0; - virtual bool setSeed(const QByteArray& seed) = 0; + int rounds() const; + virtual bool setRounds(int rounds); + QByteArray seed() const; + virtual bool setSeed(const QByteArray& seed); + virtual void randomizeSeed(); + virtual bool transform(const QByteArray& raw, QByteArray& result) const = 0; - virtual void randomizeTransformSalt() = 0; virtual QSharedPointer<Kdf> clone() const = 0; int benchmark(int msec) const; @@ -43,6 +47,9 @@ public: protected: virtual int benchmarkImpl(int msec) const = 0; + int m_rounds; + QByteArray m_seed; + private: class BenchmarkThread; const Uuid m_uuid; |