diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ba380ab2..f424d765c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,7 @@ set(keepassx_SOURCES core/Group.cpp core/ListDeleter.h core/Metadata.cpp + core/PasswordGenerator.cpp core/qsavefile.cpp core/SignalMultiplexer.cpp core/TimeDelta.cpp diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp new file mode 100644 index 000000000..88abf663a --- /dev/null +++ b/src/core/PasswordGenerator.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2013 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * 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, see . + */ + +#include "PasswordGenerator.h" + +#include "crypto/Random.h" + +PasswordGenerator* PasswordGenerator::m_instance = Q_NULLPTR; + +QString PasswordGenerator::generatePassword(int length, + const PasswordGenerator::CharClasses& classes, + const PasswordGenerator::GeneratorFlags& flags) +{ + Q_ASSERT(isValidCombination(length, classes, flags)); + + QVector groups = passwordGroups(classes, flags); + + QVector passwordChars; + Q_FOREACH (const PasswordGroup& group, groups) { + Q_FOREACH (QChar ch, group) { + passwordChars.append(ch); + } + } + + QString password; + + if (flags & CharFromEveryGroup) { + for (int i = 0; i < groups.size(); i++) { + int pos = Random::randomUInt(groups[i].size()); + + password.append(groups[i][pos]); + } + + for (int i = groups.size(); i < length; i++) { + int pos = Random::randomUInt(passwordChars.size()); + + password.append(passwordChars[pos]); + } + + // shuffle chars + for (int i = (password.size() - 1); i >= 1; i--) { + int j = Random::randomUInt(i + 1); + + QChar tmp = password[i]; + password[i] = password[j]; + password[j] = tmp; + } + } + else { + for (int i = 0; i < length; i++) { + int pos = Random::randomUInt(passwordChars.size()); + + password.append(passwordChars[pos]); + } + } + + return password; +} + +bool PasswordGenerator::isValidCombination(int length, + const PasswordGenerator::CharClasses& classes, + const PasswordGenerator::GeneratorFlags& flags) +{ + if (classes == 0) { + return false; + } + else if (length == 0) { + return false; + } + + if ((flags & CharFromEveryGroup) && (length < numCharClasses(classes))) { + return false; + } + + return true; +} + +QVector PasswordGenerator::passwordGroups(const PasswordGenerator::CharClasses& classes, + const PasswordGenerator::GeneratorFlags& flags) +{ + QVector passwordGroups; + + if (classes & LowerLetters) { + PasswordGroup group; + + for (int i = 97; i < (97 + 26); i++) { + if ((flags & ExcludeLookAlike) && (i == 108)) { // "l" + continue; + } + + group.append(i); + } + + passwordGroups.append(group); + } + if (classes & UpperLetters) { + PasswordGroup group; + + for (int i = 65; i < (65 + 26); i++) { + if ((flags & ExcludeLookAlike) && (i == 73 || i == 79)) { // "I" and "O" + continue; + } + + group.append(i); + } + + passwordGroups.append(group); + } + if (classes & Numbers) { + PasswordGroup group; + + for (int i = 48; i < (48 + 10); i++) { + if ((flags & ExcludeLookAlike) && (i == 48 || i == 49)) { // "0" and "1" + continue; + } + + group.append(i); + } + + passwordGroups.append(group); + } + if (classes & SpecialCharacters) { + PasswordGroup group; + + for (int i = 33; i <= 47; i++) { + group.append(i); + } + + for (int i = 58; i <= 64; i++) { + group.append(i); + } + + for (int i = 91; i <= 96; i++) { + group.append(i); + } + + for (int i = 123; i <= 126; i++) { + if ((flags & ExcludeLookAlike) && (i == 124)) { // "|" + continue; + } + + group.append(i); + } + + passwordGroups.append(group); + } + + return passwordGroups; +} + +int PasswordGenerator::numCharClasses(const PasswordGenerator::CharClasses& classes) +{ + int numClasses = 0; + + if (classes & LowerLetters) { + numClasses++; + } + if (classes & UpperLetters) { + numClasses++; + } + if (classes & Numbers) { + numClasses++; + } + if (classes & SpecialCharacters) { + numClasses++; + } + + return numClasses; +} + +PasswordGenerator* PasswordGenerator::instance() +{ + if (!m_instance) { + m_instance = new PasswordGenerator(); + } + + return m_instance; +} + +PasswordGenerator::PasswordGenerator() +{ +} diff --git a/src/core/PasswordGenerator.h b/src/core/PasswordGenerator.h new file mode 100644 index 000000000..a9a21acd4 --- /dev/null +++ b/src/core/PasswordGenerator.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2013 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * 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, see . + */ + +#ifndef KEEPASSX_PASSWORDGENERATOR_H +#define KEEPASSX_PASSWORDGENERATOR_H + +#include +#include +#include + +#include "core/Global.h" + +typedef QVector PasswordGroup; + +class PasswordGenerator +{ +public: + enum CharClass + { + LowerLetters = 0x1, + UpperLetters = 0x2, + Numbers = 0x4, + SpecialCharacters = 0x8 + }; + Q_DECLARE_FLAGS(CharClasses, CharClass) + + enum GeneratorFlag + { + ExcludeLookAlike = 0x1, + CharFromEveryGroup = 0x2 + }; + Q_DECLARE_FLAGS(GeneratorFlags, GeneratorFlag) + + QString generatePassword(int length, const PasswordGenerator::CharClasses& classes, + const PasswordGenerator::GeneratorFlags& flags); + bool isValidCombination(int length, const PasswordGenerator::CharClasses& classes, + const PasswordGenerator::GeneratorFlags& flags); + + static PasswordGenerator* instance(); + +private: + PasswordGenerator(); + + QVector passwordGroups(const PasswordGenerator::CharClasses& classes, + const PasswordGenerator::GeneratorFlags& flags); + int numCharClasses(const PasswordGenerator::CharClasses& classes); + + static PasswordGenerator* m_instance; + + Q_DISABLE_COPY(PasswordGenerator) +}; + +inline PasswordGenerator* passwordGenerator() { + return PasswordGenerator::instance(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::CharClasses) + +Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::GeneratorFlags) + +#endif // KEEPASSX_PASSWORDGENERATOR_H