From 366d8a0f0e1390eecfe3f16b8af1c89418d72821 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Thu, 10 May 2012 22:46:36 +0200 Subject: [PATCH] Set master key after reading the database. --- src/format/KeePass1Reader.cpp | 55 +++++++++++++++++++++++++++++----- src/format/KeePass1Reader.h | 6 ++-- tests/TestKeePass1Reader.cpp | 32 ++++++++++---------- tests/TestKeePass1Reader.h | 2 ++ tests/data/basic.kdb | Bin 1836 -> 1836 bytes 5 files changed, 68 insertions(+), 27 deletions(-) diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp index d0ff5fbf4..21448ef81 100644 --- a/src/format/KeePass1Reader.cpp +++ b/src/format/KeePass1Reader.cpp @@ -30,6 +30,8 @@ #include "crypto/CryptoHash.h" #include "format/KeePass1.h" #include "keys/CompositeKey.h" +#include "keys/FileKey.h" +#include "keys/PasswordKey.h" #include "streams/SymmetricCipherStream.h" class KeePass1Key : public CompositeKey @@ -52,8 +54,26 @@ KeePass1Reader::KeePass1Reader() } Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& password, - const QByteArray& keyfileData) + QIODevice* keyfileDevice) { + QByteArray keyfileData; + FileKey newFileKey; + + if (keyfileDevice) { + keyfileData = readKeyfile(keyfileDevice); + + if (keyfileData.isEmpty()) { + return 0; + } + if (!keyfileDevice->seek(0)) { + return 0; + } + + if (!newFileKey.load(keyfileDevice)) { + return 0; + } + } + QScopedPointer db(new Database()); QScopedPointer tmpParent(new Group()); m_db = db.data(); @@ -197,22 +217,41 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor entry->setUpdateTimeinfo(true); } + CompositeKey key; + if (!password.isEmpty()) { + key.addKey(PasswordKey(password)); + } + if (keyfileDevice) { + key.addKey(newFileKey); + } + + db->setKey(key); + return db.take(); } Database* KeePass1Reader::readDatabase(const QString& filename, const QString& password, - const QByteArray& keyfileData) + const QString& keyfileName) { - QFile file(filename); - if (!file.open(QFile::ReadOnly)) { - raiseError(file.errorString()); + QFile dbFile(filename); + if (!dbFile.open(QFile::ReadOnly)) { + raiseError(dbFile.errorString()); return 0; } - QScopedPointer db(readDatabase(&file, password, keyfileData)); + QScopedPointer keyFile; + if (!keyfileName.isEmpty()) { + keyFile.reset(new QFile(keyfileName)); + if (!keyFile->open(QFile::ReadOnly)) { + raiseError(keyFile->errorString()); + return 0; + } + } - if (file.error() != QFile::NoError) { - raiseError(file.errorString()); + QScopedPointer db(readDatabase(&dbFile, password, keyFile.data())); + + if (dbFile.error() != QFile::NoError) { + raiseError(dbFile.errorString()); return 0; } diff --git a/src/format/KeePass1Reader.h b/src/format/KeePass1Reader.h index 040343681..c90328288 100644 --- a/src/format/KeePass1Reader.h +++ b/src/format/KeePass1Reader.h @@ -35,10 +35,9 @@ class KeePass1Reader public: KeePass1Reader(); Database* readDatabase(QIODevice* device, const QString& password, - const QByteArray& keyfileData); + QIODevice* keyfileDevice); Database* readDatabase(const QString& filename, const QString& password, - const QByteArray& keyfileData); - static QByteArray readKeyfile(QIODevice* device); + const QString& keyfileName); bool hasError(); QString errorString(); @@ -61,6 +60,7 @@ private: bool parseGroupTreeState(const QByteArray& data); bool parseCustomIcons4(const QByteArray& data); void raiseError(const QString& str); + static QByteArray readKeyfile(QIODevice* device); static QDateTime dateFromPackedStruct(const QByteArray& data); static bool isMetaStream(const Entry* entry); diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index dbf11144d..587204131 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -17,7 +17,7 @@ #include "TestKeePass1Reader.h" -#include +#include #include #include "config-keepassx-tests.h" @@ -28,6 +28,10 @@ #include "core/Metadata.h" #include "crypto/Crypto.h" #include "format/KeePass1Reader.h" +#include "format/KeePass2Reader.h" +#include "format/KeePass2Writer.h" +#include "keys/FileKey.h" +#include "keys/PasswordKey.h" void TestKeePass1Reader::initTestCase() { @@ -36,7 +40,7 @@ void TestKeePass1Reader::initTestCase() QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/basic.kdb"); KeePass1Reader reader; - m_db = reader.readDatabase(filename, "masterpw", QByteArray()); + m_db = reader.readDatabase(filename, "masterpw", 0); QVERIFY(m_db); QVERIFY(!reader.hasError()); } @@ -98,6 +102,12 @@ void TestKeePass1Reader::testBasic() QCOMPARE(group2->iconNumber(), 19); } +void TestKeePass1Reader::testMasterKey() +{ + QVERIFY(m_db->hasKey()); + QCOMPARE(m_db->transformRounds(), static_cast(713)); +} + void TestKeePass1Reader::testCustomIcons() { QCOMPARE(m_db->metadata()->customIcons().size(), 1); @@ -136,12 +146,7 @@ void TestKeePass1Reader::testFileKey() QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name); QString keyFilename = QString("%1/%2.key").arg(QString(KEEPASSX_TEST_DATA_DIR), name); - QFile file(keyFilename); - QVERIFY(file.open(QIODevice::ReadOnly)); - QByteArray keyData = KeePass1Reader::readKeyfile(&file); - QVERIFY(!keyData.isEmpty()); - - Database* db = reader.readDatabase(dbFilename, QString(), keyData); + Database* db = reader.readDatabase(dbFilename, QString(), keyFilename); QVERIFY(db); QVERIFY(!reader.hasError()); QCOMPARE(db->rootGroup()->children().size(), 1); @@ -167,12 +172,7 @@ void TestKeePass1Reader::testCompositeKey() QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name); QString keyFilename = QString("%1/FileKeyHex.key").arg(QString(KEEPASSX_TEST_DATA_DIR)); - QFile file(keyFilename); - QVERIFY(file.open(QIODevice::ReadOnly)); - QByteArray keyData = KeePass1Reader::readKeyfile(&file); - QVERIFY(!keyData.isEmpty()); - - Database* db = reader.readDatabase(dbFilename, "mypassword", keyData); + Database* db = reader.readDatabase(dbFilename, "mypassword", keyFilename); QVERIFY(db); QVERIFY(!reader.hasError()); QCOMPARE(db->rootGroup()->children().size(), 1); @@ -189,7 +189,7 @@ void TestKeePass1Reader::testTwofish() QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name); - Database* db = reader.readDatabase(dbFilename, "masterpw", QByteArray()); + Database* db = reader.readDatabase(dbFilename, "masterpw", 0); QVERIFY(db); QVERIFY(!reader.hasError()); QCOMPARE(db->rootGroup()->children().size(), 1); @@ -207,7 +207,7 @@ void TestKeePass1Reader::testCP1252Password() QString dbFilename = QString("%1/%2.kdb").arg(QString(KEEPASSX_TEST_DATA_DIR), name); QString password = QString::fromUtf8("\xe2\x80\x9e\x70\x61\x73\x73\x77\x6f\x72\x64\xe2\x80\x9d"); - Database* db = reader.readDatabase(dbFilename, password, QByteArray()); + Database* db = reader.readDatabase(dbFilename, password, 0); QVERIFY(db); QVERIFY(!reader.hasError()); QCOMPARE(db->rootGroup()->children().size(), 1); diff --git a/tests/TestKeePass1Reader.h b/tests/TestKeePass1Reader.h index d9b1deb3d..cc2d1c5da 100644 --- a/tests/TestKeePass1Reader.h +++ b/tests/TestKeePass1Reader.h @@ -22,6 +22,7 @@ #include class Database; +class QIODevice; class TestKeePass1Reader : public QObject { @@ -30,6 +31,7 @@ class TestKeePass1Reader : public QObject private Q_SLOTS: void initTestCase(); void testBasic(); + void testMasterKey(); void testCustomIcons(); void testGroupExpanded(); void testFileKey(); diff --git a/tests/data/basic.kdb b/tests/data/basic.kdb index 3bf5f75a1bf128323b90d393e28c37b7f2e119aa..b0daacabcec4972da2852da366ef81c466173f54 100644 GIT binary patch literal 1836 zcmV+{2h;ci*`k_d`%AS00000200RJdW@d;2tYuZ2HL=D4AML#s*_#2jWg1D z@Y4PU000050000F31eFNQ{@xLr*2kGMMUc-mKyoSP+7+l3#h$=+D1c{vL^V zX7t{vzoAF3}RC=nK7k$gO_#*xch5;dChhIpq)Q1@4y&5g3!6eaqw zCjT$0n**%bq=u=9@ch9~isI2Ij&KTN+Q=LUsFQ4&K+tk^J95774&x-GmqCQ1y~ZOZ zsrHWS;jd&Xon&ut8O)HKhws`W@lqOLZBpU&+`|-I{mq1uH$|%)!D=|hP%K7cMEOF} z`~`k$>^E>-z!R)jtc`^-SPp)ur{Q2hvOu0yyMbe9-+JM!EodyB_;ovK0Cz>$&DSDH zg6RhoU*Oq;G>T{kvkguP<25_&eIYr$Pb=s)+zB$4OI#c5kHrH9DRwFrv_@olHDN;E z#ijxnEMo%pndrveBQ}Dqak>lj2pQh!r6yr{zH;R)u|fLMHqge|f%^gO_D!)+NS+1b z`!D>no;J4A_1th*jKeJcn5z~8Kx#Z^3Zqpmzke;$Ugqp%^9x)s+0EaU&f{Q5eJUsD z@Jh|dBlLt^V;YfS4r9~ox=8xF;GrDkELvuXDJs0@@8Oa3rq=!rj6UOX=wx2Ffs@lH zySQOk$feD#qghn_rsBiPj&9NV>}8Jn+<(}u0S;sE%KuxyV|UlmEOeqP<9Kk{->U31 zuu-E!U8N3?E*{gIK=(FQY+fuv->lvuh-MB;Ewgcj`XwD|N6(NO)nt|X_vXKKy1VnX zRNbg!MDK%7qoyYXWh6R}AyR{Gf~b1Bw2EF&9)F@EX^7XE5vMX4a6ha}$tDjD895J5|oRWi1!lGZkz;tgdwW#tPz(AX!S7KkWe<8wPD zXy1H!M|XOF6c#3{`bx7KSeCz(2KW`5OI$dc`($CW>r%W6LfQ%yGNmXxGh+d1p*9qo4;% zV#Uha&Wz@MYiP$_VJ3Zx+iBjfFdt@<2ZH`N62^Q*Q6*5pMw)^}NNmTt29F9xb}P4I zFnHbF=M>=tm=ptMe+GXhq&=&{BY)QN(7ur%o}nV~`M{Cdrd#$0Mb9XZ`60&V_p8#< zUm4u;N+TGmoR(`uxum>5c&lK{t&OZ2RZ&@S_zt+uxjC<)+ZHhq`N@fe9}?jVVIY>DfLr`mhI# zyJzrZKYV*hD9YSB^&??Zj8$#kWmvz~^86UmN@+`X*3@|w$OdTz7jC&Ydq|qwfCwnf zE?f;M(vaa#^vsG~jl$Z@s|N7yY{4^Sq7;OzoT$g1?%$vzu7G9QhCIk!&jrY;e}8to zD_aVf?u55O`rEQ%1sW-rhTBL`^Na_iNObg2R%EjJic%E@wRQ0!ztg zjWn(5?Pxj84*;zjnm^kS!u}0zUV*@!yJ6c26c%A$9ASqCtR+GfZVNFKyBCY;?ny53 z$m5PX8tw?{2p@Qh-$87OKzd%rfl#x+IYX$^0DV+=xpx_X@u{Az0KEr^>o@-rQ|e{d zS%y9YD;Iij^-FMe>k3w7^J+9&GXjs}R?z+c{%symm0-_~Hi1*%XGC$GHstp>7K3>a z;#yY{?iZPcF=Y;KLFhCsCOn+g_y+U;tStx;7?E)k^d@q0^7Y_NHN|#I7sN%Qs(-Oo zHk-zwDyi>Z4lq-f7;*l;*t}m##zV<0q3f{+0%^yRjcE}G^tUnp0_`@cMzt1{)h zg>FNzUtrRiF3DFhy{~E$4u5K`e~OUHp{fH&f4053{1(={cF!F?;Wqo!pXLXodn!i$ zdm!UyFJ_d^`@+AHzBUB(7r0u2cq`UfGTMpd-Dn2AL#wHrt0#4lGt^+@;17x7S{)U| z!-|6#LzX^KGI#)UZcFz-!{p4EU&kV(fP!N1ShT5bkfJGAQz}Rm0&5^+bVJ>NGW3=r+#f0RpDG)!8sJl!`X7sMv^3=rG@ aq0Ah6iDDy~ky*s)KAWy&lW|p0&jKimGmu6A literal 1836 zcmV+{2h;ci*`k_d`%AS00000200RIQSC+g=@spdHh?gro+`BrAi0%dCtjfi6!k}qv zXh?|$000050001vpyLY02m*pIUwLju>_+ny5Z!LSFC_^TgdSypprfFpVB-ClhZFNQ zOrP59tx6op-)29g^0$wv+RqYsmm3pM!vFw}9I#{eXVqR;^Zper~jXcrZRUZJ8 z|A-sVwD$+Ex8m~|Ver@OQiJ48j1=C?YLiuayF{Jx3tefeO{~ou?*fORKMS6s#h*5v zb>FLnroM#BgOZ0Vx_tlyCqv1#8EQqIa{^eQU8Ea3bs#Z?RtnfBb323UA8+|$pEH10 zEnslp{5W(nXa_!{*gH|~D7jD~@6)Y1My_;a!CxN3OcR*dXTw+rPtw|t9{Z?+=nH=b z@OnI4YHLBYg`MtkUPg80zEh}HH1kq6vH#>&n^Jb^L*;c3ueGQ+0kGm}hIq=Z88d02cY@{ltlvGqI52h|JkB&U#0J$Rz-(=|weZ!3SOb_S4;9P6O*l@8NIEdwW znhQoW-WzDDbhi-0D@{n(hZ_I-ar2<$vS$?u8^(l|QEH#7R|}juxuFETYP^fDuhq|^ z0zwvCD_ZQUty;J&h+oP{SD`oETFqfg$09Q@lFTKjwwSqG(7?Mr#o*uHc#b#@0x%bo z(F?s_#R;IHli9zC7BKLnmf}@{iPf2fa0|*n8o^C8l zc!4M<*Z|gTK%#e5flX3)IFj)5*tp^VgtX@oA*Kwu0gXWY^z?P?>Fwr%$amoqZ|PbX zQ|&Q1==QDTdP9S5E! zWVqT3-H<0w1i`ema~kq*@R8Qj_YewE|`_KEv9ppo-Pa8qss5zEB_Zfr9Cx?NFb7G+%5yYW5%HK!LSAv9EbmeGy9I8dHOI ziM!nVDk}_PpRH85wlnUjsQkJeZl+dbGiegqpaKSXF77h#A{kFEyr<>&wNJ?FG!}(A zN}~rJP~K<3=|y_6dOHycc~qqUelcp7(ham&VgRL$=rkw)$nJArA#LB2pU%k7Z-WaB z*`wajekcDgyjuwi8@=Z3T5CPE^j&90X3aTvtmEp5MRMEA_!p#Y-yLHe#`1u?yQFF{ zHIzY`c921?cj&D9mGmt`l91{*mPDgF5NUB8$e&JJpv030$`c;D=O#ZcBDGl|QGv1< z?Ey{HzA7x(#?Nu%nfPJlQkhve+$cb+FS5c)K83dy_i!F5G+|OD_%V-S>)^P0tAyqf zZr!jEoCq6onxzhPo5$fxJUHl9KPR-ClHrEd1P}^Yv4ndG$L$XQGi2wg+CK?!5S=^V aDRRrw&2xh8k#7%iu2IvxzX22QOK3?j$ck_P