From f89372b0c5f30800dca34480ea31f456228fb68d Mon Sep 17 00:00:00 2001 From: tariq Date: Wed, 22 Feb 2006 19:12:44 +0000 Subject: [PATCH] changed encryption method of SecStrings from Rijndael to ArcFour (much faster) git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@49 b624d157-de02-0410-bad0-e51aec6abb33 --- src/PwManager.cpp | 17 ++++-- src/crypto/arcfour.cpp | 86 +++++++++++++++++++++++++++ src/crypto/arcfour.h | 45 ++++++++++++++ src/dialogs/EditEntryDlg.cpp | 13 +++-- src/dialogs/SearchDlg.cpp | 9 +-- src/dialogs/SearchDlg.h | 2 +- src/lib/EntryView.cpp | 44 ++++++++------ src/lib/EntryView.h | 1 + src/lib/SecString.cpp | 110 ++++++++++++++++++----------------- src/lib/SecString.h | 25 ++++---- src/main.cpp | 2 + src/mainwindow.cpp | 20 ++++--- src/src.pro | 23 ++++---- 13 files changed, 281 insertions(+), 116 deletions(-) create mode 100644 src/crypto/arcfour.cpp create mode 100644 src/crypto/arcfour.h diff --git a/src/PwManager.cpp b/src/PwManager.cpp index 865f3bb..9e18b34 100755 --- a/src/PwManager.cpp +++ b/src/PwManager.cpp @@ -201,6 +201,7 @@ CEntry entry; return false; } bRet = entry.ReadEntryField(FieldType,FieldSize,(Q_UINT8*)pField); + if((FieldType == 0xFFFF) && (bRet == true)){ entry.sID=tmp_id++; Entries << entry; @@ -272,14 +273,14 @@ if(paKey == NULL) return false; KeyLen = strlen(paKey); if(KeyLen == 0) { -SecString::overwrite(paKey,Password.length() + 1); +SecString::overwrite((unsigned char*)paKey,Password.length() + 1); delete [] paKey; return false; } sha256_starts(&sha32); sha256_update(&sha32,(unsigned char*) paKey, KeyLen); sha256_finish(&sha32,MasterKey); -SecString::overwrite(paKey,Password.length() + 1); +SecString::overwrite((unsigned char*)paKey,Password.length() + 1); delete [] paKey; return true; } @@ -530,7 +531,6 @@ switch(FieldType) memcpyFromLEnd32(&ImageID, (char*)pData); break; case 0x0004: - //Title=(char*)pData; Title=QString::fromUtf8((char*)pData); break; case 0x0005: @@ -723,8 +723,9 @@ for(int i = 0; i < Entries.size(); i++){ FieldSize = Entries[i].Password.length() + 1; // Add terminating NULL character space memcpyToLEnd16(buffer+pos, &FieldType); pos += 2; memcpyToLEnd32(buffer+pos, &FieldSize); pos += 4; - memcpy(buffer+pos, Entries[i].Password.getString(),FieldSize); pos += FieldSize; - Entries[i].Password.delRef(); + Entries[i].Password.unlock(); + memcpy(buffer+pos, Entries[i].Password.string(),FieldSize); pos += FieldSize; + Entries[i].Password.lock(); FieldType = 0x0008; FieldSize = Entries[i].Additional.utf8().length() + 1; // Add terminating NULL character space @@ -1225,8 +1226,12 @@ void assertEntriesEq(KPTestResults& results, CEntry* left, CEntry* right){ kp_assert(results, left->UserName == right->UserName); size += sizeof(left->UserName); - kp_assert(results, left->Password.getString() == right->Password.getString()); + left->Password.unlock(); + right->Password.unlock(); + kp_assert(results, left->Password.string() == right->Password.string()); size += sizeof(left->Password); + left->Password.lock(); + right->Password.lock(); kp_assert(results, left->Additional == right->Additional); size += sizeof(left->Additional); diff --git a/src/crypto/arcfour.cpp b/src/crypto/arcfour.cpp new file mode 100644 index 0000000..cc47d39 --- /dev/null +++ b/src/crypto/arcfour.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2005-2006 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; 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 +#include "arcfour.h" + + +static inline void swap_byte(unsigned char *a, unsigned char *b) + { + unsigned char swapByte; + + swapByte = *a; + *a = *b; + *b = swapByte; + } + +void CArcFour::setKey(byte* key_data_ptr, int key_data_len){ +RawKey=QByteArray((const char*)key_data_ptr,key_data_len); +} + +void CArcFour::prepareKey(){ + unsigned char swapByte; + unsigned char index1; + unsigned char index2; + unsigned char* state; + short counter; + + state = &key.state[0]; + for(counter = 0; counter < 256; counter++) + state[counter] = counter; + key.x = 0; + key.y = 0; + index1 = 0; + index2 = 0; + for(counter = 0; counter < 256; counter++) + { + index2 = (RawKey.at(index1) + state[counter] + index2) % 256; + swap_byte(&state[counter], &state[index2]); + index1 = (index1 + 1) % RawKey.size(); + } +} + +void CArcFour::encrypt(const byte* src, byte* dst,int length){ +//qDebug("Key:%s",RawKey.data()); + prepareKey(); + unsigned char x; + unsigned char y; + unsigned char* state; + unsigned char xorIndex; + short counter; + + x = key.x; + y = key.y; + + state = &key.state[0]; + for(counter = 0; counter < length; counter ++) + { + x = (x + 1) % 256; + y = (state[x] + y) % 256; + swap_byte(&state[x], &state[y]); + + xorIndex = (state[x] + state[y]) % 256; + + dst[counter]=src[counter]^state[xorIndex]; + } + key.x = x; + key.y = y; +} + diff --git a/src/crypto/arcfour.h b/src/crypto/arcfour.h new file mode 100644 index 0000000..a1b2147 --- /dev/null +++ b/src/crypto/arcfour.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005-2006 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; 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. * + ***************************************************************************/ + +#ifndef _ARCFOUR_H_ +#define _ARCFOUR_H_ + +#ifndef byte +#define byte unsigned char +#endif + +class CArcFour{ +public: + void encrypt(const byte* src, byte* dst,int length); + inline void decrypt(const byte* src, byte* dst,int length){encrypt(src,dst,length);} //just for readability + void setKey(byte* key, int length); + QByteArray RawKey; +private: + void prepareKey(); + + typedef struct rc4_key{ + byte state[256]; + byte x; + byte y;}rc4_key; + rc4_key key; +}; + + +#endif \ No newline at end of file diff --git a/src/dialogs/EditEntryDlg.cpp b/src/dialogs/EditEntryDlg.cpp index 7765c20..3cad637 100755 --- a/src/dialogs/EditEntryDlg.cpp +++ b/src/dialogs/EditEntryDlg.cpp @@ -77,9 +77,10 @@ setIcon(EntryIcons[entry->ImageID]); Edit_Title->setText(entry->Title); Edit_UserName->setText(entry->UserName); Edit_URL->setText(entry->URL); -Edit_Password->setText(entry->Password.getString()); -Edit_Password_w->setText(entry->Password.getString()); -entry->Password.delRef(); +entry->Password.unlock(); +Edit_Password->setText(entry->Password.string()); +Edit_Password_w->setText(entry->Password.string()); +entry->Password.lock(); if(!config.ShowPasswords) ChangeEchoMode(); OnPasswordwLostFocus(); @@ -171,10 +172,10 @@ if(entry->URL!=Edit_URL->text()) ModFlag=true; if(entry->Additional!=Edit_Comment->text()) ModFlag=true; -QString& passw=entry->Password.getString(); -if(passw!=Edit_Password->text()) +entry->Password.unlock(); +if(entry->Password.string()!=Edit_Password->text()) ModFlag=true; -entry->Password.delRef(); +entry->Password.lock(); entry->Expire=DateTime_Expire->dateTime(); entry->LastAccess=QDateTime::currentDateTime(); diff --git a/src/dialogs/SearchDlg.cpp b/src/dialogs/SearchDlg.cpp index cff5cec..5cfa54b 100755 --- a/src/dialogs/SearchDlg.cpp +++ b/src/dialogs/SearchDlg.cpp @@ -92,15 +92,16 @@ for(int i=0;iEntries.size();i++){ else if(db->Entries[i].GroupID != group->ID)continue; } - + bool hit=false; if(checkBox_Title->isChecked()) hit=hit||search(db->Entries[i].Title); if(checkBox_Username->isChecked()) hit=hit||search(db->Entries[i].UserName); if(checkBox_URL->isChecked()) hit=hit||search(db->Entries[i].URL); if(checkBox_Comment->isChecked()) hit=hit||search(db->Entries[i].Additional); if(checkBox_Attachment->isChecked()) hit=hit||search(db->Entries[i].BinaryDesc); - if(checkBox_Password->isChecked()) hit=hit||search(db->Entries[i].Password.getString()); - db->Entries[i].Password.delRef(); + db->Entries[i].Password.unlock(); + if(checkBox_Password->isChecked()) hit=hit||search(db->Entries[i].Password.string()); + db->Entries[i].Password.lock(); if(hit)Hits.push_back(db->Entries[i].sID); } @@ -109,7 +110,7 @@ done(1); } -bool CSearchDlg::search(QString& str){ +bool CSearchDlg::search(const QString& str){ if(regexp){ QRegExp exp(txt,checkBox_Cs->isChecked()); diff --git a/src/dialogs/SearchDlg.h b/src/dialogs/SearchDlg.h index 87b3324..8b1c9a3 100755 --- a/src/dialogs/SearchDlg.h +++ b/src/dialogs/SearchDlg.h @@ -41,7 +41,7 @@ private: CGroup* group; bool regexp; PwDatabase* db; - bool search(QString& str); + bool search(const QString& str); }; #endif diff --git a/src/lib/EntryView.cpp b/src/lib/EntryView.cpp index fbeedd1..914f85c 100644 --- a/src/lib/EntryView.cpp +++ b/src/lib/EntryView.cpp @@ -69,14 +69,31 @@ ContextMenu->popup(e->globalPos()); void KeepassEntryView::updateItems(unsigned int GroupID){ + clear(); Items.clear(); if(!db)return; if(!GroupID)return; -EntryViewItem *tmp=NULL; for(int i=0;iEntries.size();i++){ - CEntry* entry=&db->Entries[i]; - if(entry->GroupID==GroupID){ + if(db->Entries[i].GroupID==GroupID) + setEntry(&db->Entries[i]); +} + +} + +void KeepassEntryView::showSearchResults(QList& results){ +clear(); +Items.clear(); +for(int j=0; jEntries.size();i++){ + if(db->Entries[i].sID == results[j]) + setEntry(&db->Entries[i]); + } +} +} + +void KeepassEntryView::setEntry(CEntry* entry){ + EntryViewItem* tmp=NULL; Items.push_back(tmp=new EntryViewItem(this)); Items.back()->pEntry=entry; int j=0; @@ -93,8 +110,9 @@ for(int i=0;iEntries.size();i++){ if(config.ListView_HidePasswords) tmp->setText(j++,"******"); else{ - tmp->setText(j++,entry->Password.getString()); - entry->Password.delRef();}} + entry->Password.unlock(); + tmp->setText(j++,entry->Password.string()); + entry->Password.lock();}} if(config.Columns[4]){ tmp->setText(j++,entry->Additional.section('\n',0,0));} if(config.Columns[5]){ @@ -108,17 +126,6 @@ for(int i=0;iEntries.size();i++){ if(config.Columns[9]){ tmp->setText(j++,entry->BinaryDesc);} Items.back()->setIcon(0,EntryIcons[entry->ImageID]); -}} -} - -void KeepassEntryView::showSearchResults(QList& results){ -updateItems(0); -for(int j=0; jpEntry->sID == results[j]) - setItemHidden(Items[i],false); - } -} } void KeepassEntryView::refreshItems(){ @@ -141,8 +148,9 @@ for(int i=0;isetText(j++,"******"); else{ - tmp->setText(j++,entry->Password.getString()); - entry->Password.delRef();}} + entry->Password.unlock(); + tmp->setText(j++,entry->Password.string()); + entry->Password.lock();}} if(config.Columns[4]){ tmp->setText(j++,entry->Additional.section('\n',0,0));} if(config.Columns[5]){ diff --git a/src/lib/EntryView.h b/src/lib/EntryView.h index 80d93c0..aebd5b5 100644 --- a/src/lib/EntryView.h +++ b/src/lib/EntryView.h @@ -38,6 +38,7 @@ public: vectorItems; QMenu *ContextMenu; private: + void setEntry(CEntry* entry); int CurrentGroup; protected: virtual void contextMenuEvent(QContextMenuEvent *event); diff --git a/src/lib/SecString.cpp b/src/lib/SecString.cpp index 35aae3b..426df69 100755 --- a/src/lib/SecString.cpp +++ b/src/lib/SecString.cpp @@ -19,83 +19,89 @@ ***************************************************************************/ #include "SecString.h" -#include #include +#include "crypto/arcfour.h" #include "random.h" using namespace std; -Q_UINT8 SecString::Key[32]={0}; +CArcFour SecString::RC4; + +SecString::operator QString(){ +return string(); +} SecString::SecString(){ -len=0; +locked=true; } +int SecString::length(){ +return crypt.size(); +} SecString::~SecString(){ -overwrite(plaintext); +lock(); } -void SecString::getString(QString & str){ -if(data.size()){ -Rijndael aes; -int r=aes.init(Rijndael::CBC, Rijndael::Decrypt,Key,Rijndael::Key32Bytes); -if(r){ cout << "AES error, code " << r << endl; - exit(-1);} -char* out=new char[len]; -r=aes.padDecrypt((unsigned char*)data.data(),data.size(),(unsigned char*)out); -if(r!=len){ cout << "AES error in SecString::getString(), r!=length, r=" << r << endl; - exit(-1);} -str=QString::fromUtf8(out,len); -overwrite(out,len); -delete [] out; -} +void SecString::lock(){ +locked=true; +overwrite(plain); +plain=QString(); } -QString& SecString::getString(){ -getString(plaintext); -return plaintext; +void SecString::unlock(){ +locked=false; +plain=QString(); +if(!crypt.length()){return;} +const unsigned char* buffer=new unsigned char[crypt.length()]; +SecString::RC4.decrypt((byte*)crypt.data(),(unsigned char*)buffer,crypt.length()); +plain=QString::fromUtf8((const char*)buffer,crypt.size()); +overwrite((unsigned char*)buffer,crypt.size()); +delete [] buffer; } -void SecString::delRef(){ -overwrite(plaintext); + +const QString& SecString::string(){ +if(locked){ + printf("Error in function SecString::string(): string is locked\n"); + return QString(">SEC_STRING_ERROR<"); +} +return plain; } -void SecString::setString(QString& str,bool DelSrc){ -Rijndael aes; -int r=aes.init(Rijndael::CBC, Rijndael::Encrypt,Key,Rijndael::Key32Bytes); -if(r){ cout << "AES error, code " << r << endl; - exit(-1);} -int il=str.length(); -char* input=new char[il]; -char* output=new char[il+16]; -memcpy(input,str.utf8(),il); -r=aes.padEncrypt((unsigned char*)input,il,(unsigned char*)output); -if(r<0){ cout << "AES error, code " << r << endl; - exit(-1);} -len=il; -data=QByteArray(output,r); -overwrite(input,il); -delete [] input; -if(DelSrc)overwrite(str); + +void SecString::setString(QString& str,bool DeleteSource){ +QByteArray StrData=str.toUtf8(); +int len=StrData.size(); +unsigned char* buffer=new unsigned char[len]; +SecString::RC4.encrypt((const unsigned char*)StrData.data(),buffer,len); +crypt=QByteArray((const char*)buffer,len); +overwrite(buffer,len); +overwrite((unsigned char*)StrData.data(),len); +delete [] buffer; +if(DeleteSource){ + overwrite(str); + str=QString();} +lock(); } -void SecString::overwrite(char* str,int strlen){ +void SecString::overwrite(unsigned char* str,int strlen){ if(strlen==0 || str==NULL)return; -getRandomBytes(str,strlen,1,false); +for(int i=0; i #include #include -#include "crypto/rijndael.h" +#include "crypto/arcfour.h" class SecString{ public: - SecString(unsigned char* key); SecString(); ~SecString(); - - void getString(QString& str); - QString& getString(); void setString(QString& str, bool DelSrc=false); - void delRef(); - static void overwrite(char* str,int len); - static void overwrite(QString& str); + void lock(); + void unlock(); + const QString& string(); + operator QString(); int length(); + + static void overwrite(unsigned char* str,int len); + static void overwrite(QString& str); static void generateSessionKey(); private: - static Q_UINT8 Key[32]; - QString plaintext; - QByteArray data; - int len; + bool locked; + static CArcFour RC4; + QByteArray crypt; + QString plain; + }; diff --git a/src/main.cpp b/src/main.cpp index 9ccfb65..22fa2b1 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -123,6 +123,8 @@ if(config.Language!="_DEUTSCH_"){ DateTimeFormat=QObject::trUtf8("dd'.'MM'.'yy' 'hh':'mm"); loadImages(); +SecString::generateSessionKey(); + KeepassMainWindow *mainWin = new KeepassMainWindow(); mainWin->show(); int r=app->exec(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f3bb4a4..42c0f3e 100755 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005 by Tarek Saidi * + * Copyright (C) 2005-2006 by Tarek Saidi * * tarek.saidi@arcor.de * * * * This program is free software; you can redistribute it and/or modify * @@ -303,9 +303,10 @@ void KeepassMainWindow::OnFileOpen(){ if(FileOpen) if(!closeDatabase())return; QString filename=QFileDialog::getOpenFileName(this,trUtf8("Databank öffnen..."),QDir::homePath(),"*.kdb"); -if(filename!=QString::null) +if(filename!=QString::null){ openDatabase(filename); } +} void KeepassMainWindow::OnFileClose(){ closeDatabase(); @@ -394,22 +395,26 @@ if(EntryView->selectedItems().size()!=1){ CEntry& entry=*((EntryViewItem*)(EntryView->selectedItems()[0]))->pEntry; QString str=trUtf8("Gruppe: %1 Titel: %2 Benutzername: %3 URL: %4 Passwort: %5 Erstellt: %6 letzte Änderung: %7 letzter Zugriff: %8 gültig bis: %9"); -str=str.arg(currentGroup()->Name).arg(entry.Title); +//todo: a "CGroup* PwDatabase::getGroup(CEntry*)" method would be a good idea +str=str.arg(db->Groups[db->getGroupIndex(entry.GroupID)].Name).arg(entry.Title); if(!config.ListView_HideUsernames) str=str.arg(entry.UserName); else str=str.arg("****"); str=str.arg(entry.URL); -if(!config.ListView_HidePasswords) str=str.arg(entry.Password.getString()); +entry.Password.unlock(); +if(!config.ListView_HidePasswords) str=str.arg(entry.Password.string()); else str=str.arg("****"); +entry.Password.lock(); + str=str.arg(entry.Creation.toString(Qt::LocalDate)) .arg(entry.LastMod.toString(Qt::LocalDate)) .arg(entry.LastAccess.toString(Qt::LocalDate)) .arg(entry.Expire.toString(Qt::LocalDate)); DetailView->setHtml(str); -entry.Password.delRef(); + } @@ -669,9 +674,10 @@ ClipboardTimer.start(config.ClipboardTimeOut*1000,true); } void KeepassMainWindow::OnEditPasswordToClipboard(){ -Clipboard->setText(currentEntry()->Password.getString(),QClipboard::Clipboard); +currentEntry()->Password.unlock(); +Clipboard->setText(currentEntry()->Password.string(),QClipboard::Clipboard); ClipboardTimer.start(config.ClipboardTimeOut*1000,true); -currentEntry()->Password.delRef(); +currentEntry()->Password.lock(); } diff --git a/src/src.pro b/src/src.pro index 5a54d01..3c86847 100755 --- a/src/src.pro +++ b/src/src.pro @@ -3,15 +3,8 @@ # Unterordner relativ zum Projektordner: ./src # Das Target ist eine Anwendung: ../bin/keepass -INSTALLS += target \ - Share +INSTALLS += Share Share.files += ../share/keepass/* -unix{ target.path = /usr/local/bin - Share.path = /usr/local/share/keepass -} -macx{ target.path = /Applications - Share.path = /Applications/keepass.app/Contents/share/keepass -} FORMS += forms/EditGroupDlg.ui \ forms/SearchDlg.ui \ forms/AboutDlg.ui \ @@ -57,7 +50,8 @@ HEADERS += lib/IniReader.h \ global.h \ main.h \ lib/GroupView.h \ - lib/EntryView.h + lib/EntryView.h \ + crypto/arcfour.h SOURCES += lib/IniReader.cpp \ lib/UrlLabel.cpp \ main.cpp \ @@ -88,7 +82,8 @@ SOURCES += lib/IniReader.cpp \ Database.cpp \ lib/KdePlugin.cpp \ lib/GroupView.cpp \ - lib/EntryView.cpp + lib/EntryView.cpp \ + crypto/arcfour.cpp QT += xml \ qt3support MOC_DIR = ../build/moc @@ -103,3 +98,11 @@ thread \ exceptions \ stl TEMPLATE = app +unix{ + target.path = /usr/local/bin + Share.path = /usr/local/share/keepass +} +macx{ + target.path = /Applications + Share.path = /Applications/keepass.app/Contents/share/keepass +}