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
    \$\begingroup\$ This might be of help to you. \$\endgroup\$ – yuri Mar 15 at 18:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.