-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve password generator, add option to minimize recurrences #1337
Conversation
Improve and extend password generator: - Switch character group buttons 'a-z' and 'A-Z' to match group indices of algorithm - Modify algorithm for 'Pick characters from every group' to actually do what the option says - Add feature 'Minimize recurrences' (GUI option, config load/save, algorithm)
To facilitate review of the new implementation/algorithm, I added debug output using qDebug(), but while working locally, this seems be an issue for the build system. Thus, I commented out those calls for now. I also recommend disabling the shuffle code block in PasswordGenerator.cpp to really see what the algorithm does. |
While you're at it, could you add an "excluded characters" text entry? |
Gladly. |
Looks like a memory leak. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code needs some cleanup.
Tested the feature and it works well, could not reproduce the memory leak, though.
@@ -20,6 +20,7 @@ | |||
|
|||
#include "crypto/Random.h" | |||
#include <zxcvbn.h> | |||
//#include <QDebug> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove commented code lines from the PR. It makes it easier to review the changes when you only see what was actually removed and added and is also a requirement for merging.
|
||
QString password; | ||
//qDebug().noquote().nospace() << "*** Begin generating password ***"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
* NOTE: | ||
* Same as below, but more readable, just for reference | ||
*/ | ||
/*while (password.length() < m_length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also don't use doc comments inside functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a multiline comment, doc comments start with '/**'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a hybrid. Don't start every line with an asterisk is what I wanted to say.
int pos = randomGen()->randomUInt(groups[group].size()); | ||
password.append(groups[group][pos]); | ||
|
||
//qDebug().noquote().nospace() << "i: " << i << ", group: " << group << ", pos: " << pos << ", groups[group][pos]: '" << groups[group][pos] << "', groups[group]: " << groups[group]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
if (m_flags & MinimizeRecurrence) { | ||
groups[group].remove(pos); | ||
if (groups[group].isEmpty()) { | ||
//qDebug().noquote().nospace() << "exhausted group " << group << ", resetting"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
password.append(passwordChars[pos]); | ||
|
||
//qDebug().noquote().nospace() << "i: " << i << ", pos: " << pos << ", passwordChars[pos]: '" << passwordChars[pos] << "', passwordChars: " << passwordChars; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
if (m_flags & MinimizeRecurrence) { | ||
passwordChars.remove(pos); | ||
if (passwordChars.isEmpty()) { | ||
//qDebug().noquote().nospace() << "exhausted passwordChars, resetting"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
} | ||
} | ||
|
||
//qDebug().noquote().nospace() << "*** End generating password ***"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
@@ -52,47 +53,99 @@ QString PasswordGenerator::generatePassword() const | |||
{ | |||
Q_ASSERT(isValid()); | |||
|
|||
const QVector<PasswordGroup> groups = passwordGroups(); | |||
const QVector<PasswordGroup> groups_original = passwordGroups(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use camelCases for local variables.
|
||
QVector<QChar> passwordChars; | ||
QVector<QChar> passwordChars_original; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
Doing this will lower the password entropy. Getting char totally randomly vs limiting some char frequency The high memory usage maybe it's a due to zxcvbn-c, it's using trees and dijkstra's algorithm to check the password entropy |
I'm not quite comfortable with limiting recurring characters, either. Chance has no memory. Obviously, "aaaaa" is not a strong password, but on the other hand, the chance of generating it randomly is diminishingly small and in the best case as probable as generating any other 5-character sequence. |
You're right, I guess I didn't think this all the way through :( Regarding the memory leak, it's not related to the password generator, it's caused by ASAN. Without ASAN, memory consumption stays low as usual. |
*/ | ||
for (int i = 0; i < m_length; i++) { | ||
int group = i % groups.size(); | ||
int pos = randomGen()->randomUInt(groups[group].size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't like either approach, because they both reduce entropy. In the original approach, the alphabet of the first groups.size()
characters is reduced to their group, in your approach the alphabet of each character in the password is reduced to the alphabet of group i % groups.size()
(which is worse).
The correct algorithm would be this (pseudo code):
if pickCharFromEveryGroup then:
passwordLength = max(passwordLength, groups.size())
forceGroupAt = Map()
for i in 0 -> groups.size() do:
forceGroupAt.put(randInt(0, passwordLength) not in forceGroupAt, i)
for i in 0 -> passwordLength do:
if pickCharFromEveryGroup and i in forceGroupAt then:
password.append(randCharFromGroup(forceGroupAt.get(i))
else:
password.append(randCharFromSelectedGroups())
That way you guarantee at least one random character from every group at a random position.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See code comments.
Actually, I meant to close this PR, since we've established that this is not a good idea. Just forgot to do it. |
I think it would still be a good idea to improve the "use characters from ever group" feature in the way I described above. If you feel like doing it, you're more than welcome. |
@phoerious: currently no, I had some time to spare during the holidays, but now I'd rather spend the little time I have on finishing PR #1305 once it got reviewed. |
Description
This improves the password generator and adds a new option/feature:
Motivation
I often wondered why passwords such as 'Rb<73<U<@>gPW97f'' are generated. Instead of having recurring characters, I'd like the algorithm to choose as many distinct characters as possible, such as 'Rb!73<U]@>gPW91f'.
Also, while reviewing the existing implementation, I noticed the 'Pick characters from every group' only adds one character of each selected group, all other characters are picked entirely random, thus one could theoretically end up with a password like 'aA0?abcdefg', which is not what the option suggests, in my opinion.
DONE
TODO
How has this been tested?
Screenshots:
Types of changes
Checklist:
-DWITH_ASAN=ON
. [REQUIRED]