mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Start implementing support for reading KeePass 1 databases.
For now only decrypting the database works. Still missing: - Actually parsing the database. - Key files. Refs #1
This commit is contained in:
parent
38e421d9c1
commit
ebce183925
@ -43,6 +43,8 @@ set(keepassx_SOURCES
|
|||||||
crypto/SymmetricCipherBackend.h
|
crypto/SymmetricCipherBackend.h
|
||||||
crypto/SymmetricCipherGcrypt.cpp
|
crypto/SymmetricCipherGcrypt.cpp
|
||||||
crypto/SymmetricCipherSalsa20.cpp
|
crypto/SymmetricCipherSalsa20.cpp
|
||||||
|
format/KeePass1.h
|
||||||
|
format/KeePass1Reader.cpp
|
||||||
format/KeePass2.h
|
format/KeePass2.h
|
||||||
format/KeePass2RandomStream.cpp
|
format/KeePass2RandomStream.cpp
|
||||||
format/KeePass2Reader.cpp
|
format/KeePass2Reader.cpp
|
||||||
|
42
src/format/KeePass1.h
Normal file
42
src/format/KeePass1.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_KEEPASS1_H
|
||||||
|
#define KEEPASSX_KEEPASS1_H
|
||||||
|
|
||||||
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
|
#include "core/Uuid.h"
|
||||||
|
|
||||||
|
namespace KeePass1
|
||||||
|
{
|
||||||
|
const quint32 SIGNATURE_1 = 0x9AA2D903;
|
||||||
|
const quint32 SIGNATURE_2 = 0xB54BFB65;
|
||||||
|
const quint32 PWM_DBVER_DW = 0x00030002;
|
||||||
|
const quint32 FILE_VERSION = 0x00030002;
|
||||||
|
const quint32 FILE_VERSION_CRITICAL_MASK = 0xFFFFFF00;
|
||||||
|
|
||||||
|
const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian;
|
||||||
|
|
||||||
|
enum EncryptionFlags
|
||||||
|
{
|
||||||
|
Rijndael = 2,
|
||||||
|
Twofish = 8
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KEEPASSX_KEEPASS1_H
|
321
src/format/KeePass1Reader.cpp
Normal file
321
src/format/KeePass1Reader.cpp
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
/*
|
||||||
|
* 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 "KeePass1Reader.h"
|
||||||
|
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QTextCodec>
|
||||||
|
|
||||||
|
#include "core/Database.h"
|
||||||
|
#include "core/Endian.h"
|
||||||
|
#include "crypto/CryptoHash.h"
|
||||||
|
#include "format/KeePass1.h"
|
||||||
|
#include "keys/CompositeKey.h"
|
||||||
|
#include "streams/SymmetricCipherStream.h"
|
||||||
|
|
||||||
|
class KeePass1Key : public CompositeKey
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual QByteArray rawKey() const;
|
||||||
|
virtual void clear();
|
||||||
|
void setPassword(const QByteArray& password);
|
||||||
|
void setKeyfileData(const QByteArray& keyfileData);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray m_password;
|
||||||
|
QByteArray m_keyfileData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
KeePass1Reader::KeePass1Reader()
|
||||||
|
: m_error(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& password,
|
||||||
|
const QByteArray& keyfileData)
|
||||||
|
{
|
||||||
|
QScopedPointer<Database> db(new Database());
|
||||||
|
m_db = db.data();
|
||||||
|
m_device = device;
|
||||||
|
m_error = false;
|
||||||
|
m_errorStr = QString();
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
quint32 signature1 = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok || signature1 != 0x9AA2D903) {
|
||||||
|
raiseError(tr("Not a KeePass database."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 signature2 = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok || signature2 != 0xB54BFB65) {
|
||||||
|
raiseError(tr("Not a KeePass database."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_encryptionFlags = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok || !(m_encryptionFlags & KeePass1::Rijndael || m_encryptionFlags & KeePass1::Twofish)) {
|
||||||
|
raiseError(tr("Unsupported encryption algorithm."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 version = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok || (version & KeePass1::FILE_VERSION_CRITICAL_MASK)
|
||||||
|
!= (KeePass1::FILE_VERSION & KeePass1::FILE_VERSION_CRITICAL_MASK)) {
|
||||||
|
raiseError(tr("Unsupported KeePass database version."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_masterSeed = m_device->read(16);
|
||||||
|
if (m_masterSeed.size() != 16) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_encryptionIV = m_device->read(16);
|
||||||
|
if (m_encryptionIV.size() != 16) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 numGroups = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 numEntries = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_contentHashHeader = m_device->read(32);
|
||||||
|
if (m_contentHashHeader.size() != 32) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_transformSeed = m_device->read(32);
|
||||||
|
if (m_transformSeed.size() != 32) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_transformRounds = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
m_db->setTransformRounds(m_transformRounds);
|
||||||
|
|
||||||
|
qint64 contentPos = m_device->pos();
|
||||||
|
|
||||||
|
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
|
||||||
|
|
||||||
|
if (!cipherStream) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
Database* KeePass1Reader::readDatabase(const QString& filename, const QString& password,
|
||||||
|
const QByteArray& keyfileData)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QFile::ReadOnly)) {
|
||||||
|
raiseError(file.errorString());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScopedPointer<Database> db(readDatabase(&file, password, keyfileData));
|
||||||
|
|
||||||
|
if (file.error() != QFile::NoError) {
|
||||||
|
raiseError(file.errorString());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeePass1Reader::hasError()
|
||||||
|
{
|
||||||
|
return m_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString KeePass1Reader::errorString()
|
||||||
|
{
|
||||||
|
return m_errorStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymmetricCipherStream* KeePass1Reader::testKeys(const QString& password, const QByteArray& keyfileData,
|
||||||
|
qint64 contentPos)
|
||||||
|
{
|
||||||
|
QList<PasswordEncoding> encodings;
|
||||||
|
encodings << Windows1252 << Latin1 << UTF8;
|
||||||
|
|
||||||
|
QScopedPointer<SymmetricCipherStream> cipherStream;
|
||||||
|
QByteArray passwordData;
|
||||||
|
QTextCodec* codec = QTextCodec::codecForName("Windows-1252");
|
||||||
|
QByteArray passwordDataCorrect = codec->fromUnicode(password);
|
||||||
|
|
||||||
|
Q_FOREACH (PasswordEncoding encoding, encodings) {
|
||||||
|
if (encoding == Windows1252) {
|
||||||
|
passwordData = passwordDataCorrect;
|
||||||
|
}
|
||||||
|
else if (encoding == Latin1) {
|
||||||
|
// KeePassX used Latin-1 encoding for passwords until version 0.3.1
|
||||||
|
// but KeePass/Win32 uses Windows Codepage 1252.
|
||||||
|
passwordData = password.toLatin1();
|
||||||
|
|
||||||
|
if (passwordData == passwordDataCorrect) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning("Testing password encoded as Latin-1.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (encoding == UTF8) {
|
||||||
|
// KeePassX used UTF-8 encoding for passwords until version 0.2.2
|
||||||
|
// but KeePass/Win32 uses Windows Codepage 1252.
|
||||||
|
passwordData = password.toUtf8();
|
||||||
|
|
||||||
|
if (passwordData == passwordDataCorrect) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning("Testing password encoded as UTF-8.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray finalKey = key(passwordData, keyfileData);
|
||||||
|
if (m_encryptionFlags & KeePass1::Rijndael) {
|
||||||
|
cipherStream.reset(new SymmetricCipherStream(m_device, SymmetricCipher::Aes256,
|
||||||
|
SymmetricCipher::Cbc, SymmetricCipher::Decrypt, finalKey, m_encryptionIV));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO twofish
|
||||||
|
}
|
||||||
|
|
||||||
|
cipherStream->open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
bool success = verifyKey(cipherStream.data());
|
||||||
|
|
||||||
|
cipherStream->reset();
|
||||||
|
cipherStream->close();
|
||||||
|
if (!m_device->seek(contentPos)) {
|
||||||
|
// TODO error
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
cipherStream->open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cipherStream.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipherStream.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray KeePass1Reader::key(const QByteArray& password, const QByteArray& keyfileData)
|
||||||
|
{
|
||||||
|
KeePass1Key key;
|
||||||
|
key.setPassword(password);
|
||||||
|
key.setKeyfileData(keyfileData);
|
||||||
|
|
||||||
|
CryptoHash hash(CryptoHash::Sha256);
|
||||||
|
hash.addData(m_masterSeed);
|
||||||
|
hash.addData(key.transform(m_transformSeed, m_transformRounds));
|
||||||
|
return hash.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KeePass1Reader::verifyKey(SymmetricCipherStream* cipherStream)
|
||||||
|
{
|
||||||
|
CryptoHash contentHash(CryptoHash::Sha256);
|
||||||
|
QByteArray buffer;
|
||||||
|
buffer.resize(16384);
|
||||||
|
qint64 readResult;
|
||||||
|
do {
|
||||||
|
readResult = cipherStream->read(buffer.data(), buffer.size());
|
||||||
|
if (readResult > 0) {
|
||||||
|
if (readResult != buffer.size()) {
|
||||||
|
buffer.resize(readResult);
|
||||||
|
}
|
||||||
|
qDebug("read %d", buffer.size());
|
||||||
|
contentHash.addData(buffer);
|
||||||
|
}
|
||||||
|
} while (readResult == buffer.size());
|
||||||
|
|
||||||
|
return contentHash.result() == m_contentHashHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeePass1Reader::raiseError(const QString& str)
|
||||||
|
{
|
||||||
|
m_error = true;
|
||||||
|
m_errorStr = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QByteArray KeePass1Key::rawKey() const
|
||||||
|
{
|
||||||
|
if (m_keyfileData.isEmpty()) {
|
||||||
|
return CryptoHash::hash(m_password, CryptoHash::Sha256);
|
||||||
|
}
|
||||||
|
else if (m_password.isEmpty()) {
|
||||||
|
return m_keyfileData;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CryptoHash keyHash(CryptoHash::Sha256);
|
||||||
|
keyHash.addData(m_password);
|
||||||
|
keyHash.addData(m_keyfileData);
|
||||||
|
return keyHash.result();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeePass1Key::clear()
|
||||||
|
{
|
||||||
|
CompositeKey::clear();
|
||||||
|
|
||||||
|
m_password.clear();
|
||||||
|
m_keyfileData.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeePass1Key::setPassword(const QByteArray& password)
|
||||||
|
{
|
||||||
|
m_password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeePass1Key::setKeyfileData(const QByteArray& keyfileData)
|
||||||
|
{
|
||||||
|
m_keyfileData = keyfileData;
|
||||||
|
}
|
67
src/format/KeePass1Reader.h
Normal file
67
src/format/KeePass1Reader.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_KEEPASS1READER_H
|
||||||
|
#define KEEPASSX_KEEPASS1READER_H
|
||||||
|
|
||||||
|
#include <QtCore/QCoreApplication>
|
||||||
|
|
||||||
|
class Database;
|
||||||
|
class SymmetricCipherStream;
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
class KeePass1Reader
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(KeePass1Reader)
|
||||||
|
|
||||||
|
public:
|
||||||
|
KeePass1Reader();
|
||||||
|
Database* readDatabase(QIODevice* device, const QString& password,
|
||||||
|
const QByteArray& keyfileData);
|
||||||
|
Database* readDatabase(const QString& filename, const QString& password,
|
||||||
|
const QByteArray& keyfileData);
|
||||||
|
bool hasError();
|
||||||
|
QString errorString();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum PasswordEncoding
|
||||||
|
{
|
||||||
|
Windows1252,
|
||||||
|
Latin1,
|
||||||
|
UTF8
|
||||||
|
};
|
||||||
|
|
||||||
|
SymmetricCipherStream* testKeys(const QString& password, const QByteArray& keyfileData,
|
||||||
|
qint64 contentPos);
|
||||||
|
QByteArray key(const QByteArray& password, const QByteArray& keyfileData);
|
||||||
|
bool verifyKey(SymmetricCipherStream* cipherStream);
|
||||||
|
void raiseError(const QString& str);
|
||||||
|
|
||||||
|
Database* m_db;
|
||||||
|
QIODevice* m_device;
|
||||||
|
quint32 m_encryptionFlags;
|
||||||
|
QByteArray m_masterSeed;
|
||||||
|
QByteArray m_encryptionIV;
|
||||||
|
QByteArray m_contentHashHeader;
|
||||||
|
QByteArray m_transformSeed;
|
||||||
|
quint32 m_transformRounds;
|
||||||
|
|
||||||
|
bool m_error;
|
||||||
|
QString m_errorStr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_KEEPASS1READER_H
|
@ -127,6 +127,7 @@ add_unit_test(NAME testmodified SOURCES TestModified.cpp MOCS TestModified.h LIB
|
|||||||
|
|
||||||
add_unit_test(NAME testdeletedobjects SOURCES TestDeletedObjects.cpp MOCS TestDeletedObjects.h LIBS ${TEST_LIBRARIES})
|
add_unit_test(NAME testdeletedobjects SOURCES TestDeletedObjects.cpp MOCS TestDeletedObjects.h LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
|
add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp MOCS TestKeePass1Reader.h LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
if(WITH_GUI_TESTS)
|
if(WITH_GUI_TESTS)
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
|
45
tests/TestKeePass1Reader.cpp
Normal file
45
tests/TestKeePass1Reader.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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 "TestKeePass1Reader.h"
|
||||||
|
|
||||||
|
#include <QtTest/QTest>
|
||||||
|
|
||||||
|
#include "config-keepassx-tests.h"
|
||||||
|
#include "tests.h"
|
||||||
|
#include "core/Database.h"
|
||||||
|
#include "crypto/Crypto.h"
|
||||||
|
#include "format/KeePass1Reader.h"
|
||||||
|
|
||||||
|
void TestKeePass1Reader::initTestCase()
|
||||||
|
{
|
||||||
|
Crypto::init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestKeePass1Reader::testBasic()
|
||||||
|
{
|
||||||
|
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/basic.kdb");
|
||||||
|
|
||||||
|
KeePass1Reader reader;
|
||||||
|
Database* db = reader.readDatabase(filename, "masterpw", QByteArray());
|
||||||
|
QVERIFY(db);
|
||||||
|
QVERIFY(!reader.hasError());
|
||||||
|
|
||||||
|
delete db;
|
||||||
|
}
|
||||||
|
|
||||||
|
KEEPASSX_QTEST_CORE_MAIN(TestKeePass1Reader)
|
32
tests/TestKeePass1Reader.h
Normal file
32
tests/TestKeePass1Reader.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_TESTKEEPASS1READER_H
|
||||||
|
#define KEEPASSX_TESTKEEPASS1READER_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
class TestKeePass1Reader : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void initTestCase();
|
||||||
|
void testBasic();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_TESTKEEPASS1READER_H
|
BIN
tests/data/basic.kdb
Normal file
BIN
tests/data/basic.kdb
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user