1 /***************************************************************************
2 * Copyright (C) 2005-2008 by Tarek Saidi *
3 * tarek.saidi@arcor.de *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; version 2 of the License. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
18 ***************************************************************************/
20 #include "Kdb3Database.h"
22 #include "crypto/twoclass.h"
26 #define UNEXP_ERROR error=QString("Unexpected error in: %1, Line:%2").arg(__FILE__).arg(__LINE__);
28 const QDateTime Date_Never(QDate(2999,12,28),QTime(23,59,59));
31 bool Kdb3Database::EntryHandleLessThan(const IEntryHandle* This,const IEntryHandle* Other){
32 if(!This->isValid() && Other->isValid())return true;
33 if(This->isValid() && !Other->isValid())return false;
34 if(!This->isValid() && !Other->isValid())return false;
35 return This->visualIndex()<Other->visualIndex();
38 bool Kdb3Database::EntryHandleLessThanStd(const IEntryHandle* This,const IEntryHandle* Other){
39 int comp = This->title().compare(Other->title());
40 if (comp < 0) return true;
41 else if (comp > 0) return false;
43 comp = This->username().compare(Other->username());
44 if (comp < 0) return true;
45 else if (comp > 0) return false;
50 bool Kdb3Database::StdEntryLessThan(const Kdb3Database::StdEntry& This,const Kdb3Database::StdEntry& Other){
51 return This.Index<Other.Index;
55 Kdb3Database::Kdb3Database() : File(NULL), RawMasterKey(32), RawMasterKey_CP1252(32),
56 RawMasterKey_Latin1(32), RawMasterKey_UTF8(32), MasterKey(32){
59 QString Kdb3Database::getError(){
63 void Kdb3Database::addIcon(const QPixmap& icon){
68 QPixmap& Kdb3Database::icon(int i){
69 if(i>=builtinIcons()+CustomIcons.size())
73 return CustomIcons[i-builtinIcons()];
76 void Kdb3Database::removeIcon(int id){
79 if(id >= CustomIcons.size()) return;
80 CustomIcons.removeAt(id); // .isNull()==true
81 for(int i=0;i<Entries.size();i++){
82 if(Entries[i].Image == id+builtinIcons())
84 if(Entries[i].Image>id+builtinIcons())
87 for(int i=0;i<Groups.size();i++){
88 if(Groups[i].Image == id+builtinIcons())
90 if(Groups[i].Image>id+builtinIcons())
96 void Kdb3Database::replaceIcon(int id,const QPixmap& icon){
97 if(id<builtinIcons())return;
98 CustomIcons[id-builtinIcons()]=icon;
102 int Kdb3Database::numIcons(){
103 return builtinIcons()+CustomIcons.size();
106 bool Kdb3Database::parseMetaStream(const StdEntry& entry){
108 qDebug("Found Metastream: %s", CSTR(entry.Comment));
110 if(entry.Comment=="KPX_GROUP_TREE_STATE"){
111 parseGroupTreeStateMetaStream(entry.Binary);
114 else if(entry.Comment=="KPX_CUSTOM_ICONS_4"){
115 parseCustomIconsMetaStream(entry.Binary);
118 else if(entry.Comment=="KPX_CUSTOM_ICONS_3"){
119 if (!hasV4IconMetaStream)
120 parseCustomIconsMetaStreamV3(entry.Binary);
123 else if(entry.Comment=="KPX_CUSTOM_ICONS_2"){
124 qDebug("Removed old CuIcMeSt v2");
127 else if(entry.Comment=="KPX_CUSTOM_ICONS"){
128 qDebug("Removed old CuIcMeSt v1");
132 return false; //unknown MetaStream
135 bool Kdb3Database::isMetaStream(StdEntry& p){
136 if(p.Binary.isNull()) return false;
137 if(p.Comment == "") return false;
138 if(p.BinaryDesc != "bin-stream") return false;
139 if(p.Title != "Meta-Info") return false;
140 if(p.Username != "SYSTEM") return false;
141 if(p.Url != "$") return false;
142 if(p.Image != 0) return false;
146 void Kdb3Database::parseCustomIconsMetaStream(const QByteArray& dta){
147 //Rev 4 (KeePassX 0.3.2)
148 quint32 NumIcons,NumEntries,NumGroups,offset;
149 memcpyFromLEnd32(&NumIcons,dta.data());
150 memcpyFromLEnd32(&NumEntries,dta.data()+4);
151 memcpyFromLEnd32(&NumGroups,dta.data()+8);
154 for(int i=0;i<NumIcons;i++){
155 CustomIcons << QPixmap();
157 memcpyFromLEnd32(&Size,dta.data()+offset);
158 if(offset+Size > dta.size()){
160 qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error.");
164 if(!CustomIcons.back().loadFromData((const unsigned char*)dta.data()+offset,Size,"PNG")){
166 qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error.");
170 if(offset > dta.size()){
172 qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error.");
176 for(int i=0;i<NumEntries;i++){
179 EntryUuid.fromRaw(dta.data()+offset);
181 memcpyFromLEnd32(&Icon,dta.data()+offset);
183 StdEntry* entry=getEntry(EntryUuid);
185 entry->Image=Icon+BUILTIN_ICONS;
187 for(int i=0;i<NumGroups;i++){
188 quint32 GroupId,Icon;
189 memcpyFromLEnd32(&GroupId,dta.data()+offset);
191 memcpyFromLEnd32(&Icon,dta.data()+offset);
193 StdGroup* Group=getGroup(GroupId);
195 Group->Image=Icon+BUILTIN_ICONS;
200 void Kdb3Database::parseCustomIconsMetaStreamV3(const QByteArray& dta){
202 quint32 NumIcons,NumEntries,NumGroups,offset;
203 memcpyFromLEnd32(&NumIcons,dta.data());
204 memcpyFromLEnd32(&NumEntries,dta.data()+4);
205 memcpyFromLEnd32(&NumGroups,dta.data()+8);
208 for(int i=0;i<NumIcons;i++){
209 CustomIcons << QPixmap();
211 memcpyFromLEnd32(&Size,dta.data()+offset);
212 if(offset+Size > dta.size()){
214 qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error.");
218 if(!CustomIcons.back().loadFromData((const unsigned char*)dta.data()+offset,Size,"PNG")){
220 qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error.");
224 if(offset > dta.size()){
226 qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error.");
230 for(int i=0;i<NumEntries;i++){
233 EntryUuid.fromRaw(dta.data()+offset);
235 memcpyFromLEnd32(&Icon,dta.data()+offset);
237 StdEntry* entry=getEntry(EntryUuid);
240 entry->Image=Icon+4; // Since v0.3.2 the BUILTIN_ICONS number has increased by 4
245 for(int i=0;i<NumGroups;i++){
246 quint32 GroupId,Icon;
247 memcpyFromLEnd32(&GroupId,dta.data()+offset);
249 memcpyFromLEnd32(&Icon,dta.data()+offset);
251 StdGroup* Group=getGroup(GroupId);
253 if (Group->Image>=65)
254 Group->Image=Icon+4; // Since v0.3.2 the BUILTIN_ICONS number has increased by 4
262 void Kdb3Database::parseGroupTreeStateMetaStream(const QByteArray& dta){
264 qWarning("Discarded metastream KPX_GROUP_TREE_STATE because of a parsing error.");
268 memcpyFromLEnd32(&Num,dta.data());
269 if(Num*5!=dta.size()-4){
270 qWarning("Discarded metastream KPX_GROUP_TREE_STATE because of a parsing error.");
273 TreeStateMetaStream.clear();
274 for(int i=0;i<Num;i++){
277 memcpyFromLEnd32(&GroupID,dta.data()+4+5*i);
278 memcpy(&IsExpanded,dta.data()+8+5*i,1);
279 TreeStateMetaStream.insert(GroupID,(bool)IsExpanded);
284 void Kdb3Database::createGroupTreeStateMetaStream(StdEntry* e){
285 e->BinaryDesc="bin-stream";
286 e->Title="Meta-Info";
287 e->Username="SYSTEM";
288 e->Comment="KPX_GROUP_TREE_STATE";
291 if(Groups.size())e->GroupId=Groups[0].Id;
293 quint32 Num=Groups.size();
295 memcpyToLEnd32(bin.data(),&Num);
296 for(int i=0;i<Num;i++){
297 memcpyToLEnd32(bin.data()+4+5*i,&Groups[i].Id);
298 if(Groups[i].IsExpanded)
306 Kdb3Database::StdEntry* Kdb3Database::getEntry(const KpxUuid& uuid){
307 for(int i=0; i<Entries.size();i++)
308 if(Entries[i].Uuid==uuid)return &Entries[i];
312 Kdb3Database::StdGroup* Kdb3Database::getGroup(quint32 Id){
313 for(int i=0; i<Groups.size();i++)
314 if(Groups[i].Id==Id)return &Groups[i];
319 //! Extracts one entry from raw decrypted data.
320 bool Kdb3Database::readEntryField(StdEntry* entry, quint16 FieldType, quint32 FieldSize, quint8 *pData){
327 entry->Uuid=KpxUuid(pData);
330 memcpyFromLEnd32(&entry->GroupId, (char*)pData);
333 memcpyFromLEnd32(&entry->Image, (char*)pData);
336 entry->Title=QString::fromUtf8((char*)pData);
339 entry->Url=QString::fromUtf8((char*)pData);
342 entry->Username=QString::fromUtf8((char*)pData);
345 QString s=QString::fromUtf8((char*)pData);
346 entry->Password.setString(s,true);
349 entry->Comment=QString::fromUtf8((char*)pData);
352 entry->Creation=dateFromPackedStruct5(pData);
355 entry->LastMod=dateFromPackedStruct5(pData);
358 entry->LastAccess=dateFromPackedStruct5(pData);
361 entry->Expire=dateFromPackedStruct5(pData);
364 entry->BinaryDesc=QString::fromUtf8((char*)pData);
368 entry->Binary=QByteArray((char*)pData,FieldSize);
370 entry->Binary=QByteArray();
380 //! Extracts one group from raw decrypted data.
381 bool Kdb3Database::readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint8 *pData)
389 memcpyFromLEnd32(&group->Id, (char*)pData);
392 group->Title=QString::fromUtf8((char*)pData);
394 case 0x0003: //not longer used by KeePassX but part of the KDB format
396 case 0x0004: //not longer used by KeePassX but part of the KDB format
398 case 0x0005: //not longer used by KeePassX but part of the KDB format
400 case 0x0006: //not longer used by KeePassX but part of the KDB format
403 memcpyFromLEnd32(&group->Image, (char*)pData);
407 memcpyFromLEnd16(&Level, (char*)pData);
408 Levels.append(Level);
411 //not used by KeePassX but part of the KDB format
412 //memcpyFromLEnd32(&Flags, (char*)pData);
417 return false; // Field unsupported
420 return true; // Field supported
423 bool Kdb3Database::createGroupTree(QList<quint32>& Levels){
424 if(Levels[0]!=0) return false;
425 //find the parent for every group
426 for(int i=0;i<Groups.size();i++){
428 Groups[i].Parent=&RootGroup;
429 Groups[i].Index=RootGroup.Children.size();
430 RootGroup.Children.append(&Groups[i]);
434 //the first item with a lower level is the parent
436 if(Levels[j]<Levels[i]){
437 if(Levels[i]-Levels[j]!=1)return false;
440 if(j==0)return false; //No parent found
442 Groups[i].Parent=&Groups[j];
443 Groups[i].Index=Groups[j].Children.size();
444 Groups[i].Parent->Children.append(&Groups[i]);
447 QList<int> EntryIndexCounter;
448 for(int i=0;i<Groups.size();i++)EntryIndexCounter << 0;
450 for(int e=0;e<Entries.size();e++){
452 for(int g=0;g<Groups.size();g++){
453 if(Entries[e].GroupId==Groups[g].Id){
459 if (groupIndex == -1) {
460 qWarning("Orphaned entry found, assigning to first group");
461 for(int g=0;g<Groups.size();g++){
462 if(Groups[g].Id == RootGroup.Children[0]->Id){
469 Groups[groupIndex].Entries.append(&Entries[e]);
470 Entries[e].Group=&Groups[groupIndex];
471 Entries[e].Index=EntryIndexCounter[groupIndex];
472 EntryIndexCounter[groupIndex]++;
478 void Kdb3Database::createHandles(){
479 for(int i=0;i<Groups.size();i++){
480 GroupHandles.append(GroupHandle(this));
481 Groups[i].Handle=&GroupHandles.back();
482 GroupHandles.back().Group=&Groups[i];
484 for(int i=0;i<Entries.size();i++){
485 EntryHandles.append(EntryHandle(this));
486 Entries[i].Handle=&EntryHandles.back();
487 EntryHandles.back().Entry=&Entries[i];
491 void Kdb3Database::restoreGroupTreeState(){
492 switch (config->groupTreeState()){
493 case KpxConfig::RestoreLast:
494 for(int i=0;i<Groups.size();i++){
495 if(TreeStateMetaStream.contains(Groups[i].Id))
496 Groups[i].IsExpanded=TreeStateMetaStream.value(Groups[i].Id);
500 case KpxConfig::ExpandAll:
501 for(int i=0;i<Groups.size();i++)
502 Groups[i].IsExpanded=true;
505 case KpxConfig::DoNothing:
510 bool Kdb3Database::load(QString identifier, bool readOnly){
511 return loadReal(identifier, readOnly, false);
514 #define LOAD_RETURN_CLEANUP \
520 bool Kdb3Database::loadReal(QString filename, bool readOnly, bool differentEncoding) {
521 File = new QFile(filename);
523 if(!File->open(QIODevice::ReadOnly)){
524 error=tr("Could not open file.");
531 if(!File->open(QIODevice::ReadWrite)){
532 if(!File->open(QIODevice::ReadOnly)){
533 error=tr("Could not open file.");
544 openedReadOnly = readOnly;
546 unsigned long total_size,crypto_size;
547 quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags;
548 quint8 FinalRandomSeed[16];
549 quint8 ContentsHash[32];
550 quint8 EncryptionIV[16];
552 total_size=File->size();
553 char* buffer = new char[total_size];
554 File->read(buffer,total_size);
556 if(total_size < DB_HEADER_SIZE){
557 error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)");
561 memcpyFromLEnd32(&Signature1,buffer);
562 memcpyFromLEnd32(&Signature2,buffer+4);
563 memcpyFromLEnd32(&Flags,buffer+8);
564 memcpyFromLEnd32(&Version,buffer+12);
565 memcpy(FinalRandomSeed,buffer+16,16);
566 memcpy(EncryptionIV,buffer+32,16);
567 memcpyFromLEnd32(&NumGroups,buffer+48);
568 memcpyFromLEnd32(&NumEntries,buffer+52);
569 memcpy(ContentsHash,buffer+56,32);
570 memcpy(TransfRandomSeed,buffer+88,32);
571 memcpyFromLEnd32(&KeyTransfRounds,buffer+120);
573 if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){
574 error=tr("Wrong Signature");
578 if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){
579 error=tr("Unsupported File Version.");
583 if (Flags & PWM_FLAG_RIJNDAEL)
584 Algorithm = Rijndael_Cipher;
585 else if (Flags & PWM_FLAG_TWOFISH)
586 Algorithm = Twofish_Cipher;
588 error=tr("Unknown Encryption Algorithm.");
592 RawMasterKey.unlock();
594 KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds);
599 sha.update(FinalRandomSeed,16);
600 sha.update(*MasterKey,32);
601 sha.finish(FinalKey);
606 if(Algorithm == Rijndael_Cipher){
608 aes.key256(FinalKey);
609 aes.cbc_decrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,total_size-DB_HEADER_SIZE,(unsigned char*)EncryptionIV);
610 crypto_size=total_size-((quint8*)buffer)[total_size-1]-DB_HEADER_SIZE;
612 else if(Algorithm == Twofish_Cipher){
614 if (twofish.init(FinalKey, 32, EncryptionIV) != true){
615 error=tr("Unable to initialize the twofish algorithm.");
618 crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE,
619 total_size - DB_HEADER_SIZE, (quint8 *)buffer + DB_HEADER_SIZE);
622 error=tr("Unknown encryption algorithm.");
626 if ((crypto_size > 2147483446) || (!crypto_size && NumGroups)){
627 error=tr("Decryption failed.\nThe key is wrong or the file is damaged.");
631 SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size);
633 if(memcmp(ContentsHash, FinalKey, 32) != 0){
634 if(PotentialEncodingIssueLatin1){
639 RawMasterKey.copyData(RawMasterKey_Latin1);
640 PotentialEncodingIssueLatin1 = false;
641 qDebug("Decryption failed. Retrying with Latin-1.");
642 return loadReal(filename, readOnly, true); // second try
644 if(PotentialEncodingIssueUTF8){
649 RawMasterKey.copyData(RawMasterKey_UTF8);
650 PotentialEncodingIssueUTF8 = false;
651 qDebug("Decryption failed. Retrying with UTF-8.");
652 return loadReal(filename, readOnly, true); // second/third try
654 error=tr("Hash test failed.\nThe key is wrong or the file is damaged.");
659 unsigned long pos = DB_HEADER_SIZE;
665 QList<quint32> Levels;
666 RootGroup.Title="$ROOT$";
667 RootGroup.Parent=NULL;
668 RootGroup.Handle=NULL;
670 for(unsigned long CurGroup = 0; CurGroup < NumGroups; )
674 memcpyFromLEnd16(&FieldType, pField);
675 pField += 2; pos += 2;
676 if (pos >= total_size){
677 error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
681 memcpyFromLEnd32(&FieldSize, pField);
682 pField += 4; pos += 4;
683 if (pos >= (total_size + FieldSize)){
684 error=tr("Unexpected error: Offset is out of range.").append(" [G2]");
688 bRet = readGroupField(&group,Levels, FieldType, (quint8 *)pField);
689 if ((FieldType == 0xFFFF) && (bRet == true)){
691 CurGroup++; // Now and ONLY now the counter gets increased
695 if (pos >= total_size){
696 error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
703 for (unsigned long CurEntry = 0; CurEntry < NumEntries;)
707 memcpyFromLEnd16(&FieldType, pField);
708 pField += 2; pos += 2;
709 if(pos >= total_size){
710 error=tr("Unexpected error: Offset is out of range.").append(" [E1]");
714 memcpyFromLEnd32(&FieldSize, pField);
715 pField += 4; pos += 4;
716 if (pos >= (total_size + FieldSize)){
717 error=tr("Unexpected error: Offset is out of range.").append(" [E2]");
721 bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField);
723 if((FieldType == 0xFFFF) && (bRet == true)){
726 qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data());
732 if (pos >= total_size){
733 error=tr("Unexpected error: Offset is out of range.").append(" [E3]");
738 if(!createGroupTree(Levels)){
739 error=tr("Invalid group tree.");
745 hasV4IconMetaStream = false;
746 for(int i=0;i<Entries.size();i++){
747 if(isMetaStream(Entries[i]) && Entries[i].Comment=="KPX_CUSTOM_ICONS_4"){
748 hasV4IconMetaStream = true;
753 //Remove the metastreams from the entry list
754 for(int i=0;i<Entries.size();i++){
755 if(isMetaStream(Entries[i])){
756 if(!parseMetaStream(Entries[i]))
757 UnknownMetaStreams << Entries[i];
763 int* EntryIndices=new int[Groups.size()];
764 for(int i=0;i<Groups.size();i++)EntryIndices[i]=0;
766 for(int g=0;g<Groups.size();g++){
767 for(int e=0;e<Entries.size();e++){
768 if(Entries[e].GroupId==Groups[g].Id){
769 Entries[e].Index=EntryIndices[g];
774 delete [] EntryIndices;
776 restoreGroupTreeState();
778 passwordEncodingChanged = differentEncoding;
779 if (differentEncoding) {
780 RawMasterKey.copyData(RawMasterKey_CP1252);
787 QDateTime Kdb3Database::dateFromPackedStruct5(const unsigned char* pBytes){
788 quint32 dw1, dw2, dw3, dw4, dw5;
789 dw1 = (quint32)pBytes[0]; dw2 = (quint32)pBytes[1]; dw3 = (quint32)pBytes[2];
790 dw4 = (quint32)pBytes[3]; dw5 = (quint32)pBytes[4];
791 int y = (dw1 << 6) | (dw2 >> 2);
792 int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6);
793 int d = (dw3 >> 1) & 0x0000001F;
794 int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4);
795 int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6);
796 int s = dw5 & 0x0000003F;
797 return QDateTime(QDate(y,mon,d),QTime(h,min,s));
801 void Kdb3Database::dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){
802 pBytes[0] = (quint8)(((quint32)d.date().year() >> 6) & 0x0000003F);
803 pBytes[1] = (quint8)((((quint32)d.date().year() & 0x0000003F) << 2) | (((quint32)d.date().month() >> 2) & 0x00000003));
804 pBytes[2] = (quint8)((((quint32)d.date().month() & 0x00000003) << 6) | (((quint32)d.date().day() & 0x0000001F) << 1) | (((quint32)d.time().hour() >> 4) & 0x00000001));
805 pBytes[3] = (quint8)((((quint32)d.time().hour() & 0x0000000F) << 4) | (((quint32)d.time().minute() >> 2) & 0x0000000F));
806 pBytes[4] = (quint8)((((quint32)d.time().minute() & 0x00000003) << 6) | ((quint32)d.time().second() & 0x0000003F));
810 int Kdb3Database::numGroups(){
811 return Groups.size();
814 int Kdb3Database::numEntries(){
815 return Entries.size();
818 void Kdb3Database::deleteGroup(StdGroup* group){
820 while(group->Children.size())
821 deleteGroup(group->Children.front());
823 QList<IEntryHandle*> GroupEntries;
824 GroupEntries=entries(group->Handle);
825 deleteEntries(GroupEntries);
827 Q_ASSERT(group==group->Parent->Children[group->Index]);
828 group->Parent->Children.removeAt(group->Index);
829 for(int i=group->Index;i<group->Parent->Children.size();i++){
830 group->Parent->Children[i]->Index--;
832 group->Handle->invalidate();
834 for(int i=0;i<Groups.size();i++){
835 if(&Groups[i]==group){
844 void Kdb3Database::deleteGroup(IGroupHandle* group){
845 deleteGroup(((GroupHandle*)group)->Group);
849 void Kdb3Database::GroupHandle::setIndex(int index){
850 quint32 ParentId=((GroupHandle*)parent())->Id;
851 int Pos=pDB->getGroupListIndex(this);
853 // Move the group to the new position in the list
855 NewPos=pDB->getGroupListIndex((GroupHandle*)parent());
858 pDB->Groups.move(Pos,NewPos+1);
860 pDB->Groups.move(Pos,NewPos);
863 for(NewPos;NewPos<pDB->Groups.size();NewPos++){
864 if(pDB->Groups[NewPos].ParentId==ParentId && pDB->Groups[NewPos].Index+1==index)
867 //skip the children of the found sibling
868 for(NewPos;NewPos<Groups.size();NewPos++){
870 pDB->Groups.move(Pos,NewPos);
874 // adjust the indices
876 for(int i=0;i<pDB->Groups.size();i++){
877 if(pDB->Groups[i].ParentId==ParentId){
878 pDB->Groups[i].Index=NewIndex;
885 bool Kdb3Database::convHexToBinaryKey(char* HexKey, char* dst){
886 QString hex=QString::fromAscii(HexKey,64);
887 for(int i=0; i<64; i+=2){
890 bin=hex.mid(i,2).toUInt(&err,16);
891 if(!err)return false;
892 memcpy(dst+(i/2),&bin,1);
897 bool Kdb3Database::setKey(const QString& password,const QString& keyfile){
898 if(!password.isEmpty() && !keyfile.isEmpty())
899 return setCompositeKey(password,keyfile);
900 if(!password.isEmpty())
901 return setPasswordKey(password);
902 if(!keyfile.isEmpty())
903 return setFileKey(keyfile);
908 bool Kdb3Database::setPasswordKey(const QString& Password){
909 Q_ASSERT(Password.size());
910 QTextCodec* codec=QTextCodec::codecForName("Windows-1252");
911 QByteArray Password_CP1252 = codec->fromUnicode(Password);
912 RawMasterKey_CP1252.unlock();
913 SHA256::hashBuffer(Password_CP1252.data(),*RawMasterKey_CP1252,Password_CP1252.size());
914 RawMasterKey_CP1252.lock();
915 RawMasterKey.copyData(RawMasterKey_CP1252);
917 QByteArray Password_Latin1 = Password.toLatin1();
918 QByteArray Password_UTF8 = Password.toUtf8();
919 PotentialEncodingIssueLatin1 = false;
920 PotentialEncodingIssueUTF8 = false;
922 if (Password_Latin1 != Password_CP1252){
923 // KeePassX used Latin-1 encoding for passwords until version 0.3.1
924 // but KeePass/Win32 uses Windows Codepage 1252.
925 // To stay compatible with databases created with KeePassX <= 0.3.1
926 // the loading function gives both encodings a try.
927 PotentialEncodingIssueLatin1 = true;
928 RawMasterKey_Latin1.unlock();
929 SHA256::hashBuffer(Password_Latin1.data(),*RawMasterKey_Latin1,Password_Latin1.size());
930 RawMasterKey_Latin1.lock();
933 if (Password_UTF8 != Password_CP1252){
934 // KeePassX used UTF-8 encoding for passwords until version 0.2.2
935 // but KeePass/Win32 uses Windows Codepage 1252.
936 // To stay compatible with databases created with KeePassX <= 0.2.2
937 // the loading function gives both encodings a try.
938 PotentialEncodingIssueUTF8 = true;
939 RawMasterKey_UTF8.unlock();
940 SHA256::hashBuffer(Password_UTF8.data(),*RawMasterKey_UTF8,Password_UTF8.size());
941 RawMasterKey_UTF8.lock();
947 bool Kdb3Database::setFileKey(const QString& filename){
948 QFile file(filename);
949 if(!file.open(QIODevice::ReadOnly|QIODevice::Unbuffered)){
950 error=decodeFileError(file.error());
953 qint64 FileSize=file.size();
955 error=tr("Key file is empty.");
958 RawMasterKey.unlock();
960 if(file.read((char*)(*RawMasterKey),32) != 32){
961 error=decodeFileError(file.error());
970 if(file.read(hex,64) != 64){
971 error=decodeFileError(file.error());
975 if (convHexToBinaryKey(hex,(char*)(*RawMasterKey))){
981 unsigned char* buffer[2048];
984 read = file.read((char*)buffer,2048);
986 sha.update(buffer,read);
987 } while (read == 2048);
988 sha.finish(*RawMasterKey);
993 bool Kdb3Database::setCompositeKey(const QString& Password,const QString& filename){
996 setPasswordKey(Password);
997 RawMasterKey.unlock();
998 sha.update(*RawMasterKey,32);
1001 if(!setFileKey(filename))return false;
1002 RawMasterKey.unlock();
1003 sha.update(*RawMasterKey,32);
1004 sha.finish(*RawMasterKey);
1005 RawMasterKey.lock();
1010 QList<IEntryHandle*> Kdb3Database::entries(){
1011 QList<IEntryHandle*> handles;
1012 for(int i=0; i<EntryHandles.size(); i++){
1013 if(EntryHandles[i].isValid())handles.append(&EntryHandles[i]);
1018 QList<IEntryHandle*> Kdb3Database::expiredEntries(){
1019 QList<IEntryHandle*> handles;
1020 for(int i=0; i<EntryHandles.size(); i++){
1021 if(EntryHandles[i].isValid() &&
1022 (EntryHandles[i].expire()<=QDateTime::currentDateTime()) &&
1023 (EntryHandles[i].expire()!=Date_Never))
1024 handles.append(&EntryHandles[i]);
1029 QList<IEntryHandle*> Kdb3Database::entries(IGroupHandle* Group){
1030 QList<IEntryHandle*> handles;
1031 for(int i=0; i<EntryHandles.size(); i++){
1032 if(EntryHandles[i].isValid() && (EntryHandles[i].group()==Group))
1033 handles.append(&EntryHandles[i]);
1035 qSort(handles.begin(),handles.end(),EntryHandleLessThan);
1040 QList<IEntryHandle*> Kdb3Database::entriesSortedStd(IGroupHandle* Group){
1041 QList<IEntryHandle*> handles;
1042 for(int i=0; i<EntryHandles.size(); i++){
1043 if(EntryHandles[i].isValid() && (EntryHandles[i].group()==Group))
1044 handles.append(&EntryHandles[i]);
1046 qSort(handles.begin(),handles.end(),EntryHandleLessThanStd);
1051 void Kdb3Database::deleteEntry(IEntryHandle* entry){
1054 for(j=0;j<Entries.size();j++){
1055 if(&Entries[j]==((EntryHandle*)entry)->Entry)
1058 Entries[j].Handle->invalidate();
1059 Entries.removeAt(j);
1062 void Kdb3Database::moveEntry(IEntryHandle* entry, IGroupHandle* group){
1063 ((EntryHandle*)entry)->Entry->GroupId=((GroupHandle*)group)->Group->Id;
1064 ((EntryHandle*)entry)->Entry->Group=((GroupHandle*)group)->Group;
1068 void Kdb3Database::deleteEntries(QList<IEntryHandle*> entries){
1069 if(!entries.size())return;
1070 StdGroup* Group=((EntryHandle*)entries[0])->Entry->Group;
1071 for(int i=0;i<entries.size();i++){
1073 for(j=0;j<Entries.size();j++){
1074 if(&Entries[j]==((EntryHandle*)entries[i])->Entry)
1077 Group->Children.removeAt(Entries[j].Index);
1078 Entries[j].Handle->invalidate();
1079 Entries.removeAt(j);
1082 for(int i=0;i<Group->Children.size();i++){
1083 Group->Children[i]->Index=i;
1087 QList<IGroupHandle*> Kdb3Database::groups(){
1088 QList<IGroupHandle*> handles;
1089 for(int i=0; i<GroupHandles.size(); i++){
1090 if(GroupHandles[i].isValid())handles.append(&GroupHandles[i]);
1095 quint32 Kdb3Database::getNewGroupId(){
1101 if(!id)continue; //group IDs must not be 0
1102 for(int j=0;j<Groups.size();j++){
1103 if(Groups[j].Id==id){
1112 IGroupHandle* Kdb3Database::addGroup(const CGroup* group,IGroupHandle* ParentHandle){
1113 GroupHandles.append(GroupHandle(this));
1114 Groups.append(*group);
1115 Groups.back().Id=getNewGroupId();
1116 Groups.back().Handle=&GroupHandles.back();
1117 GroupHandles.back().Group=&Groups.back();
1119 Groups.back().Parent=((GroupHandle*)ParentHandle)->Group;
1120 Groups.back().Index=Groups.back().Parent->Children.size();
1121 Groups.back().Parent->Children.append(&Groups.back());
1124 // Insert to root group. Try to keep Backup group at the end.
1125 Groups.back().Parent=&RootGroup;
1126 Groups.back().Index=RootGroup.Children.size();
1127 int position = RootGroup.Children.size();
1128 if (group->Title!="Backup" && !RootGroup.Children.isEmpty() && RootGroup.Children.last()->Title=="Backup"){
1129 RootGroup.Children.last()->Index = Groups.back().Index;
1130 Groups.back().Index--;
1133 RootGroup.Children.insert(position, &Groups.back());
1135 return &GroupHandles.back();
1138 IGroupHandle* Kdb3Database::backupGroup(bool create){
1139 IGroupHandle* group = NULL;
1140 QList<IGroupHandle*> allGroups = groups();
1141 for (int i=0; i<allGroups.size(); i++){
1142 if (allGroups[i]->parent()==NULL && allGroups[i]->title()=="Backup"){
1143 group = allGroups[i];
1148 if (group==NULL && create){
1150 newGroup.Title = "Backup";
1152 group = addGroup(&newGroup, NULL);
1158 Kdb3Database::StdEntry::StdEntry(){
1163 Kdb3Database::StdGroup::StdGroup(){
1170 Kdb3Database::StdGroup::StdGroup(const CGroup& other){
1179 void Kdb3Database::EntryHandle::setTitle(const QString& Title){Entry->Title=Title; }
1180 void Kdb3Database::EntryHandle::setUsername(const QString& Username){Entry->Username=Username;}
1181 void Kdb3Database::EntryHandle::setUrl(const QString& Url){Entry->Url=Url;}
1182 void Kdb3Database::EntryHandle::setPassword(const SecString& Password){Entry->Password=Password;}
1183 void Kdb3Database::EntryHandle::setExpire(const KpxDateTime& s){Entry->Expire=s;}
1184 void Kdb3Database::EntryHandle::setCreation(const KpxDateTime& s){Entry->Creation=s;}
1185 void Kdb3Database::EntryHandle::setLastAccess(const KpxDateTime& s){Entry->LastAccess=s;}
1186 void Kdb3Database::EntryHandle::setLastMod(const KpxDateTime& s){Entry->LastMod=s;}
1187 void Kdb3Database::EntryHandle::setBinaryDesc(const QString& s){Entry->BinaryDesc=s;}
1188 void Kdb3Database::EntryHandle::setComment(const QString& s){Entry->Comment=s;}
1189 void Kdb3Database::EntryHandle::setBinary(const QByteArray& s){Entry->Binary=s;}
1190 void Kdb3Database::EntryHandle::setImage(const quint32& s){Entry->Image=s;}
1191 KpxUuid Kdb3Database::EntryHandle::uuid()const{return Entry->Uuid;}
1192 IGroupHandle* Kdb3Database::EntryHandle::group()const{return Entry->Group->Handle;}
1193 quint32 Kdb3Database::EntryHandle::image()const{return Entry->Image;}
1194 QString Kdb3Database::EntryHandle::title()const{return Entry->Title;}
1195 QString Kdb3Database::EntryHandle::url()const{return Entry->Url;}
1196 QString Kdb3Database::EntryHandle::username()const{return Entry->Username;}
1197 SecString Kdb3Database::EntryHandle::password()const{return Entry->Password;}
1198 QString Kdb3Database::EntryHandle::comment()const{return Entry->Comment;}
1199 QString Kdb3Database::EntryHandle::binaryDesc()const{return Entry->BinaryDesc;}
1200 KpxDateTime Kdb3Database::EntryHandle::creation()const{return Entry->Creation;}
1201 KpxDateTime Kdb3Database::EntryHandle::lastMod()const{return Entry->LastMod;}
1202 KpxDateTime Kdb3Database::EntryHandle::lastAccess()const{return Entry->LastAccess;}
1203 KpxDateTime Kdb3Database::EntryHandle::expire()const{return Entry->Expire;}
1204 QByteArray Kdb3Database::EntryHandle::binary()const{return Entry->Binary;}
1205 quint32 Kdb3Database::EntryHandle::binarySize()const{return Entry->Binary.size();}
1207 QString Kdb3Database::EntryHandle::friendlySize()const
1209 quint32 binsize = binarySize();
1222 if (binsize < 1048576)
1228 if (binsize < 1073741824)
1236 faktor = 1073741824;
1240 return (QString::number((float)binsize / (float)faktor, 'f', prec) + " " + unit);
1243 int Kdb3Database::EntryHandle::visualIndex()const{return Entry->Index;}
1244 void Kdb3Database::EntryHandle::setVisualIndexDirectly(int i){Entry->Index=i;}
1245 bool Kdb3Database::EntryHandle::isValid()const{return valid;}
1247 CEntry Kdb3Database::EntryHandle::data()const{
1248 return *this->Entry;
1251 void Kdb3Database::EntryHandle::setVisualIndex(int index){
1252 QList<IEntryHandle*>Entries=pDB->entries(Entry->Group->Handle);
1253 Entries.move(visualIndex(),index);
1254 for(int i=0;i<Entries.size();i++){
1255 dynamic_cast<Kdb3Database::EntryHandle*>(Entries[i])->Entry->Index=index;
1259 Kdb3Database::EntryHandle::EntryHandle(Kdb3Database* db){
1266 bool Kdb3Database::GroupHandle::isValid(){return valid;}
1267 QString Kdb3Database::GroupHandle::title(){return Group->Title;}
1268 quint32 Kdb3Database::GroupHandle::image(){return Group->Image;}
1269 int Kdb3Database::GroupHandle::index(){return Group->Index;}
1270 void Kdb3Database::GroupHandle::setTitle(const QString& Title){Group->Title=Title;}
1271 void Kdb3Database::GroupHandle::setExpanded(bool IsExpanded){Group->IsExpanded=IsExpanded;}
1272 bool Kdb3Database::GroupHandle::expanded(){return Group->IsExpanded;}
1273 void Kdb3Database::GroupHandle::setImage(const quint32& New){Group->Image=New;}
1276 Kdb3Database::GroupHandle::GroupHandle(Kdb3Database* db){
1282 IGroupHandle* Kdb3Database::GroupHandle::parent(){
1283 return (IGroupHandle*)Group->Parent->Handle;
1286 int Kdb3Database::GroupHandle::level(){
1288 StdGroup* group=Group;
1289 while(group->Parent){
1290 group=group->Parent;
1298 QList<IGroupHandle*> Kdb3Database::GroupHandle::children(){
1299 QList<IGroupHandle*> children;
1300 for(int i=0; i < Group->Children.size(); i++){
1301 children.append(Group->Children[i]->Handle);
1307 void memcpyFromLEnd32(quint32* dst,const char* src){
1308 if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1309 memcpy(((char*)dst)+3,src+0,1);
1310 memcpy(((char*)dst)+2,src+1,1);
1311 memcpy(((char*)dst)+1,src+2,1);
1312 memcpy(((char*)dst)+0,src+3,1);
1319 void memcpyFromLEnd16(quint16* dst,const char* src){
1320 if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1321 memcpy(((char*)dst)+1,src+0,1);
1322 memcpy(((char*)dst)+0,src+1,1);
1329 void memcpyToLEnd32(char* dst,const quint32* src){
1330 if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1331 memcpy(dst+0,((char*)src)+3,1);
1332 memcpy(dst+1,((char*)src)+2,1);
1333 memcpy(dst+2,((char*)src)+1,1);
1334 memcpy(dst+3,((char*)src)+0,1);
1341 void memcpyToLEnd16(char* dst,const quint16* src){
1342 if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
1343 memcpy(dst+0,((char*)src)+1,1);
1344 memcpy(dst+1,((char*)src)+0,1);
1351 bool Kdb3Database::save(){
1353 error=tr("The database must contain at least one group.");
1357 if (!File->isOpen()) {
1358 if(!File->open(QIODevice::ReadWrite)){
1359 error=tr("Could not open file.");
1364 if(!(File->openMode() & QIODevice::WriteOnly)){
1365 error = tr("The database has been opened read-only.");
1369 //Delete old backup entries
1370 if (config->backup() && config->backupDelete() && config->backupDeleteAfter()>0 && backupGroup()){
1371 QDateTime time = QDateTime::currentDateTime().addDays(-config->backupDeleteAfter());
1372 QList<IEntryHandle*> backupEntries = entries(backupGroup());
1373 for (int i=0; i<backupEntries.size(); i++){
1374 if (backupEntries[i]->lastMod()<time)
1375 deleteEntry(backupEntries[i]);
1379 quint32 NumGroups,NumEntries,Signature1,Signature2,Flags,Version;
1380 quint8 FinalRandomSeed[16];
1381 quint8 ContentsHash[32];
1382 quint8 EncryptionIV[16];
1384 unsigned int FileSize;
1386 QList<StdEntry> MetaStreams;
1387 MetaStreams << StdEntry();
1388 createCustomIconsMetaStream(&MetaStreams.back());
1389 MetaStreams << StdEntry();
1390 createGroupTreeStateMetaStream(&MetaStreams.back());
1392 FileSize=DB_HEADER_SIZE;
1393 // Get the size of all groups (94 Byte + length of the name string)
1394 for(int i = 0; i < Groups.size(); i++){
1395 FileSize += 94 + Groups[i].Title.toUtf8().length()+1;
1397 // Get the size of all entries
1398 for(int i = 0; i < Entries.size(); i++){
1401 +Entries[i].Title.toUtf8().length()+1
1402 +Entries[i].Username.toUtf8().length()+1
1403 +Entries[i].Url.toUtf8().length()+1
1404 +Entries[i].Password.length()+1
1405 +Entries[i].Comment.toUtf8().length()+1
1406 +Entries[i].BinaryDesc.toUtf8().length()+1
1407 +Entries[i].Binary.length();
1410 for(int i=0; i < UnknownMetaStreams.size(); i++){
1413 +UnknownMetaStreams[i].Comment.toUtf8().length()+1
1414 +UnknownMetaStreams[i].Binary.length();
1417 for(int i=0; i < MetaStreams.size(); i++){
1420 +MetaStreams[i].Comment.toUtf8().length()+1
1421 +MetaStreams[i].Binary.length();
1425 // Round up filesize to 16-byte boundary for Rijndael/Twofish
1426 FileSize = (FileSize + 16) - (FileSize % 16);
1427 char* buffer=new char[FileSize+16];
1429 Signature1 = PWM_DBSIG_1;
1430 Signature2 = PWM_DBSIG_2;
1431 Flags = PWM_FLAG_SHA2;
1432 if(Algorithm == Rijndael_Cipher) Flags |= PWM_FLAG_RIJNDAEL;
1433 else if(Algorithm == Twofish_Cipher) Flags |= PWM_FLAG_TWOFISH;
1434 Version = PWM_DBVER_DW;
1435 NumGroups = Groups.size();
1436 NumEntries = Entries.size()+UnknownMetaStreams.size()+MetaStreams.size();
1438 QList<StdEntry> saveEntries = Entries;
1439 qSort(saveEntries.begin(),saveEntries.end(),StdEntryLessThan);
1441 randomize(FinalRandomSeed,16);
1442 randomize(EncryptionIV,16);
1444 unsigned int pos=DB_HEADER_SIZE; // Skip the header, it will be written later
1446 serializeGroups(buffer,pos);
1447 serializeEntries(saveEntries,buffer,pos);
1448 serializeEntries(UnknownMetaStreams,buffer,pos);
1449 serializeEntries(MetaStreams,buffer,pos);
1450 SHA256::hashBuffer(buffer+DB_HEADER_SIZE,ContentsHash,pos-DB_HEADER_SIZE);
1451 memcpyToLEnd32(buffer,&Signature1);
1452 memcpyToLEnd32(buffer+4,&Signature2);
1453 memcpyToLEnd32(buffer+8,&Flags);
1454 memcpyToLEnd32(buffer+12,&Version);
1455 memcpy(buffer+16,FinalRandomSeed,16);
1456 memcpy(buffer+32,EncryptionIV,16);
1457 memcpyToLEnd32(buffer+48,&NumGroups);
1458 memcpyToLEnd32(buffer+52,&NumEntries);
1459 memcpy(buffer+56,ContentsHash,32);
1460 memcpy(buffer+88,TransfRandomSeed,32);
1461 memcpyToLEnd32(buffer+120,&KeyTransfRounds);
1462 quint8 FinalKey[32];
1465 sha.update(FinalRandomSeed,16);
1467 sha.update(*MasterKey,32);
1469 sha.finish(FinalKey);
1471 unsigned long EncryptedPartSize;
1473 if(Algorithm == Rijndael_Cipher){
1474 EncryptedPartSize=((pos-DB_HEADER_SIZE)/16+1)*16;
1475 quint8 PadLen=EncryptedPartSize-(pos-DB_HEADER_SIZE);
1476 for(int i=0;i<PadLen;i++)
1477 ((quint8*)buffer)[DB_HEADER_SIZE+EncryptedPartSize-1-i]=PadLen;
1479 aes.key256(FinalKey);
1480 aes.cbc_encrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,EncryptedPartSize,(unsigned char*)EncryptionIV);
1482 else{ // Algorithm == Twofish_Cipher
1484 if(twofish.init(FinalKey, 32, EncryptionIV) == false){
1489 EncryptedPartSize = (unsigned long)twofish.padEncrypt((quint8*)buffer+DB_HEADER_SIZE,
1490 pos - DB_HEADER_SIZE,(quint8*)buffer+DB_HEADER_SIZE);
1492 if((EncryptedPartSize > (0xFFFFFFE - 202)) || (!EncryptedPartSize && Groups.size())){
1498 int size = EncryptedPartSize+DB_HEADER_SIZE;
1500 if (!saveFileTransactional(buffer, size)) {
1501 error=decodeFileError(File->error());
1507 //if(SearchGroupID!=-1)Groups.push_back(SearchGroup);
1511 bool Kdb3Database::saveFileTransactional(char* buffer, int size) {
1512 QString orgFilename = File->fileName();
1513 QFile* tmpFile = new QFile(orgFilename + ".tmp");
1514 if (!tmpFile->open(QIODevice::WriteOnly|QIODevice::Truncate)) {
1519 if (tmpFile->write(buffer,size) != size) {
1524 if (!syncFile(tmpFile))
1525 qWarning("Unable to flush file to disk");
1527 if (!File->remove()) {
1533 if (!tmpFile->rename(orgFilename)) {
1535 File = new QFile(orgFilename);
1539 if (!tmpFile->open(QIODevice::ReadWrite)) {
1547 void Kdb3Database::createCustomIconsMetaStream(StdEntry* e){
1549 e->BinaryDesc="bin-stream";
1550 e->Title="Meta-Info";
1551 e->Username="SYSTEM";
1552 e->Comment="KPX_CUSTOM_ICONS_4";
1554 if(Groups.size())e->GroupId=Groups[0].Id;
1556 quint32 NumEntries=0;
1557 for(quint32 i=0;i<Entries.size();i++){
1558 if (Entries[i].Image>=BUILTIN_ICONS)
1561 quint32 NumGroups=0;
1562 for(quint32 i=0;i<Groups.size();i++){
1563 if (Groups[i].Image>=BUILTIN_ICONS)
1566 Size+=8*NumGroups+20*NumEntries;
1567 Size+=CustomIcons.size()*1000; // 1KB
1568 e->Binary.reserve(Size);
1569 e->Binary.resize(12);
1570 quint32 NumIcons=CustomIcons.size();
1572 memcpyToLEnd32(e->Binary.data(),&NumIcons);
1573 memcpyToLEnd32(e->Binary.data()+4,&NumEntries);
1574 memcpyToLEnd32(e->Binary.data()+8,&NumGroups);
1575 for(int i=0;i<CustomIcons.size();i++){
1580 QBuffer buffer(&png);
1581 CustomIcons[i].save(&buffer,"PNG",0);
1583 memcpyToLEnd32(ImgSizeBin,&ImgSize);
1584 e->Binary.append(QByteArray::fromRawData(ImgSizeBin,4));
1585 e->Binary.append(png);
1588 for(quint32 i=0;i<Entries.size();i++){
1589 if (Entries[i].Image>=BUILTIN_ICONS){
1591 Entries[i].Uuid.toRaw(Bin);
1592 quint32 id=Entries[i].Image-BUILTIN_ICONS;
1593 memcpyToLEnd32(Bin+16,&id);
1594 e->Binary.append(QByteArray::fromRawData(Bin,20));
1597 for(quint32 i=0;i<Groups.size();i++){
1598 if (Groups[i].Image>=BUILTIN_ICONS){
1600 memcpyToLEnd32(Bin,&Groups[i].Id);
1601 quint32 id=Groups[i].Image-BUILTIN_ICONS;
1602 memcpyToLEnd32(Bin+4,&id);
1603 e->Binary.append(QByteArray::fromRawData(Bin,8));
1608 QList<IGroupHandle*> Kdb3Database::sortedGroups(){
1609 QList<IGroupHandle*> SortedGroups;
1610 appendChildrenToGroupList(SortedGroups,RootGroup);
1611 return SortedGroups;
1615 void Kdb3Database::appendChildrenToGroupList(QList<IGroupHandle*>& list,StdGroup& group){
1616 for(int i=0;i<group.Children.size();i++){
1617 list << group.Children[i]->Handle;
1618 appendChildrenToGroupList(list,*group.Children[i]);
1623 void Kdb3Database::appendChildrenToGroupList(QList<StdGroup*>& list,StdGroup& group){
1624 for(int i=0;i<group.Children.size();i++){
1625 list << group.Children[i];
1626 appendChildrenToGroupList(list,*group.Children[i]);
1631 void Kdb3Database::serializeGroups(char* buffer,unsigned int& pos){
1634 quint32 Flags=0; //unused
1635 QList<StdGroup*>SortedGroups;
1636 appendChildrenToGroupList(SortedGroups,RootGroup);
1638 for(int i=0; i < SortedGroups.size(); i++){
1639 unsigned char Date[5];
1640 dateToPackedStruct5(Date_Never,Date);
1642 StdGroup* group=SortedGroups[i];
1643 while(group->Parent){
1645 group=group->Parent;
1649 FieldType = 0x0001; FieldSize = 4;
1650 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1651 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1652 memcpyToLEnd32(buffer+pos, &SortedGroups[i]->Id); pos += 4;
1654 FieldType = 0x0002; FieldSize = SortedGroups[i]->Title.toUtf8().length() + 1;
1655 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1656 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1657 memcpy(buffer+pos, SortedGroups[i]->Title.toUtf8(),FieldSize); pos += FieldSize;
1659 FieldType = 0x0003; FieldSize = 5; //Creation
1660 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1661 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1662 memcpy(buffer+pos, Date,5); pos+=5;
1664 FieldType = 0x0004; FieldSize = 5; //LastMod
1665 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1666 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1667 memcpy(buffer+pos, Date,5); pos+=5;
1669 FieldType = 0x0005; FieldSize = 5; //LastAccess
1670 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1671 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1672 memcpy(buffer+pos, Date,5); pos+=5;
1674 FieldType = 0x0006; FieldSize = 5; //Expire
1675 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1676 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1677 memcpy(buffer+pos, Date,5); pos+=5;
1679 FieldType = 0x0007; FieldSize = 4;
1680 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1681 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1682 memcpyToLEnd32(buffer+pos, &SortedGroups[i]->Image); pos += 4;
1684 FieldType = 0x0008; FieldSize = 2;
1685 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1686 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1687 memcpyToLEnd16(buffer+pos, &Level); pos += 2;
1689 FieldType = 0x0009; FieldSize = 4;
1690 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1691 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1692 memcpyToLEnd32(buffer+pos, &Flags); pos += 4;
1694 FieldType = 0xFFFF; FieldSize = 0;
1695 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1696 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1702 void Kdb3Database::serializeEntries(QList<StdEntry>& EntryList,char* buffer,unsigned int& pos){
1705 for(int i = 0; i < EntryList.size(); i++){
1706 FieldType = 0x0001; FieldSize = 16;
1707 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1708 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1709 EntryList[i].Uuid.toRaw(buffer+pos); pos += 16;
1711 FieldType = 0x0002; FieldSize = 4;
1712 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1713 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1714 memcpyToLEnd32(buffer+pos, &EntryList[i].GroupId); pos += 4;
1716 FieldType = 0x0003; FieldSize = 4;
1717 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1718 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1719 memcpyToLEnd32(buffer+pos,&EntryList[i].Image); pos += 4;
1723 FieldSize = EntryList[i].Title.toUtf8().length() + 1; // Add terminating NULL character space
1724 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1725 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1726 memcpy(buffer+pos, EntryList[i].Title.toUtf8(),FieldSize); pos += FieldSize;
1729 FieldSize = EntryList[i].Url.toUtf8().length() + 1; // Add terminating NULL character space
1730 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1731 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1732 memcpy(buffer+pos, EntryList[i].Url.toUtf8(),FieldSize); pos += FieldSize;
1735 FieldSize = EntryList[i].Username.toUtf8().length() + 1; // Add terminating NULL character space
1736 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1737 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1738 memcpy(buffer+pos, EntryList[i].Username.toUtf8(),FieldSize); pos += FieldSize;
1741 FieldSize = EntryList[i].Password.length() + 1; // Add terminating NULL character space
1742 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1743 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1744 EntryList[i].Password.unlock();
1745 memcpy(buffer+pos, EntryList[i].Password.string().toUtf8(),FieldSize); pos += FieldSize;
1746 EntryList[i].Password.lock();
1749 FieldSize = EntryList[i].Comment.toUtf8().length() + 1; // Add terminating NULL character space
1750 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1751 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1752 memcpy(buffer+pos, EntryList[i].Comment.toUtf8(),FieldSize); pos += FieldSize;
1754 FieldType = 0x0009; FieldSize = 5;
1755 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1756 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1757 dateToPackedStruct5(EntryList[i].Creation,(unsigned char*)buffer+pos); pos+=5;
1760 FieldType = 0x000A; FieldSize = 5;
1761 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1762 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1763 dateToPackedStruct5(EntryList[i].LastMod,(unsigned char*)buffer+pos); pos+=5;
1765 FieldType = 0x000B; FieldSize = 5;
1766 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1767 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1768 dateToPackedStruct5(EntryList[i].LastAccess,(unsigned char*)buffer+pos); pos+=5;
1770 FieldType = 0x000C; FieldSize = 5;
1771 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1772 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1773 dateToPackedStruct5(EntryList[i].Expire,(unsigned char*)buffer+pos); pos+=5;
1776 FieldSize = EntryList[i].BinaryDesc.toUtf8().length() + 1; // Add terminating NULL character space
1777 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1778 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1779 memcpy(buffer+pos, EntryList[i].BinaryDesc.toUtf8(),FieldSize); pos += FieldSize;
1781 FieldType = 0x000E; FieldSize = EntryList[i].Binary.length();
1782 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1783 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1784 if((!EntryList[i].Binary.isNull()) && (FieldSize != 0))
1785 memcpy(buffer+pos, EntryList[i].Binary.data(), FieldSize);
1788 FieldType = 0xFFFF; FieldSize = 0;
1789 memcpyToLEnd16(buffer+pos, &FieldType); pos += 2;
1790 memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4;
1794 bool Kdb3Database::close(){
1800 void Kdb3Database::create(){
1802 RootGroup.Title="$ROOT$";
1803 RootGroup.Parent=NULL;
1804 RootGroup.Handle=NULL;
1805 Algorithm=Rijndael_Cipher;
1806 KeyTransfRounds=50000;
1810 bool Kdb3Database::isKeyError(){
1819 IEntryHandle* Kdb3Database::cloneEntry(const IEntryHandle* entry){
1821 dolly=*((EntryHandle*)entry)->Entry;
1822 dolly.Uuid.generate();
1823 Entries.append(dolly);
1824 EntryHandles.append(EntryHandle(this));
1825 EntryHandles.back().Entry=&Entries.back();
1826 Entries.back().Handle=&EntryHandles.back();
1827 return &EntryHandles.back();
1830 IEntryHandle* Kdb3Database::newEntry(IGroupHandle* group){
1832 Entry.Uuid.generate();
1833 Entry.Group=((GroupHandle*)group)->Group;
1834 Entry.GroupId=Entry.Group->Id;
1835 Entries.append(Entry);
1836 EntryHandles.append(EntryHandle(this));
1837 EntryHandles.back().Entry=&Entries.back();
1838 Entries.back().Handle=&EntryHandles.back();
1839 return &EntryHandles.back();
1842 IEntryHandle* Kdb3Database::addEntry(const CEntry* NewEntry, IGroupHandle* Group){
1843 StdEntry Entry(*((StdEntry*)NewEntry));
1844 Entry.Uuid.generate();
1845 Entry.Group=((GroupHandle*)Group)->Group;
1846 Entry.GroupId=Entry.Group->Id;
1847 Entries.append(Entry);
1848 EntryHandles.append(EntryHandle(this));
1849 EntryHandles.back().Entry=&Entries.back();
1850 Entries.back().Handle=&EntryHandles.back();
1851 return &EntryHandles.back();
1854 void Kdb3Database::deleteLastEntry(){
1855 Entries.removeAt(Entries.size()-1);
1856 EntryHandles.back().invalidate();
1859 bool Kdb3Database::isParent(IGroupHandle* parent, IGroupHandle* child){
1860 StdGroup* group=((GroupHandle*)child)->Group;
1861 while(group->Parent!=&RootGroup){
1862 if(group->Parent==((GroupHandle*)parent)->Group)return true;
1863 group=group->Parent;
1870 void Kdb3Database::cleanUpHandles(){}
1872 bool Kdb3Database::searchStringContains(const QString& search, const QString& string,bool Cs, bool RegExp){
1874 QRegExp exp(search,Cs ? Qt::CaseSensitive : Qt::CaseInsensitive);
1875 if(string.contains(exp)==0)return false;}
1877 if(string.contains(search,Cs ? Qt::CaseSensitive : Qt::CaseInsensitive)==0)return false;
1882 void Kdb3Database::getEntriesRecursive(IGroupHandle* Group, QList<IEntryHandle*>& EntryList){
1883 EntryList<<entries(Group);
1884 for(int i=0;i<((GroupHandle*)Group)->Group->Children.size(); i++){
1885 getEntriesRecursive(((GroupHandle*)Group)->Group->Children[i]->Handle,EntryList);
1889 QList<IEntryHandle*> Kdb3Database::search(IGroupHandle* Group,const QString& search, bool CaseSensitive, bool RegExp, bool Recursive,bool* Fields){
1890 bool fields[6]={true,true,true,false,true,true};
1893 QList<IEntryHandle*> SearchEntries;
1894 if(search==QString())return Group ? entries(Group) : entries();
1897 getEntriesRecursive(Group,SearchEntries);
1899 SearchEntries=entries(Group);
1902 SearchEntries=entries();
1904 IGroupHandle* bGroup = backupGroup();
1906 QList<IEntryHandle*> ResultEntries;
1907 for(int i=0; i<SearchEntries.size(); i++){
1908 IGroupHandle* entryGroup = SearchEntries[i]->group();
1909 while (entryGroup->parent())
1910 entryGroup = entryGroup->parent();
1911 if (entryGroup == bGroup)
1915 if(Fields[0])match=match||searchStringContains(search,SearchEntries[i]->title(),CaseSensitive,RegExp);
1916 if(Fields[1])match=match||searchStringContains(search,SearchEntries[i]->username(),CaseSensitive,RegExp);
1917 if(Fields[2])match=match||searchStringContains(search,SearchEntries[i]->url(),CaseSensitive,RegExp);
1918 SecString Password=SearchEntries[i]->password();
1920 if(Fields[3])match=match||searchStringContains(search,Password.string(),CaseSensitive,RegExp);
1922 if(Fields[4])match=match||searchStringContains(search,SearchEntries[i]->comment(),CaseSensitive,RegExp);
1923 if(Fields[5])match=match||searchStringContains(search,SearchEntries[i]->binaryDesc(),CaseSensitive,RegExp);
1925 ResultEntries << SearchEntries[i];
1928 return ResultEntries;
1931 void Kdb3Database::rebuildIndices(QList<StdGroup*>& list){
1932 for(int i=0;i<list.size();i++){
1938 void Kdb3Database::moveGroup(IGroupHandle* groupHandle,IGroupHandle* NewParent,int Pos){
1940 StdGroup* Group=((GroupHandle*)groupHandle)->Group;
1942 Parent=((GroupHandle*)NewParent)->Group;
1945 Group->Parent->Children.removeAt(Group->Index);
1946 rebuildIndices(Group->Parent->Children);
1947 Group->Parent=Parent;
1949 Parent->Children.append(Group);
1953 Q_ASSERT(Parent->Children.size()>=Pos);
1954 Parent->Children.insert(Pos,Group);
1956 rebuildIndices(Parent->Children);
1959 bool Kdb3Database::changeFile(const QString& filename){
1960 QFile* tmpFile = new QFile(filename);
1961 if(!tmpFile->open(QIODevice::ReadWrite)){
1962 error = decodeFileError(File->error());
1975 void Kdb3Database::generateMasterKey(){
1976 randomize(TransfRandomSeed,32);
1977 RawMasterKey.unlock();
1979 KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds);
1980 RawMasterKey.lock();
1984 /*void Kdb3Database::copyTree(Kdb3Database* db, GroupHandle* orgGroup, IGroupHandle* parent) {
1985 IGroupHandle* newParent = db->addGroup(orgGroup->Group, parent);
1987 QList<IEntryHandle*> entryList = entries(orgGroup);
1988 for (int i=0; i<entryList.size(); i++) {
1989 EntryHandle* entry = static_cast<EntryHandle*>(entryList[i]);
1990 db->addEntry(entry->Entry, newParent);
1993 QList<IGroupHandle*> children = orgGroup->children();
1994 for (int i=0; i<children.size(); i++) {
1995 GroupHandle* child = static_cast<GroupHandle*>(children[i]);
1996 copyTree(db, child, newParent);
2000 IDatabase* Kdb3Database::groupToNewDb(IGroupHandle* group){
2001 Kdb3Database* db = new Kdb3Database();
2003 copyTree(db, static_cast<GroupHandle*>(group), NULL);
2005 db->changeFile("/ramtmp/test.kdb");
2007 qWarning("%s", CSTR(db->error));
2013 void KeyTransform::transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds){
2014 KeyTransform* ktLeft = new KeyTransform(&src[0], &dst[0], KeySeed, rounds);
2015 KeyTransform* ktRight = new KeyTransform(&src[16], &dst[16], KeySeed, rounds);
2020 SHA256::hashBuffer(dst,dst,32);
2025 KeyTransform::KeyTransform(quint8* pSrc, quint8* pDst, quint8* pKeySeed, int pRounds){
2032 void KeyTransform::run(){
2034 aes.key256(KeySeed);
2036 for (int i=0; i<rounds; i++){
2037 aes.ecb_encrypt(dst,dst,16);
2042 int KeyTransformBenchmark::benchmark(int pMSecs){
2043 KeyTransformBenchmark* ktbLeft = new KeyTransformBenchmark(pMSecs);
2044 KeyTransformBenchmark* ktbRight = new KeyTransformBenchmark(pMSecs);
2049 int num = std::min(ktbLeft->rounds, ktbRight->rounds);
2056 KeyTransformBenchmark::KeyTransformBenchmark(int pMSecs){
2061 void KeyTransformBenchmark::run(){
2063 memset(KeySeed, 0x4B, 32);
2065 memset(dst, 0x7E, 16);
2071 aes.key256(KeySeed);
2074 for (int i=0; i<64; i++){
2075 aes.ecb_encrypt(dst,dst,16);
2078 } while (t.elapsed() < msecs);