/*************************************************************************** * Copyright (C) 2005 by Tarek Saidi * * tarek@linux * * * * 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; either version 2 of the License, or * * (at your option) any later version. * * * * 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 "crypto/sha256.h" #include "crypto/rijndael.h" #include "crypto/twoclass.h" #include "lib/random.h" using namespace std; #include "PwManager.h" QString PwDatabase::getError(){ if(Errors.size()){ QString r=Errors.front(); Errors.pop_front(); return r; } else return QString(trUtf8("unbekannter Fehler")); } QString PwDatabase::getErrors(){ QString r; for(int i=0; i 2147483446) || (crypto_size == 0)){err=trUtf8("Entschlüsselung nicht möglich - der Schlüssel ist falsch oder die Datei beschädigt."); return false;} sha256_starts(&sha32); sha256_update(&sha32,(unsigned char *)buffer + DB_HEADER_SIZE,crypto_size); sha256_finish(&sha32,(unsigned char *)FinalKey); if(memcmp(ContentsHash, FinalKey, 32) != 0) { err=trUtf8("Hash-Test fehlgeschlage: der Schlüssl ist falsch oder die Datei ist beschädigt."); return false;} unsigned long tmp_id=0; unsigned long pos = DB_HEADER_SIZE; Q_UINT16 FieldType; Q_UINT32 FieldSize; char* pField; bool bRet; CGroup group; for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) { pField = buffer+pos; memcpyFromLEnd16(&FieldType, pField); pField += 2; pos += 2; if(pos >= total_size) { return false; } memcpyFromLEnd32(&FieldSize, pField); pField += 4; pos += 4; if(pos >= (total_size + FieldSize)) { return false;} bRet = group.ReadGroupField(FieldType, FieldSize, (Q_UINT8 *)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) { return false;} } CEntry entry; for(unsigned long CurEntry = 0; CurEntry < NumEntries;) { pField = buffer+pos; memcpyFromLEnd16(&FieldType, pField); pField += 2; pos += 2; if(pos >= total_size){ return false;} memcpyFromLEnd32(&FieldSize, pField); pField += 4; pos += 4; if(pos >= (total_size + FieldSize)) { return false; } bRet = entry.ReadEntryField(FieldType,FieldSize,(Q_UINT8*)pField); if((FieldType == 0xFFFF) && (bRet == true)){ entry.sID=tmp_id++; Entries << entry; CurEntry++;} // Now and ONLY now the counter gets increased pField += FieldSize; pos += FieldSize; if(pos >= total_size) { return false; } } unsigned long CurGID, g, e, z, num; delete [] buffer; for(int i=0;iGroupID=dst->ID; } bool CGroup::ReadGroupField(Q_UINT16 FieldType, Q_UINT32 FieldSize, Q_UINT8 *pData) { switch(FieldType) { case 0x0000: // Ignore field break; case 0x0001: memcpyFromLEnd32(&ID, (char*)pData); break; case 0x0002: //Name.fromUtf8((char*)pData); Name=QString::fromUtf8((char*)pData); break; case 0x0003: Creation=dateFromPackedStruct5(pData); break; case 0x0004: LastMod=dateFromPackedStruct5(pData); break; case 0x0005: LastAccess=dateFromPackedStruct5(pData); break; case 0x0006: Expire=dateFromPackedStruct5(pData); break; case 0x0007: memcpyFromLEnd32(&ImageID, (char*)pData); break; case 0x0008: memcpyFromLEnd16(&Level, (char*)pData); break; case 0x0009: memcpyFromLEnd32(&Flags, (char*)pData); break; case 0xFFFF: break; default: return false; // Field unsupported } return true; // Field supported } PwDatabase::PwDatabase(){ SearchGroupID=-1; } PwDatabase::~PwDatabase(){ } bool CEntry::ReadEntryField(Q_UINT16 FieldType, Q_UINT32 FieldSize, Q_UINT8 *pData){ switch(FieldType) { case 0x0000: // Ignore field break; case 0x0001: memcpy(ID, pData, 16); break; case 0x0002: memcpyFromLEnd32(&GroupID, (char*)pData); break; case 0x0003: memcpyFromLEnd32(&ImageID, (char*)pData); break; case 0x0004: //Title=(char*)pData; Title=QString::fromUtf8((char*)pData); break; case 0x0005: URL=QString::fromUtf8((char*)pData); break; case 0x0006: UserName=QString::fromUtf8((char*)pData); break; case 0x0007:{ QString s=QString::fromUtf8((char*)pData); Password.setString(s,true); break;} case 0x0008: Additional=QString::fromUtf8((char*)pData); break; case 0x0009: Creation=dateFromPackedStruct5(pData); break; case 0x000A: LastMod=dateFromPackedStruct5(pData); break; case 0x000B: LastAccess=dateFromPackedStruct5(pData); break; case 0x000C: Expire=dateFromPackedStruct5(pData); break; case 0x000D: BinaryDesc=(char*)pData; break; case 0x000E: if(FieldSize != 0) { ///@TODO: im Destruktor löschen ///@TODO: im Konstruktor auf Null pBinaryData = new Q_UINT8[FieldSize]; memcpy(pBinaryData, pData, FieldSize); BinaryDataLength = FieldSize; } else {pBinaryData=0;} break; case 0xFFFF: ///@TODO: Alle Elemente geladen - Status setzen oder so was break; default: return false; // Field unsupported } return true; // Field processed } bool PwDatabase::closeDatabase(){ Groups.clear(); Entries.clear(); return true; } bool PwDatabase::saveDatabase(){ CGroup SearchGroup; Q_UINT32 NumGroups,NumEntries,Signature1,Signature2,Flags,Version; Q_UINT8 TrafoRandomSeed[32]; Q_UINT8 FinalRandomSeed[16]; Q_UINT8 ContentsHash[32]; Q_UINT8 EncryptionIV[16]; /* if(SearchGroupID!=-1){ for(int i=0;iAdd Metastreams 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].Name.utf8().length()+1; } // Get the size of all entries for(int i = 0; i < Entries.size(); i++){ FileSize += 134 +Entries[i].Title.utf8().length()+1 +Entries[i].UserName.utf8().length()+1 +Entries[i].URL.utf8().length()+1 +Entries[i].Password.length()+1 +Entries[i].Additional.utf8().length()+1 +Entries[i].BinaryDesc.utf8().length()+1 +Entries[i].BinaryDataLength;} // 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(CryptoAlgorithmus == ALGO_AES) Flags |= PWM_FLAG_RIJNDAEL; else if(CryptoAlgorithmus == ALGO_TWOFISH) Flags |= PWM_FLAG_TWOFISH; Version = PWM_DBVER_DW; NumGroups = Groups.size(); //-> (+MetaStreams) NumEntries = Entries.size(); getRandomBytes(FinalRandomSeed,1,16,false); getRandomBytes(TrafoRandomSeed,1,32,false); getRandomBytes(EncryptionIV,1,16,false); Q_UINT16 FieldType; Q_UINT32 FieldSize; int pos=DB_HEADER_SIZE; // Skip the header, it will be written later for(int i=0; i < Groups.size(); i++){ FieldType = 0x0001; FieldSize = 4; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Groups[i].ID, 4); pos += 4; FieldType = 0x0002; FieldSize = Groups[i].Name.utf8().length() + 1; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Groups[i].Name.utf8(),FieldSize); pos += FieldSize; FieldType = 0x0003; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Groups[i].Creation,(unsigned char*)buffer+pos); pos += 5; FieldType = 0x0004; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Groups[i].LastMod,(unsigned char*)buffer+pos);pos += 5; FieldType = 0x0005; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Groups[i].LastAccess,(unsigned char*)buffer+pos);pos += 5; FieldType = 0x0006; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Groups[i].Expire,(unsigned char*)buffer+pos);pos += 5; FieldType = 0x0007; FieldSize = 4; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Groups[i].ImageID, 4); pos += 4; FieldType = 0x0008; FieldSize = 2; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Groups[i].Level, 2); pos += 2; FieldType = 0x0009; FieldSize = 4; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Groups[i].Flags, 4); pos += 4; FieldType = 0xFFFF; FieldSize = 0; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; } for(int i = 0; i < Entries.size(); i++){ FieldType = 0x0001; FieldSize = 16; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Entries[i].ID, 16); pos += 16; FieldType = 0x0002; FieldSize = 4; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Entries[i].GroupID, 4); pos += 4; FieldType = 0x0003; FieldSize = 4; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, &Entries[i].ImageID, 4); pos += 4; FieldType = 0x0004; FieldSize = Entries[i].Title.utf8().length() + 1; // Add terminating NULL character space memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Entries[i].Title.utf8(),FieldSize); pos += FieldSize; FieldType = 0x0005; FieldSize = Entries[i].URL.utf8().length() + 1; // Add terminating NULL character space memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Entries[i].URL.utf8(),FieldSize); pos += FieldSize; FieldType = 0x0006; FieldSize = Entries[i].UserName.utf8().length() + 1; // Add terminating NULL character space memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Entries[i].UserName.utf8(),FieldSize); pos += FieldSize; FieldType = 0x0007; FieldSize = Entries[i].Password.length() + 1; // Add terminating NULL character space memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Entries[i].Password.getString(),FieldSize); pos += FieldSize; Entries[i].Password.delRef(); FieldType = 0x0008; FieldSize = Entries[i].Additional.utf8().length() + 1; // Add terminating NULL character space memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Entries[i].Additional.utf8(),FieldSize); pos += FieldSize; FieldType = 0x0009; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Entries[i].Creation,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000A; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Entries[i].LastMod,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000B; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Entries[i].LastAccess,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000C; FieldSize = 5; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; dateToPackedStruct5(Entries[i].Expire,(unsigned char*)buffer+pos); pos+=5; FieldType = 0x000D; FieldSize = Entries[i].BinaryDesc.utf8().length() + 1; // Add terminating NULL character space memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; memcpy(buffer+pos, Entries[i].BinaryDesc.utf8(),FieldSize); pos += FieldSize; FieldType = 0x000E; FieldSize = Entries[i].BinaryDataLength; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; if((Entries[i].pBinaryData != NULL) && (FieldSize != 0)) memcpy(buffer+pos, Entries[i].pBinaryData, FieldSize); pos += FieldSize; FieldType = 0xFFFF; FieldSize = 0; memcpy(buffer+pos, &FieldType, 2); pos += 2; memcpy(buffer+pos, &FieldSize, 4); pos += 4; } sha256_context context; sha256_starts(&context); sha256_update(&context,(unsigned char*)buffer+DB_HEADER_SIZE, pos - DB_HEADER_SIZE); sha256_finish(&context,(unsigned char*)ContentsHash); memcpy(buffer,&Signature1,4); memcpy(buffer+4,&Signature2,4); memcpy(buffer+8,&Flags,4); memcpy(buffer+12,&Version,4); memcpy(buffer+16,FinalRandomSeed,16); memcpy(buffer+32,EncryptionIV,16); memcpy(buffer+48,&NumGroups,4); memcpy(buffer+52,&NumEntries,4); memcpy(buffer+56,ContentsHash,32); memcpy(buffer+88,TrafoRandomSeed,32); memcpy(buffer+120,&KeyEncRounds,4); transformKey(MasterKey,TransformedMasterKey,TrafoRandomSeed,KeyEncRounds); Q_UINT8 FinalKey[32]; sha256_starts(&context); sha256_update(&context,FinalRandomSeed, 16); sha256_update(&context,TransformedMasterKey, 32); sha256_finish(&context,FinalKey); unsigned long EncryptedPartSize; if(CryptoAlgorithmus == ALGO_AES){ Rijndael aes; // Initialize Rijndael/AES if(aes.init(Rijndael::CBC, Rijndael::Encrypt, FinalKey,Rijndael::Key32Bytes, EncryptionIV) != RIJNDAEL_SUCCESS){ //TODO:ERR_MSG delete [] buffer; return false;} EncryptedPartSize = (unsigned long)aes.padEncrypt((Q_UINT8*)buffer+DB_HEADER_SIZE, pos - DB_HEADER_SIZE, (Q_UINT8*)buffer+DB_HEADER_SIZE); }else if(CryptoAlgorithmus == ALGO_TWOFISH){ CTwofish twofish; if(twofish.init(FinalKey, 32, EncryptionIV) == false){ //TODO:ERR_MSG delete [] buffer; return false;} EncryptedPartSize = (unsigned long)twofish.padEncrypt((Q_UINT8*)buffer+DB_HEADER_SIZE, pos - DB_HEADER_SIZE, (Q_UINT8*)buffer+DB_HEADER_SIZE); } if((EncryptedPartSize > 2147483446) || (EncryptedPartSize == 0)){ //TODO:ERR_MSG delete [] buffer; return false; } if(file.open(QIODevice::ReadWrite | QIODevice::Truncate)==false){ //TODO:ERR_MSG delete [] buffer; return false; } if(file.writeBlock(buffer,EncryptedPartSize+DB_HEADER_SIZE)!=EncryptedPartSize+DB_HEADER_SIZE){ //TODO:ERR_MSG file.close(); delete [] buffer; return false; } file.close(); delete [] buffer; if(SearchGroupID!=-1)Groups.push_back(SearchGroup); return true; } CGroup* PwDatabase::addGroup(CGroup* parent){ CGroup group; if(parent)group.Level=parent->Level+1; group.ImageID=0; group.ID=getNewGroupId(); if(!Groups.size() || !parent){ Groups << group; return &Groups.back(); } else { int i=Groups.indexOf(*parent)+1; for(i;iLevel)break; Groups.insert(i,group); return &Groups[i];} } void PwDatabase::deleteGroup(unsigned long id){ for(int i=0;iLevel)break; } NumChilds=i-GroupIndex-1; qDebug("NUMCHILDS=%i\n",NumChilds); //delete entries for(i=GroupIndex; i<=GroupIndex+NumChilds; i++){ for(int j=0; j=0; i--){ Groups.removeAt(GroupIndex+i); } } int PwDatabase::getGroupIndex(CGroup* g){ return getGroupIndex(g->ID); } int PwDatabase::getGroupIndex(unsigned long ID){ int i=0; for(i;iGroups.size();i++){ int NewGroupID; if(isGroupIdInUse(db->Groups[i].ID)==true) NewGroupID=getNewGroupId(); else NewGroupID=db->Groups[i].ID; for(int j=0;jEntries.size();j++){ if(db->Entries[j].GroupID==db->Groups[i].ID){ Entries.push_back(db->Entries[j]); Entries.back().GroupID=NewGroupID; Entries.back().sID=getNewEntrySid();} } Groups.push_back(db->Groups[i]); Groups.back().ID=NewGroupID; } } bool PwDatabase::isGroupIdInUse(Q_UINT32 id){ for(int i=0;iID); int ParentIndex=getGroupIndex(parent->ID); if(ParentIndex > ChildIndex) return false; for(int i=ChildIndex; i>ParentIndex;i--){ if(Groups[i].Level<=parent->Level)return false; } return true; } Q_UINT32 PwDatabase::getNewGroupId(){ Q_UINT32 id; bool used; while(1){ used=false; getRandomBytes(&id,4,1,false); if(!id)continue; //darf nicht 0 sein (KDB-Format v3) for(int j=0;jLevel) break; } NumSubGroups=i-GroupIndex-1; int LevelDiff; QList tmp; if(DstGroup) LevelDiff=DstGroup->Level - group->Level +1; else LevelDiff=-group->Level; for(i=GroupIndex; i<=GroupIndex+NumSubGroups; i++){ tmp << Groups[i]; tmp.back().Level+=LevelDiff; } for(i=0; i<=NumSubGroups; i++) Groups.removeAt(GroupIndex); int NumParentSubGroups; if(DstGroup){ DstIndex=Groups.indexOf(*DstGroup); NumParentSubGroups=getNumberOfChilds(DstGroup);} else{ DstIndex=0; NumParentSubGroups=Groups.size();} if(pos==-1)DstIndex+=(NumParentSubGroups+1); else{ int n=0; //Counter for direct (first-level) childs for(i=0; iLevel+1)n++; } DstIndex+=n; } for(i=NumSubGroups; i>=0; i--) Groups.insert(DstIndex,tmp[i]); } int PwDatabase::getNumberOfChilds(CGroup* group){ if(!group)return Groups.size(); int GroupIndex=Groups.indexOf(*group); int i; for(i=GroupIndex+1; iLevel) break; } return (i-GroupIndex-1); } void memcpyFromLEnd32(Q_UINT32* dst,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(Q_UINT16* dst,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); } const QDateTime Date_Never(QDate(2999,12,28),QTime(23,59,59)); QDateTime dateFromPackedStruct5(const unsigned char* pBytes){ Q_UINT32 dw1, dw2, dw3, dw4, dw5; dw1 = (Q_UINT32)pBytes[0]; dw2 = (Q_UINT32)pBytes[1]; dw3 = (Q_UINT32)pBytes[2]; dw4 = (Q_UINT32)pBytes[3]; dw5 = (Q_UINT32)pBytes[4]; int y = (dw1 << 6) | (dw2 >> 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)); } void dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){ pBytes[0] = (Q_UINT8)(((Q_UINT32)d.date().year() >> 6) & 0x0000003F); pBytes[1] = (Q_UINT8)((((Q_UINT32)d.date().year() & 0x0000003F) << 2) | (((Q_UINT32)d.date().month() >> 2) & 0x00000003)); pBytes[2] = (Q_UINT8)((((Q_UINT32)d.date().month() & 0x00000003) << 6) | (((Q_UINT32)d.date().day() & 0x0000001F) << 1) | (((Q_UINT32)d.time().hour() >> 4) & 0x00000001)); pBytes[3] = (Q_UINT8)((((Q_UINT32)d.time().hour() & 0x0000000F) << 4) | (((Q_UINT32)d.time().minute() >> 2) & 0x0000000F)); pBytes[4] = (Q_UINT8)((((Q_UINT32)d.time().minute() & 0x00000003) << 6) | ((Q_UINT32)d.time().second() & 0x0000003F)); }