2
\$\begingroup\$

My goal in creating this project was to create a password generator that was simple enough to be useful as well as sophisticated enough in its random-number generator so as to not be contrived.

I used Boost Program Options for argument parsing. The interface seems extremely easy to use, and the fact that it's both more readable and allows for writing idiomatic C++ seem pretty good reasons to not use getopt.h in this case.

Using the --passwords or -n flags the user may set the number of passwords to generate. Likewise, the --length or -l flags set the length of the password. I looked around for a standard list of character symbols to use, but I couldn't find anything like a standard, so my default symbol list is "!@#$``~|\\%^&*()[]-_=+{}';:,<.>/?". (Note: the backtick is repeated so StackOverflow's markdown highlights everything properly.) The user may use the --simple-symbols flag to use only "!@#$&?".

The actual random number generation was what I wanted to explore with this project. While researching this question I came across some very similar projects like this one, which used Sodium, but I'm wondering if the method I'm using here is as good, or at least viable.

I used std::random_device to generate my random numbers, along with std::uniform_int_distribution, to ensure as random and uniform an output as possible. I'm looking for a way to check whether std::random_device will work as expected; since it may not be non-deterministic if there is no hardware implementation, I would like to at least warn the user during execution.

Lastly, I wanted to make the alphabets used for password generation as extensible as possible, but after implementing them, it seems a little too much for the benefit gained in return. It theoretically allows a user to not have to use numbers or letters, or whatever, but with the benefit of hindsight this doesn't look like a good feature.

PGEN.hpp

#ifndef PGEN_INCLUDES_PGEN_H_
#define PGEN_INCLUDES_PGEN_H_

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <iostream>
#include <random>
#include <string>

#include <boost/program_options.hpp>

#endif // PGEN_INCLUDES_PGEN_H_

main.cpp

#include "PGEN.hpp"

namespace Options = boost::program_options;

int main(int argc, char *argv[])
{
    /** Program Defaults
     *
     */
    const auto  DefaultPasswordsToCreate = 1;
    const auto  DefaultPasswordLength    = 16;

    const std::string DefaultSymbolList  = "!@#$`~|\\%^&*()[]-_=+{}';:,<.>/?";
    const std::string SimplerSymbolList  = "!@#$&?";

    /** Program Options
     *
     */
    bool verbose = false;

    /** Password parameters:
     *
     */
    int N              = DefaultPasswordsToCreate;
    int PasswordLength = DefaultPasswordLength;

    std::string AlphabetUpper   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    std::string AlphabetLower   = "abcdefghijklmnopqrstuvwxyz";
    std::string AlphabetNumeric = "0123456789";
    std::string AlphabetSymbols = DefaultSymbolList;

    Options::options_description OptionsDescription("Program Options");
    OptionsDescription.add_options()
        ("help,h", "Display this help message and exit.")
        ("version", "Print program version information.")
        ("passwords,n", Options::value<int>(&N)->default_value(DefaultPasswordsToCreate), "Number of passwords to generate.")
        ("length,l", Options::value<int>(&PasswordLength)->default_value(DefaultPasswordLength), "Length of passwords to generate.")
        ("symbols", Options::value<std::string>(&AlphabetSymbols)->default_value(DefaultSymbolList), "Pass in custom legal symbols list.")
        ("simple-symbols", "Use simpler symbol list.")
        ("verbose,v", "Log additional info to stdout during execution.")
    ;

    Options::variables_map ArgsInput;
    Options::store(Options::parse_command_line(argc, argv, OptionsDescription), ArgsInput);
    Options::notify(ArgsInput);

    if (ArgsInput.count("help"))
    {
        std::cout << OptionsDescription << std::endl;

        return EXIT_SUCCESS;
    }

    if (ArgsInput.count("version"))
    {
        std::cout << "[Program Version Information...]" << std::endl;

        return EXIT_SUCCESS;
    }

    if (ArgsInput.count("verbose"))
    {
        verbose = true;
    }

    if (ArgsInput.count("symbols"))
    {
        // TODO: Validate symbol list input.

        if (verbose) {
            std::cout << "Legal Symbols: " << AlphabetSymbols << std::endl;
        }
    }

    if (ArgsInput.count("simple-symbols"))
    {
        AlphabetSymbols = SimplerSymbolList;

        if (verbose) {
            std::cout << "Legal Symbols: " << AlphabetSymbols << std::endl;
        }
    }

    // TODO: Refactor to idiomatic C++.
    const auto AlphabetSize = 62 + strlen(AlphabetSymbols.c_str());
    char* Alphabet = static_cast<char *>(malloc(sizeof(char) * AlphabetSize));
    sprintf(Alphabet, "%s%s%s%s", AlphabetUpper.c_str(), AlphabetLower.c_str(), AlphabetNumeric.c_str(), AlphabetSymbols.c_str());

    std::random_device rd;
    std::uniform_int_distribution<int> dist(0, AlphabetSize);

    for (auto i = 0; i < N; ++i) {
        for (auto n = 0; n < PasswordLength; ++n) {
            const auto r = dist(rd);
            std::cout << Alphabet[r];
        }

        std::cout << std::endl;
    }

    return EXIT_SUCCESS;
}

Directory Structure

PGEN
|-- Makefile
|
|-- src
|   |_ main.cpp
|
|__ include
    |_ PGEN.hpp

Makefile

SHELL = /bin/sh

.SUFFIXES:
.SUFFIXES: .cpp .hpp

vpath %.cpp src
vpath %.hpp include

PROGRAM = pgen

all: $(PROGRAM)

$(PROGRAM): main.o
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ -lboost_program_options

main.o: main.cpp
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include $(TARGET_ARCH) -c -o $@ $^

.PHONY: clean
clean:
    rm -rf *.o $(PROGRAM)

To build I usually used the following:

make CXX=g++ CXXFLAGS="-std=c++17 -Wall -Wextra -pedantic"

Example:

$ ./pgen
i=2TBdeDK8E8L%xk

$ ./pgen -n 10 -l 24
zE'S9l(6C3r_F8!V6)[Q!ZiW
-2iJVbW3(4@556vL-@v&naM
96T`-Bl3;MkVmAVHogx]|}X
F~~UJ$tpmrZ2Phf2/WEzlY>$
QLfD5m}29%o#E;\-}MM.QiGb
V'Rwv^\)Rw~5zL5X6a'9{;MJ
RO,m`-K&^G=f/^7IK+@>4@7b
F?fdM6)+.Qcmd<&II;R}1ao
xI6-VCv'ct\g2Da;OfhN^-td
Ayai>)'PQ=T,=zyoEg[Fod`G

$ ./pgen -n -10 -l 24 --simple-symbols
z@?IquGoOw8IhqBJCoat4YM0
qjOXC!1o0ZjfHQezxfHyYe2
4#uyBN5oGoWqbmw!eYTNwW@e
I65bEWLHHg8&3WXhtUXIxX6W
i95SuYP0Ab#d0FUUmpIbGGzk
gOF!EHe1!LeaiP#I?UoD$84v
jPLzBFv2IIqp7iQ$z4@ORW$&
d4eM#NeCaxNf?hGroLBgp&E
QPNZ849rKJnDO$rVhFGkfWWZ
0Ch&IzAQN8PjfUFCMBtVwaVC
\$\endgroup\$
1
  • 1
    \$\begingroup\$ This might be of help to you. \$\endgroup\$
    – yuri
    Mar 15, 2019 at 18:43

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.