From a5d427d2629fde787eb2ef90c58c446dc52015d1 Mon Sep 17 00:00:00 2001 From: sniperbeamer Date: Tue, 30 Sep 2008 14:26:24 +0000 Subject: [PATCH] Added 2 new password generator options git-svn-id: https://svn.code.sf.net/p/keepassx/code/trunk@227 b624d157-de02-0410-bad0-e51aec6abb33 --- changelog | 2 +- src/Kdb3Database.cpp | 427 ++++++++++++++++----------------- src/KpxConfig.h | 4 + src/crypto/twoclass.cpp | 2 +- src/dialogs/PasswordGenDlg.cpp | 164 ++++++++----- src/dialogs/PasswordGenDlg.h | 8 +- src/forms/PasswordGenDlg.ui | 28 ++- src/lib/EntryView.cpp | 1 + src/lib/SecString.cpp | 2 +- src/lib/random.cpp | 8 +- src/lib/random.h | 4 +- src/mainwindow.cpp | 2 +- 12 files changed, 364 insertions(+), 288 deletions(-) diff --git a/changelog b/changelog index f55cc69..4433a14 100644 --- a/changelog +++ b/changelog @@ -1,7 +1,7 @@ ---------------------------- 0.3.3 (2008-08-11) ---------------------------- -- fix an error when opening twofish encrypted databases +- fixed error when opening twofish encrypted databases (Bug #2025075) ---------------------------- 0.3.2 (2008-07-20) diff --git a/src/Kdb3Database.cpp b/src/Kdb3Database.cpp index 20ac1fd..68274d4 100644 --- a/src/Kdb3Database.cpp +++ b/src/Kdb3Database.cpp @@ -478,237 +478,237 @@ void Kdb3Database::restoreGroupTreeState(){ return false; bool Kdb3Database::load(QString filename){ -unsigned long total_size,crypto_size; -quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags; -quint8 FinalRandomSeed[16]; -quint8 ContentsHash[32]; -quint8 EncryptionIV[16]; - -File = new QFile(filename); -if(!File->open(QIODevice::ReadWrite)){ - if(!File->open(QIODevice::ReadOnly)){ - error=tr("Could not open file."); - delete File; - File = NULL; - return false; + unsigned long total_size,crypto_size; + quint32 Signature1,Signature2,Version,NumGroups,NumEntries,Flags; + quint8 FinalRandomSeed[16]; + quint8 ContentsHash[32]; + quint8 EncryptionIV[16]; + + File = new QFile(filename); + if(!File->open(QIODevice::ReadWrite)){ + if(!File->open(QIODevice::ReadOnly)){ + error=tr("Could not open file."); + delete File; + File = NULL; + return false; + } } -} -total_size=File->size(); -char* buffer = new char[total_size]; -File->read(buffer,total_size); - -if(total_size < DB_HEADER_SIZE){ - error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)"); - LOAD_RETURN_CLEANUP -} - -memcpyFromLEnd32(&Signature1,buffer); -memcpyFromLEnd32(&Signature2,buffer+4); -memcpyFromLEnd32(&Flags,buffer+8); -memcpyFromLEnd32(&Version,buffer+12); -memcpy(FinalRandomSeed,buffer+16,16); -memcpy(EncryptionIV,buffer+32,16); -memcpyFromLEnd32(&NumGroups,buffer+48); -memcpyFromLEnd32(&NumEntries,buffer+52); -memcpy(ContentsHash,buffer+56,32); -memcpy(TransfRandomSeed,buffer+88,32); -memcpyFromLEnd32(&KeyTransfRounds,buffer+120); - -if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){ - error=tr("Wrong Signature"); - LOAD_RETURN_CLEANUP -} - -if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){ - error=tr("Unsupported File Version."); - LOAD_RETURN_CLEANUP -} - -if (Flags & PWM_FLAG_RIJNDAEL) - Algorithm = Rijndael_Cipher; -else if (Flags & PWM_FLAG_TWOFISH) - Algorithm = Twofish_Cipher; -else{ - error=tr("Unknown Encryption Algorithm."); - LOAD_RETURN_CLEANUP -} - -RawMasterKey.unlock(); -MasterKey.unlock(); -KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds); - -quint8 FinalKey[32]; - -SHA256 sha; -sha.update(FinalRandomSeed,16); -sha.update(*MasterKey,32); -sha.finish(FinalKey); - -RawMasterKey.lock(); -MasterKey.lock(); - -if(Algorithm == Rijndael_Cipher){ - AESdecrypt aes; - aes.key256(FinalKey); - aes.cbc_decrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,total_size-DB_HEADER_SIZE,(unsigned char*)EncryptionIV); - crypto_size=total_size-((quint8*)buffer)[total_size-1]-DB_HEADER_SIZE; -} -else if(Algorithm == Twofish_Cipher){ - CTwofish twofish; - if (twofish.init(FinalKey, 32, EncryptionIV) != true){ - error=tr("Unable to initalize the twofish algorithm."); + total_size=File->size(); + char* buffer = new char[total_size]; + File->read(buffer,total_size); + + if(total_size < DB_HEADER_SIZE){ + error=tr("Unexpected file size (DB_TOTAL_SIZE < DB_HEADER_SIZE)"); LOAD_RETURN_CLEANUP } - crypto_size = (unsigned long)twofish.padDecrypt((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."); - LOAD_RETURN_CLEANUP -} -SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size); - -if(memcmp(ContentsHash, FinalKey, 32) != 0){ - if(PotentialEncodingIssue){ - delete[] buffer; - delete File; - File = NULL; - // KeePassX used Latin-1 encoding for passwords until version 0.3.1 - // 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. - - RawMasterKey.copyData(RawMasterKey_Latin1); - PotentialEncodingIssue=false; - qDebug("Decryption failed. Retrying with Latin-1."); - return load(filename); // second try - } - error=tr("Hash test failed.\nThe key is wrong or the file is damaged."); - KeyError=true; - LOAD_RETURN_CLEANUP -} - -unsigned long pos = DB_HEADER_SIZE; -quint16 FieldType; -quint32 FieldSize; -char* pField; -bool bRet; -StdGroup group; -QList Levels; -RootGroup.Title="$ROOT$"; -RootGroup.Parent=NULL; -RootGroup.Handle=NULL; - -for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) -{ - 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]"); + + memcpyFromLEnd32(&Signature1,buffer); + memcpyFromLEnd32(&Signature2,buffer+4); + memcpyFromLEnd32(&Flags,buffer+8); + memcpyFromLEnd32(&Version,buffer+12); + memcpy(FinalRandomSeed,buffer+16,16); + memcpy(EncryptionIV,buffer+32,16); + memcpyFromLEnd32(&NumGroups,buffer+48); + memcpyFromLEnd32(&NumEntries,buffer+52); + memcpy(ContentsHash,buffer+56,32); + memcpy(TransfRandomSeed,buffer+88,32); + memcpyFromLEnd32(&KeyTransfRounds,buffer+120); + + if((Signature1!=PWM_DBSIG_1) || (Signature2!=PWM_DBSIG_2)){ + error=tr("Wrong Signature"); LOAD_RETURN_CLEANUP } - - memcpyFromLEnd32(&FieldSize, pField); - pField += 4; pos += 4; - if (pos >= (total_size + FieldSize)){ - error=tr("Unexpected error: Offset is out of range.").append(" [G2]"); + + if((Version & 0xFFFFFF00) != (PWM_DBVER_DW & 0xFFFFFF00)){ + error=tr("Unsupported File Version."); LOAD_RETURN_CLEANUP } - - bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)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){ - error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); + + if (Flags & PWM_FLAG_RIJNDAEL) + Algorithm = Rijndael_Cipher; + else if (Flags & PWM_FLAG_TWOFISH) + Algorithm = Twofish_Cipher; + else{ + error=tr("Unknown Encryption Algorithm."); LOAD_RETURN_CLEANUP } -} - -StdEntry entry; - -for (unsigned long CurEntry = 0; CurEntry < NumEntries;) -{ - pField = buffer+pos; - - memcpyFromLEnd16(&FieldType, pField); - pField += 2; pos += 2; - if(pos >= total_size){ - error=tr("Unexpected error: Offset is out of range.").append(" [E1]"); + + RawMasterKey.unlock(); + MasterKey.unlock(); + KeyTransform::transform(*RawMasterKey,*MasterKey,TransfRandomSeed,KeyTransfRounds); + + quint8 FinalKey[32]; + + SHA256 sha; + sha.update(FinalRandomSeed,16); + sha.update(*MasterKey,32); + sha.finish(FinalKey); + + RawMasterKey.lock(); + MasterKey.lock(); + + if(Algorithm == Rijndael_Cipher){ + AESdecrypt aes; + aes.key256(FinalKey); + aes.cbc_decrypt((unsigned char*)buffer+DB_HEADER_SIZE,(unsigned char*)buffer+DB_HEADER_SIZE,total_size-DB_HEADER_SIZE,(unsigned char*)EncryptionIV); + crypto_size=total_size-((quint8*)buffer)[total_size-1]-DB_HEADER_SIZE; + } + else if(Algorithm == Twofish_Cipher){ + CTwofish twofish; + if (twofish.init(FinalKey, 32, EncryptionIV) != true){ + error=tr("Unable to initalize the twofish algorithm."); + LOAD_RETURN_CLEANUP + } + crypto_size = (unsigned long)twofish.padDecrypt((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."); LOAD_RETURN_CLEANUP } - - memcpyFromLEnd32(&FieldSize, pField); - pField += 4; pos += 4; - if (pos >= (total_size + FieldSize)){ - error=tr("Unexpected error: Offset is out of range.").append(" [E2]"); + SHA256::hashBuffer(buffer+DB_HEADER_SIZE,FinalKey,crypto_size); + + if(memcmp(ContentsHash, FinalKey, 32) != 0){ + if(PotentialEncodingIssue){ + delete[] buffer; + delete File; + File = NULL; + // KeePassX used Latin-1 encoding for passwords until version 0.3.1 + // 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. + + RawMasterKey.copyData(RawMasterKey_Latin1); + PotentialEncodingIssue=false; + qDebug("Decryption failed. Retrying with Latin-1."); + return load(filename); // second try + } + error=tr("Hash test failed.\nThe key is wrong or the file is damaged."); + KeyError=true; LOAD_RETURN_CLEANUP } - - bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField); - - if((FieldType == 0xFFFF) && (bRet == true)){ - Entries << entry; - if(!entry.GroupId) - qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data()); - CurEntry++; + + unsigned long pos = DB_HEADER_SIZE; + quint16 FieldType; + quint32 FieldSize; + char* pField; + bool bRet; + StdGroup group; + QList Levels; + RootGroup.Title="$ROOT$"; + RootGroup.Parent=NULL; + RootGroup.Handle=NULL; + + for(unsigned long CurGroup = 0; CurGroup < NumGroups; ) + { + 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]"); + LOAD_RETURN_CLEANUP + } + + memcpyFromLEnd32(&FieldSize, pField); + pField += 4; pos += 4; + if (pos >= (total_size + FieldSize)){ + error=tr("Unexpected error: Offset is out of range.").append(" [G2]"); + LOAD_RETURN_CLEANUP + } + + bRet = readGroupField(&group,Levels, FieldType, FieldSize, (quint8 *)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){ + error=tr("Unexpected error: Offset is out of range.").append(" [G1]"); + LOAD_RETURN_CLEANUP + } } - - pField += FieldSize; - pos += FieldSize; - if (pos >= total_size){ - error=tr("Unexpected error: Offset is out of range.").append(" [E3]"); + + StdEntry entry; + + for (unsigned long CurEntry = 0; CurEntry < NumEntries;) + { + pField = buffer+pos; + + memcpyFromLEnd16(&FieldType, pField); + pField += 2; pos += 2; + if(pos >= total_size){ + error=tr("Unexpected error: Offset is out of range.").append(" [E1]"); + LOAD_RETURN_CLEANUP + } + + memcpyFromLEnd32(&FieldSize, pField); + pField += 4; pos += 4; + if (pos >= (total_size + FieldSize)){ + error=tr("Unexpected error: Offset is out of range.").append(" [E2]"); + LOAD_RETURN_CLEANUP + } + + bRet = readEntryField(&entry,FieldType,FieldSize,(quint8*)pField); + + if((FieldType == 0xFFFF) && (bRet == true)){ + Entries << entry; + if(!entry.GroupId) + qDebug("NULL: %i, '%s'", (int)CurEntry, (char*)entry.Title.toUtf8().data()); + CurEntry++; + } + + pField += FieldSize; + pos += FieldSize; + if (pos >= total_size){ + error=tr("Unexpected error: Offset is out of range.").append(" [E3]"); + LOAD_RETURN_CLEANUP + } + } + + if(!createGroupTree(Levels)){ + error=tr("Invalid group tree."); LOAD_RETURN_CLEANUP } -} - -if(!createGroupTree(Levels)){ - error=tr("Invalid group tree."); - LOAD_RETURN_CLEANUP -} - -delete [] buffer; - -hasV4IconMetaStream = false; -for(int i=0;i max 1024 - KeyTransfRounds=10000 + ran; + KeyTransfRounds=50000; KeyError=false; } diff --git a/src/KpxConfig.h b/src/KpxConfig.h index f2dc1b8..c1367fd 100644 --- a/src/KpxConfig.h +++ b/src/KpxConfig.h @@ -75,6 +75,8 @@ public: 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();} + bool pwGenExcludeLookAlike(){return settings.value("Options/PwGenExcludeLookAlike").toBool();} + bool pwGenEveryGroup(){return settings.value("Options/PwGenEveryGroup").toBool();} int pwGenLength(){return settings.value("Options/PwGenLength",25).toInt();} QBitArray pwGenOptions(){return stringToBitArray(settings.value("Options/PwGenOptions","11111000011110").toString(),14);} bool rememberLastKey(){return settings.value("Options/RememberLastKey",true).toBool();} @@ -136,6 +138,8 @@ public: 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 setPwGenExcludeLookAlike(bool value){settings.setValue("Options/PwGenExcludeLookAlike",value);} + void setPwGenEveryGroup(bool value){settings.setValue("Options/PwGenEveryGroup",value);} void setPwGenLength(int value){settings.setValue("Options/PwGenLength",value);} void setPwGenOptions(const QBitArray& value){settings.setValue("Options/PwGenOptions",bitArrayToString(value));} void setRememberLastKey(bool value){settings.setValue("Options/RememberLastKey",value);} diff --git a/src/crypto/twoclass.cpp b/src/crypto/twoclass.cpp index 471f710..7d7f6c6 100644 --- a/src/crypto/twoclass.cpp +++ b/src/crypto/twoclass.cpp @@ -18,7 +18,7 @@ ***************************************************************************/ -#include +#include #include "twoclass.h" static bool g_bInitialized = false; diff --git a/src/dialogs/PasswordGenDlg.cpp b/src/dialogs/PasswordGenDlg.cpp index 7083c26..6ce4987 100644 --- a/src/dialogs/PasswordGenDlg.cpp +++ b/src/dialogs/PasswordGenDlg.cpp @@ -22,6 +22,7 @@ #include "dialogs/PasswordGenDlg.h" #include "dialogs/CollectEntropyDlg.h" +#include "random.h" #include "apg/randpass.h" #include "apg/pronpass.h" @@ -109,6 +110,8 @@ CGenPwDialog::CGenPwDialog(QWidget* parent, bool StandAloneMode,Qt::WFlags fl) checkBoxPN->setChecked(true); checkBoxPS->setChecked(false); } + Check_ExcludeLookAlike->setChecked(config->pwGenExcludeLookAlike()); + Check_EveryGroup->setChecked(config->pwGenEveryGroup()); Spin_Num->setValue(config->pwGenLength()); adjustSize(); setMaximumSize(size()); @@ -139,6 +142,8 @@ CGenPwDialog::~CGenPwDialog(){ pwGenOptions.setBit(12,checkBoxPN->isChecked()); pwGenOptions.setBit(13,checkBoxPS->isChecked()); config->setPwGenOptions(pwGenOptions); + config->setPwGenExcludeLookAlike(Check_ExcludeLookAlike->isChecked()); + config->setPwGenEveryGroup(Check_EveryGroup->isChecked()); config->setPwGenLength(Spin_Num->value()); } @@ -198,21 +203,6 @@ void CGenPwDialog::OnGeneratePw() gen_pron_pass(buffer, hyphenated_word, length, length, mode); delete[] hyphenated_word; } - else if (Radio_1->isChecked() && !checkBox5->isChecked() && - !checkBox6->isChecked() && !checkBox7->isChecked()) - { - unsigned int mode = 0; - if (checkBox1->isChecked()) - mode |= S_CL; - if (checkBox2->isChecked()) - mode |= S_SL; - if (checkBox3->isChecked()) - mode |= S_NB; - if (checkBox4->isChecked()) - mode |= S_SS; - - gen_rand_pass(buffer, length, length, mode); - } else{ generatePasswordInternal(buffer, length); } @@ -221,24 +211,6 @@ void CGenPwDialog::OnGeneratePw() delete[] buffer; } -int CGenPwDialog::AddToAssoctable(char* table,int start,int end,int pos){ - for(int i=start;i<=end;i++){ - table[pos]=i; - pos++; - } - return (end-start)+1; -} - - -bool CGenPwDialog::trim(unsigned char &x, int r){ - if(xcurrentIndex()==0){ @@ -340,6 +312,32 @@ void CGenPwDialog::SwapEchoMode(){ } } +void CGenPwDialog::AddToAssoctable(char* table,int start,int end,int& pos){ + for (int i=start;i<=end;i++){ + if (Check_ExcludeLookAlike->isChecked()){ + switch (i){ + case 48: // 0 + case 79: // O + case 49: // 1 + case 73: // I + case 108: // l + case 124: // | + continue; + } + } + table[pos]=i; + pos++; + } +} + +CGenPwDialog::PwGroup CGenPwDialog::AddToAssoctableGroup(char* table,int start,int end,int& pos){ + PwGroup group; + group.start = pos; + AddToAssoctable(table,start,end,pos); + group.end = pos-1; + return group; +} + void CGenPwDialog::generatePasswordInternal(char* buffer, int length){ /*------------------------------------------------------- ASCII @@ -355,26 +353,62 @@ void CGenPwDialog::generatePasswordInternal(char* buffer, int length){ int num=0; char assoctable[255]; + int groups=0; + bool ensureEveryGroup = false; + QList groupTable; if(Radio_1->isChecked()){ - if(checkBox1->isChecked()) - num+=AddToAssoctable(assoctable,65,90,num); - if(checkBox2->isChecked()) - num+=AddToAssoctable(assoctable,97,122,num); - if(checkBox3->isChecked()) - num+=AddToAssoctable(assoctable,48,57,num); + if (Check_EveryGroup->isChecked()){ + if (checkBox1->isChecked()) groups++; + if (checkBox2->isChecked()) groups++; + if (checkBox3->isChecked()) groups++; + if (checkBox4->isChecked()) groups++; + if (checkBox5->isChecked()) groups++; + if (checkBox6->isChecked()) groups++; + if (checkBox7->isChecked()) groups++; + if (groups<=length) + ensureEveryGroup = true; + } + + if(checkBox1->isChecked()){ + if (ensureEveryGroup) groupTable.append(AddToAssoctableGroup(assoctable,65,90,num)); + else AddToAssoctable(assoctable,65,90,num); + } + if(checkBox2->isChecked()){ + if (ensureEveryGroup) groupTable.append(AddToAssoctableGroup(assoctable,97,122,num)); + else AddToAssoctable(assoctable,97,122,num); + } + if(checkBox3->isChecked()){ + if (ensureEveryGroup) groupTable.append(AddToAssoctableGroup(assoctable,48,57,num)); + else AddToAssoctable(assoctable,48,57,num); + } if(checkBox4->isChecked()){ - num+=AddToAssoctable(assoctable,33,47,num); - num+=AddToAssoctable(assoctable,58,64,num); - num+=AddToAssoctable(assoctable,91,96,num); - num+=AddToAssoctable(assoctable,123,126,num); + PwGroup group; + group.start = num; + AddToAssoctable(assoctable,33,44,num); + AddToAssoctable(assoctable,46,47,num); + AddToAssoctable(assoctable,58,64,num); + AddToAssoctable(assoctable,91,94,num); + AddToAssoctable(assoctable,96,96,num); + AddToAssoctable(assoctable,123,126,num); + if (ensureEveryGroup){ + group.end = num-1; + groupTable.append(group); + } + + } + if(checkBox5->isChecked()){ + if (ensureEveryGroup) groupTable.append(AddToAssoctableGroup(assoctable,32,32,num)); + else AddToAssoctable(assoctable,32,32,num); + } + if(checkBox6->isChecked()){ + if (ensureEveryGroup) groupTable.append(AddToAssoctableGroup(assoctable,45,45,num)); + else AddToAssoctable(assoctable,45,45,num); + } + if(checkBox7->isChecked()){ + if (ensureEveryGroup) groupTable.append(AddToAssoctableGroup(assoctable,95,95,num)); + else AddToAssoctable(assoctable,95,95,num); } - if(checkBox5->isChecked()) - num+=AddToAssoctable(assoctable,32,32,num); - if(checkBox6->isChecked() && !checkBox4->isChecked()) - num+=AddToAssoctable(assoctable,45,45,num); - if(checkBox7->isChecked() && !checkBox4->isChecked()) - num+=AddToAssoctable(assoctable,95,95,num); } else{ QString str=Edit_chars->text(); @@ -393,14 +427,30 @@ void CGenPwDialog::generatePasswordInternal(char* buffer, int length){ EntropyCollected=true; } } - - unsigned char tmp; - for(int i=0;i charPos; + for (int i=0; i0 0 462 - 491 + 543 @@ -43,7 +43,7 @@ 0 0 440 - 219 + 271 @@ -102,7 +102,7 @@ - U&nderline + &Underline Alt+N @@ -125,7 +125,7 @@ - White &Spaces + &White Spaces Alt+S @@ -148,7 +148,7 @@ - Minus + &Minus @@ -219,6 +219,20 @@ + + + + Exclude look-alike characters + + + + + + + Ensure that password contains characters from every group + + + @@ -227,7 +241,7 @@ 0 0 440 - 219 + 271 @@ -250,7 +264,7 @@ - + 15 diff --git a/src/lib/EntryView.cpp b/src/lib/EntryView.cpp index fb1b394..c7aaa4d 100644 --- a/src/lib/EntryView.cpp +++ b/src/lib/EntryView.cpp @@ -274,6 +274,7 @@ void KeepassEntryView::OnEditOpenUrl(){ void KeepassEntryView::OnEditCopyUrl(){ if (selectedItems().size() == 0) return; QString url = ((EntryViewItem*)selectedItems().first())->EntryHandle->url(); + if (url.trimmed().isEmpty()) return; if (url.startsWith("cmd://") && url.length()>6) url = url.right(url.length()-6); diff --git a/src/lib/SecString.cpp b/src/lib/SecString.cpp index 2ae1ca1..a24bdfb 100644 --- a/src/lib/SecString.cpp +++ b/src/lib/SecString.cpp @@ -126,7 +126,7 @@ void SecData::unlock(){ } void SecData::copyData(quint8* src){ - unlock(); + locked = false; memcpy(data, src, length); lock(); } diff --git a/src/lib/random.cpp b/src/lib/random.cpp index cf0c36a..4602191 100644 --- a/src/lib/random.cpp +++ b/src/lib/random.cpp @@ -47,10 +47,14 @@ void getEntropy(quint8* buffer, int length){ } } -quint32 randint(quint32 n){ +quint32 randint(quint32 limit){ quint32 rand; randomize(&rand, 4); - return (rand % n); + return (rand % limit); +} + +quint32 randintRange(quint32 min, quint32 max){ + return min + randint(max-min+1); } #if defined(Q_WS_X11) || defined(Q_WS_MAC) diff --git a/src/lib/random.h b/src/lib/random.h index c303949..636da72 100644 --- a/src/lib/random.h +++ b/src/lib/random.h @@ -33,10 +33,12 @@ extern "C" { #endif void getEntropy(quint8* buffer, int length); -quint32 randint(quint32 n); +quint32 randint(quint32 limit); // generate random number: 0 <= n < limit #ifdef __cplusplus } #endif +quint32 randintRange(quint32 min, quint32 max); // generate random number: min <= n <= max + #endif diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 916a205..9e1c5eb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -354,6 +354,7 @@ void KeepassMainWindow::setupMenus(){ EditPasswordToClipboardAction->setShortcut(tr("Ctrl+C")); EditUsernameToClipboardAction->setShortcut(tr("Ctrl+B")); EditOpenUrlAction->setShortcut(tr("Ctrl+U")); + EditCopyUrlAction->setShortcut(tr("Ctrl+I")); EditNewEntryAction->setShortcut(tr("Ctrl+Y")); EditEditEntryAction->setShortcut(tr("Ctrl+E")); EditDeleteEntryAction->setShortcut(tr("Ctrl+D")); @@ -630,7 +631,6 @@ void KeepassMainWindow::setStateFileOpen(bool IsOpen){ void KeepassMainWindow::setStateFileModified(bool mod){ if (config->autoSaveChange() && mod && db->file()){ OnFileSave(); - mod = false; } ModFlag=mod;