diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a1c0ba91..5fa79d081 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,7 @@ configure_file( config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepa set(keepassx_SOURCES core/Database.cpp + core/Endian.cpp core/Entry.cpp core/Group.cpp core/Metadata.cpp @@ -28,6 +29,7 @@ set(keepassx_SOURCES crypto/CryptoHash.cpp crypto/Random.cpp crypto/SymmetricCipher.cpp + format/KeePass2Reader.cpp format/KeePass2XmlReader.cpp format/KeePass2XmlWriter.cpp gui/DatabaseWidget.cpp @@ -35,6 +37,11 @@ set(keepassx_SOURCES gui/EntryView.cpp gui/GroupModel.cpp gui/GroupView.cpp + keys/CompositeKey.cpp + keys/PasswordKey.cpp + streams/HashedBlockStream.cpp + streams/LayeredStream.cpp + streams/SymmetricCipherStream.cpp ) automoc4_add_library( keepassx_core STATIC ${keepassx_SOURCES} ) diff --git a/src/core/Endian.cpp b/src/core/Endian.cpp new file mode 100644 index 000000000..8fca50f12 --- /dev/null +++ b/src/core/Endian.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#include "Endian.h" + +#include +#include + +namespace Endian +{ + +qint16 bytesToInt16(const QByteArray& ba, QSysInfo::Endian byteOrder) +{ + Q_ASSERT(ba.size() == 2); + + if (byteOrder == QSysInfo::LittleEndian) { + return qFromLittleEndian(reinterpret_cast(ba.constData())); + } + else { + return qFromBigEndian(reinterpret_cast(ba.constData())); + } +} + +qint32 bytesToInt32(const QByteArray& ba, QSysInfo::Endian byteOrder) +{ + Q_ASSERT(ba.size() == 4); + + if (byteOrder == QSysInfo::LittleEndian) { + return qFromLittleEndian(reinterpret_cast(ba.constData())); + } + else { + return qFromBigEndian(reinterpret_cast(ba.constData())); + } +} + +qint64 bytesToInt64(const QByteArray& ba, QSysInfo::Endian byteOrder) +{ + Q_ASSERT(ba.size() == 8); + + if (byteOrder == QSysInfo::LittleEndian) { + return qFromLittleEndian(reinterpret_cast(ba.constData())); + } + else { + return qFromBigEndian(reinterpret_cast(ba.constData())); + } +} + +quint16 bytesToUInt16(const QByteArray& ba, QSysInfo::Endian byteOrder) +{ + return bytesToInt16(ba, byteOrder); +} + +quint32 bytesToUInt32(const QByteArray& ba, QSysInfo::Endian byteOrder) +{ + return bytesToInt32(ba, byteOrder); +} + +quint64 bytesToUInt64(const QByteArray& ba, QSysInfo::Endian byteOrder) +{ + return bytesToInt64(ba, byteOrder); +} + +qint16 readInt16(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok) +{ + QByteArray ba = device->read(2); + + if (ba.size() != 2) { + *ok = false; + return 0; + } + else { + *ok = true; + return bytesToUInt16(ba, byteOrder); + } +} + +qint32 readInt32(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok) +{ + QByteArray ba = device->read(4); + + if (ba.size() != 4) { + *ok = false; + return 0; + } + else { + *ok = true; + return bytesToUInt32(ba, byteOrder); + } +} + +qint64 readInt64(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok) +{ + QByteArray ba = device->read(8); + + if (ba.size() != 8) { + *ok = false; + return 0; + } + else { + *ok = true; + return bytesToUInt64(ba, byteOrder); + } +} + +quint16 readUInt16(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok) +{ + return readInt16(device, byteOrder, ok); +} + +quint32 readUInt32(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok) +{ + return readInt32(device, byteOrder, ok); +} + +quint64 readUInt64(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok) +{ + return readInt64(device, byteOrder, ok); +} + +} // namespace Endian diff --git a/src/core/Endian.h b/src/core/Endian.h new file mode 100644 index 000000000..8958cc400 --- /dev/null +++ b/src/core/Endian.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#ifndef KEEPASSX_ENDIAN_H +#define KEEPASSX_ENDIAN_H + +#include +#include + +class QIODevice; + +namespace Endian +{ + qint16 bytesToInt16(const QByteArray& ba, QSysInfo::Endian byteOrder); + quint16 bytesToUInt16(const QByteArray& ba, QSysInfo::Endian byteOrder); + qint32 bytesToInt32(const QByteArray& ba, QSysInfo::Endian byteOrder); + quint32 bytesToUInt32(const QByteArray& ba, QSysInfo::Endian byteOrder); + qint64 bytesToInt64(const QByteArray& ba, QSysInfo::Endian byteOrder); + quint64 bytesToUInt64(const QByteArray& ba, QSysInfo::Endian byteOrder); + + qint16 readInt16(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok); + quint16 readUInt16(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok); + qint32 readInt32(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok); + quint32 readUInt32(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok); + qint64 readInt64(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok); + quint64 readUInt64(QIODevice* device, QSysInfo::Endian byteOrder, bool* ok); +}; + +#endif // KEEPASSX_ENDIAN_H diff --git a/src/format/DatabaseReader.h b/src/format/DatabaseReader.h deleted file mode 100644 index d9ea3b997..000000000 --- a/src/format/DatabaseReader.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#ifndef KEEPASSX_DATABASEREADER_H -#define KEEPASSX_DATABASEREADER_H - -#include - -class Database; -class QIODevice; - -class DatabaseReader -{ -public: - virtual Database* readDatabase(QIODevice* device) = 0; - virtual bool error() = 0; - virtual QString errorString() = 0; -}; - -#endif // KEEPASSX_DATABASEREADER_H diff --git a/src/format/DatabaseWriter.h b/src/format/DatabaseWriter.h deleted file mode 100644 index 1624fad30..000000000 --- a/src/format/DatabaseWriter.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#ifndef KEEPASSX_DATABASEWRITER_H -#define KEEPASSX_DATABASEWRITER_H - -#include - -class Database; -class QIODevice; - -class DatabaseWriter -{ -public: - virtual void writeDatabase(QIODevice* device, Database* db) = 0; -}; - -#endif // KEEPASSX_DATABASEWRITER_H diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h new file mode 100644 index 000000000..0785a4eac --- /dev/null +++ b/src/format/KeePass2.h @@ -0,0 +1,53 @@ + /* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#ifndef KEEPASSX_KEEPASS2_H +#define KEEPASSX_KEEPASS2_H + +#include + +namespace KeePass2 +{ + const quint32 SIGNATURE_1 = 0x9AA2D903; + const quint32 SIGNATURE_2 = 0xB54BFB67; + const quint32 FILE_VERSION = 0x00020000; + const quint32 FILE_VERSION_CRITICAL_MASK = 0xFFFF0000; + + enum HeaderFieldID + { + EndOfHeader = 0, + Comment = 1, + CipherID = 2, + CompressionFlags = 3, + MasterSeed = 4, + TransformSeed = 5, + TransformRounds = 6, + EncryptionIV = 7, + ProtectedStreamKey = 8, + StreamStartBytes = 9, + InnerRandomStreamID = 10 + }; + + enum CompressionAlgorithm + { + CompressionNone = 0, + CompressionGZip = 1, + CompressionCount = 2 + }; +} + +#endif // KEEPASSX_KEEPASS2_H diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp new file mode 100644 index 000000000..23d99fb35 --- /dev/null +++ b/src/format/KeePass2Reader.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#include "KeePass2Reader.h" + +#include +#include +#include + +#include "KeePass2.h" +#include "KeePass2XmlReader.h" +#include "crypto/CryptoHash.h" +#include "streams/HashedBlockStream.h" +#include "streams/SymmetricCipherStream.h" + +const QSysInfo::Endian KeePass2Reader::BYTEORDER = QSysInfo::LittleEndian; + +Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key) +{ + m_device = device; + m_error = false; + m_errorStr = QString(); + m_headerEnd = false; + m_cipher = Uuid(); + + bool ok; + + quint32 signature1 = Endian::readUInt32(m_device, BYTEORDER, &ok); + if (!ok || signature1 != KeePass2::SIGNATURE_1) { + raiseError("1"); + return 0; + } + + quint32 signature2 = Endian::readUInt32(m_device, BYTEORDER, &ok); + if (!ok || signature2 != KeePass2::SIGNATURE_2) { + raiseError("2"); + return 0; + } + + quint32 version = Endian::readUInt32(m_device, BYTEORDER, &ok) & KeePass2::FILE_VERSION_CRITICAL_MASK; + quint32 expectedVersion = KeePass2::FILE_VERSION & KeePass2::FILE_VERSION_CRITICAL_MASK; + // TODO do we support old Kdbx versions? + if (!ok || (version != expectedVersion)) { + raiseError("3"); + return 0; + } + + while (readHeaderField() && !error()) { + } + + CryptoHash hash(CryptoHash::Sha256); + hash.addData(m_masterSeed); + hash.addData(key.transform(m_transformSeed, m_transformRounds)); + QByteArray finalKey = hash.result(); + + SymmetricCipherStream cipherStream(device, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt, finalKey, m_encryptionIV); + cipherStream.open(QIODevice::ReadOnly); + + QByteArray realStart = cipherStream.read(32); + + if (realStart != m_streamStartBytes) { + raiseError("4"); + } + + HashedBlockStream hashedStream(&cipherStream); + hashedStream.open(QIODevice::ReadOnly); + + KeePass2XmlReader xmlReader; + Database* db = xmlReader.readDatabase(&hashedStream); + return db; +} + +Database* KeePass2Reader::readDatabase(const QString& filename, const CompositeKey& key) +{ + QFile file(filename); + file.open(QFile::ReadOnly); + Database* db = readDatabase(&file, key); + // TODO check for QFile errors + return db; +} + +bool KeePass2Reader::error() +{ + return m_error; +} + +QString KeePass2Reader::errorString() +{ + // TODO + return QString(); +} + +void KeePass2Reader::raiseError(const QString& str) +{ + // TODO + qWarning("KeePass2Reader error: %s", qPrintable(str)); + m_error = true; +} + +bool KeePass2Reader::readHeaderField() +{ + QByteArray fieldIDArray = m_device->read(1); + if (fieldIDArray.size() != 1) { + raiseError(""); + return false; + } + quint8 fieldID = fieldIDArray.at(0); + + bool ok; + quint16 fieldLen = Endian::readUInt16(m_device, BYTEORDER, &ok); + if (!ok) { + raiseError(""); + return false; + } + + QByteArray fieldData; + if (fieldLen != 0) { + fieldData = m_device->read(fieldLen); + if (fieldData.size() != fieldLen) { + raiseError(""); + return false; + } + } + + switch (fieldID) { + case KeePass2::EndOfHeader: + m_headerEnd = true; + break; + + case KeePass2::CipherID: + setCipher(fieldData); + break; + + case KeePass2::CompressionFlags: + setCompressionFlags(fieldData); + break; + + case KeePass2::MasterSeed: + setMasterSeed(fieldData); + break; + + case KeePass2::TransformSeed: + setTransformSeed(fieldData); + break; + + case KeePass2::TransformRounds: + setTansformRounds(fieldData); + break; + + case KeePass2::EncryptionIV: + setEncryptionIV(fieldData); + break; + + case KeePass2::ProtectedStreamKey: + setProtectedStreamKey(fieldData); + break; + + case KeePass2::StreamStartBytes: + setStreamStartBytes(fieldData); + break; + + case KeePass2::InnerRandomStreamID: + setInnerRandomStreamID(fieldData); + break; + + default: + qWarning("Unknown header field read: id=%d", fieldID); + break; + } + + return !m_headerEnd; +} + +void KeePass2Reader::setCipher(const QByteArray& data) +{ + if (data.size() != Uuid::LENGTH) { + raiseError(""); + } + else { + m_cipher = Uuid(data); + } +} + +void KeePass2Reader::setCompressionFlags(const QByteArray& data) +{ + if (data.size() != 4) { + raiseError(""); + } + else { + quint32 id = Endian::bytesToUInt32(data, BYTEORDER); + + if (id >= KeePass2::CompressionCount) { + raiseError(""); + } + else { + m_compression = id; + } + } +} + +void KeePass2Reader::setMasterSeed(const QByteArray& data) +{ + if (data.size() != 32) { + raiseError(""); + } + else { + m_masterSeed = data; + } +} + +void KeePass2Reader::setTransformSeed(const QByteArray& data) +{ + if (data.size() != 32) { + raiseError(""); + } + else { + m_transformSeed = data; + } +} + +void KeePass2Reader::setTansformRounds(const QByteArray& data) +{ + if (data.size() != 8) { + raiseError(""); + } + else { + m_transformRounds = Endian::bytesToUInt64(data, BYTEORDER); + } +} + +void KeePass2Reader::setEncryptionIV(const QByteArray& data) +{ + if (data.size() != 16) { + raiseError(""); + } + else { + m_encryptionIV = data; + } +} + +void KeePass2Reader::setProtectedStreamKey(const QByteArray& data) +{ + // TODO ignore? +} + +void KeePass2Reader::setStreamStartBytes(const QByteArray& data) +{ + if (data.size() != 32) { + raiseError(""); + } + else { + m_streamStartBytes = data; + } +} + +void KeePass2Reader::setInnerRandomStreamID(const QByteArray& data) +{ + // TODO ignore? +} diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h new file mode 100644 index 000000000..396348036 --- /dev/null +++ b/src/format/KeePass2Reader.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#ifndef KEEPASSX_KEEPASS2READER_H +#define KEEPASSX_KEEPASS2READER_H + +#include + +#include "core/Endian.h" +#include "core/Uuid.h" +#include "keys/CompositeKey.h" + +class Database; + +class KeePass2Reader +{ + Q_DECLARE_TR_FUNCTIONS(KeePass2Reader); + +public: + Database* readDatabase(QIODevice* device, const CompositeKey& key); + Database* readDatabase(const QString& filename, const CompositeKey& key); + bool error(); + QString errorString(); + +private: + void raiseError(const QString& str); + + bool readHeaderField(); + + void setCipher(const QByteArray& data); + void setCompressionFlags(const QByteArray& data); + void setMasterSeed(const QByteArray& data); + void setTransformSeed(const QByteArray& data); + void setTansformRounds(const QByteArray& data); + void setEncryptionIV(const QByteArray& data); + void setProtectedStreamKey(const QByteArray& data); + void setStreamStartBytes(const QByteArray& data); + void setInnerRandomStreamID(const QByteArray& data); + + static const QSysInfo::Endian BYTEORDER; + + QIODevice* m_device; + bool m_error; + QString m_errorStr; + bool m_headerEnd; + + Uuid m_cipher; + int m_compression; + QByteArray m_masterSeed; + QByteArray m_transformSeed; + quint64 m_transformRounds; + QByteArray m_encryptionIV; + QByteArray m_streamStartBytes; +}; + +#endif // KEEPASSX_KEEPASS2READER_H diff --git a/src/format/KeePass2XmlReader.cpp b/src/format/KeePass2XmlReader.cpp index 3da526854..2522d8970 100644 --- a/src/format/KeePass2XmlReader.cpp +++ b/src/format/KeePass2XmlReader.cpp @@ -57,7 +57,7 @@ Database* KeePass2XmlReader::readDatabase(QIODevice* device) Database* KeePass2XmlReader::readDatabase(const QString& filename) { QFile file(filename); - file.open(QIODevice::ReadOnly | QIODevice::Text); + file.open(QIODevice::ReadOnly); return readDatabase(&file); } diff --git a/src/format/KeePass2XmlReader.h b/src/format/KeePass2XmlReader.h index c9e715ac9..f6c4d8993 100644 --- a/src/format/KeePass2XmlReader.h +++ b/src/format/KeePass2XmlReader.h @@ -23,7 +23,6 @@ #include #include -#include "DatabaseReader.h" #include "core/TimeInfo.h" #include "core/Uuid.h" @@ -32,7 +31,7 @@ class Entry; class Group; class Metadata; -class KeePass2XmlReader : public DatabaseReader +class KeePass2XmlReader { Q_DECLARE_TR_FUNCTIONS(KeePass2XmlReader); diff --git a/src/format/KeePass2XmlWriter.h b/src/format/KeePass2XmlWriter.h index e2149d6ef..8050697c6 100644 --- a/src/format/KeePass2XmlWriter.h +++ b/src/format/KeePass2XmlWriter.h @@ -23,7 +23,6 @@ #include #include -#include "DatabaseWriter.h" #include "core/Database.h" #include "core/Entry.h" #include "core/TimeInfo.h" @@ -32,7 +31,7 @@ class Group; class Metadata; -class KeePass2XmlWriter : public DatabaseWriter +class KeePass2XmlWriter { public: KeePass2XmlWriter(); diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp new file mode 100644 index 000000000..1fd5249f8 --- /dev/null +++ b/src/keys/CompositeKey.cpp @@ -0,0 +1,113 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#include "CompositeKey.h" + +#include + +#include "crypto/CryptoHash.h" +#include "crypto/SymmetricCipher.h" + +class KeyTransformation : public QThread +{ + Q_OBJECT + +public: + KeyTransformation(const QByteArray& key, const QByteArray& seed, int rounds); + QByteArray result(); + +protected: + void run(); + +private: + QByteArray m_key; + QByteArray m_seed; + int m_rounds; + QByteArray m_result; +}; + +CompositeKey::~CompositeKey() +{ + qDeleteAll(m_keys); +} + +CompositeKey* CompositeKey::clone() const +{ + return new CompositeKey(*this); +} + + +QByteArray CompositeKey::rawKey() const +{ + CryptoHash cryptoHash(CryptoHash::Sha256); + + Q_FOREACH (Key* key, m_keys) { + cryptoHash.addData(key->rawKey()); + } + + return cryptoHash.result(); +} + +QByteArray CompositeKey::transform(const QByteArray& seed, int rounds) const +{ + QByteArray key = rawKey(); + + KeyTransformation* transform1 = new KeyTransformation(key.left(16), seed, rounds); + KeyTransformation* transform2 = new KeyTransformation(key.right(16), seed, rounds); + + transform1->start(); + transform2->start(); + + transform1->wait(); + transform2->wait(); + + QByteArray transformed; + transformed.append(transform1->result()); + transformed.append(transform2->result()); + + return CryptoHash::hash(transformed, CryptoHash::Sha256); +} + +void CompositeKey::addKey(const Key& key) +{ + m_keys.append(key.clone()); +} + +KeyTransformation::KeyTransformation(const QByteArray& key, const QByteArray& seed, int rounds) + : m_key(key) + , m_seed(seed) + , m_rounds(rounds) + , m_result(key) +{ +} + +void KeyTransformation::run() +{ + QByteArray iv(16, 0); + SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Encrypt, m_seed, iv); + + for (int i=0; i +* +* 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 . +*/ + +#ifndef KEEPASSX_COMPOSITEKEY_H +#define KEEPASSX_COMPOSITEKEY_H + +#include + +#include "Key.h" + +class CompositeKey : public Key +{ +public: + ~CompositeKey(); + CompositeKey* clone() const; + + QByteArray rawKey() const; + QByteArray transform(const QByteArray& seed, int rounds) const; + void addKey(const Key& key); + +private: + QList m_keys; +}; + +#endif // KEEPASSX_COMPOSITEKEY_H diff --git a/src/keys/Key.h b/src/keys/Key.h new file mode 100644 index 000000000..b80a6b7ee --- /dev/null +++ b/src/keys/Key.h @@ -0,0 +1,30 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#ifndef KEEPASSX_KEY_H +#define KEEPASSX_KEY_H + +#include + +class Key +{ +public: + virtual QByteArray rawKey() const = 0; + virtual Key* clone() const = 0; +}; + +#endif // KEEPASSX_KEY_H diff --git a/src/keys/PasswordKey.cpp b/src/keys/PasswordKey.cpp new file mode 100644 index 000000000..b48e9186a --- /dev/null +++ b/src/keys/PasswordKey.cpp @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#include "PasswordKey.h" + +#include "crypto/CryptoHash.h" + +QByteArray PasswordKey::rawKey() const +{ + return m_key; +} + +void PasswordKey::setPassword(const QString& password) +{ + m_key = CryptoHash::hash(password.toUtf8(), CryptoHash::Sha256); +} + +PasswordKey* PasswordKey::clone() const +{ + return new PasswordKey(*this); +} diff --git a/src/keys/PasswordKey.h b/src/keys/PasswordKey.h new file mode 100644 index 000000000..259f1c825 --- /dev/null +++ b/src/keys/PasswordKey.h @@ -0,0 +1,36 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#ifndef KEEPASSX_PASSWORDKEY_H +#define KEEPASSX_PASSWORDKEY_H + +#include + +#include "Key.h" + +class PasswordKey : public Key +{ +public: + QByteArray rawKey() const; + void setPassword(const QString& password); + PasswordKey* clone() const; + +private: + QByteArray m_key; +}; + +#endif // KEEPASSX_PASSWORDKEY_H diff --git a/src/streams/HashedBlockStream.cpp b/src/streams/HashedBlockStream.cpp new file mode 100644 index 000000000..a63dba2c4 --- /dev/null +++ b/src/streams/HashedBlockStream.cpp @@ -0,0 +1,145 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#include "HashedBlockStream.h" + +#include + +#include "crypto/CryptoHash.h" + +const QSysInfo::Endian HashedBlockStream::BYTEORDER = QSysInfo::LittleEndian; + +HashedBlockStream::HashedBlockStream(QIODevice* baseDevice) + : LayeredStream(baseDevice) + , m_blockSize(1024*1024) +{ + init(); +} + +HashedBlockStream::HashedBlockStream(QIODevice* baseDevice, qint32 blockSize) + : LayeredStream(baseDevice) + , m_blockSize(blockSize) +{ + init(); +} + +void HashedBlockStream::init() +{ + m_bufferPos = 0; + m_blockIndex = 0; + m_eof = false; +} + +void HashedBlockStream::close() +{ + LayeredStream::close(); +} + +qint64 HashedBlockStream::readData(char* data, qint64 maxSize) +{ + if (m_eof) { + return 0; + } + + qint64 bytesRemaining = maxSize; + qint64 offset = 0; + + while (bytesRemaining > 0) { + if (m_bufferPos == m_buffer.size()) { + if (!readHashedBlock()) { + return maxSize - bytesRemaining; + } + } + + int bytesToCopy = qMin(bytesRemaining, static_cast(m_buffer.size() - m_bufferPos)); + + memcpy(data + offset, m_buffer.constData() + m_bufferPos, bytesToCopy); + + offset += bytesToCopy; + m_bufferPos += bytesToCopy; + bytesRemaining -= bytesToCopy; + } + + return maxSize; +} + +bool HashedBlockStream::readHashedBlock() +{ + bool ok; + + quint32 index = Endian::readUInt32(m_baseDevice, BYTEORDER, &ok); + if (!ok || index != m_blockIndex) { + // TODO error + Q_ASSERT(false); + return false; + } + + QByteArray hash = m_baseDevice->read(32); + if (hash.size() != 32) { + // TODO error + Q_ASSERT(false); + return false; + } + + m_blockSize = Endian::readInt32(m_baseDevice, BYTEORDER, &ok); + if (!ok || m_blockSize < 0) { + // TODO error + Q_ASSERT(false); + return false; + } + + if (m_blockSize == 0) { + if (hash.count(static_cast(0)) != 32) { + // TODO error + Q_ASSERT(false); + return false; + } + + m_eof = true; + return false; + } + + m_buffer = m_baseDevice->read(m_blockSize); + if (m_buffer.size() != m_blockSize) { + // TODO error + Q_ASSERT(false); + return false; + } + + if (hash != CryptoHash::hash(m_buffer, CryptoHash::Sha256)) { + // TODO error + Q_ASSERT(false); + return false; + } + + m_bufferPos = 0; + m_blockIndex++; + + return true; +} + +qint64 HashedBlockStream::writeData(const char* data, qint64 maxSize) +{ + // TODO implement + return 0; +} + +bool HashedBlockStream::writeHashedBlock() +{ + // TODO implement + return false; +} diff --git a/src/streams/HashedBlockStream.h b/src/streams/HashedBlockStream.h new file mode 100644 index 000000000..626974878 --- /dev/null +++ b/src/streams/HashedBlockStream.h @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#ifndef KEEPASSX_HASHEDBLOCKSTREAM_H +#define KEEPASSX_HASHEDBLOCKSTREAM_H + +#include "LayeredStream.h" +#include "core/Endian.h" +#include "crypto/CryptoHash.h" + +class HashedBlockStream : public LayeredStream +{ + Q_OBJECT + +public: + explicit HashedBlockStream(QIODevice* baseDevice); + HashedBlockStream(QIODevice* baseDevice, qint32 blockSize); + + void close(); + +protected: + qint64 readData(char* data, qint64 maxSize); + qint64 writeData(const char* data, qint64 maxSize); + +private: + void init(); + bool readHashedBlock(); + bool writeHashedBlock(); + + static const QSysInfo::Endian BYTEORDER; + qint32 m_blockSize; + QByteArray m_buffer; + int m_bufferPos; + quint32 m_blockIndex; + bool m_eof; +}; + +#endif // KEEPASSX_HASHEDBLOCKSTREAM_H diff --git a/src/streams/LayeredStream.cpp b/src/streams/LayeredStream.cpp new file mode 100644 index 000000000..dc4dbd828 --- /dev/null +++ b/src/streams/LayeredStream.cpp @@ -0,0 +1,72 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#include "LayeredStream.h" + +LayeredStream::LayeredStream(QIODevice* baseDevice) + : QIODevice(baseDevice) + , m_baseDevice(baseDevice) +{ + connect(baseDevice, SIGNAL(aboutToClose()), SLOT(closeStream())); +} + +bool LayeredStream::isSequential() const +{ + return true; +} + +QString LayeredStream::errorString() const +{ + return m_baseDevice->errorString(); +} + +bool LayeredStream::open(QIODevice::OpenMode mode) +{ + // filter out all other modes + mode &= QIODevice::ReadWrite; + + if (mode == QIODevice::ReadWrite) { + qWarning("Reading and writing at the same time is not supported."); + return false; + } + else if (openMode() & mode) { + return true; + } + else if (!(m_baseDevice->openMode() & mode)) { + qWarning("Base device is not opened correctly."); + return false; + } + else { + setOpenMode(mode | QIODevice::Unbuffered); + return true; + } +} + +qint64 LayeredStream::readData(char* data, qint64 maxSize) +{ + return m_baseDevice->read(data, maxSize); +} + +qint64 LayeredStream::writeData(const char* data, qint64 maxSize) +{ + return m_baseDevice->write(data, maxSize); +} + +void LayeredStream::closeStream() +{ + close(); +} diff --git a/src/streams/LayeredStream.h b/src/streams/LayeredStream.h new file mode 100644 index 000000000..86e9d1a6b --- /dev/null +++ b/src/streams/LayeredStream.h @@ -0,0 +1,44 @@ +/* +* Copyright (C) 2010 Felix Geyer +* +* 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 . +*/ + +#ifndef KEEPASSX_LAYEREDSTREAM_H +#define KEEPASSX_LAYEREDSTREAM_H + +#include + +class LayeredStream : public QIODevice +{ + Q_OBJECT + +public: + explicit LayeredStream(QIODevice* baseDevice); + + bool isSequential() const; + virtual QString errorString() const; + bool open(QIODevice::OpenMode mode); + +protected: + qint64 readData(char* data, qint64 maxSize); + qint64 writeData(const char* data, qint64 maxSize); + + QIODevice* m_baseDevice; + +private Q_SLOTS: + void closeStream(); +}; + +#endif // KEEPASSX_LAYEREDSTREAM_H diff --git a/src/streams/SymmetricCipherStream.cpp b/src/streams/SymmetricCipherStream.cpp new file mode 100644 index 000000000..483abefc3 --- /dev/null +++ b/src/streams/SymmetricCipherStream.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#include "SymmetricCipherStream.h" + +SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode, + SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv) + : LayeredStream(baseDevice) + , m_bufferPos(0) + , m_eof(false) +{ + m_cipher = new SymmetricCipher(algo, mode, direction, key, iv); +} + +bool SymmetricCipherStream::reset() +{ + m_buffer.clear(); + m_bufferPos = 0; + m_cipher->reset(); + return true; +} + +qint64 SymmetricCipherStream::readData(char* data, qint64 maxSize) +{ + // TODO m_eof is probably wrong and should be removed + if (m_eof) { + return 0; + } + + qint64 bytesRemaining = maxSize; + qint64 offset = 0; + + while (bytesRemaining > 0) { + if (m_bufferPos == m_buffer.size()) { + if (!readBlock()) { + return maxSize - bytesRemaining; + } + } + + int bytesToCopy = qMin(bytesRemaining, static_cast(m_buffer.size() - m_bufferPos)); + + memcpy(data + offset, m_buffer.constData() + m_bufferPos, bytesToCopy); + + offset += bytesToCopy; + m_bufferPos += bytesToCopy; + bytesRemaining -= bytesToCopy; + } + + return maxSize; +} + +bool SymmetricCipherStream::readBlock() +{ + m_buffer = m_baseDevice->read(m_cipher->blockSize()); + + if (m_buffer.size() != m_cipher->blockSize()) { + m_eof = true; + // TODO check if m_buffer.size()!=0 + return false; + } + else { + m_cipher->processInPlace(m_buffer); + m_bufferPos = 0; + return true; + } +} + +qint64 SymmetricCipherStream::writeData(const char* data, qint64 maxSize) +{ + // TODO implement + return 0; +} + +bool SymmetricCipherStream::writeBlock() +{ + // TODO implement + return false; +} diff --git a/src/streams/SymmetricCipherStream.h b/src/streams/SymmetricCipherStream.h new file mode 100644 index 000000000..1891f91c8 --- /dev/null +++ b/src/streams/SymmetricCipherStream.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Felix Geyer + * + * 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 . + */ + +#ifndef KEEPASSX_SYMMETRICCIPHERSTREAM_H +#define KEEPASSX_SYMMETRICCIPHERSTREAM_H + +#include + +#include "LayeredStream.h" +#include "crypto/SymmetricCipher.h" + +class SymmetricCipherStream : public LayeredStream +{ + Q_OBJECT + +public: + SymmetricCipherStream(QIODevice* baseDevice, SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode, + SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv); + bool reset(); + +protected: + qint64 readData(char* data, qint64 maxSize); + qint64 writeData(const char* data, qint64 maxSize); + +private: + bool readBlock(); + bool writeBlock(); + + SymmetricCipher* m_cipher; + QByteArray m_buffer; + int m_bufferPos; + bool m_eof; +}; + +#endif // KEEPASSX_SYMMETRICCIPHERSTREAM_H