/*************************************************************************** * Copyright (C) 2005-2007 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 "global.h" #include #include #include #include #include #include #include #include #include "crypto/twoclass.h" #include "crypto/aescpp.h" #include "crypto/sha256.h" #include "crypto/yarrow.h" #include "lib/random.h" using namespace std; #include "Kdb3Database.h" #include "KpxConfig.h" #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 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 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_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;iOldImage=entry->Image; entry->Image=Icon; } } for(int i=0;iOldImage=Group->Image; 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->OldImage=0; 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); entry->OldImage=entry->Image; 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, quint32 FieldSize, 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); group->OldImage=group->Image; 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]Childs.append(&Groups[i]); } QList EntryIndexCounter; for(int i=0;igroupTreeState()){ case KpxConfig::RestoreLast: for(int i=0;iopen(QIODevice::ReadWrite)){ if(!File->open(QIODevice::ReadOnly)){ error=tr("Could not open file."); delete File; File=NULL; return false; } } 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)"); return false; } 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"); return false;} if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){ error=tr("Unsupported File Version."); return false;} if(Flags & PWM_FLAG_RIJNDAEL) Algorithm = Rijndael_Cipher; else if(Flags & PWM_FLAG_TWOFISH) Algorithm = Twofish_Cipher; else{error=tr("Unknown Encryption Algorithm."); return false;} if(!transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds))return false; quint8 FinalKey[32]; SHA256 sha; sha.update(FinalRandomSeed,16); sha.update(MasterKey,32); sha.finish(FinalKey); 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){return false;} crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE, total_size - DB_HEADER_SIZE, (quint8 *)buffer + DB_HEADER_SIZE); } if((crypto_size > 2147483446) || (!crypto_size && NumGroups)){error=tr("Decryption failed.\nThe key is wrong or the file is damaged."); return false;} SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size); if(memcmp(ContentsHash, FinalKey, 32) != 0){ error=tr("Hash test failed.\nThe key is wrong or the file is damaged."); KeyError=true; return false;} unsigned long tmp_id=0; 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]"); return false; } memcpyFromLEnd32(&FieldSize, pField); pField += 4; pos += 4; if(pos >= (total_size + FieldSize)){ error=tr("Unexpected error: Offset is out of range.").append(" [G2]"); return false;} bRet = readGroupField(&group,Levels, FieldType, FieldSize, (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]"); return false;} } 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]"); return false;} memcpyFromLEnd32(&FieldSize, pField); pField += 4; pos += 4; if(pos >= (total_size + FieldSize)) { error=tr("Unexpected error: Offset is out of range.").append(" [E2]"); return false; } 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]"); return false; } } if(!createGroupTree(Levels)){ error=tr("Invalid group tree."); return false; } unsigned long CurGID, g, e, z, num; delete [] buffer; //Remove the metastreams from the entry list 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)); } bool Kdb3Database::transformKey(quint8* src,quint8* dst,quint8* KeySeed,int rounds){ quint8 tmp[32]; AESencrypt aes; aes.key256(KeySeed); memcpy(tmp,src,32); for(int i=0;iChilds.size()) deleteGroup(group->Childs.front()); QList GroupEntries; GroupEntries=entries(group->Handle); deleteEntries(GroupEntries); Q_ASSERT(group==group->Parent->Childs[group->Index]); group->Parent->Childs.removeAt(group->Index); for(int i=group->Index;iParent->Childs.size();i++){ group->Parent->Childs[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 childs 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::fromAscii(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; } /* void Kdb3Database::authByPwd(QString& Password){ if(!Password.size()) { memcpy(RawMasterKey,QByteArray(32,'\0').data(),32); return; } SHA256::hashBuffer(Password.toUtf8().data(),RawMasterKey,Password.toUtf8().size()); return; }*/ void Kdb3Database::authByPwd(QString& Password){ if(!Password.size()) { memcpy(RawMasterKey,QByteArray(32,'\0').data(),32); return; } SHA256::hashBuffer(Password.toLatin1().data(),RawMasterKey,Password.toLatin1().size()); QByteArray lat,utf; utf=Password.toUtf8(); lat=Password.toLatin1(); char *Lat=lat.data(); char *Utf=utf.data(); return; } bool Kdb3Database::authByFile(QString& filename){ QFile file(filename); if(!file.open(QIODevice::ReadOnly|QIODevice::Unbuffered)){ error=decodeFileError(file.error()); return false; } unsigned long FileSize=file.size(); if(FileSize == 0){ error=tr("Key file is empty."); return false; } if(FileSize == 32){ if(file.read((char*)RawMasterKey,32) != 32){ error=decodeFileError(file.error()); return false;} return true; } if(FileSize == 64){ char hex[64]; if(file.read(hex,64) != 64){ error=decodeFileError(file.error()); return false;} if(convHexToBinaryKey(hex,(char*)RawMasterKey))return true; } SHA256 sha; unsigned char* buffer = new unsigned char[2048]; while(1) { unsigned long read=file.read((char*)buffer,2048); if(read == 0) break; sha.update(buffer,read); if(read != 2048) break; } sha.finish(RawMasterKey); delete [] buffer; return true; } bool Kdb3Database::authByFileAndPwd(QString& Password, QString& filename){ unsigned char PasswordKey[32]; unsigned char FileKey[32]; if(!authByFile(filename))return false; memcpy(FileKey,RawMasterKey,32); authByPwd(Password); memcpy(PasswordKey,RawMasterKey,32); SHA256 sha; sha.update(PasswordKey,32); sha.update(FileKey,32); sha.finish(RawMasterKey); 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; iEntry->Group; int j; for(j=0;jEntry) 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->Childs.removeAt(Entries[j].Index); Entries[j].Handle->invalidate(); Entries.removeAt(j); } for(int i=0;iChilds.size();i++){ Group->Childs[i]->Index=i; } }; QList Kdb3Database::groups(){ QList handles; for(int i=0; iGroup; Groups.back().Index=Groups.back().Parent->Childs.size(); Groups.back().Parent->Childs.append(&Groups.back()); } else{ Groups.back().Parent=&RootGroup; Groups.back().Index=RootGroup.Childs.size(); Groups.back().Parent->Childs.append(&Groups.back()); } return &GroupHandles.back(); } Kdb3Database::StdGroup::StdGroup(const CGroup& other){ OldImage=0; Index=0; Id=other.Id; Image=other.Image; Title=other.Title; } 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;} void Kdb3Database::EntryHandle::setOldImage(const quint32& s){Entry->OldImage=s;} KpxUuid Kdb3Database::EntryHandle::uuid(){return Entry->Uuid;} IGroupHandle* Kdb3Database::EntryHandle::group(){return Entry->Group->Handle;} quint32 Kdb3Database::EntryHandle::image(){return Entry->Image;} quint32 Kdb3Database::EntryHandle::oldImage(){return Entry->OldImage;} QString Kdb3Database::EntryHandle::title(){return Entry->Title;} QString Kdb3Database::EntryHandle::url(){return Entry->Url;} QString Kdb3Database::EntryHandle::username(){return Entry->Username;} SecString Kdb3Database::EntryHandle::password(){return Entry->Password;} QString Kdb3Database::EntryHandle::comment(){return Entry->Comment;} QString Kdb3Database::EntryHandle::binaryDesc(){return Entry->BinaryDesc;} KpxDateTime Kdb3Database::EntryHandle::creation(){return Entry->Creation;} KpxDateTime Kdb3Database::EntryHandle::lastMod(){return Entry->LastMod;} KpxDateTime Kdb3Database::EntryHandle::lastAccess(){return Entry->LastAccess;} KpxDateTime Kdb3Database::EntryHandle::expire(){return Entry->Expire;} QByteArray Kdb3Database::EntryHandle::binary(){return Entry->Binary;} quint32 Kdb3Database::EntryHandle::binarySize(){return Entry->Binary.size();} QString Kdb3Database::EntryHandle::friendlySize() { 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;} 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; } bool Kdb3Database::GroupHandle::isValid(){return valid;} void Kdb3Database::GroupHandle::setOldImage(const quint32& s){Group->OldImage=s;} QString Kdb3Database::GroupHandle::title(){return Group->Title;} quint32 Kdb3Database::GroupHandle::oldImage(){return Group->OldImage;} 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) { if(Group->Image < pDB->builtinIcons() && New >= pDB->builtinIcons()) Group->OldImage=Group->Image; if(New < pDB->builtinIcons()) Group->OldImage=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::childs(){ QList childs; for(int i=0;iChilds.size();i++){ childs.append(Group->Childs[i]->Handle); } return childs; } 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; } quint32 NumGroups,NumEntries,Signature1,Signature2,Flags,Version; quint8 TransfRandomSeed[32]; quint8 FinalRandomSeed[16]; quint8 ContentsHash[32]; quint8 EncryptionIV[16]; if(!(File->openMode() & QIODevice::WriteOnly)){ File->close(); } if(!File->isOpen()){ if(!File->open(QIODevice::ReadWrite)){ error= tr("Could not open file for writing."); return false; } } /* This is only a fix for a bug in the implementation of the metastream creation in KeePassX 0.2.1. to restore lost icons. It should be removed after a while. -----------------------------------------------------------------------------------*/ for(int i=0;i 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 +=164 +UnknownMetaStreams[i].Comment.toUtf8().length()+1 +UnknownMetaStreams[i].Binary.length(); } for(int i=0; i < MetaStreams.size(); i++){ FileSize +=164 +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(TransfRandomSeed,32); randomize(EncryptionIV,16); unsigned int pos=DB_HEADER_SIZE; // Skip the header, it will be written later serializeGroups(Groups,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); transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds); quint8 FinalKey[32]; SHA256 sha; sha.update(FinalRandomSeed,16); sha.update(MasterKey,32); 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; } File->resize(0); //truncate if(File->write(buffer,EncryptedPartSize+DB_HEADER_SIZE)!=EncryptedPartSize+DB_HEADER_SIZE){ delete [] buffer; return false; } File->flush(); delete [] buffer; //if(SearchGroupID!=-1)Groups.push_back(SearchGroup); 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_3"; e->Url="$"; e->OldImage=0; if(Groups.size())e->GroupId=Groups[0].Id; int Size=12; quint32 NumEntries=Entries.size(); quint32 NumGroups=Groups.size(); 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;iBinary.append(QByteArray::fromRawData(Bin,20)); } for(quint32 i=0;iBinary.append(QByteArray::fromRawData(Bin,8)); } } QList Kdb3Database::sortedGroups(){ QList SortedGroups; appendChildsToGroupList(SortedGroups,RootGroup); return SortedGroups; } void Kdb3Database::appendChildsToGroupList(QList& list,StdGroup& group){ for(int i=0;iHandle; appendChildsToGroupList(list,*group.Childs[i]); } } void Kdb3Database::appendChildsToGroupList(QList& list,StdGroup& group){ for(int i=0;i& GroupList,char* buffer,unsigned int& pos){ quint16 FieldType; quint32 FieldSize; quint32 Flags=0; //unused QListSortedGroups; appendChildsToGroupList(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]->OldImage); 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].OldImage); 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(){ return true; } void Kdb3Database::create(){ File=NULL; RootGroup.Title="$ROOT$"; RootGroup.Parent=NULL; RootGroup.Handle=NULL; Algorithm=Rijndael_Cipher; KeyTransfRounds=6000; 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->Childs.size(); i++){ getEntriesRecursive(((GroupHandle*)Group)->Group->Childs[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(); for(int i=0;ititle(),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){ SearchEntries.removeAt(i); i--; } } return SearchEntries; } void Kdb3Database::rebuildIndices(QList& list){ for(int i=0;iIndex=i; } } bool Kdb3Database::createKeyFile(const QString& filename,int length, bool Hex){ QFile file(filename); if(!file.open(QIODevice::WriteOnly|QIODevice::Truncate|QIODevice::Unbuffered)){ error=decodeFileError(file.error()); return false; } if(Hex)length*=2; unsigned char* key=new unsigned char[length]; randomize(key,length); if(Hex){ for(int i=0; i9)key[i]='A'+dig1-10; else key[i]='0'+dig1; if(dig2>9)key[i+1]='A'+dig2-10; else key[i+1]='0'+dig2; } } if(file.write((char*)key,length)==-1){ delete [] key; error=decodeFileError(file.error()); file.close(); return false; } file.close(); delete [] key; return true; } 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->Childs.removeAt(Group->Index); rebuildIndices(Group->Parent->Childs); Group->Parent=Parent; if(Pos==-1){ Parent->Childs.append(Group); } else { Q_ASSERT(Parent->Childs.size()>=Pos); Parent->Childs.insert(Pos,Group); } rebuildIndices(Parent->Childs); } bool Kdb3Database::changeFile(const QString& filename){ if(File) delete File; if(filename==QString()){ File=NULL; return true; } File=new QFile(filename); if(!File->open(QIODevice::ReadWrite)){ if(!File->open(QIODevice::ReadOnly)){ error=decodeFileError(File->error()); return false; } } return true; } void Kdb3Database::moveToTrash(IEntryHandle* entry){ TrashEntry trash=*((TrashEntry*)dynamic_cast(entry)->Entry); IGroupHandle* CurGroup=entry->group(); while(CurGroup){ trash.GroupPath << CurGroup->title(); CurGroup=CurGroup->parent(); } deleteEntry(entry); trash.Group=NULL; TrashEntries.append(trash); TrashHandles.append(EntryHandle(this)); TrashHandles.back().Entry=&TrashEntries.back(); TrashEntries.back().Handle=&TrashHandles.back(); } void Kdb3Database::emptyTrash(){ TrashEntries.clear(); TrashHandles.clear(); } QList Kdb3Database::trashEntries(){ QList handles; for(int i=0; i