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
master
tariq 19 years ago
parent 68f1d12d74
commit f89372b0c5
  1. 17
      src/PwManager.cpp
  2. 86
      src/crypto/arcfour.cpp
  3. 45
      src/crypto/arcfour.h
  4. 13
      src/dialogs/EditEntryDlg.cpp
  5. 9
      src/dialogs/SearchDlg.cpp
  6. 2
      src/dialogs/SearchDlg.h
  7. 44
      src/lib/EntryView.cpp
  8. 1
      src/lib/EntryView.h
  9. 110
      src/lib/SecString.cpp
  10. 25
      src/lib/SecString.h
  11. 2
      src/main.cpp
  12. 20
      src/mainwindow.cpp
  13. 23
      src/src.pro

@ -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);

@ -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 <QByteArray>
#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;
}

@ -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

@ -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();

@ -92,15 +92,16 @@ for(int i=0;i<db->Entries.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());

@ -41,7 +41,7 @@ private:
CGroup* group;
bool regexp;
PwDatabase* db;
bool search(QString& str);
bool search(const QString& str);
};
#endif

@ -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;i<db->Entries.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<Q_UINT32>& results){
clear();
Items.clear();
for(int j=0; j<results.size(); j++){
for(int i=0; i<db->Entries.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;i<db->Entries.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;i<db->Entries.size();i++){
if(config.Columns[9]){
tmp->setText(j++,entry->BinaryDesc);}
Items.back()->setIcon(0,EntryIcons[entry->ImageID]);
}}
}
void KeepassEntryView::showSearchResults(QList<Q_UINT32>& results){
updateItems(0);
for(int j=0; j<results.size(); j++){
for(int i=0; i<Items.size();i++){
if(Items[i]->pEntry->sID == results[j])
setItemHidden(Items[i],false);
}
}
}
void KeepassEntryView::refreshItems(){
@ -141,8 +148,9 @@ for(int i=0;i<Items.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]){

@ -38,6 +38,7 @@ public:
vector<EntryViewItem*>Items;
QMenu *ContextMenu;
private:
void setEntry(CEntry* entry);
int CurrentGroup;
protected:
virtual void contextMenuEvent(QContextMenuEvent *event);

@ -19,83 +19,89 @@
***************************************************************************/
#include "SecString.h"
#include <qmessagebox.h>
#include <iostream>
#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<strlen; i++){
str[i]=0;
}
}
void SecString::overwrite(QString &str){
if(str.length()==0)return;
char* tmp=new char[str.length()];
getRandomBytes(tmp,str.length(),1,false);
str=tmp;
delete [] tmp;
for(int i=0; i<str.length(); i++){
((char*)str.data())[i]=0;
}
int SecString::length(){
return len;
}
void SecString::generateSessionKey(){
getRandomBytes(Key,32,1,false);
}
void SecString::generateSessionKey(){
CArcFour arc;
unsigned char* sessionkey=new unsigned char[32];
getRandomBytes(sessionkey,32,1,false);
RC4.setKey(sessionkey,32);
delete [] sessionkey;
}

@ -23,28 +23,29 @@
#include <QByteArray>
#include <qstring.h>
#include <qglobal.h>
#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;
};

@ -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();

@ -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("<B>Gruppe: </B>%1 <B>Titel: </B>%2 <B>Benutzername: </B>%3 <B>URL: </B><a href=%4>%4</a> <B>Passwort: </B>%5 <B>Erstellt: </B>%6 <B>letzte Änderung: </B>%7 <B>letzter Zugriff: </B>%8 <B>gültig bis: </B>%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();
}

@ -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
}