Master key transformations (rounds) are now computed in two threads (from KeePass 1.11)

Added a button to measure the number of rounds that can be calculated in 1 second
Don't try to open a URL when it's empty
Improved code formatting

git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@194 b624d157-de02-0410-bad0-e51aec6abb33
master
sniperbeamer 17 years ago
parent b0289a8b3c
commit 96ac6e31f1
  1. 289
      src/Kdb3Database.cpp
  2. 35
      src/Kdb3Database.h
  3. 9
      src/dialogs/DatabaseSettingsDlg.cpp
  4. 5
      src/dialogs/DatabaseSettingsDlg.h
  5. 22
      src/forms/DatabaseSettingsDlg.ui
  6. 3
      src/lib/tools.cpp

@ -433,12 +433,12 @@ if(!File->open(QIODevice::ReadWrite)){
} }
total_size=File->size(); total_size=File->size();
char* buffer = new char[total_size]; char* buffer = new char[total_size];
DECRYPT:
File->read(buffer,total_size); File->read(buffer,total_size);
if(total_size < DB_HEADER_SIZE){ if(total_size < DB_HEADER_SIZE){
error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)"); error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)");
return false; } return false;
}
memcpyFromLEnd32(&Signature1,buffer); memcpyFromLEnd32(&Signature1,buffer);
memcpyFromLEnd32(&Signature2,buffer+4); memcpyFromLEnd32(&Signature2,buffer+4);
@ -453,20 +453,27 @@ memcpy(TransfRandomSeed,buffer+88,32);
memcpyFromLEnd32(&KeyTransfRounds,buffer+120); memcpyFromLEnd32(&KeyTransfRounds,buffer+120);
if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){ if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){
error=tr("Wrong Signature"); error=tr("Wrong Signature");
return false;} return false;
}
if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){ if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){
error=tr("Unsupported File Version."); error=tr("Unsupported File Version.");
return false;} return false;
}
if(Flags & PWM_FLAG_RIJNDAEL) Algorithm = Rijndael_Cipher; if (Flags & PWM_FLAG_RIJNDAEL)
else if(Flags & PWM_FLAG_TWOFISH) Algorithm = Twofish_Cipher; Algorithm = Rijndael_Cipher;
else{error=tr("Unknown Encryption Algorithm."); else if (Flags & PWM_FLAG_TWOFISH)
return false;} Algorithm = Twofish_Cipher;
else{
error=tr("Unknown Encryption Algorithm.");
return false;
}
if(!transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds))return false; KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
quint8 FinalKey[32]; quint8 FinalKey[32];
SHA256 sha; SHA256 sha;
@ -482,12 +489,16 @@ if(Algorithm == Rijndael_Cipher){
} }
else if(Algorithm == Twofish_Cipher){ else if(Algorithm == Twofish_Cipher){
CTwofish twofish; CTwofish twofish;
if(twofish.init(FinalKey, 32, EncryptionIV) != true){return false;} if (twofish.init(FinalKey, 32, EncryptionIV) != true)
return false;
crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE, crypto_size = (unsigned long)twofish.padDecrypt((quint8 *)buffer + DB_HEADER_SIZE,
total_size - DB_HEADER_SIZE, (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;} 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); SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size);
if(memcmp(ContentsHash, FinalKey, 32) != 0){ if(memcmp(ContentsHash, FinalKey, 32) != 0){
@ -518,65 +529,73 @@ RootGroup.Title="$ROOT$";
RootGroup.Parent=NULL; RootGroup.Parent=NULL;
RootGroup.Handle=NULL; RootGroup.Handle=NULL;
for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) for(unsigned long CurGroup = 0; CurGroup < NumGroups; )
{ {
pField = buffer+pos; 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); memcpyFromLEnd16(&FieldType, pField);
pField += 4; pos += 4; pField += 2; pos += 2;
if(pos >= (total_size + FieldSize)){ if (pos >= total_size){
error=tr("Unexpected error: Offset is out of range.").append(" [G2]"); error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
return false;} return false;
}
bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)pField); memcpyFromLEnd32(&FieldSize, pField);
if((FieldType == 0xFFFF) && (bRet == true)){ pField += 4; pos += 4;
Groups << group; if (pos >= (total_size + FieldSize)){
CurGroup++;} // Now and ONLY now the counter gets increased error=tr("Unexpected error: Offset is out of range.").append(" [G2]");
return false;
}
pField += FieldSize; bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)pField);
pos += FieldSize; if ((FieldType == 0xFFFF) && (bRet == true)){
if(pos >= total_size) { Groups << group;
error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); CurGroup++; // Now and ONLY now the counter gets increased
return false;}
} }
pField += FieldSize;
pos += FieldSize;
if (pos >= total_size){
error=tr("Unexpected error: Offset is out of range.").append(" [G1]");
return false;
}
}
StdEntry entry; StdEntry entry;
for(unsigned long CurEntry = 0; CurEntry < NumEntries;) for (unsigned long CurEntry = 0; CurEntry < NumEntries;)
{ {
pField = buffer+pos; pField = buffer+pos;
memcpyFromLEnd16(&FieldType, pField); memcpyFromLEnd16(&FieldType, pField);
pField += 2; pos += 2; pField += 2; pos += 2;
if(pos >= total_size){ if(pos >= total_size){
error=tr("Unexpected error: Offset is out of range.").append(" [E1]"); error=tr("Unexpected error: Offset is out of range.").append(" [E1]");
return false;} return false;
}
memcpyFromLEnd32(&FieldSize, pField); memcpyFromLEnd32(&FieldSize, pField);
pField += 4; pos += 4; pField += 4; pos += 4;
if(pos >= (total_size + FieldSize)) { if (pos >= (total_size + FieldSize)){
error=tr("Unexpected error: Offset is out of range.").append(" [E2]"); error=tr("Unexpected error: Offset is out of range.").append(" [E2]");
return false; } return false;
}
bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField); bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField);
if((FieldType == 0xFFFF) && (bRet == true)){ if((FieldType == 0xFFFF) && (bRet == true)){
Entries << entry; Entries << entry;
if(!entry.GroupId)qDebug("NULL: %i, '%s'",(int)CurEntry,(char*)entry.Title.toUtf8().data()); if(!entry.GroupId)
CurEntry++;} qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data());
CurEntry++;
}
pField += FieldSize; pField += FieldSize;
pos += FieldSize; pos += FieldSize;
if(pos >= total_size) { if (pos >= total_size){
error=tr("Unexpected error: Offset is out of range.").append(" [E3]"); error=tr("Unexpected error: Offset is out of range.").append(" [E3]");
return false; } return false;
} }
}
if(!createGroupTree(Levels)){ if(!createGroupTree(Levels)){
error=tr("Invalid group tree."); error=tr("Invalid group tree.");
@ -591,8 +610,8 @@ for(int i=0;i<Entries.size();i++){
if(!parseMetaStream(Entries[i])) if(!parseMetaStream(Entries[i]))
UnknownMetaStreams << Entries[i]; UnknownMetaStreams << Entries[i];
Entries.removeAt(i); Entries.removeAt(i);
i--;} i--;
}
} }
int* EntryIndices=new int[Groups.size()]; int* EntryIndices=new int[Groups.size()];
@ -614,38 +633,25 @@ return true;
} }
QDateTime Kdb3Database::dateFromPackedStruct5(const unsigned char* pBytes){ QDateTime Kdb3Database::dateFromPackedStruct5(const unsigned char* pBytes){
quint32 dw1, dw2, dw3, dw4, dw5; quint32 dw1, dw2, dw3, dw4, dw5;
dw1 = (quint32)pBytes[0]; dw2 = (quint32)pBytes[1]; dw3 = (quint32)pBytes[2]; dw1 = (quint32)pBytes[0]; dw2 = (quint32)pBytes[1]; dw3 = (quint32)pBytes[2];
dw4 = (quint32)pBytes[3]; dw5 = (quint32)pBytes[4]; dw4 = (quint32)pBytes[3]; dw5 = (quint32)pBytes[4];
int y = (dw1 << 6) | (dw2 >> 2); int y = (dw1 << 6) | (dw2 >> 2);
int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); int mon = ((dw2 & 0x00000003) << 2) | (dw3 >> 6);
int d = (dw3 >> 1) & 0x0000001F; int d = (dw3 >> 1) & 0x0000001F;
int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); int h = ((dw3 & 0x00000001) << 4) | (dw4 >> 4);
int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); int min = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6);
int s = dw5 & 0x0000003F; int s = dw5 & 0x0000003F;
return QDateTime(QDate(y,mon,d),QTime(h,min,s)); return QDateTime(QDate(y,mon,d),QTime(h,min,s));
} }
void Kdb3Database::dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){ void Kdb3Database::dateToPackedStruct5(const QDateTime& d,unsigned char* pBytes){
pBytes[0] = (quint8)(((quint32)d.date().year() >> 6) & 0x0000003F); 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[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[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[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)); 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;i<rounds;i++){
aes.ecb_encrypt(tmp,tmp,32);
}
SHA256::hashBuffer(tmp,dst,32);
return true;
} }
@ -657,8 +663,6 @@ int Kdb3Database::numEntries(){
return Entries.size(); return Entries.size();
} }
void Kdb3Database::deleteGroup(StdGroup* group){ void Kdb3Database::deleteGroup(StdGroup* group){
while(group->Childs.size()) while(group->Childs.size())
@ -1097,25 +1101,23 @@ else
} }
void memcpyToLEnd32(char* dst,const quint32* src){ void memcpyToLEnd32(char* dst,const quint32* src){
if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
if(QSysInfo::ByteOrder==QSysInfo::BigEndian){ memcpy(dst+0,((char*)src)+3,1);
memcpy(dst+0,((char*)src)+3,1); memcpy(dst+1,((char*)src)+2,1);
memcpy(dst+1,((char*)src)+2,1); memcpy(dst+2,((char*)src)+1,1);
memcpy(dst+2,((char*)src)+1,1); memcpy(dst+3,((char*)src)+0,1);
memcpy(dst+3,((char*)src)+0,1); }
} else
else memcpy(dst,src,4);
memcpy(dst,src,4);
} }
void memcpyToLEnd16(char* dst,const quint16* src){ void memcpyToLEnd16(char* dst,const quint16* src){
if (QSysInfo::ByteOrder==QSysInfo::BigEndian){
if(QSysInfo::ByteOrder==QSysInfo::BigEndian){ memcpy(dst+0,((char*)src)+1,1);
memcpy(dst+0,((char*)src)+1,1); memcpy(dst+1,((char*)src)+0,1);
memcpy(dst+1,((char*)src)+0,1); }
} else
else memcpy(dst,src,2);
memcpy(dst,src,2);
} }
bool Kdb3Database::save(){ bool Kdb3Database::save(){
@ -1231,7 +1233,7 @@ bool Kdb3Database::save(){
memcpy(buffer+56,ContentsHash,32); memcpy(buffer+56,ContentsHash,32);
memcpy(buffer+88,TransfRandomSeed,32); memcpy(buffer+88,TransfRandomSeed,32);
memcpyToLEnd32(buffer+120,&KeyTransfRounds); memcpyToLEnd32(buffer+120,&KeyTransfRounds);
transformKey(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds); KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds);
quint8 FinalKey[32]; quint8 FinalKey[32];
SHA256 sha; SHA256 sha;
@ -1719,3 +1721,74 @@ QList<IEntryHandle*> Kdb3Database::trashEntries(){
handles << &TrashHandles[i]; handles << &TrashHandles[i];
return handles; return handles;
} }
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();
while (ktLeft->isRunning() || ktRight->isRunning()){
QThread::msleep(100);
}
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; i<rounds; i++){
aes.ecb_encrypt(dst,dst,16);
}
}
int KeyTransformBenchmark::benchmark(int pMSecs){
KeyTransformBenchmark* ktbLeft = new KeyTransformBenchmark(pMSecs);
KeyTransformBenchmark* ktbRight = new KeyTransformBenchmark(pMSecs);
ktbLeft->start();
ktbRight->start();
while (ktbLeft->isRunning() || ktbRight->isRunning()){
QThread::msleep(100);
}
int num = 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);
}

@ -20,6 +20,8 @@
#ifndef _STD_DATABASE_H_ #ifndef _STD_DATABASE_H_
#define _STD_DATABASE_H_ #define _STD_DATABASE_H_
#include <QThread>
#define DB_HEADER_SIZE 124 #define DB_HEADER_SIZE 124
#define PWM_DBSIG_1 0x9AA2D903 #define PWM_DBSIG_1 0x9AA2D903
#define PWM_DBSIG_2 0xB54BFB65 #define PWM_DBSIG_2 0xB54BFB65
@ -210,7 +212,6 @@ private:
bool readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint32 FieldSize, quint8 *pData); bool readGroupField(StdGroup* group,QList<quint32>& Levels,quint16 FieldType, quint32 FieldSize, quint8 *pData);
bool createGroupTree(QList<quint32>& Levels); bool createGroupTree(QList<quint32>& Levels);
void createHandles(); void createHandles();
bool transformKey(quint8* src,quint8* dst,quint8* KeySeed,int rounds);
void invalidateHandle(StdEntry* entry); void invalidateHandle(StdEntry* entry);
bool convHexToBinaryKey(char* HexKey, char* dst); bool convHexToBinaryKey(char* HexKey, char* dst);
quint32 getNewGroupId(); quint32 getNewGroupId();
@ -252,4 +253,36 @@ private:
quint8 MasterKey[32]; quint8 MasterKey[32];
}; };
class KeyTransform : public QThread{
Q_OBJECT
public:
static void transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds);
private:
KeyTransform(quint8* pSrc, quint8* pDst, quint8* pKeySeed, int pRounds);
quint8* src;
quint8* dst;
quint8* KeySeed;
int rounds;
protected:
void run();
};
class KeyTransformBenchmark : public QThread{
Q_OBJECT
public:
static int benchmark(int pMSecs);
private:
KeyTransformBenchmark(int pMSecs);
int msecs;
int rounds;
protected:
void run();
};
#endif #endif

@ -20,6 +20,7 @@
#include "DatabaseSettingsDlg.h" #include "DatabaseSettingsDlg.h"
#include "Kdb3Database.h"
CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db, bool modal, Qt::WFlags fl) CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db, bool modal, Qt::WFlags fl)
@ -34,9 +35,11 @@ CDbSettingsDlg::CDbSettingsDlg(QWidget* parent,IDatabase* db, bool modal, Qt::W
ComboAlgo->insertItem(0,tr("AES(Rijndael): 256 Bit (default)")); ComboAlgo->insertItem(0,tr("AES(Rijndael): 256 Bit (default)"));
ComboAlgo->insertItem(1,tr("Twofish: 256 Bit")); ComboAlgo->insertItem(1,tr("Twofish: 256 Bit"));
ComboAlgo->setCurrentIndex(database->cryptAlgorithm()); //Achtung: AlgoID muss gleich dem ComboBox Index sein! ComboAlgo->setCurrentIndex(database->cryptAlgorithm()); //Achtung: AlgoID muss gleich dem ComboBox Index sein!
EditRounds->setText(QString::number(database->keyTransfRounds())); EditRounds->setText(QString::number( database->keyTransfRounds() ));
ButtonBench->setIcon(getIcon("alarmclock"));
connect( ButtonBox, SIGNAL( accepted() ), this, SLOT( OnOK() ) ); connect( ButtonBox, SIGNAL( accepted() ), this, SLOT( OnOK() ) );
connect( ButtonBox, SIGNAL( rejected() ), this, SLOT( OnCancel() ) ); connect( ButtonBox, SIGNAL( rejected() ), this, SLOT( OnCancel() ) );
connect( ButtonBench, SIGNAL( clicked() ), this, SLOT( OnBenchmark() ) );
} }
CDbSettingsDlg::~CDbSettingsDlg() CDbSettingsDlg::~CDbSettingsDlg()
@ -76,3 +79,7 @@ void CDbSettingsDlg::OnOK()
database->setCryptAlgorithm((CryptAlgorithm)ComboAlgo->currentIndex()); database->setCryptAlgorithm((CryptAlgorithm)ComboAlgo->currentIndex());
done(1); done(1);
} }
void CDbSettingsDlg::OnBenchmark(){
EditRounds->setText(QString::number( KeyTransformBenchmark::benchmark(1000) ));
}

@ -32,8 +32,9 @@ class CDbSettingsDlg : public QDialog, private Ui_DatabaseSettingsDlg
virtual void paintEvent(QPaintEvent *); virtual void paintEvent(QPaintEvent *);
private slots: private slots:
virtual void OnCancel(); void OnCancel();
virtual void OnOK(); void OnOK();
void OnBenchmark();
private: private:
IKdbSettings* database; IKdbSettings* database;

@ -68,7 +68,27 @@
</widget> </widget>
</item> </item>
<item row="1" column="1" > <item row="1" column="1" >
<widget class="QLineEdit" name="EditRounds" /> <layout class="QHBoxLayout" >
<item>
<widget class="QLineEdit" name="EditRounds" />
</item>
<item>
<widget class="QPushButton" name="ButtonBench" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<string>Calculate rounds for a 1-second delay on this computer</string>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -82,6 +82,9 @@ void openBrowser(IEntryHandle* entry){
} }
void openBrowser(const QString& UrlString){ void openBrowser(const QString& UrlString){
if (UrlString.trimmed().isEmpty())
return;
if (UrlString.startsWith("cmd://") && UrlString.length()>6){ if (UrlString.startsWith("cmd://") && UrlString.length()>6){
QProcess::startDetached(UrlString.right(UrlString.length()-6)); QProcess::startDetached(UrlString.right(UrlString.length()-6));
return; return;