I've created this C++ random password generator. You can set length, you can enable custom symbols. Then I went to The Password Meter and started checking some generated passwords of mine.
Based on the formula here, I've created a simplified version of their grading system, and also created a function for it. Now I can just do password_score(generated_password)
and set a limit for the security of my password.
Here are some examples and how my program performs:
###
Generated password strength: 100/100
Password has been copied to clipboard!
### 1a,pA!T0c0&7 (PasswordMeter score: 100%)
###
Generated password strength: 100/100
Password has been copied to clipboard!
### 82F^Vh11Gl}1 (PasswordMeter score: 100%)
###
Generated password strength: 98/100
Password has been copied to clipboard!
### !3V44'w1 (PasswordMeter score: 100%)
###
Generated password strength: 64/100
Password has been copied to clipboard!
### $]1V9q (PasswordMeter score: 70%)
Am I doing it right? I know that there's always some room for improvement. I'll take any advice to make it better. Thanks!
string password_generator(const int length_of_password = 12, bool enable_symbols = false, bool copy_to_clipboard = false)
{
vector<char> password;
srand (static_cast<unsigned int>(time(nullptr)));
//generates lowercase letters
for(auto c = 1; c <= length_of_password; c = c + 4)
{
const auto v1 = rand() % 26;
password.push_back(v1 + 'a');
}
//generates uppercase letters
for(auto g = 3; g <= length_of_password; g = g + 4)
{
const auto v2 = rand() % 26;
password.push_back(v2 + 'A');
}
//generates numbers
for(auto k = 0; k <= length_of_password; k = k + 2)
{
const auto v3 = rand() % 10;
password.push_back(v3 + '0');
}
if(enable_symbols)
{
//generates symbols
for(auto g = 1; g <= length_of_password; g = g + 4)
{
const auto choice = rand() % 3;
if(choice == 0)
{
const auto v4 = rand() % 14;
password.push_back(v4 + '!');
}
if(choice == 1)
{
const auto v5 = rand() % 5;
password.push_back(v5 + '[');
}
if(choice == 2)
{
const auto v6 = rand() % 4;
password.push_back(v6 + '{');
}
}
}
random_device r;
shuffle(password.begin(), password.end(), default_random_engine(r()));
string returning_password;
for(auto i = 0; i < length_of_password; i++)
{
returning_password.push_back(password[i]);
}
if(copy_to_clipboard)
to_clipboard(returning_password);
return returning_password;
}
Here's a main file: (I've just started learning to work with argv
s so there might be some mistakes. Most of the functions in this program are something I've created -made easier-, they're custom. I strongly recommend checking bottom GitHub link first.)
#include <duman.h>
using namespace std;
int main(const int argc, char* argv[])
{
cerr << "###\n";
if(argc > 1 && argc <= 2)
{
if(string(argv[1]) == "-h")
{
cerr << "Usage : " << get_file_name(argv[0]) << " -<Password_Length> -<Minimum_Password_Security_Score out of 100>\n";
cerr << "Example: " << get_file_name(argv[0]) << " -12 -80\n\n";
cerr << "Password will be automatically copied to your clipboard!\n";
cerr << "###\n";
return 1;
}
if(string(argv[1]) == "-v")
{
cerr << "Version: 1.3.0\n";
cerr << "###\n";
return 2;
}
if(string(argv[1]) == "-length" || string(argv[1]) == "-l")
{
cerr << "Try to use the command as:\n";
cerr << get_file_name(argv[0]) << " -8 -98 # for a password that has length of 8 and security score of minimum 98\n";
cerr << "###\n";
return 2;
}
}
if(argc > 2 && argc <= 3)
{
string raw_argument1 = argv[1];
string raw_argument2 = argv[2];
if(isalpha(raw_argument1[1]) || isalpha(raw_argument2[1]))
{
cerr << "Invalid parameters! Use -h to see how to use this program.\n";
cerr << "###\n";
return 1;
}
if(raw_argument1[0] != '-')
{
cerr << "Did you mean: " << get_file_name(argv[0]) << " -" << argv[1] << " " << raw_argument2 << " # missing dash before an argument!\n";
cerr << "###\n";
return 1;
}
if(raw_argument2[0] != '-')
{
cerr << "Did you mean: " << get_file_name(argv[0]) << " " << raw_argument1 << " -" << argv[2] << " # missing dash before an argument!\n";
cerr << "###\n";
return 1;
}
if(string(argv[1]).empty() || string(argv[2]).empty())
{
cerr << "Missing parameters! Use -h to see how to use this program!\n";
cerr << "###\n";
return 1;
}
raw_argument1.erase(raw_argument1.begin());
raw_argument2.erase(raw_argument2.begin());
const auto first_argument = stoi(raw_argument1);
const auto second_argument = stoi(raw_argument2);
auto seconds_since_start = 0;
const auto start = time(nullptr);
auto generated_password = password_generator(first_argument, true, true);
while(password_score(generated_password) < second_argument)
{
if(password_score(generated_password) >= second_argument)
{
break;
}
generated_password = password_generator(first_argument, true, true);
seconds_since_start += static_cast<int>(difftime(time(nullptr), start));
if(seconds_since_start > 50)
{
cerr << "Request timed out. Couldn't generate a password with the given parameters.\n";
cerr << "###\n";
return 1;
}
}
cerr << "Generated password strength: " << password_score(generated_password) << "/100\n";
cerr << "Password has been copied to clipboard!\n";
cerr << "###\n";
return 0;
}
if(argc > 3)
{
cerr << "Unsupported number of parameters!\n";
return 1;
}
cerr << "Commands:\n";
cerr << "1. " << get_file_name(argv[0]) << " -length -security_level # Security level is XYZ out of 100\n";
cerr << "2. " << get_file_name(argv[0]) << " -h\n";
cerr << "3. " << get_file_name(argv[0]) << " -v\n";
cerr << "###\n";
}
duman.h
is a header file I've created for myself. So, whenever I solve a problem in some way I turn it into a function and save it in that header. You can reach it here: https://github.com/duman/duman.h
2 Answers 2
Advice 1
The most important issue with your password generator function is the fact that you keep creating the character vector over and over again. I suggest you roll a class that has that character vector as a field and construct it only once:
class password_generator {
public:
password_generator() {
... // Construct your alphabet.
}
private:
std::vector<char> m_alphabet;
};
Advice 2
if(choice == 0)
{
...
}
if(choice == 1)
{
...
}
Just use switch
here:
switch (rand() % 2) {
case 0:
...
case 1:
...
case 2:
...
}
Advice 3
#include "stdafx.h"
: In the build settings of Visual Studio, search something like "Use precompiled headers"; set to No
. This will remove the need for including that header file.
Advice 4
#include <duman.h>
: this won't compile on Xcode. The convention is that you use <header>
for standard C++ library headers, and "header.h"
for your own header files.
Advice 5
using namespace std;
This one is a poor practice since it abuses your scope with bunch of identifiers/type names. Use instead:
#include "funky.hpp"
using funky::person;
using funky::darth_vader;
Advice 6
In your main
driver you output to cerr
. This is not what is expected of a command line program. *nix like OS'es has two "handles" one for standard output and one for standard error stream. Your conventional *nix guru will expect normal output to cout
and only error stuff to cerr
.
-
1\$\begingroup\$ 1. Great suggestion! Will do now. 2. Is it better performance wise? 3. That thing was annoying thanks. 4. Oh... didn't know that. I just put the file inside INCLUDE folders of Visual Studio and using it from there. 5. Well... alright. 6. I thought since cerr doesn't have any buffers it could give faster output. Obviously cout is there to "out"put. \$\endgroup\$Tuğberk Kaan Duman– Tuğberk Kaan Duman2018年01月05日 20:36:18 +00:00Commented Jan 5, 2018 at 20:36
-
\$\begingroup\$ @TuğberkKaanDuman 2.: Most definitely. When there is a match, other two values will not be tested. Also, I believe that for very large
switch
statement, the compiler may perform binary search over case values. \$\endgroup\$coderodde– coderodde2018年01月05日 20:53:25 +00:00Commented Jan 5, 2018 at 20:53 -
\$\begingroup\$ Recommending that OP disable precompiled headers is not particularly important, and in fact may be quite poor advice if
duman.h
takes a long time to compile. At the least you should justify this advice. \$\endgroup\$Reinderien– Reinderien2024年08月05日 13:06:58 +00:00Commented Aug 5, 2024 at 13:06 -
1\$\begingroup\$ Using a
case 2:
branch inside theswitch (rand() % 2) { ... }
block doesn't seem very promising to me. \$\endgroup\$CiaPan– CiaPan2024年08月05日 14:23:32 +00:00Commented Aug 5, 2024 at 14:23
Just a word on password strength: the string '$]1V9q' should not get rated 70% safe. It shouldn't even be rated any percentage safe, because it's a very weak password. Length is the decisive factor in password strength and should weigh far more strongly than other element. Even though the website is originally at fault here for using an inaccurate algorithm, you should avoid making the same mistake!
-
\$\begingroup\$ It was checked against passwordmeter.com if you read the post. Then again my library uses it -> github.com/duman/duman.h/blob/master/duman.h thanks for answering 1 year old question though. :) \$\endgroup\$Tuğberk Kaan Duman– Tuğberk Kaan Duman2019年06月03日 13:39:42 +00:00Commented Jun 3, 2019 at 13:39
-
\$\begingroup\$ I know it was, my point is that the website itself uses a nonsensical algorithm and you should be wary of copying anything without checking the source when you're working on cryptographic systems. \$\endgroup\$Daniel– Daniel2019年06月03日 20:39:03 +00:00Commented Jun 3, 2019 at 20:39