mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-04 08:55:31 -05:00
Add initial support for encrypted database fields.
This commit is contained in:
parent
ce834cda46
commit
cfb55828b6
@ -114,6 +114,16 @@ const QHash<QString, QByteArray>& Entry::attachments() const
|
||||
return m_binaries;
|
||||
}
|
||||
|
||||
bool Entry::isAttributeProtected(const QString& key) const
|
||||
{
|
||||
return m_protectedAttributes.contains(key);
|
||||
}
|
||||
|
||||
bool Entry::isAttachmentProtected(const QString& key) const
|
||||
{
|
||||
return m_protectedAttachments.contains(key);
|
||||
}
|
||||
|
||||
QString Entry::title() const
|
||||
{
|
||||
return m_attributes.value("Title");
|
||||
@ -207,9 +217,12 @@ void Entry::addAutoTypeAssociation(const AutoTypeAssociation& assoc)
|
||||
m_autoTypeAssociations << assoc;
|
||||
}
|
||||
|
||||
void Entry::addAttribute(const QString& key, const QString& value)
|
||||
void Entry::addAttribute(const QString& key, const QString& value, bool protect)
|
||||
{
|
||||
m_attributes.insert(key, value);
|
||||
if (protect) {
|
||||
m_protectedAttributes.insert(key);
|
||||
}
|
||||
|
||||
// TODO add all visible oclumns
|
||||
if (key == "Title") {
|
||||
@ -217,9 +230,24 @@ void Entry::addAttribute(const QString& key, const QString& value)
|
||||
}
|
||||
}
|
||||
|
||||
void Entry::addAttachment(const QString& key, const QByteArray& value)
|
||||
void Entry::removeAttribute(const QString& key)
|
||||
{
|
||||
m_attributes.remove(key);
|
||||
m_protectedAttributes.remove(key);
|
||||
}
|
||||
|
||||
void Entry::addAttachment(const QString& key, const QByteArray& value, bool protect)
|
||||
{
|
||||
m_binaries.insert(key, value);
|
||||
if (protect) {
|
||||
m_protectedAttachments.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
void Entry::removeAttachment(const QString& key)
|
||||
{
|
||||
m_binaries.remove(key);
|
||||
m_protectedAttachments.remove(key);
|
||||
}
|
||||
|
||||
void Entry::setTitle(const QString& title)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define KEEPASSX_ENTRY_H
|
||||
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtGui/QColor>
|
||||
#include <QtGui/QIcon>
|
||||
@ -57,6 +58,8 @@ public:
|
||||
const QList<AutoTypeAssociation>& autoTypeAssociations() const;
|
||||
const QHash<QString, QString>& attributes() const;
|
||||
const QHash<QString, QByteArray>& attachments() const;
|
||||
bool isAttributeProtected(const QString& key) const;
|
||||
bool isAttachmentProtected(const QString& key) const;
|
||||
QString title() const;
|
||||
QString url() const;
|
||||
QString username() const;
|
||||
@ -75,8 +78,10 @@ public:
|
||||
void setAutoTypeObfuscation(int obfuscation);
|
||||
void setDefaultAutoTypeSequence(const QString& sequence);
|
||||
void addAutoTypeAssociation(const AutoTypeAssociation& assoc);
|
||||
void addAttribute(const QString& key, const QString& value);
|
||||
void addAttachment(const QString& key, const QByteArray& value);
|
||||
void addAttribute(const QString& key, const QString& value, bool protect = false);
|
||||
void removeAttribute(const QString& key);
|
||||
void addAttachment(const QString& key, const QByteArray& value, bool protect = false);
|
||||
void removeAttachment(const QString& key);
|
||||
void setTitle(const QString& title);
|
||||
void setUrl(const QString& url);
|
||||
void setUsername(const QString& username);
|
||||
@ -109,6 +114,8 @@ private:
|
||||
QList<AutoTypeAssociation> m_autoTypeAssociations;
|
||||
QHash<QString, QString> m_attributes;
|
||||
QHash<QString, QByteArray> m_binaries;
|
||||
QSet<QString> m_protectedAttributes;
|
||||
QSet<QString> m_protectedAttachments;
|
||||
|
||||
QList<Entry*> m_history;
|
||||
Group* m_group;
|
||||
|
@ -47,6 +47,12 @@ namespace KeePass2
|
||||
StreamStartBytes = 9,
|
||||
InnerRandomStreamID = 10
|
||||
};
|
||||
|
||||
enum ProtectedStreamAlgo
|
||||
{
|
||||
ArcFourVariant = 1,
|
||||
Salsa20 = 2
|
||||
};
|
||||
}
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2_H
|
||||
|
@ -62,6 +62,8 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
||||
while (readHeaderField() && !error()) {
|
||||
}
|
||||
|
||||
// TODO check if all header fields have been parsed
|
||||
|
||||
QByteArray transformedMasterKey = key.transform(m_db->transformSeed(), m_db->transformRounds());
|
||||
m_db->setTransformedMasterKey(transformedMasterKey);
|
||||
|
||||
@ -96,8 +98,14 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
||||
xmlDevice = ioCompressor.data();
|
||||
}
|
||||
|
||||
QByteArray protectedStreamKey = CryptoHash::hash(m_protectedStreamKey, CryptoHash::Sha256);
|
||||
QByteArray protectedStreamIv("\xE8\x30\x09\x4B\x97\x20\x5D\x2A");
|
||||
|
||||
SymmetricCipher protectedStream(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Decrypt,
|
||||
protectedStreamKey, protectedStreamIv);
|
||||
|
||||
KeePass2XmlReader xmlReader;
|
||||
xmlReader.readDatabase(xmlDevice, m_db);
|
||||
xmlReader.readDatabase(xmlDevice, m_db, &protectedStream);
|
||||
// TODO forward error messages from xmlReader
|
||||
return m_db;
|
||||
}
|
||||
@ -279,7 +287,12 @@ void KeePass2Reader::setEncryptionIV(const QByteArray& data)
|
||||
|
||||
void KeePass2Reader::setProtectedStreamKey(const QByteArray& data)
|
||||
{
|
||||
// TODO ignore?
|
||||
if (data.size() != 32) {
|
||||
raiseError("");
|
||||
}
|
||||
else {
|
||||
m_protectedStreamKey = data;
|
||||
}
|
||||
}
|
||||
|
||||
void KeePass2Reader::setStreamStartBytes(const QByteArray& data)
|
||||
@ -294,5 +307,14 @@ void KeePass2Reader::setStreamStartBytes(const QByteArray& data)
|
||||
|
||||
void KeePass2Reader::setInnerRandomStreamID(const QByteArray& data)
|
||||
{
|
||||
// TODO ignore?
|
||||
if (data.size() != 4) {
|
||||
raiseError("");
|
||||
}
|
||||
else {
|
||||
quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER);
|
||||
|
||||
if (id != KeePass2::Salsa20) {
|
||||
raiseError("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ private:
|
||||
QByteArray m_masterSeed;
|
||||
QByteArray m_encryptionIV;
|
||||
QByteArray m_streamStartBytes;
|
||||
QByteArray m_protectedStreamKey;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2READER_H
|
||||
|
@ -24,17 +24,19 @@
|
||||
#include "core/Metadata.h"
|
||||
|
||||
KeePass2XmlReader::KeePass2XmlReader()
|
||||
: m_db(0)
|
||||
: m_cipher(0)
|
||||
, m_db(0)
|
||||
, m_meta(0)
|
||||
{
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db)
|
||||
void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, SymmetricCipher* cipher)
|
||||
{
|
||||
m_xml.setDevice(device);
|
||||
|
||||
m_db = db;
|
||||
m_meta = m_db->metadata();
|
||||
m_cipher = cipher;
|
||||
|
||||
m_tmpParent = new Group();
|
||||
m_tmpParent->setParent(m_db);
|
||||
@ -516,7 +518,21 @@ void KeePass2XmlReader::parseEntryString(Entry *entry)
|
||||
key = readString();
|
||||
}
|
||||
else if (m_xml.name() == "Value") {
|
||||
entry->addAttribute(key, readString());
|
||||
QXmlStreamAttributes attr = m_xml.attributes();
|
||||
QString value = readString();
|
||||
|
||||
bool isProtected = attr.hasAttribute("Protected") && (attr.value("Protected") == "True");
|
||||
|
||||
if (isProtected && !value.isEmpty()) {
|
||||
if (m_cipher) {
|
||||
value = m_cipher->process(QByteArray::fromBase64(value.toAscii()));
|
||||
}
|
||||
else {
|
||||
raiseError();
|
||||
}
|
||||
}
|
||||
|
||||
entry->addAttribute(key, value, isProtected);
|
||||
}
|
||||
else {
|
||||
skipCurrentElement();
|
||||
@ -534,7 +550,16 @@ void KeePass2XmlReader::parseEntryBinary(Entry *entry)
|
||||
key = readString();
|
||||
}
|
||||
else if (m_xml.name() == "Value") {
|
||||
entry->addAttachment(key, readBinary());
|
||||
QByteArray value = readBinary();
|
||||
QXmlStreamAttributes attr = m_xml.attributes();
|
||||
|
||||
bool isProtected = attr.hasAttribute("Protected") && (attr.value("Protected") == "True");
|
||||
|
||||
if (isProtected && !value.isEmpty()) {
|
||||
m_cipher->processInPlace(value);
|
||||
}
|
||||
|
||||
entry->addAttachment(key, value, isProtected);
|
||||
}
|
||||
else {
|
||||
skipCurrentElement();
|
||||
|
@ -25,11 +25,13 @@
|
||||
|
||||
#include "core/TimeInfo.h"
|
||||
#include "core/Uuid.h"
|
||||
#include "crypto/SymmetricCipher.h"
|
||||
|
||||
class Database;
|
||||
class Entry;
|
||||
class Group;
|
||||
class Metadata;
|
||||
class SymmetricCipher;
|
||||
|
||||
class KeePass2XmlReader
|
||||
{
|
||||
@ -38,7 +40,7 @@ class KeePass2XmlReader
|
||||
public:
|
||||
KeePass2XmlReader();
|
||||
Database* readDatabase(QIODevice* device);
|
||||
void readDatabase(QIODevice* device, Database* db);
|
||||
void readDatabase(QIODevice* device, Database* db, SymmetricCipher* cipher = 0);
|
||||
Database* readDatabase(const QString& filename);
|
||||
bool error();
|
||||
QString errorString();
|
||||
@ -77,6 +79,7 @@ private:
|
||||
void skipCurrentElement();
|
||||
|
||||
QXmlStreamReader m_xml;
|
||||
SymmetricCipher* m_cipher;
|
||||
Database* m_db;
|
||||
Metadata* m_meta;
|
||||
Group* m_tmpParent;
|
||||
|
@ -21,20 +21,23 @@
|
||||
#include <QtCore/QFile>
|
||||
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/SymmetricCipher.h"
|
||||
|
||||
KeePass2XmlWriter::KeePass2XmlWriter()
|
||||
: m_db(0)
|
||||
, m_meta(0)
|
||||
, m_cipher(0)
|
||||
{
|
||||
m_xml.setAutoFormatting(true);
|
||||
m_xml.setAutoFormattingIndent(-1); // 1 tab
|
||||
m_xml.setCodec("UTF-8");
|
||||
}
|
||||
|
||||
void KeePass2XmlWriter::writeDatabase(QIODevice* device, Database* db)
|
||||
void KeePass2XmlWriter::writeDatabase(QIODevice* device, Database* db, SymmetricCipher* cipher)
|
||||
{
|
||||
m_db = db;
|
||||
m_meta = db->metadata();
|
||||
m_cipher = cipher;
|
||||
|
||||
m_xml.setDevice(device);
|
||||
|
||||
@ -50,11 +53,11 @@ void KeePass2XmlWriter::writeDatabase(QIODevice* device, Database* db)
|
||||
m_xml.writeEndDocument();
|
||||
}
|
||||
|
||||
void KeePass2XmlWriter::writeDatabase(const QString& filename, Database* db)
|
||||
void KeePass2XmlWriter::writeDatabase(const QString& filename, Database* db, SymmetricCipher* cipher)
|
||||
{
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly|QIODevice::Truncate);
|
||||
writeDatabase(&file, db);
|
||||
writeDatabase(&file, db, cipher);
|
||||
}
|
||||
|
||||
void KeePass2XmlWriter::writeMetadata()
|
||||
@ -271,15 +274,50 @@ void KeePass2XmlWriter::writeEntry(const Entry* entry)
|
||||
|
||||
Q_FOREACH (const QString& key, entry->attributes().keys()) {
|
||||
m_xml.writeStartElement("String");
|
||||
writeString("Key", key);
|
||||
writeString("Value", entry->attributes().value(key));
|
||||
|
||||
bool protect = ( ((key == "Title") && m_meta->protectTitle()) ||
|
||||
((key == "UserName") && m_meta->protectUsername()) ||
|
||||
((key == "Password") && m_meta->protectPassword()) ||
|
||||
((key == "URL") && m_meta->protectUrl()) ||
|
||||
((key == "Notes") && m_meta->protectNotes()) ||
|
||||
entry->isAttributeProtected(key) ) &&
|
||||
m_cipher;
|
||||
|
||||
m_xml.writeStartElement("Key");
|
||||
if (protect) {
|
||||
m_xml.writeAttribute("Protected", "True");
|
||||
}
|
||||
m_xml.writeCharacters(key);
|
||||
m_xml.writeEndElement();
|
||||
|
||||
if (protect) {
|
||||
writeBinary("Value", m_cipher->process(entry->attributes().value(key).toUtf8()));
|
||||
}
|
||||
else {
|
||||
writeString("Value", entry->attributes().value(key));
|
||||
}
|
||||
|
||||
m_xml.writeEndElement();
|
||||
}
|
||||
|
||||
Q_FOREACH (const QString& key, entry->attachments().keys()) {
|
||||
m_xml.writeStartElement("Binary");
|
||||
writeString("Key", key);
|
||||
writeBinary("Value", entry->attachments().value(key));
|
||||
|
||||
bool protect = entry->isAttachmentProtected(key) && m_cipher;
|
||||
m_xml.writeStartElement("Key");
|
||||
if (protect) {
|
||||
m_xml.writeAttribute("Protected", "True");
|
||||
}
|
||||
m_xml.writeCharacters(key);
|
||||
m_xml.writeEndElement();
|
||||
|
||||
if (protect) {
|
||||
writeBinary("Value", m_cipher->process(entry->attachments().value(key)));
|
||||
}
|
||||
else {
|
||||
writeBinary("Value", entry->attachments().value(key));
|
||||
}
|
||||
|
||||
m_xml.writeEndElement();
|
||||
}
|
||||
|
||||
|
@ -30,13 +30,14 @@
|
||||
|
||||
class Group;
|
||||
class Metadata;
|
||||
class SymmetricCipher;
|
||||
|
||||
class KeePass2XmlWriter
|
||||
{
|
||||
public:
|
||||
KeePass2XmlWriter();
|
||||
void writeDatabase(QIODevice* device, Database* db);
|
||||
void writeDatabase(const QString& filename, Database* db);
|
||||
void writeDatabase(QIODevice* device, Database* db, SymmetricCipher* cipher = 0);
|
||||
void writeDatabase(const QString& filename, Database* db, SymmetricCipher* cipher = 0);
|
||||
bool error();
|
||||
QString errorString();
|
||||
|
||||
@ -71,6 +72,7 @@ private:
|
||||
QXmlStreamWriter m_xml;
|
||||
Database* m_db;
|
||||
Metadata* m_meta;
|
||||
SymmetricCipher* m_cipher;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2XMLWRITER_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user