From f215ffa3fa540074ef5c3fe956f3223f266bb3ba Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Wed, 21 Dec 2011 23:22:07 +0100 Subject: Implement support for key files. Closes #5. --- src/CMakeLists.txt | 1 + src/keys/FileKey.cpp | 275 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/keys/FileKey.h | 50 ++++++++++ 3 files changed, 326 insertions(+) create mode 100644 src/keys/FileKey.cpp create mode 100644 src/keys/FileKey.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c10a19d93..4f8e41b29 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(keepassx_SOURCES gui/KeyOpenDialog.cpp gui/MainWindow.cpp keys/CompositeKey.cpp + keys/FileKey.cpp keys/Key.h keys/PasswordKey.cpp streams/HashedBlockStream.cpp diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp new file mode 100644 index 000000000..40f9db64e --- /dev/null +++ b/src/keys/FileKey.cpp @@ -0,0 +1,275 @@ +/* +* Copyright (C) 2011 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 "FileKey.h" + +#include +#include + +#include "crypto/CryptoHash.h" +#include "crypto/Random.h" + +FileKey::FileKey() +{ +} + +bool FileKey::load(QIODevice* device) +{ + // we may need to read the file multiple times + if (device->isSequential()) { + return false; + } + + if (device->size() == 0) { + return false; + } + + if (!device->reset()) { + return false; + } + if (loadXml(device)) { + return true; + } + + if (!device->reset()) { + return false; + } + if (loadBinary(device)) { + return true; + } + + if (!device->reset()) { + return false; + } + if (loadHex(device)) { + return true; + } + + if (!device->reset()) { + return false; + } + if (loadHashed(device)) { + return true; + } + + return false; +} + +bool FileKey::load(const QString& fileName, QString* errorMsg) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + if (errorMsg) { + *errorMsg = file.errorString(); + } + return false; + } + bool result = load(&file); + + file.close(); + + if (file.error()) { + result = false; + if (errorMsg) { + *errorMsg = file.errorString(); + } + } + + return result; +} + +QByteArray FileKey::rawKey() const +{ + return m_key; +} + +FileKey* FileKey::clone() const +{ + return new FileKey(*this); +} + +void FileKey::create(QIODevice* device) +{ + QXmlStreamWriter xmlWriter(device); + + xmlWriter.writeStartDocument("1.0"); + + xmlWriter.writeStartElement("KeyFile"); + + xmlWriter.writeStartElement("Meta"); + + xmlWriter.writeTextElement("Version", "1.00"); + + xmlWriter.writeEndElement(); + + xmlWriter.writeStartElement("Key"); + + QByteArray data = Random::randomArray(32); + xmlWriter.writeTextElement("Data", QString::fromAscii(data.toBase64())); + + xmlWriter.writeEndElement(); + + xmlWriter.writeEndDocument(); +} + +bool FileKey::create(const QString& fileName, QString* errorMsg) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly)) { + if (errorMsg) { + *errorMsg = file.errorString(); + } + return false; + } + create(&file); + + file.close(); + + if (file.error()) { + if (errorMsg) { + *errorMsg = file.errorString(); + } + + return false; + } + else { + return true; + } +} + +bool FileKey::loadXml(QIODevice *device) +{ + QXmlStreamReader xmlReader(device); + + if (!xmlReader.error() && xmlReader.readNextStartElement()) { + if (xmlReader.name() != "KeyFile") { + return false; + } + } + else { + return false; + } + + bool correctMeta = false; + QByteArray data; + + while (!xmlReader.error() && xmlReader.readNextStartElement()) { + if (xmlReader.name() == "Meta") { + correctMeta = loadXmlMeta(xmlReader); + } + else if (xmlReader.name() == "Key") { + data = loadXmlKey(xmlReader); + } + } + + if (!xmlReader.error() && correctMeta && !data.isEmpty()) { + m_key = data; + return true; + } + else { + return false; + } +} + +bool FileKey::loadXmlMeta(QXmlStreamReader& xmlReader) +{ + bool corectVersion = false; + + while (!xmlReader.error() && xmlReader.readNextStartElement()) { + if (xmlReader.name() == "Version") { + // TODO error message about incompatible key file version + if (xmlReader.readElementText() == "1.00") { + corectVersion = true; + } + } + } + + return corectVersion; +} + +QByteArray FileKey::loadXmlKey(QXmlStreamReader& xmlReader) +{ + QByteArray data; + + while (!xmlReader.error() && xmlReader.readNextStartElement()) { + if (xmlReader.name() == "Data") { + // TODO do we need to enforce a specific data.size()? + data = QByteArray::fromBase64(xmlReader.readElementText().toAscii()); + } + } + + return data; +} + +bool FileKey::loadBinary(QIODevice *device) +{ + if (device->size() != 32) { + return false; + } + + m_key = device->readAll(); + return true; +} + +bool FileKey::loadHex(QIODevice *device) +{ + if (device->size() != 64) { + return false; + } + + QByteArray data = device->readAll(); + + if (!isHex(data)) { + return false; + } + + QByteArray key = QByteArray::fromHex(data); + + if (key.size() != 32) { + return false; + } + + m_key = key; + return true; +} + +bool FileKey::loadHashed(QIODevice *device) +{ + CryptoHash cryptoHash(CryptoHash::Sha256); + + QByteArray buffer; + + while (!device->atEnd()) { + buffer = device->read(1024); + cryptoHash.addData(buffer); + } + + m_key = cryptoHash.result(); + + return true; +} + +bool FileKey::isHex(const QByteArray& ba) +{ + Q_FOREACH (char c, ba) { + if ( !( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) ) { + return false; + } + } + + return true; +} diff --git a/src/keys/FileKey.h b/src/keys/FileKey.h new file mode 100644 index 000000000..cc2c87255 --- /dev/null +++ b/src/keys/FileKey.h @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2011 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_FILEKEY_H +#define KEEPASSX_FILEKEY_H + +#include + +#include "keys/Key.h" + +class QIODevice; + +class FileKey : public Key +{ +public: + FileKey(); + bool load(QIODevice* device); + bool load(const QString& fileName, QString* errorMsg = 0); + QByteArray rawKey() const; + FileKey* clone() const; + static void create(QIODevice* device); + static bool create(const QString& fileName, QString* errorMsg = 0); + +private: + bool loadXml(QIODevice* device); + bool loadXmlMeta(QXmlStreamReader& xmlReader); + QByteArray loadXmlKey(QXmlStreamReader& xmlReader); + bool loadBinary(QIODevice* device); + bool loadHex(QIODevice* device); + bool loadHashed(QIODevice* device); + static bool isHex(const QByteArray& ba); + + QByteArray m_key; +}; + +#endif // KEEPASSX_FILEKEY_H -- cgit v1.2.3