mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-03 12:06:25 -04:00
Add support for various algorithms for kdbx4
* Add SHA512 support to CryptoHash * Add ChaCha20 support * Add HMAC support * Add new HmacBlockStream, used in KDBX 4 * Add support for ChaCha20 protected stream
This commit is contained in:
parent
4532108678
commit
6a0d05e1ef
23 changed files with 616 additions and 25 deletions
|
@ -22,6 +22,7 @@
|
|||
|
||||
const Uuid KeePass2::CIPHER_AES = Uuid(QByteArray::fromHex("31c1f2e6bf714350be5805216afc5aff"));
|
||||
const Uuid KeePass2::CIPHER_TWOFISH = Uuid(QByteArray::fromHex("ad68f29f576f4bb9a36ad47af965346c"));
|
||||
const Uuid KeePass2::CIPHER_CHACHA20 = Uuid(QByteArray::fromHex("D6038A2B8B6F4CB5A524339A31DBB59A"));
|
||||
|
||||
const Uuid KeePass2::KDF_AES = Uuid(QByteArray::fromHex("C9D9F39A628A4460BF740D08C18A4FEA"));
|
||||
|
||||
|
@ -30,6 +31,7 @@ const QByteArray KeePass2::INNER_STREAM_SALSA20_IV("\xE8\x30\x09\x4B\x97\x20\x5D
|
|||
const QList<KeePass2::UuidNamePair> KeePass2::CIPHERS {
|
||||
KeePass2::UuidNamePair(KeePass2::CIPHER_AES, "AES: 256-bit"),
|
||||
KeePass2::UuidNamePair(KeePass2::CIPHER_TWOFISH, "Twofish: 256-bit"),
|
||||
KeePass2::UuidNamePair(KeePass2::CIPHER_CHACHA20, "ChaCha20: 256-bit")
|
||||
};
|
||||
const QList<KeePass2::UuidNamePair> KeePass2::KDFS {
|
||||
KeePass2::UuidNamePair(KeePass2::KDF_AES, "AES-KDF"),
|
||||
|
@ -53,6 +55,20 @@ Uuid KeePass2::kdfToUuid(const Kdf& kdf)
|
|||
}
|
||||
}
|
||||
|
||||
KeePass2::ProtectedStreamAlgo KeePass2::idToProtectedStreamAlgo(quint32 id)
|
||||
{
|
||||
switch (id) {
|
||||
case static_cast<quint32>(KeePass2::ArcFourVariant):
|
||||
return KeePass2::ArcFourVariant;
|
||||
case static_cast<quint32>(KeePass2::Salsa20):
|
||||
return KeePass2::Salsa20;
|
||||
case static_cast<quint32>(KeePass2::ChaCha20):
|
||||
return KeePass2::ChaCha20;
|
||||
default:
|
||||
return KeePass2::InvalidProtectedStreamAlgo;
|
||||
}
|
||||
}
|
||||
|
||||
KeePass2::UuidNamePair::UuidNamePair(const Uuid& uuid, const QString& name)
|
||||
: m_uuid(uuid)
|
||||
, m_name(name)
|
||||
|
|
|
@ -37,6 +37,7 @@ namespace KeePass2
|
|||
|
||||
extern const Uuid CIPHER_AES;
|
||||
extern const Uuid CIPHER_TWOFISH;
|
||||
extern const Uuid CIPHER_CHACHA20;
|
||||
|
||||
extern const Uuid KDF_AES;
|
||||
|
||||
|
@ -75,11 +76,14 @@ namespace KeePass2
|
|||
enum ProtectedStreamAlgo
|
||||
{
|
||||
ArcFourVariant = 1,
|
||||
Salsa20 = 2
|
||||
Salsa20 = 2,
|
||||
ChaCha20 = 3,
|
||||
InvalidProtectedStreamAlgo = -1
|
||||
};
|
||||
|
||||
Kdf* uuidToKdf(const Uuid& uuid);
|
||||
Uuid kdfToUuid(const Kdf& kdf);
|
||||
ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id);
|
||||
}
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2_H
|
||||
|
|
|
@ -20,16 +20,27 @@
|
|||
#include "crypto/CryptoHash.h"
|
||||
#include "format/KeePass2.h"
|
||||
|
||||
KeePass2RandomStream::KeePass2RandomStream()
|
||||
: m_cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt)
|
||||
KeePass2RandomStream::KeePass2RandomStream(KeePass2::ProtectedStreamAlgo algo)
|
||||
: m_cipher(mapAlgo(algo), SymmetricCipher::Stream, SymmetricCipher::Encrypt)
|
||||
, m_offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool KeePass2RandomStream::init(const QByteArray& key)
|
||||
{
|
||||
return m_cipher.init(CryptoHash::hash(key, CryptoHash::Sha256),
|
||||
KeePass2::INNER_STREAM_SALSA20_IV);
|
||||
switch (m_cipher.algorithm()) {
|
||||
case SymmetricCipher::Salsa20:
|
||||
return m_cipher.init(CryptoHash::hash(key, CryptoHash::Sha256),
|
||||
KeePass2::INNER_STREAM_SALSA20_IV);
|
||||
case SymmetricCipher::ChaCha20: {
|
||||
QByteArray keyIv = CryptoHash::hash(key, CryptoHash::Sha512);
|
||||
return m_cipher.init(keyIv.left(32), keyIv.mid(32, 12));
|
||||
}
|
||||
default:
|
||||
qWarning("Invalid stream algorithm (%d)", m_cipher.algorithm());
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray KeePass2RandomStream::randomBytes(int size, bool* ok)
|
||||
|
@ -109,3 +120,14 @@ bool KeePass2RandomStream::loadBlock()
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
SymmetricCipher::Algorithm KeePass2RandomStream::mapAlgo(KeePass2::ProtectedStreamAlgo algo) {
|
||||
switch (algo) {
|
||||
case KeePass2::ChaCha20:
|
||||
return SymmetricCipher::ChaCha20;
|
||||
case KeePass2::Salsa20:
|
||||
return SymmetricCipher::Salsa20;
|
||||
default:
|
||||
return SymmetricCipher::InvalidAlgorithm;
|
||||
}
|
||||
}
|
|
@ -21,11 +21,13 @@
|
|||
#include <QByteArray>
|
||||
|
||||
#include "crypto/SymmetricCipher.h"
|
||||
#include "KeePass2.h"
|
||||
|
||||
class KeePass2RandomStream
|
||||
{
|
||||
public:
|
||||
KeePass2RandomStream();
|
||||
KeePass2RandomStream(KeePass2::ProtectedStreamAlgo algo);
|
||||
|
||||
bool init(const QByteArray& key);
|
||||
QByteArray randomBytes(int size, bool* ok);
|
||||
QByteArray process(const QByteArray& data, bool* ok);
|
||||
|
@ -38,6 +40,8 @@ private:
|
|||
SymmetricCipher m_cipher;
|
||||
QByteArray m_buffer;
|
||||
int m_offset;
|
||||
|
||||
static SymmetricCipher::Algorithm mapAlgo(KeePass2::ProtectedStreamAlgo algo);
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2RANDOMSTREAM_H
|
||||
|
|
|
@ -41,6 +41,7 @@ KeePass2Reader::KeePass2Reader()
|
|||
, m_headerEnd(false)
|
||||
, m_saveXml(false)
|
||||
, m_db(nullptr)
|
||||
, m_irsAlgo(KeePass2::InvalidProtectedStreamAlgo)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -164,7 +165,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
|||
xmlDevice = ioCompressor.data();
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream;
|
||||
KeePass2RandomStream randomStream(m_irsAlgo);
|
||||
if (!randomStream.init(m_protectedStreamKey)) {
|
||||
raiseError(randomStream.errorString());
|
||||
return nullptr;
|
||||
|
@ -447,9 +448,14 @@ void KeePass2Reader::setInnerRandomStreamID(const QByteArray& data)
|
|||
}
|
||||
else {
|
||||
quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER);
|
||||
|
||||
if (id != KeePass2::Salsa20) {
|
||||
m_irsAlgo = KeePass2::idToProtectedStreamAlgo(id);
|
||||
if (m_irsAlgo == KeePass2::ArcFourVariant || m_irsAlgo == KeePass2::InvalidProtectedStreamAlgo) {
|
||||
raiseError("Unsupported random stream algorithm");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeePass2::ProtectedStreamAlgo KeePass2Reader::protectedStreamAlgo() const
|
||||
{
|
||||
return m_irsAlgo;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QCoreApplication>
|
||||
|
||||
#include "keys/CompositeKey.h"
|
||||
#include "format/KeePass2.h"
|
||||
|
||||
class Database;
|
||||
class QIODevice;
|
||||
|
@ -38,6 +39,7 @@ public:
|
|||
void setSaveXml(bool save);
|
||||
QByteArray xmlData();
|
||||
QByteArray streamKey();
|
||||
KeePass2::ProtectedStreamAlgo protectedStreamAlgo() const;
|
||||
|
||||
private:
|
||||
void raiseError(const QString& errorMessage);
|
||||
|
@ -67,6 +69,7 @@ private:
|
|||
QByteArray m_encryptionIV;
|
||||
QByteArray m_streamStartBytes;
|
||||
QByteArray m_protectedStreamKey;
|
||||
KeePass2::ProtectedStreamAlgo m_irsAlgo;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2READER_H
|
||||
|
|
|
@ -71,7 +71,7 @@ KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device,
|
|||
return qMakePair(RepairFailed, nullptr);
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream;
|
||||
KeePass2RandomStream randomStream(reader.protectedStreamAlgo());
|
||||
randomStream.init(reader.streamKey());
|
||||
KeePass2XmlReader xmlReader;
|
||||
QBuffer buffer(&xmlData);
|
||||
|
|
|
@ -131,7 +131,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
|||
m_device = ioCompressor.data();
|
||||
}
|
||||
|
||||
KeePass2RandomStream randomStream;
|
||||
KeePass2RandomStream randomStream(KeePass2::Salsa20);
|
||||
if (!randomStream.init(protectedStreamKey)) {
|
||||
raiseError(randomStream.errorString());
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue