2
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
3
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 2 or (at your option)
8
* version 3 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#include "PasswordGenerator.h"
21
#include "crypto/Random.h"
23
const int PasswordGenerator::DefaultLength = 32;
24
const char* PasswordGenerator::DefaultCustomCharacterSet = "";
25
const char* PasswordGenerator::DefaultExcludedChars = "";
27
PasswordGenerator::PasswordGenerator()
28
: m_length(PasswordGenerator::DefaultLength)
29
, m_classes(PasswordGenerator::CharClass::DefaultCharset)
30
, m_flags(PasswordGenerator::GeneratorFlag::DefaultFlags)
31
, m_custom(PasswordGenerator::DefaultCustomCharacterSet)
32
, m_excluded(PasswordGenerator::DefaultExcludedChars)
36
void PasswordGenerator::setLength(int length)
41
void PasswordGenerator::setCharClasses(const PasswordGenerator::CharClasses& classes)
46
void PasswordGenerator::setCustomCharacterSet(const QString& customCharacterSet)
48
m_custom = customCharacterSet;
50
void PasswordGenerator::setExcludedCharacterSet(const QString& excludedCharacterSet)
52
m_excluded = excludedCharacterSet;
55
void PasswordGenerator::setFlags(const GeneratorFlags& flags)
60
QString PasswordGenerator::generatePassword() const
64
const QVector<PasswordGroup> groups = passwordGroups();
66
QVector<QChar> passwordChars;
67
for (const PasswordGroup& group : groups) {
68
for (QChar ch : group) {
69
passwordChars.append(ch);
75
if (m_flags & CharFromEveryGroup) {
76
for (const auto& group : groups) {
77
int pos = randomGen()->randomUInt(static_cast<quint32>(group.size()));
79
password.append(group[pos]);
82
for (int i = groups.size(); i < m_length; i++) {
83
int pos = randomGen()->randomUInt(static_cast<quint32>(passwordChars.size()));
85
password.append(passwordChars[pos]);
89
for (int i = (password.size() - 1); i >= 1; i--) {
90
int j = randomGen()->randomUInt(static_cast<quint32>(i + 1));
92
QChar tmp = password[i];
93
password[i] = password[j];
97
for (int i = 0; i < m_length; i++) {
98
int pos = randomGen()->randomUInt(static_cast<quint32>(passwordChars.size()));
100
password.append(passwordChars[pos]);
107
bool PasswordGenerator::isValid() const
109
if (m_classes == CharClass::NoClass && m_custom.isEmpty()) {
111
} else if (m_length <= 0) {
115
if ((m_flags & CharFromEveryGroup) && (m_length < numCharClasses())) {
119
return !passwordGroups().isEmpty();
122
QVector<PasswordGroup> PasswordGenerator::passwordGroups() const
124
QVector<PasswordGroup> passwordGroups;
126
if (m_classes & LowerLetters) {
129
for (int i = 97; i <= (97 + 25); i++) {
131
if ((m_flags & ExcludeLookAlike) && (i == 108)) { // "l"
138
passwordGroups.append(group);
140
if (m_classes & UpperLetters) {
143
for (int i = 65; i <= (65 + 25); i++) {
145
if ((m_flags & ExcludeLookAlike) && (i == 66 || i == 71 || i == 73 || i == 79)) { //"B", "G", "I" and "O"
152
passwordGroups.append(group);
154
if (m_classes & Numbers) {
157
for (int i = 48; i < (48 + 10); i++) {
158
if ((m_flags & ExcludeLookAlike) && (i == 48 || i == 49 || i == 54 || i == 56)) { // "0", "1", "6", and "8"
165
passwordGroups.append(group);
167
if (m_classes & Braces) {
178
passwordGroups.append(group);
180
if (m_classes & Punctuation) {
189
passwordGroups.append(group);
191
if (m_classes & Quotes) {
198
passwordGroups.append(group);
200
if (m_classes & Dashes) {
208
if (!(m_flags & ExcludeLookAlike)) {
209
group.append(124); // "|"
212
passwordGroups.append(group);
214
if (m_classes & Math) {
226
passwordGroups.append(group);
228
if (m_classes & Logograms) {
232
for (int i = 35; i <= 38; i++) {
241
passwordGroups.append(group);
243
if (m_classes & EASCII) {
246
// [U+0080, U+009F] are C1 control characters,
247
// U+00A0 is non-breaking space
248
for (int i = 161; i <= 172; i++) {
251
// U+00AD is soft hyphen (format character)
252
for (int i = 174; i <= 255; i++) {
253
if ((m_flags & ExcludeLookAlike) && (i == 249)) { // "﹒"
259
passwordGroups.append(group);
261
if (!m_custom.isEmpty()) {
264
for (const auto& ch : m_custom) {
265
if (!group.contains(ch)) {
270
passwordGroups.append(group);
273
// Loop over character groups and remove excluded characters from them;
274
// remove empty groups
276
while (i != passwordGroups.size()) {
277
PasswordGroup group = passwordGroups[i];
279
for (QChar ch : m_excluded) {
280
int j = group.indexOf(ch);
283
j = group.indexOf(ch);
286
if (!group.isEmpty()) {
287
passwordGroups.replace(i, group);
290
passwordGroups.remove(i);
294
return passwordGroups;
297
int PasswordGenerator::numCharClasses() const
299
// Actually compute the non empty password groups
300
auto non_empty_groups = passwordGroups();
301
return non_empty_groups.size();
304
int PasswordGenerator::getMinLength() const
306
if ((m_flags & CharFromEveryGroup)) {
307
return numCharClasses();
311
void PasswordGenerator::reset()
313
m_classes = CharClass::DefaultCharset;
314
m_flags = GeneratorFlag::DefaultFlags;
315
m_custom = DefaultCustomCharacterSet;
316
m_excluded = DefaultExcludedChars;
317
m_length = DefaultLength;
319
int PasswordGenerator::getLength() const
323
const PasswordGenerator::GeneratorFlags& PasswordGenerator::getFlags() const
327
const PasswordGenerator::CharClasses& PasswordGenerator::getActiveClasses() const
331
const QString& PasswordGenerator::getCustomCharacterSet() const
335
const QString& PasswordGenerator::getExcludedCharacterSet() const