From 598d2047166f54a037a34337b7bc799d432676d4 Mon Sep 17 00:00:00 2001 From: sniperbeamer Date: Sun, 28 Sep 2008 16:33:46 +0000 Subject: [PATCH] Cache and protect MasterKey - speeds up saving a lot Added option to save database after every change Improved license information git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@226 b624d157-de02-0410-bad0-e51aec6abb33 --- COPYING | 44 ++++++++++++++++---- src/Database.h | 1 + src/Kdb3Database.cpp | 72 +++++++++++++++++++++++---------- src/Kdb3Database.h | 11 +++-- src/KpxConfig.h | 2 + src/crypto/sha256.cpp | 3 +- src/crypto/sha256.h | 3 +- src/dialogs/SettingsDlg.cpp | 12 ++++++ src/dialogs/SettingsDlg.h | 2 + src/forms/SettingsDlg.ui | 21 ++++++---- src/import/Import_PwManager.cpp | 4 +- src/lib/SecString.cpp | 43 ++++++++++++++++++++ src/lib/SecString.h | 21 ++++++++++ src/lib/tools.cpp | 10 +++++ src/lib/tools.h | 1 + src/mainwindow.cpp | 32 +++++++++------ 16 files changed, 224 insertions(+), 58 deletions(-) diff --git a/COPYING b/COPYING index 322a7ca..99bb160 100644 --- a/COPYING +++ b/COPYING @@ -1,11 +1,5 @@ Copyright (C) 2005-2007 Tarek Saidi -Copyright (C) 2003-2007 Dominik Reichl -Copyright (C) 2001 Niels Müler -Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc. -Copyright (C) 2003, 2004 Michael Buesch -Copyright (c) 2003 Dr Brian Gladman, Worcester, UK -Copyright (C) 2001-2003 Christophe Devine -Copyright (C) 1992-2007 Trolltech ASA +Copyright (C) 2007-2008 Felix Geyer 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 @@ -16,6 +10,42 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text of the GNU General Public License below for more details. + +Other licenses: + +apg/*: + Copyright (c) 1999, 2000, 2001, 2002, 2003 Adel I. Mirzazhanov + 3-clause BSD license + +crypto/aes*: + Copyright (c) 1998-2008, Brian Gladman, Worcester + 3-clause BSD license + +crypto/arcfour*: + Copyright (C) 2003-2008 Dominik Reichl + GPLv2 or later + +crypto/blowfish*: + Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 2003 by Michael Buesch + Copyright (C) 2007 by Tarek Saidi + GPLv2 + +crypto/sha256*: + Copyright (C) 2001-2003 by Christophe Devine + Copyright (C) 2005-2006 by Tarek Saidi + GPLv2 + +crypto/two*: + Copyright (C) 2005-2007 Tarek Saidi + Copyright (c) 2003,2004 Dominik Reichl + GPLv2 + +crypto/yarrow*: + Copyright (C) 2007 Tarek Saidi + Copyright (C) 2001 Niels Müler + GPLv2 + --------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE diff --git a/src/Database.h b/src/Database.h index bc0bc13..0670d42 100644 --- a/src/Database.h +++ b/src/Database.h @@ -239,6 +239,7 @@ public: virtual bool setKey(const QString& password,const QString& keyfile)=0; virtual bool isKeyError()=0; + virtual void generateMasterKey()=0; //! Loads a database. /*! It is not allowed to call this function if a database is already loaded. diff --git a/src/Kdb3Database.cpp b/src/Kdb3Database.cpp index 8e57bf8..20ac1fd 100644 --- a/src/Kdb3Database.cpp +++ b/src/Kdb3Database.cpp @@ -40,6 +40,9 @@ bool StdEntryLessThan(const Kdb3Database::StdEntry& This,const Kdb3Database::Std } +Kdb3Database::Kdb3Database() : RawMasterKey(32), RawMasterKey_Latin1(32), MasterKey(32){ +} + QString Kdb3Database::getError(){ return error; } @@ -477,7 +480,6 @@ void Kdb3Database::restoreGroupTreeState(){ bool Kdb3Database::load(QString filename){ unsigned long total_size,crypto_size; quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags; -quint8 TransfRandomSeed[32]; quint8 FinalRandomSeed[16]; quint8 ContentsHash[32]; quint8 EncryptionIV[16]; @@ -531,16 +533,20 @@ else{ LOAD_RETURN_CLEANUP } - -KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds); +RawMasterKey.unlock(); +MasterKey.unlock(); +KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds); quint8 FinalKey[32]; SHA256 sha; sha.update(FinalRandomSeed,16); -sha.update(MasterKey,32); +sha.update(*MasterKey,32); sha.finish(FinalKey); +RawMasterKey.lock(); +MasterKey.lock(); + if(Algorithm == Rijndael_Cipher){ AESdecrypt aes; aes.key256(FinalKey); @@ -572,7 +578,8 @@ if(memcmp(ContentsHash, FinalKey, 32) != 0){ // but KeePass/Win32 uses Windows Codepage 1252. // Too stay compatible with databases created with KeePassX <= 0.3.1 // the loading function gives both encodings a try. - memcpy(RawMasterKey,RawMasterKey_Latin1,32); + + RawMasterKey.copyData(RawMasterKey_Latin1); PotentialEncodingIssue=false; qDebug("Decryption failed. Retrying with Latin-1."); return load(filename); // second try @@ -829,7 +836,9 @@ bool Kdb3Database::setPasswordKey(const QString& Password){ assert(Password.size()); QTextCodec* codec=QTextCodec::codecForName("Windows-1252"); QByteArray Password_CP1252 = codec->fromUnicode(Password); - SHA256::hashBuffer(Password_CP1252.data(),RawMasterKey,Password_CP1252.size()); + RawMasterKey.unlock(); + SHA256::hashBuffer(Password_CP1252.data(),*RawMasterKey,Password_CP1252.size()); + RawMasterKey.lock(); QByteArray Password_Latin1 = Password.toLatin1(); if(Password_Latin1 != Password_CP1252){ // KeePassX used Latin-1 encoding for passwords until version 0.3.1 @@ -837,7 +846,9 @@ bool Kdb3Database::setPasswordKey(const QString& Password){ // Too stay compatible with databases created with KeePassX <= 0.3.1 // the loading function gives both encodings a try. PotentialEncodingIssue = true; - SHA256::hashBuffer(Password_Latin1.data(),RawMasterKey_Latin1,Password_Latin1.size()); + RawMasterKey_Latin1.unlock(); + SHA256::hashBuffer(Password_Latin1.data(),*RawMasterKey_Latin1,Password_Latin1.size()); + RawMasterKey_Latin1.lock(); } else { // If the password does not contain problematic characters we don't need @@ -858,21 +869,27 @@ bool Kdb3Database::setFileKey(const QString& filename){ error=tr("Key file is empty."); return false; } + RawMasterKey.unlock(); if(FileSize == 32){ - if(file.read((char*)RawMasterKey,32) != 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)) + if (convHexToBinaryKey(hex,(char*)(*RawMasterKey))){ + RawMasterKey.lock(); return true; + } } SHA256 sha; unsigned char* buffer = new unsigned char[2048]; @@ -883,22 +900,25 @@ bool Kdb3Database::setFileKey(const QString& filename){ sha.update(buffer,read); if(read != 2048) break; } - sha.finish(RawMasterKey); + sha.finish(*RawMasterKey); + RawMasterKey.lock(); delete [] buffer; return true; } bool Kdb3Database::setCompositeKey(const QString& Password,const QString& filename){ - unsigned char PasswordKey[32]; - unsigned char FileKey[32]; + SHA256 sha; + if(!setFileKey(filename))return false; - memcpy(FileKey,RawMasterKey,32); + RawMasterKey.unlock(); + sha.update(*RawMasterKey,32); + RawMasterKey.lock(); + setPasswordKey(Password); - memcpy(PasswordKey,RawMasterKey,32); - SHA256 sha; - sha.update(PasswordKey,32); - sha.update(FileKey,32); - sha.finish(RawMasterKey); + RawMasterKey.unlock(); + sha.update(*RawMasterKey,32); + sha.finish(*RawMasterKey); + RawMasterKey.lock(); return true; } @@ -1187,7 +1207,6 @@ bool Kdb3Database::save(){ return false; } quint32 NumGroups,NumEntries,Signature1,Signature2,Flags,Version; - quint8 TransfRandomSeed[32]; quint8 FinalRandomSeed[16]; quint8 ContentsHash[32]; quint8 EncryptionIV[16]; @@ -1260,7 +1279,6 @@ bool Kdb3Database::save(){ 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 @@ -1281,12 +1299,13 @@ bool Kdb3Database::save(){ memcpy(buffer+56,ContentsHash,32); memcpy(buffer+88,TransfRandomSeed,32); memcpyToLEnd32(buffer+120,&KeyTransfRounds); - KeyTransform::transform(RawMasterKey,MasterKey,TransfRandomSeed,KeyTransfRounds); quint8 FinalKey[32]; SHA256 sha; sha.update(FinalRandomSeed,16); - sha.update(MasterKey,32); + MasterKey.unlock(); + sha.update(*MasterKey,32); + MasterKey.lock(); sha.finish(FinalKey); unsigned long EncryptedPartSize; @@ -1805,6 +1824,15 @@ QList Kdb3Database::trashEntries(){ return handles; } +void Kdb3Database::generateMasterKey(){ + randomize(TransfRandomSeed,32); + RawMasterKey.unlock(); + MasterKey.unlock(); + KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds); + RawMasterKey.lock(); + MasterKey.lock(); +} + void KeyTransform::transform(quint8* src, quint8* dst, quint8* KeySeed, int rounds){ KeyTransform* ktLeft = new KeyTransform(&src[0], &dst[0], KeySeed, rounds); diff --git a/src/Kdb3Database.h b/src/Kdb3Database.h index 63cc233..1e1c6ef 100644 --- a/src/Kdb3Database.h +++ b/src/Kdb3Database.h @@ -137,6 +137,7 @@ public: QStringList GroupPath; }; + Kdb3Database(); virtual ~Kdb3Database(){}; virtual bool load(QString identifier); virtual bool save(); @@ -187,7 +188,8 @@ public: virtual void moveGroup(IGroupHandle* Group,IGroupHandle* NewParent,int Position); virtual IGroupHandle* addGroup(const CGroup* Group,IGroupHandle* Parent); virtual bool isParent(IGroupHandle* parent, IGroupHandle* child); - + + virtual void generateMasterKey(); @@ -241,9 +243,10 @@ private: QMap TreeStateMetaStream; unsigned int KeyTransfRounds; CryptAlgorithm Algorithm; - quint8 RawMasterKey[32]; - quint8 RawMasterKey_Latin1[32]; - quint8 MasterKey[32]; + SecData RawMasterKey; + SecData RawMasterKey_Latin1; + SecData MasterKey; + quint8 TransfRandomSeed[32]; bool hasV4IconMetaStream; }; diff --git a/src/KpxConfig.h b/src/KpxConfig.h index 6dbc6d5..f2dc1b8 100644 --- a/src/KpxConfig.h +++ b/src/KpxConfig.h @@ -72,6 +72,7 @@ public: QString mountDir(){return settings.value("Options/MountDir",DEFAULT_MOUNT_DIR).toString();} bool openLastFile(){return settings.value("Options/OpenLastFile",true).toBool();} bool autoSave(){return settings.value("Options/AutoSave",false).toBool();} + bool autoSaveChange(){return settings.value("Options/AutoSaveChange",false).toBool();} int pwGenCategory(){return settings.value("Options/PwGenCategory",0).toInt();} QString pwGenCharList(){return settings.value("Options/PwGenCharList").toString();} int pwGenLength(){return settings.value("Options/PwGenLength",25).toInt();} @@ -132,6 +133,7 @@ public: void setMountDir(const QString& value){settings.setValue("Options/MountDir",value);} void setOpenLastFile(bool value){settings.setValue("Options/OpenLastFile",value);} void setAutoSave(bool value){settings.setValue("Options/AutoSave",value);} + void setAutoSaveChange(bool value){settings.setValue("Options/AutoSaveChange",value);} void setPwGenCategory(int value){settings.setValue("Options/PwGenCategory",value);} void setPwGenCharList(const QString& value){settings.setValue("Options/PwGenCharList",value);} void setPwGenLength(int value){settings.setValue("Options/PwGenLength",value);} diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index b171bff..acc8883 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -1,7 +1,6 @@ /*************************************************************************** + * Copyright (C) 2001-2003 by Christophe Devine * * Copyright (C) 2005-2006 by Tarek Saidi * - * based on the FIPS-180-2 compliant SHA-256 implementation of * - * Christophe Devine. * * * * 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 * diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index e6b851c..f3bdd3b 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -1,7 +1,6 @@ /*************************************************************************** + * Copyright (C) 2001-2003 by Christophe Devine * * Copyright (C) 2005-2006 by Tarek Saidi * - * based on the FIPS-180-2 compliant SHA-256 implementation of * - * Christophe Devine. * * * * 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 * diff --git a/src/dialogs/SettingsDlg.cpp b/src/dialogs/SettingsDlg.cpp index 361c695..316cc2b 100644 --- a/src/dialogs/SettingsDlg.cpp +++ b/src/dialogs/SettingsDlg.cpp @@ -51,6 +51,8 @@ CSettingsDlg::CSettingsDlg(QWidget* parent):QDialog(parent,Qt::Dialog) connect(Button_CustomizeEntryDetails,SIGNAL(clicked()),this,SLOT(OnCustomizeEntryDetails())); connect(CheckBox_InactivityLock, SIGNAL(toggled(bool)), SLOT(OnInactivityLockChange(bool))); + connect(CheckBox_AutoSave, SIGNAL(toggled(bool)), SLOT(OnAutoSaveToggle(bool))); + connect(CheckBox_AutoSaveChange, SIGNAL(toggled(bool)), SLOT(OnAutoSaveChangeToggle(bool))); #if !defined(AUTOTYPE) Box_AutoType->setVisible(false); @@ -76,6 +78,7 @@ CSettingsDlg::CSettingsDlg(QWidget* parent):QDialog(parent,Qt::Dialog) CheckBox_StartLocked->setChecked(config->startLocked()); checkBox_SaveFileDlgHistory->setChecked(config->saveFileDlgHistory()); CheckBox_AutoSave->setChecked(config->autoSave()); + CheckBox_AutoSaveChange->setChecked(config->autoSaveChange()); checkBox_AskBeforeDelete->setChecked(config->askBeforeDelete()); switch(config->groupTreeState()){ @@ -211,6 +214,7 @@ void CSettingsDlg::apply(){ config->setOpenLastFile(CheckBox_OpenLast->isChecked()); config->setRememberLastKey(CheckBox_RememberLastKey->isChecked()); config->setAutoSave(CheckBox_AutoSave->isChecked()); + config->setAutoSaveChange(CheckBox_AutoSaveChange->isChecked()); config->setAskBeforeDelete(checkBox_AskBeforeDelete->isChecked()); //Appearence @@ -330,6 +334,14 @@ void CSettingsDlg::OnInactivityLockChange(bool checked){ SpinBox_InacitivtyTime->setEnabled(checked); } +void CSettingsDlg::OnAutoSaveToggle(bool checked){ + CheckBox_AutoSaveChange->setEnabled(!checked); +} + +void CSettingsDlg::OnAutoSaveChangeToggle(bool checked){ + CheckBox_AutoSave->setEnabled(!checked); +} + #ifdef GLOBAL_AUTOTYPE void CSettingsDlg::resetGlobalShortcut(){ AutoType::unregisterGlobalShortcut(); diff --git a/src/dialogs/SettingsDlg.h b/src/dialogs/SettingsDlg.h index 25a838f..9330b3a 100644 --- a/src/dialogs/SettingsDlg.h +++ b/src/dialogs/SettingsDlg.h @@ -45,6 +45,8 @@ class CSettingsDlg : public QDialog, private Ui_SettingsDialog void OnBrowserCmdBrowse(); void OnCustomizeEntryDetails(); void OnInactivityLockChange(bool checked); + void OnAutoSaveToggle(bool checked); + void OnAutoSaveChangeToggle(bool checked); #ifdef GLOBAL_AUTOTYPE private slots: diff --git a/src/forms/SettingsDlg.ui b/src/forms/SettingsDlg.ui index b9d8dc2..63690b9 100644 --- a/src/forms/SettingsDlg.ui +++ b/src/forms/SettingsDlg.ui @@ -6,7 +6,7 @@ 0 0 606 - 475 + 479 @@ -52,7 +52,7 @@ 0 0 584 - 341 + 345 @@ -290,6 +290,13 @@ + + + + Automatically save database after every change + + + @@ -318,7 +325,7 @@ 0 0 584 - 341 + 345 @@ -678,7 +685,7 @@ 0 0 584 - 341 + 345 @@ -840,7 +847,7 @@ 0 0 584 - 341 + 345 @@ -885,7 +892,7 @@ 0 0 584 - 341 + 345 @@ -1005,7 +1012,7 @@ 0 0 584 - 341 + 345 diff --git a/src/import/Import_PwManager.cpp b/src/import/Import_PwManager.cpp index e84d5ba..7d4d0ce 100644 --- a/src/import/Import_PwManager.cpp +++ b/src/import/Import_PwManager.cpp @@ -105,8 +105,8 @@ bool Import_PwManager::importDatabase(QWidget* GuiParent, IDatabase* db){ if(!parseXmlContent((char*)xml)){ delete [] xml; - QMessageBox::critical(GuiParent,tr("Import Failed"),tr("Invalid XML data (see stdout for details).")); return false;} - database->setKey(password,QString()); + QMessageBox::critical(GuiParent,tr("Import Failed"),tr("Invalid XML data (see stdout for details).")); return false; + } return true; } diff --git a/src/lib/SecString.cpp b/src/lib/SecString.cpp index cbf1f8d..2ae1ca1 100644 --- a/src/lib/SecString.cpp +++ b/src/lib/SecString.cpp @@ -98,3 +98,46 @@ void SecString::generateSessionKey(){ randomize(sessionkey, 32); RC4.setKey(sessionkey, 32); } + + +SecData::SecData(int len) : locked(true){ + length = len; + data = new quint8[len]; +} + +SecData::~SecData(){ + if (!locked){ + for (int i=0; iautoSave()){ + if(config->autoSave() && db->file()){ if(!OnFileSave()) return false; } else{ QMessageBox::StandardButton r=QMessageBox::question(this,tr("Save modified file?"), - tr("The current file was modified. Do you want\nto save the changes?"), + tr("The current file was modified.\nDo you want to save the changes?"), QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); if(r==QMessageBox::Cancel) return false; //Cancel if(r==QMessageBox::Yes){ //Yes (Save file) @@ -536,8 +536,9 @@ void KeepassMainWindow::OnFileNewKdb(){ if(!closeDatabase())return; if (IsLocked) resetLock(); - db=db_new; - db->setKey(dlg.password(),dlg.keyFile()); + db=db_new; + db->setKey(dlg.password(),dlg.keyFile()); + db->generateMasterKey(); setWindowTitle(QString("[%1][*] - KeePassX").arg(tr("new"))); GroupView->db=db; EntryView->db=db; @@ -545,7 +546,6 @@ void KeepassMainWindow::OnFileNewKdb(){ EntryView->showGroup(NULL); setStateFileOpen(true); setStateFileModified(true); - FileOpen=true; setupDatabaseConnections(db); setStateGroupSelected(NONE); setStateEntrySelected(NONE); @@ -592,6 +592,7 @@ void KeepassMainWindow::setStateFileOpen(bool IsOpen){ FileCloseAction->setEnabled(IsOpen||IsLocked); FileSettingsAction->setEnabled(IsOpen); FileChangeKeyAction->setEnabled(IsOpen); + menuExport->setEnabled(IsOpen); EditSearchAction->setEnabled(IsOpen); GroupView->setEnabled(IsOpen); EntryView->setEnabled(IsOpen); @@ -627,10 +628,11 @@ void KeepassMainWindow::setStateFileOpen(bool IsOpen){ void KeepassMainWindow::setStateFileModified(bool mod){ - if(!FileOpen){ - FileSaveAction->setIcon(getIcon("filesave")); - return; + if (config->autoSaveChange() && mod && db->file()){ + OnFileSave(); + mod = false; } + ModFlag=mod; if(mod) FileSaveAction->setIcon(getIcon("filesave")); @@ -884,7 +886,10 @@ bool KeepassMainWindow::OnFileSaveAs(){ void KeepassMainWindow::OnFileSettings(){ CDbSettingsDlg dlg(this,db); - if(dlg.exec()) setStateFileModified(true); + if(dlg.exec()){ + db->generateMasterKey(); + setStateFileModified(true); + } } void KeepassMainWindow::OnFileChangeKey(){ @@ -892,8 +897,9 @@ void KeepassMainWindow::OnFileChangeKey(){ QString filename = file ? file->fileName() : QString(); PasswordDialog dlg(this,PasswordDialog::Mode_Change,PasswordDialog::Flag_None,filename); if(dlg.exec()==PasswordDialog::Exit_Ok){ - setStateFileModified(true); db->setKey(dlg.password(),dlg.keyFile()); + db->generateMasterKey(); + setStateFileModified(true); } } @@ -920,6 +926,7 @@ void KeepassMainWindow::OnImport(QAction* action){ } db=tmpdb; db->setKey(dlg.password(),dlg.keyFile()); + db->generateMasterKey(); GroupView->db=db; EntryView->db=db; setupDatabaseConnections(db); @@ -989,7 +996,7 @@ void KeepassMainWindow::OnUsernPasswVisibilityChanged(bool value){ } void KeepassMainWindow::OnFileModified(){ -setStateFileModified(true); + setStateFileModified(true); } void KeepassMainWindow::closeEvent(QCloseEvent* e){ @@ -1017,7 +1024,8 @@ void KeepassMainWindow::closeEvent(QCloseEvent* e){ if(!closeDatabase()){ ShutingDown=false; e->ignore(); - return;} + return; + } else e->accept(); }