Initial commit of a word-based password generator.

The diceware algorithm is a password generation algorithm
where users roll dice several times (around 7) to generate
a random number.  This random number is then used to index into
a word list and select a word.  This process is repeated until
between 3 and 12 words are selected.  With dozens to hundreds of
dice rolls, this can get very tedious.

The passgen program performs this work in C++.  It uses the /dev/urandom
device on all modern UNIX systems to get randomness.  This randomness
is used to index into a word list.  It uses 18 bits of randomness per
word.  The word list is more than 262144 words long and thus is suitable
for this use.

Before running a random sample of the words are discarded from the list.
The list is also randomly reshuffled before beginning the process to
ensure that a random selection of words are removed.  The reshuffling
also means that the same stream of bits from /dev/urandom will not
generate the same password.  It will, however, be slightly dependent
upon the randomness in the pre-shuffle.  This randomness does not
improve the security of those passwords.

Words smaller than 4 letters are also removed.  18 bits requires
3 bytes to store cleanly, and more than 3 bytes in base32 (which is
kind of like what we're generating these passwords in).  Thus
those shorter words represent a compression -- this lowers the
minimum number of bytes necessary to encode the password and reduces
the search surface slightly.  (The worst case would be 4 words having
two letters -- 8 bytes, at 5 bits per byte is 40 bits of search
space.  4 words at 18 bits per word is 72 bits of search space.
You lose 32 bits of total search space there, alone!)

(Note: This is a commit of the latest passgen, but using sha256 object store.)
This commit is contained in:
2025-12-27 15:24:38 -05:00
commit cbc4b52526
6 changed files with 268732 additions and 0 deletions

114
pingen.cc Normal file
View File

@ -0,0 +1,114 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <string>
#include <exception>
#include <stdexcept>
#include <random>
#include <cstdint>
#include <cctype>
#include <cstdlib>
#include <cstddef>
#include <climits>
#include <cassert>
#include <boost/lexical_cast.hpp>
#undef BITS
#undef DOMAIN
#undef SKIP
#undef DOIT
#undef DEBUG
#define DEBUG SKIP
#define DOIT if( true )
#define SKIP if( false )
using std::begin;
using std::end;
namespace
{
namespace C
{
const std::size_t bits= 4;
const std::size_t domain= 16;
}
class Failure : public std::runtime_error
{
public:
explicit Failure() : std::runtime_error( "Failure" ) {}
explicit Failure( const std::string &s ) : std::runtime_error( "Failure: " + s ) {}
};
auto
openRandom()
{
std::ifstream r( "/dev/urandom" );
if( r.bad() || r.fail() ) throw Failure();
return r;
};
template< typename T >
class safe_vector : public std::vector< T >
{
public:
using std::vector< T >::vector;
template< typename Idx >
auto operator []( const Idx &i ) const { return this->at( i ); }
template< typename Idx >
auto operator []( const Idx &i ) { return this->at( i ); }
};
}
int
main( const int argcnt, const char *const *const argvec )
try
{
auto rnd= openRandom();
const int digitsDesired = ( argcnt == 1 ) ? 8 : boost::lexical_cast< int >( argvec[ 1 ] );
std::cout << "We are going to make a pin that has " << digitsDesired << " digits." << std::endl;
uint64_t randomness;
std::string digits;
std::size_t bitsInRnd= 0;
do
{
if( bitsInRnd < C::bits )
{
rnd.read( reinterpret_cast< char * >( &randomness ), sizeof( randomness ) );
if( rnd.bad() || rnd.fail() || rnd.eof() ) throw Failure();
bitsInRnd= sizeof( randomness ) * CHAR_BIT;
}
const auto digit= randomness % ( C::domain );
randomness>>= C::bits;
bitsInRnd-= C::bits;
if( digit > 9 ) continue;
DEBUG std::cout << digit << std::endl;
digits.push_back( digit + '0' );
}
while( digits.size() < digitsDesired );
std::cout << "" << digits << "" << std::endl;
std::cout << "Your pin has " << digits.size() << " words in its makeup." << std::endl;
return EXIT_SUCCESS;
}
catch( const std::exception &ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
return EXIT_FAILURE;
}