Implement reading encrypted kdbx files.

This commit is contained in:
Felix Geyer 2010-09-13 23:24:36 +02:00
parent bb6ae3a014
commit 9f282928e8
22 changed files with 1290 additions and 71 deletions

View file

@ -0,0 +1,145 @@
/*
* 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 "HashedBlockStream.h"
#include <cstring>
#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<qint64>(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<char>(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;
}

View file

@ -0,0 +1,52 @@
/*
* 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/>.
*/
#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

View file

@ -0,0 +1,72 @@
/*
* 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 "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();
}

View file

@ -0,0 +1,44 @@
/*
* 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/>.
*/
#ifndef KEEPASSX_LAYEREDSTREAM_H
#define KEEPASSX_LAYEREDSTREAM_H
#include <QtCore/QIODevice>
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

View file

@ -0,0 +1,92 @@
/*
* 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 "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<qint64>(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;
}

View file

@ -0,0 +1,49 @@
/*
* 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/>.
*/
#ifndef KEEPASSX_SYMMETRICCIPHERSTREAM_H
#define KEEPASSX_SYMMETRICCIPHERSTREAM_H
#include <QtCore/QByteArray>
#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