/*************************************************************************** * Copyright (C) 2005-2008 by Tarek Saidi * * tarek.saidi@arcor.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; version 2 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "Kdb3Database.h" #include "crypto/twoclass.h" #include #include #define UNEXP_ERROR error=QString("Unexpected error in: %1, Line:%2").arg(__FILE__).arg(__LINE__); const QDateTime Date_Never(QDate(2999,12,28),QTime(23,59,59)); bool Kdb3Database::EntryHandleLessThan(const IEntryHandle* This,const IEntryHandle* Other){ if(!This->isValid() && Other->isValid())return true; if(This->isValid() && !Other->isValid())return false; if(!This->isValid() && !Other->isValid())return false; return This->visualIndex()visualIndex(); } bool Kdb3Database::EntryHandleLessThanStd(const IEntryHandle* This,const IEntryHandle* Other){ int comp = This->title().compare(Other->title()); if (comp < 0) return true; else if (comp > 0) return false; comp = This->username().compare(Other->username()); if (comp < 0) return true; else if (comp > 0) return false; return true; } bool Kdb3Database::StdEntryLessThan(const Kdb3Database::StdEntry& This,const Kdb3Database::StdEntry& Other){ return This.Index=builtinIcons()+CustomIcons.size()) return EntryIcons[0]; if(i= CustomIcons.size()) return; CustomIcons.removeAt(id); // .isNull()==true for(int i=0;iid+builtinIcons()) Entries[i].Image--; } for(int i=0;iid+builtinIcons()) Groups[i].Image--; } emit iconsModified(); } void Kdb3Database::replaceIcon(int id,const QPixmap& icon){ if(id dta.size()){ CustomIcons.clear(); qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error."); return; } offset+=4; if(!CustomIcons.back().loadFromData((const unsigned char*)dta.data()+offset,Size,"PNG")){ CustomIcons.clear(); qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error."); return; } offset+=Size; if(offset > dta.size()){ CustomIcons.clear(); qWarning("Discarded metastream KPX_CUSTOM_ICONS_4 because of a parsing error."); return; } } for(int i=0;iImage=Icon+BUILTIN_ICONS; } for(int i=0;iImage=Icon+BUILTIN_ICONS; } return; } void Kdb3Database::parseCustomIconsMetaStreamV3(const QByteArray& dta){ //Rev 3 quint32 NumIcons,NumEntries,NumGroups,offset; memcpyFromLEnd32(&NumIcons,dta.data()); memcpyFromLEnd32(&NumEntries,dta.data()+4); memcpyFromLEnd32(&NumGroups,dta.data()+8); offset=12; CustomIcons.clear(); for(int i=0;i dta.size()){ CustomIcons.clear(); qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error."); return; } offset+=4; if(!CustomIcons.back().loadFromData((const unsigned char*)dta.data()+offset,Size,"PNG")){ CustomIcons.clear(); qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error."); return; } offset+=Size; if(offset > dta.size()){ CustomIcons.clear(); qWarning("Discarded metastream KPX_CUSTOM_ICONS_3 because of a parsing error."); return; } } for(int i=0;i=65) entry->Image=Icon+4; // Since v0.3.2 the BUILTIN_ICONS number has increased by 4 else entry->Image=Icon; } } for(int i=0;iImage>=65) Group->Image=Icon+4; // Since v0.3.2 the BUILTIN_ICONS number has increased by 4 else Group->Image=Icon; } } return; } void Kdb3Database::parseGroupTreeStateMetaStream(const QByteArray& dta){ if(dta.size()<4){ qWarning("Discarded metastream KPX_GROUP_TREE_STATE because of a parsing error."); return; } quint32 Num; memcpyFromLEnd32(&Num,dta.data()); if(Num*5!=dta.size()-4){ qWarning("Discarded metastream KPX_GROUP_TREE_STATE because of a parsing error."); return; } TreeStateMetaStream.clear(); for(int i=0;iBinaryDesc="bin-stream"; e->Title="Meta-Info"; e->Username="SYSTEM"; e->Comment="KPX_GROUP_TREE_STATE"; e->Url="$"; e->Image=0; if(Groups.size())e->GroupId=Groups[0].Id; QByteArray bin; quint32 Num=Groups.size(); bin.resize(Num*5+4); memcpyToLEnd32(bin.data(),&Num); for(int i=0;iBinary=bin; } Kdb3Database::StdEntry* Kdb3Database::getEntry(const KpxUuid& uuid){ for(int i=0; iUuid=KpxUuid(pData); break; case 0x0002: memcpyFromLEnd32(&entry->GroupId, (char*)pData); break; case 0x0003: memcpyFromLEnd32(&entry->Image, (char*)pData); break; case 0x0004: entry->Title=QString::fromUtf8((char*)pData); break; case 0x0005: entry->Url=QString::fromUtf8((char*)pData); break; case 0x0006: entry->Username=QString::fromUtf8((char*)pData); break; case 0x0007:{ QString s=QString::fromUtf8((char*)pData); entry->Password.setString(s,true); break;} case 0x0008: entry->Comment=QString::fromUtf8((char*)pData); break; case 0x0009: entry->Creation=dateFromPackedStruct5(pData); break; case 0x000A: entry->LastMod=dateFromPackedStruct5(pData); break; case 0x000B: entry->LastAccess=dateFromPackedStruct5(pData); break; case 0x000C: entry->Expire=dateFromPackedStruct5(pData); break; case 0x000D: entry->BinaryDesc=QString::fromUtf8((char*)pData); break; case 0x000E: if(FieldSize != 0) entry->Binary=QByteArray((char*)pData,FieldSize); else entry->Binary=QByteArray(); break; case 0xFFFF: break; default: return false; } return true; } //! Extracts one group from raw decrypted data. bool Kdb3Database::readGroupField(StdGroup* group,QList& Levels,quint16 FieldType, quint8 *pData) { switch(FieldType) { case 0x0000: // Ignore field break; case 0x0001: memcpyFromLEnd32(&group->Id, (char*)pData); break; case 0x0002: group->Title=QString::fromUtf8((char*)pData); break; case 0x0003: //not longer used by KeePassX but part of the KDB format break; case 0x0004: //not longer used by KeePassX but part of the KDB format break; case 0x0005: //not longer used by KeePassX but part of the KDB format break; case 0x0006: //not longer used by KeePassX but part of the KDB format break; case 0x0007: memcpyFromLEnd32(&group->Image, (char*)pData); break; case 0x0008: quint16 Level; memcpyFromLEnd16(&Level, (char*)pData); Levels.append(Level); break; case 0x0009: //not used by KeePassX but part of the KDB format //memcpyFromLEnd32(&Flags, (char*)pData); break; case 0xFFFF: break; default: return false; // Field unsupported } return true; // Field supported } bool Kdb3Database::createGroupTree(QList& Levels){ if(Levels[0]!=0) return false; //find the parent for every group for(int i=0;i=0;j--){ if(Levels[j]Children.append(&Groups[i]); } QList EntryIndexCounter; for(int i=0;iId){ groupIndex = g; break; } } } Groups[groupIndex].Entries.append(&Entries[e]); Entries[e].Group=&Groups[groupIndex]; Entries[e].Index=EntryIndexCounter[groupIndex]; EntryIndexCounter[groupIndex]++; } return true; } void Kdb3Database::createHandles(){ for(int i=0;igroupTreeState()){ case KpxConfig::RestoreLast: for(int i=0;iopen(QIODevice::ReadOnly)){ error=tr("Could not open file."); delete File; File = NULL; return false; } } else { if(!File->open(QIODevice::ReadWrite)){ if(!File->open(QIODevice::ReadOnly)){ error=tr("Could not open file."); delete File; File = NULL; return false; } else{ readOnly = true; } } } openedReadOnly = readOnly; unsigned long total_size,crypto_size; quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags; quint8 FinalRandomSeed[16]; quint8 ContentsHash[32]; quint8 EncryptionIV[16]; total_size=File->size(); char* buffer = new char[total_size]; File->read(buffer,total_size); if(total_size < DB_HEADER_SIZE){ error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)"); LOAD_RETURN_CLEANUP } memcpyFromLEnd32(&Signature1,buffer); memcpyFromLEnd32(&Signature2,buffer+4); memcpyFromLEnd32(&Flags,buffer+8); memcpyFromLEnd32(&Version,buffer+12); memcpy(FinalRandomSeed,buffer+16,16); memcpy(EncryptionIV,buffer+32,16); memcpyFromLEnd32(&NumGroups,buffer+48); memcpyFromLEnd32(&NumEntries,buffer+52); memcpy(ContentsHash,buffer+56,32); memcpy(TransfRandomSeed,buffer+88,32); memcpyFromLEnd32(&KeyTransfRounds,buffer+120); if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){ error=tr("Wrong Signature"); LOAD_RETURN_CLEANUP } if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){ error=tr("Unsupported File Version."); LOAD_RETURN_CLEANUP } if (Flags & PWM_FLAG_RIJNDAEL) Algorithm = Rijndael_Cipher; else if (Flags & PWM_FLAG_TWOFISH) Algorithm = Twofish_Cipher; else{ error=tr("Unknown Encryption Algorithm."); LOAD_RETURN_CLEANUP } RawMasterKey.unlock(); MasterKey.unlock(); KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds); quint8 FinalKey[32]; SHA256 sha; sha.update(FinalRandomSeed,16); sha.update(*MasterKey,32); sha.finish(FinalKey); RawMasterKey.lock(); MasterKey.lock(); if(Algorithm == Rijndael_Cipher){ AESdecrypt aes; aes.key256(FinalKey); aes.cbc_decrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,total_size-DB_HEADER_SIZE,(unsigned char*)EncryptionIV); crypto_size=total_size-((quint8*)buffer)[total_size-1]-DB_HEADER_SIZE; } else if(Algorithm == Twofish_Cipher){ CTwofish twofish; if (twofish.init(FinalKey, 32, EncryptionIV) != true){ error=tr("Unable to initialize the twofish algorithm."); LOAD_RETURN_CLEANUP } crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE, total_size - DB_HEADER_SIZE, (quint8 *)buffer + DB_HEADER_SIZE); } else{ error=tr("Unknown encryption algorithm."); LOAD_RETURN_CLEANUP } if ((crypto_size > 2147483446) || (!crypto_size && NumGroups)){ error=tr("Decryption failed.\nThe key is wrong or the file is damaged."); KeyError=true; LOAD_RETURN_CLEANUP } SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size); if(memcmp(ContentsHash, FinalKey, 32) != 0){ if(PotentialEncodingIssueLatin1){ delete[] buffer; delete File; File = NULL; RawMasterKey.copyData(RawMasterKey_Latin1); PotentialEncodingIssueLatin1 = false; qDebug("Decryption failed. Retrying with Latin-1."); return loadReal(filename, readOnly, true); // second try } if(PotentialEncodingIssueUTF8){ delete[] buffer; delete File; File = NULL; RawMasterKey.copyData(RawMasterKey_UTF8); PotentialEncodingIssueUTF8 = false; qDebug("Decryption failed. Retrying with UTF-8."); return loadReal(filename, readOnly, true); // second/third try } error=tr("Hash test failed.\nThe key is wrong or the file is damaged."); KeyError=true; LOAD_RETURN_CLEANUP } unsigned long pos = DB_HEADER_SIZE; quint16 FieldType; quint32 FieldSize; char* pField; bool bRet; StdGroup group; QList Levels; RootGroup.Title="$ROOT$"; RootGroup.Parent=NULL; RootGroup.Handle=NULL; for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) { pField = buffer+pos; memcpyFromLEnd16(&FieldType, pField); pField += 2; pos += 2; if (pos >= total_size){ error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); LOAD_RETURN_CLEANUP } memcpyFromLEnd32(&FieldSize, pField); pField += 4; pos += 4; if (pos >= (total_size + FieldSize)){ error=tr("Unexpected error: Offset is out of range.").append(" [G2]"); LOAD_RETURN_CLEANUP } bRet = readGroupField(&group,Levels, FieldType, (quint8 *)pField); if ((FieldType == 0xFFFF) && (bRet == true)){ Groups << group; CurGroup++; // Now and ONLY now the counter gets increased } pField += FieldSize; pos += FieldSize; if (pos >= total_size){ error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); LOAD_RETURN_CLEANUP } } StdEntry entry; for (unsigned long CurEntry = 0; CurEntry < NumEntries;) { pField = buffer+pos; memcpyFromLEnd16(&FieldType, pField); pField += 2; pos += 2; if(pos >= total_size){ error=tr("Unexpected error: Offset is out of range.").append(" [E1]"); LOAD_RETURN_CLEANUP } memcpyFromLEnd32(&FieldSize, pField); pField += 4; pos += 4; if (pos >= (total_size + FieldSize)){ error=tr("Unexpected error: Offset is out of range.").append(" [E2]"); LOAD_RETURN_CLEANUP } bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField); if((FieldType == 0xFFFF) && (bRet == true)){ Entries << entry; if(!entry.GroupId) qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data()); CurEntry++; } pField += FieldSize; pos += FieldSize; if (pos >= total_size){ error=tr("Unexpected error: Offset is out of range.").append(" [E3]"); LOAD_RETURN_CLEANUP } } if(!createGroupTree(Levels)){ error=tr("Invalid group tree."); LOAD_RETURN_CLEANUP } delete [] buffer; hasV4IconMetaStream = false; for(int i=0;i> 2); int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); int d = (dw3 >> 1) & 0x0000001F; int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); int s = dw5 & 0x0000003F; return QDateTime(QDate(y,mon,d),QTime(h,min,s)); } void Kdb3Database::dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){ pBytes[0] = (quint8)(((quint32)d.date().year() >> 6) & 0x0000003F); pBytes[1] = (quint8)((((quint32)d.date().year() & 0x0000003F) << 2) | (((quint32)d.date().month() >> 2) & 0x00000003)); pBytes[2] = (quint8)((((quint32)d.date().month() & 0x00000003) << 6) | (((quint32)d.date().day() & 0x0000001F) << 1) | (((quint32)d.time().hour() >> 4) & 0x00000001)); pBytes[3] = (quint8)((((quint32)d.time().hour() & 0x0000000F) << 4) | (((quint32)d.time().minute() >> 2) & 0x0000000F)); pBytes[4] = (quint8)((((quint32)d.time().minute() & 0x00000003) << 6) | ((quint32)d.time().second() & 0x0000003F)); } int Kdb3Database::numGroups(){ return Groups.size(); } int Kdb3Database::numEntries(){ return Entries.size(); } void Kdb3Database::deleteGroup(StdGroup* group){ while(group->Children.size()) deleteGroup(group->Children.front()); QList GroupEntries; GroupEntries=entries(group->Handle); deleteEntries(GroupEntries); Q_ASSERT(group==group->Parent->Children[group->Index]); group->Parent->Children.removeAt(group->Index); for(int i=group->Index;iParent->Children.size();i++){ group->Parent->Children[i]->Index--; } group->Handle->invalidate(); for(int i=0;iGroup); } /* void Kdb3Database::GroupHandle::setIndex(int index){ quint32 ParentId=((GroupHandle*)parent())->Id; int Pos=pDB->getGroupListIndex(this); int NewPos=0; // Move the group to the new position in the list if(ParentId) NewPos=pDB->getGroupListIndex((GroupHandle*)parent()); if(!index){ if(ParentId) pDB->Groups.move(Pos,NewPos+1); else pDB->Groups.move(Pos,NewPos); } else{ for(NewPos;NewPosGroups.size();NewPos++){ if(pDB->Groups[NewPos].ParentId==ParentId && pDB->Groups[NewPos].Index+1==index) break; } //skip the children of the found sibling for(NewPos;NewPosGroups.move(Pos,NewPos); } } // adjust the indices int NewIndex=0; for(int i=0;iGroups.size();i++){ if(pDB->Groups[i].ParentId==ParentId){ pDB->Groups[i].Index=NewIndex; NewIndex++; } } } */ bool Kdb3Database::convHexToBinaryKey(char* HexKey, char* dst){ QString hex=QString::fromLatin1(HexKey,64); for(int i=0; i<64; i+=2){ bool err; quint8 bin; bin=hex.mid(i,2).toUInt(&err,16); if(!err)return false; memcpy(dst+(i/2),&bin,1); } return true; } bool Kdb3Database::setKey(const QString& password,const QString& keyfile){ if(!password.isEmpty() && !keyfile.isEmpty()) return setCompositeKey(password,keyfile); if(!password.isEmpty()) return setPasswordKey(password); if(!keyfile.isEmpty()) return setFileKey(keyfile); Q_ASSERT(false); return false; } bool Kdb3Database::setPasswordKey(const QString& Password){ Q_ASSERT(Password.size()); QTextCodec* codec=QTextCodec::codecForName("Windows-1252"); QByteArray Password_CP1252 = codec->fromUnicode(Password); RawMasterKey_CP1252.unlock(); SHA256::hashBuffer(Password_CP1252.data(),*RawMasterKey_CP1252,Password_CP1252.size()); RawMasterKey_CP1252.lock(); RawMasterKey.copyData(RawMasterKey_CP1252); QByteArray Password_Latin1 = Password.toLatin1(); QByteArray Password_UTF8 = Password.toUtf8(); PotentialEncodingIssueLatin1 = false; PotentialEncodingIssueUTF8 = false; if (Password_Latin1 != Password_CP1252){ // KeePassX used Latin-1 encoding for passwords until version 0.3.1 // but KeePass/Win32 uses Windows Codepage 1252. // To stay compatible with databases created with KeePassX <= 0.3.1 // the loading function gives both encodings a try. PotentialEncodingIssueLatin1 = true; RawMasterKey_Latin1.unlock(); SHA256::hashBuffer(Password_Latin1.data(),*RawMasterKey_Latin1,Password_Latin1.size()); RawMasterKey_Latin1.lock(); } if (Password_UTF8 != Password_CP1252){ // KeePassX used UTF-8 encoding for passwords until version 0.2.2 // but KeePass/Win32 uses Windows Codepage 1252. // To stay compatible with databases created with KeePassX <= 0.2.2 // the loading function gives both encodings a try. PotentialEncodingIssueUTF8 = true; RawMasterKey_UTF8.unlock(); SHA256::hashBuffer(Password_UTF8.data(),*RawMasterKey_UTF8,Password_UTF8.size()); RawMasterKey_UTF8.lock(); } return true; } bool Kdb3Database::setFileKey(const QString& filename){ QFile file(filename); if(!file.open(QIODevice::ReadOnly|QIODevice::Unbuffered)){ error=decodeFileError(file.error()); return false; } qint64 FileSize=file.size(); if(FileSize == 0){ error=tr("Key file is empty."); return false; } RawMasterKey.unlock(); if(FileSize == 32){ if(file.read((char*)(*RawMasterKey),32) != 32){ error=decodeFileError(file.error()); RawMasterKey.lock(); return false; } RawMasterKey.lock(); return true; } if(FileSize == 64){ char hex[64]; if(file.read(hex,64) != 64){ error=decodeFileError(file.error()); RawMasterKey.lock(); return false; } if (convHexToBinaryKey(hex,(char*)(*RawMasterKey))){ RawMasterKey.lock(); return true; } file.seek(0); } SHA256 sha; unsigned char* buffer[2048]; unsigned long read; do { read = file.read((char*)buffer,2048); if (read != 0) sha.update(buffer,read); } while (read == 2048); sha.finish(*RawMasterKey); RawMasterKey.lock(); return true; } bool Kdb3Database::setCompositeKey(const QString& Password,const QString& filename){ SHA256 sha; setPasswordKey(Password); RawMasterKey.unlock(); sha.update(*RawMasterKey,32); RawMasterKey.lock(); if(!setFileKey(filename))return false; RawMasterKey.unlock(); sha.update(*RawMasterKey,32); sha.finish(*RawMasterKey); RawMasterKey.lock(); return true; } QList Kdb3Database::entries(){ QList handles; for(int i=0; i Kdb3Database::expiredEntries(){ QList handles; for(int i=0; i Kdb3Database::entries(IGroupHandle* Group){ QList handles; for(int i=0; i Kdb3Database::entriesSortedStd(IGroupHandle* Group){ QList handles; for(int i=0; iEntry) break; } Entries[j].Handle->invalidate(); Entries.removeAt(j); } void Kdb3Database::moveEntry(IEntryHandle* entry, IGroupHandle* group){ ((EntryHandle*)entry)->Entry->GroupId=((GroupHandle*)group)->Group->Id; ((EntryHandle*)entry)->Entry->Group=((GroupHandle*)group)->Group; } void Kdb3Database::deleteEntries(QList entries){ if(!entries.size())return; StdGroup* Group=((EntryHandle*)entries[0])->Entry->Group; for(int i=0;iEntry) break; } Group->Children.removeAt(Entries[j].Index); Entries[j].Handle->invalidate(); Entries.removeAt(j); } for(int i=0;iChildren.size();i++){ Group->Children[i]->Index=i; } }; QList Kdb3Database::groups(){ QList handles; for(int i=0; iGroup; Groups.back().Index=Groups.back().Parent->Children.size(); Groups.back().Parent->Children.append(&Groups.back()); } else{ // Insert to root group. Try to keep Backup group at the end. Groups.back().Parent=&RootGroup; Groups.back().Index=RootGroup.Children.size(); int position = RootGroup.Children.size(); if (group->Title!="Backup" && !RootGroup.Children.isEmpty() && RootGroup.Children.last()->Title=="Backup"){ RootGroup.Children.last()->Index = Groups.back().Index; Groups.back().Index--; position--; } RootGroup.Children.insert(position, &Groups.back()); } return &GroupHandles.back(); } IGroupHandle* Kdb3Database::backupGroup(bool create){ IGroupHandle* group = NULL; QList allGroups = groups(); for (int i=0; iparent()==NULL && allGroups[i]->title()=="Backup"){ group = allGroups[i]; break; } } if (group==NULL && create){ CGroup newGroup; newGroup.Title = "Backup"; newGroup.Image = 4; group = addGroup(&newGroup, NULL); } return group; } Kdb3Database::StdEntry::StdEntry(){ Handle = NULL; Group = NULL; } Kdb3Database::StdGroup::StdGroup(){ Index=0; Id=0; Parent=NULL; Handle=NULL; } Kdb3Database::StdGroup::StdGroup(const CGroup& other){ Index=0; Id=other.Id; Image=other.Image; Title=other.Title; Parent=NULL; Handle=NULL; } void Kdb3Database::EntryHandle::setTitle(const QString& Title){Entry->Title=Title; } void Kdb3Database::EntryHandle::setUsername(const QString& Username){Entry->Username=Username;} void Kdb3Database::EntryHandle::setUrl(const QString& Url){Entry->Url=Url;} void Kdb3Database::EntryHandle::setPassword(const SecString& Password){Entry->Password=Password;} void Kdb3Database::EntryHandle::setExpire(const KpxDateTime& s){Entry->Expire=s;} void Kdb3Database::EntryHandle::setCreation(const KpxDateTime& s){Entry->Creation=s;} void Kdb3Database::EntryHandle::setLastAccess(const KpxDateTime& s){Entry->LastAccess=s;} void Kdb3Database::EntryHandle::setLastMod(const KpxDateTime& s){Entry->LastMod=s;} void Kdb3Database::EntryHandle::setBinaryDesc(const QString& s){Entry->BinaryDesc=s;} void Kdb3Database::EntryHandle::setComment(const QString& s){Entry->Comment=s;} void Kdb3Database::EntryHandle::setBinary(const QByteArray& s){Entry->Binary=s;} void Kdb3Database::EntryHandle::setImage(const quint32& s){Entry->Image=s;} KpxUuid Kdb3Database::EntryHandle::uuid()const{return Entry->Uuid;} IGroupHandle* Kdb3Database::EntryHandle::group()const{return Entry->Group->Handle;} quint32 Kdb3Database::EntryHandle::image()const{return Entry->Image;} QString Kdb3Database::EntryHandle::title()const{return Entry->Title;} QString Kdb3Database::EntryHandle::url()const{return Entry->Url;} QString Kdb3Database::EntryHandle::username()const{return Entry->Username;} SecString Kdb3Database::EntryHandle::password()const{return Entry->Password;} QString Kdb3Database::EntryHandle::comment()const{return Entry->Comment;} QString Kdb3Database::EntryHandle::binaryDesc()const{return Entry->BinaryDesc;} KpxDateTime Kdb3Database::EntryHandle::creation()const{return Entry->Creation;} KpxDateTime Kdb3Database::EntryHandle::lastMod()const{return Entry->LastMod;} KpxDateTime Kdb3Database::EntryHandle::lastAccess()const{return Entry->LastAccess;} KpxDateTime Kdb3Database::EntryHandle::expire()const{return Entry->Expire;} QByteArray Kdb3Database::EntryHandle::binary()const{return Entry->Binary;} quint32 Kdb3Database::EntryHandle::binarySize()const{return Entry->Binary.size();} QString Kdb3Database::EntryHandle::friendlySize()const { quint32 binsize = binarySize(); QString unit; uint faktor; int prec; if (binsize < 1024) { unit = tr("Bytes"); faktor = 1; prec = 0; } else { if (binsize < 1048576) { unit = tr("KiB"); faktor = 1024; } else if (binsize < 1073741824) { unit = tr("MiB"); faktor = 1048576; } else { unit = tr("GiB"); faktor = 1073741824; } prec = 1; } return (QString::number((float)binsize / (float)faktor, 'f', prec) + " " + unit); } int Kdb3Database::EntryHandle::visualIndex()const{return Entry->Index;} void Kdb3Database::EntryHandle::setVisualIndexDirectly(int i){Entry->Index=i;} bool Kdb3Database::EntryHandle::isValid()const{return valid;} CEntry Kdb3Database::EntryHandle::data()const{ return *this->Entry; } void Kdb3Database::EntryHandle::setVisualIndex(int index){ QListEntries=pDB->entries(Entry->Group->Handle); Entries.move(visualIndex(),index); for(int i=0;i(Entries[i])->Entry->Index=index; } } Kdb3Database::EntryHandle::EntryHandle(Kdb3Database* db){ pDB=db; valid=true; Entry=NULL; } bool Kdb3Database::GroupHandle::isValid(){return valid;} QString Kdb3Database::GroupHandle::title(){return Group->Title;} quint32 Kdb3Database::GroupHandle::image(){return Group->Image;} int Kdb3Database::GroupHandle::index(){return Group->Index;} void Kdb3Database::GroupHandle::setTitle(const QString& Title){Group->Title=Title;} void Kdb3Database::GroupHandle::setExpanded(bool IsExpanded){Group->IsExpanded=IsExpanded;} bool Kdb3Database::GroupHandle::expanded(){return Group->IsExpanded;} void Kdb3Database::GroupHandle::setImage(const quint32& New){Group->Image=New;} Kdb3Database::GroupHandle::GroupHandle(Kdb3Database* db){ pDB=db; valid=true; Group=NULL; } IGroupHandle* Kdb3Database::GroupHandle::parent(){ return (IGroupHandle*)Group->Parent->Handle; } int Kdb3Database::GroupHandle::level(){ int i=0; StdGroup* group=Group; while(group->Parent){ group=group->Parent; i++; } i--; return i; } QList Kdb3Database::GroupHandle::children(){ QList children; for(int i=0; i < Group->Children.size(); i++){ children.append(Group->Children[i]->Handle); } return children; } void memcpyFromLEnd32(quint32* dst,const char* src){ if (QSysInfo::ByteOrder==QSysInfo::BigEndian){ memcpy(((char*)dst)+3,src+0,1); memcpy(((char*)dst)+2,src+1,1); memcpy(((char*)dst)+1,src+2,1); memcpy(((char*)dst)+0,src+3,1); } else{ memcpy(dst,src,4); } } void memcpyFromLEnd16(quint16* dst,const char* src){ if (QSysInfo::ByteOrder==QSysInfo::BigEndian){ memcpy(((char*)dst)+1,src+0,1); memcpy(((char*)dst)+0,src+1,1); } else{ memcpy(dst,src,2); } } void memcpyToLEnd32(char* dst,const quint32* src){ if (QSysInfo::ByteOrder==QSysInfo::BigEndian){ memcpy(dst+0,((char*)src)+3,1); memcpy(dst+1,((char*)src)+2,1); memcpy(dst+2,((char*)src)+1,1); memcpy(dst+3,((char*)src)+0,1); } else{ memcpy(dst,src,4); } } void memcpyToLEnd16(char* dst,const quint16* src){ if (QSysInfo::ByteOrder==QSysInfo::BigEndian){ memcpy(dst+0,((char*)src)+1,1); memcpy(dst+1,((char*)src)+0,1); } else{ memcpy(dst,src,2); } } bool Kdb3Database::save(){ if(!Groups.size()){ error=tr("The database must contain at least one group."); return false; } if (!File->isOpen()) { if(!File->open(QIODevice::ReadWrite)){ error=tr("Could not open file."); return false; } } if(!(File->openMode() & QIODevice::WriteOnly)){ error = tr("The database has been opened read-only."); return false; } //Delete old backup entries if (config->backup() && config->backupDelete() && config->backupDeleteAfter()>0 && backupGroup()){ QDateTime time = QDateTime::currentDateTime().addDays(-config->backupDeleteAfter()); QList backupEntries = entries(backupGroup()); for (int i=0; ilastMod() MetaStreams; MetaStreams << StdEntry(); createCustomIconsMetaStream(&MetaStreams.back()); MetaStreams << StdEntry(); createGroupTreeStateMetaStream(&MetaStreams.back()); FileSize=DB_HEADER_SIZE; // Get the size of all groups (94 Byte + length of the name string) for(int i = 0; i < Groups.size(); i++){ FileSize += 94 + Groups[i].Title.toUtf8().length()+1; } // Get the size of all entries for(int i = 0; i < Entries.size(); i++){ FileSize += 134 +Entries[i].Title.toUtf8().length()+1 +Entries[i].Username.toUtf8().length()+1 +Entries[i].Url.toUtf8().length()+1 +Entries[i].Password.length()+1 +Entries[i].Comment.toUtf8().length()+1 +Entries[i].BinaryDesc.toUtf8().length()+1 +Entries[i].Binary.length(); } for(int i=0; i < UnknownMetaStreams.size(); i++){ FileSize +=165 +UnknownMetaStreams[i].Comment.toUtf8().length()+1 +UnknownMetaStreams[i].Binary.length(); } for(int i=0; i < MetaStreams.size(); i++){ FileSize +=165 +MetaStreams[i].Comment.toUtf8().length()+1 +MetaStreams[i].Binary.length(); } // Round up filesize to 16-byte boundary for Rijndael/Twofish FileSize = (FileSize + 16) - (FileSize % 16); char* buffer=new char[FileSize+16]; Signature1 = PWM_DBSIG_1; Signature2 = PWM_DBSIG_2; Flags = PWM_FLAG_SHA2; if(Algorithm == Rijndael_Cipher) Flags |= PWM_FLAG_RIJNDAEL; else if(Algorithm == Twofish_Cipher) Flags |= PWM_FLAG_TWOFISH; Version = PWM_DBVER_DW; NumGroups = Groups.size(); NumEntries = Entries.size()+UnknownMetaStreams.size()+MetaStreams.size(); QList saveEntries = Entries; qSort(saveEntries.begin(),saveEntries.end(),StdEntryLessThan); randomize(FinalRandomSeed,16); randomize(EncryptionIV,16); unsigned int pos=DB_HEADER_SIZE; // Skip the header, it will be written later serializeGroups(buffer,pos); serializeEntries(saveEntries,buffer,pos); serializeEntries(UnknownMetaStreams,buffer,pos); serializeEntries(MetaStreams,buffer,pos); SHA256::hashBuffer(buffer+DB_HEADER_SIZE,ContentsHash,pos-DB_HEADER_SIZE); memcpyToLEnd32(buffer,&Signature1); memcpyToLEnd32(buffer+4,&Signature2); memcpyToLEnd32(buffer+8,&Flags); memcpyToLEnd32(buffer+12,&Version); memcpy(buffer+16,FinalRandomSeed,16); memcpy(buffer+32,EncryptionIV,16); memcpyToLEnd32(buffer+48,&NumGroups); memcpyToLEnd32(buffer+52,&NumEntries); memcpy(buffer+56,ContentsHash,32); memcpy(buffer+88,TransfRandomSeed,32); memcpyToLEnd32(buffer+120,&KeyTransfRounds); quint8 FinalKey[32]; SHA256 sha; sha.update(FinalRandomSeed,16); MasterKey.unlock(); sha.update(*MasterKey,32); MasterKey.lock(); sha.finish(FinalKey); unsigned long EncryptedPartSize; if(Algorithm == Rijndael_Cipher){ EncryptedPartSize=((pos-DB_HEADER_SIZE)/16+1)*16; quint8 PadLen=EncryptedPartSize-(pos-DB_HEADER_SIZE); for(int i=0;i (0xFFFFFFE - 202)) || (!EncryptedPartSize && Groups.size())){ UNEXP_ERROR delete [] buffer; return false; } int size = EncryptedPartSize+DB_HEADER_SIZE; if (!saveFileTransactional(buffer, size)) { error=decodeFileError(File->error()); delete [] buffer; return false; } delete [] buffer; //if(SearchGroupID!=-1)Groups.push_back(SearchGroup); return true; } bool Kdb3Database::saveFileTransactional(char* buffer, int size) { QString orgFilename = File->fileName(); QFile* tmpFile = new QFile(orgFilename + ".tmp"); if (!tmpFile->open(QIODevice::WriteOnly|QIODevice::Truncate)) { tmpFile->remove(); delete tmpFile; return false; } if (tmpFile->write(buffer,size) != size) { tmpFile->remove(); delete tmpFile; return false; } if (!syncFile(tmpFile)) qWarning("Unable to flush file to disk"); tmpFile->close(); if (!File->remove()) { delete tmpFile; return false; } delete File; File = NULL; if (!tmpFile->rename(orgFilename)) { delete tmpFile; File = new QFile(orgFilename); return false; } File = tmpFile; if (!tmpFile->open(QIODevice::ReadWrite)) { delete tmpFile; return false; } return true; } void Kdb3Database::createCustomIconsMetaStream(StdEntry* e){ /* Rev 3 */ e->BinaryDesc="bin-stream"; e->Title="Meta-Info"; e->Username="SYSTEM"; e->Comment="KPX_CUSTOM_ICONS_4"; e->Url="$"; if(Groups.size())e->GroupId=Groups[0].Id; int Size=12; quint32 NumEntries=0; for(quint32 i=0;i=BUILTIN_ICONS) NumEntries++; } quint32 NumGroups=0; for(quint32 i=0;i=BUILTIN_ICONS) NumGroups++; } Size+=8*NumGroups+20*NumEntries; Size+=CustomIcons.size()*1000; // 1KB e->Binary.reserve(Size); e->Binary.resize(12); quint32 NumIcons=CustomIcons.size(); memcpyToLEnd32(e->Binary.data(),&NumIcons); memcpyToLEnd32(e->Binary.data()+4,&NumEntries); memcpyToLEnd32(e->Binary.data()+8,&NumGroups); for(int i=0;iBinary.append(QByteArray::fromRawData(ImgSizeBin,4)); e->Binary.append(png); } for(quint32 i=0;i=BUILTIN_ICONS){ char Bin[20]; Entries[i].Uuid.toRaw(Bin); quint32 id=Entries[i].Image-BUILTIN_ICONS; memcpyToLEnd32(Bin+16,&id); e->Binary.append(QByteArray::fromRawData(Bin,20)); } } for(quint32 i=0;i=BUILTIN_ICONS){ char Bin[8]; memcpyToLEnd32(Bin,&Groups[i].Id); quint32 id=Groups[i].Image-BUILTIN_ICONS; memcpyToLEnd32(Bin+4,&id); e->Binary.append(QByteArray::fromRawData(Bin,8)); } } } QList Kdb3Database::sortedGroups(){ QList SortedGroups; appendChildrenToGroupList(SortedGroups,RootGroup); return SortedGroups; } void Kdb3Database::appendChildrenToGroupList(QList& list,StdGroup& group){ for(int i=0;iHandle; appendChildrenToGroupList(list,*group.Children[i]); } } void Kdb3Database::appendChildrenToGroupList(QList& list,StdGroup& group){ for(int i=0;iSortedGroups; appendChildrenToGroupList(SortedGroups,RootGroup); for(int i=0; i < SortedGroups.size(); i++){ unsigned char Date[5]; dateToPackedStruct5(Date_Never,Date); quint16 Level=0; StdGroup* group=SortedGroups[i]; while(group->Parent){ Level++; group=group->Parent; } Level--; FieldType = 0x0001; FieldSize = 4; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpyToLEnd32(buffer+pos, &SortedGroups[i]->Id); pos += 4; FieldType = 0x0002; FieldSize = SortedGroups[i]->Title.toUtf8().length() + 1; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, SortedGroups[i]->Title.toUtf8(),FieldSize); pos += FieldSize; FieldType = 0x0003; FieldSize = 5; //Creation memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, Date,5); pos+=5; FieldType = 0x0004; FieldSize = 5; //LastMod memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, Date,5); pos+=5; FieldType = 0x0005; FieldSize = 5; //LastAccess memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, Date,5); pos+=5; FieldType = 0x0006; FieldSize = 5; //Expire memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, Date,5); pos+=5; FieldType = 0x0007; FieldSize = 4; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpyToLEnd32(buffer+pos, &SortedGroups[i]->Image); pos += 4; FieldType = 0x0008; FieldSize = 2; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpyToLEnd16(buffer+pos, &Level); pos += 2; FieldType = 0x0009; FieldSize = 4; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpyToLEnd32(buffer+pos, &Flags); pos += 4; FieldType = 0xFFFF; FieldSize = 0; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; } } void Kdb3Database::serializeEntries(QList& EntryList,char* buffer,unsigned int& pos){ quint16 FieldType; quint32 FieldSize; for(int i = 0; i < EntryList.size(); i++){ FieldType = 0x0001; FieldSize = 16; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; EntryList[i].Uuid.toRaw(buffer+pos); pos += 16; FieldType = 0x0002; FieldSize = 4; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpyToLEnd32(buffer+pos, &EntryList[i].GroupId); pos += 4; FieldType = 0x0003; FieldSize = 4; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpyToLEnd32(buffer+pos,&EntryList[i].Image); pos += 4; FieldType = 0x0004; FieldSize = EntryList[i].Title.toUtf8().length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, EntryList[i].Title.toUtf8(),FieldSize); pos += FieldSize; FieldType = 0x0005; FieldSize = EntryList[i].Url.toUtf8().length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, EntryList[i].Url.toUtf8(),FieldSize); pos += FieldSize; FieldType = 0x0006; FieldSize = EntryList[i].Username.toUtf8().length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, EntryList[i].Username.toUtf8(),FieldSize); pos += FieldSize; FieldType = 0x0007; FieldSize = EntryList[i].Password.length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; EntryList[i].Password.unlock(); memcpy(buffer+pos, EntryList[i].Password.string().toUtf8(),FieldSize); pos += FieldSize; EntryList[i].Password.lock(); FieldType = 0x0008; FieldSize = EntryList[i].Comment.toUtf8().length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, EntryList[i].Comment.toUtf8(),FieldSize); pos += FieldSize; FieldType = 0x0009; FieldSize = 5; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; dateToPackedStruct5(EntryList[i].Creation,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000A; FieldSize = 5; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; dateToPackedStruct5(EntryList[i].LastMod,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000B; FieldSize = 5; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; dateToPackedStruct5(EntryList[i].LastAccess,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000C; FieldSize = 5; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; dateToPackedStruct5(EntryList[i].Expire,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000D; FieldSize = EntryList[i].BinaryDesc.toUtf8().length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; memcpy(buffer+pos, EntryList[i].BinaryDesc.toUtf8(),FieldSize); pos += FieldSize; FieldType = 0x000E; FieldSize = EntryList[i].Binary.length(); memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; if((!EntryList[i].Binary.isNull()) && (FieldSize != 0)) memcpy(buffer+pos, EntryList[i].Binary.data(), FieldSize); pos += FieldSize; FieldType = 0xFFFF; FieldSize = 0; memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; } } bool Kdb3Database::close(){ if (File!=NULL) delete File; return true; } void Kdb3Database::create(){ File=NULL; RootGroup.Title="$ROOT$"; RootGroup.Parent=NULL; RootGroup.Handle=NULL; Algorithm=Rijndael_Cipher; KeyTransfRounds=50000; KeyError=false; } bool Kdb3Database::isKeyError(){ if(KeyError){ KeyError=false; return true; } else return false; } IEntryHandle* Kdb3Database::cloneEntry(const IEntryHandle* entry){ StdEntry dolly; dolly=*((EntryHandle*)entry)->Entry; dolly.Uuid.generate(); Entries.append(dolly); EntryHandles.append(EntryHandle(this)); EntryHandles.back().Entry=&Entries.back(); Entries.back().Handle=&EntryHandles.back(); return &EntryHandles.back(); } IEntryHandle* Kdb3Database::newEntry(IGroupHandle* group){ StdEntry Entry; Entry.Uuid.generate(); Entry.Group=((GroupHandle*)group)->Group; Entry.GroupId=Entry.Group->Id; Entries.append(Entry); EntryHandles.append(EntryHandle(this)); EntryHandles.back().Entry=&Entries.back(); Entries.back().Handle=&EntryHandles.back(); return &EntryHandles.back(); } IEntryHandle* Kdb3Database::addEntry(const CEntry* NewEntry, IGroupHandle* Group){ StdEntry Entry(*((StdEntry*)NewEntry)); Entry.Uuid.generate(); Entry.Group=((GroupHandle*)Group)->Group; Entry.GroupId=Entry.Group->Id; Entries.append(Entry); EntryHandles.append(EntryHandle(this)); EntryHandles.back().Entry=&Entries.back(); Entries.back().Handle=&EntryHandles.back(); return &EntryHandles.back(); } void Kdb3Database::deleteLastEntry(){ Entries.removeAt(Entries.size()-1); EntryHandles.back().invalidate(); } bool Kdb3Database::isParent(IGroupHandle* parent, IGroupHandle* child){ StdGroup* group=((GroupHandle*)child)->Group; while(group->Parent!=&RootGroup){ if(group->Parent==((GroupHandle*)parent)->Group)return true; group=group->Parent; } return false; } void Kdb3Database::cleanUpHandles(){} bool Kdb3Database::searchStringContains(const QString& search, const QString& string,bool Cs, bool RegExp){ if(RegExp){ QRegExp exp(search,Cs ? Qt::CaseSensitive : Qt::CaseInsensitive); if(string.contains(exp)==0)return false;} else if(string.contains(search,Cs ? Qt::CaseSensitive : Qt::CaseInsensitive)==0)return false; return true; } void Kdb3Database::getEntriesRecursive(IGroupHandle* Group, QList& EntryList){ EntryList<Group->Children.size(); i++){ getEntriesRecursive(((GroupHandle*)Group)->Group->Children[i]->Handle,EntryList); } } QList Kdb3Database::search(IGroupHandle* Group,const QString& search, bool CaseSensitive, bool RegExp, bool Recursive,bool* Fields){ bool fields[6]={true,true,true,false,true,true}; if(!Fields) Fields=fields; QList SearchEntries; if(search==QString())return Group ? entries(Group) : entries(); if(Group){ if(Recursive) getEntriesRecursive(Group,SearchEntries); else SearchEntries=entries(Group); } else SearchEntries=entries(); IGroupHandle* bGroup = backupGroup(); QList ResultEntries; for(int i=0; igroup(); while (entryGroup->parent()) entryGroup = entryGroup->parent(); if (entryGroup == bGroup) continue; bool match=false; if(Fields[0])match=match||searchStringContains(search,SearchEntries[i]->title(),CaseSensitive,RegExp); if(Fields[1])match=match||searchStringContains(search,SearchEntries[i]->username(),CaseSensitive,RegExp); if(Fields[2])match=match||searchStringContains(search,SearchEntries[i]->url(),CaseSensitive,RegExp); SecString Password=SearchEntries[i]->password(); Password.unlock(); if(Fields[3])match=match||searchStringContains(search,Password.string(),CaseSensitive,RegExp); Password.lock(); if(Fields[4])match=match||searchStringContains(search,SearchEntries[i]->comment(),CaseSensitive,RegExp); if(Fields[5])match=match||searchStringContains(search,SearchEntries[i]->binaryDesc(),CaseSensitive,RegExp); if(match) ResultEntries << SearchEntries[i]; } return ResultEntries; } void Kdb3Database::rebuildIndices(QList& list){ for(int i=0;iIndex=i; } } void Kdb3Database::moveGroup(IGroupHandle* groupHandle,IGroupHandle* NewParent,int Pos){ StdGroup* Parent; StdGroup* Group=((GroupHandle*)groupHandle)->Group; if(NewParent) Parent=((GroupHandle*)NewParent)->Group; else Parent=&RootGroup; Group->Parent->Children.removeAt(Group->Index); rebuildIndices(Group->Parent->Children); Group->Parent=Parent; if(Pos==-1){ Parent->Children.append(Group); } else { Q_ASSERT(Parent->Children.size()>=Pos); Parent->Children.insert(Pos,Group); } rebuildIndices(Parent->Children); } bool Kdb3Database::changeFile(const QString& filename){ QFile* tmpFile = new QFile(filename); if(!tmpFile->open(QIODevice::ReadWrite)){ error = decodeFileError(File->error()); delete tmpFile; return false; } if (File) delete File; File = tmpFile; return true; } void Kdb3Database::generateMasterKey(){ randomize(TransfRandomSeed,32); RawMasterKey.unlock(); MasterKey.unlock(); KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds); RawMasterKey.lock(); MasterKey.lock(); } /*void Kdb3Database::copyTree(Kdb3Database* db, GroupHandle* orgGroup, IGroupHandle* parent) { IGroupHandle* newParent = db->addGroup(orgGroup->Group, parent); QList entryList = entries(orgGroup); for (int i=0; i(entryList[i]); db->addEntry(entry->Entry, newParent); } QList children = orgGroup->children(); for (int i=0; i(children[i]); copyTree(db, child, newParent); } } IDatabase* Kdb3Database::groupToNewDb(IGroupHandle* group){ Kdb3Database* db = new Kdb3Database(); db->create(); copyTree(db, static_cast(group), NULL); db->changeFile("/ramtmp/test.kdb"); if (!db->save()) qWarning("%s", CSTR(db->error)); return db; }*/ void KeyTransform::transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds){ KeyTransform* ktLeft = new KeyTransform(&src[0], &dst[0], KeySeed, rounds); KeyTransform* ktRight = new KeyTransform(&src[16], &dst[16], KeySeed, rounds); ktLeft->start(); ktRight->start(); ktLeft->wait(); ktRight->wait(); SHA256::hashBuffer(dst,dst,32); delete ktLeft; delete ktRight; } KeyTransform::KeyTransform(quint8* pSrc, quint8* pDst, quint8* pKeySeed, int pRounds){ src = pSrc; dst = pDst; KeySeed = pKeySeed; rounds = pRounds; } void KeyTransform::run(){ AESencrypt aes; aes.key256(KeySeed); memcpy(dst,src,16); for (int i=0; istart(); ktbRight->start(); ktbLeft->wait(); ktbRight->wait(); int num = std::min(ktbLeft->rounds, ktbRight->rounds); delete ktbLeft; delete ktbRight; return num; } KeyTransformBenchmark::KeyTransformBenchmark(int pMSecs){ msecs = pMSecs; rounds = 0; } void KeyTransformBenchmark::run(){ quint8 KeySeed[32]; memset(KeySeed, 0x4B, 32); quint8 dst[16]; memset(dst, 0x7E, 16); QTime t; t.start(); AESencrypt aes; aes.key256(KeySeed); do { for (int i=0; i<64; i++){ aes.ecb_encrypt(dst,dst,16); } rounds += 64; } while (t.elapsed() < msecs); }