XKPasswd.pm becomes Crypt::HSXKPasswd

It’s been a while since I released a new version of XKPasswd.pm, the open source Perl module that powers the secure memorable password generator at www.xkpasswd.net. The main reason for the big gap is that I needed to learn some new skills to get the code to where I wanted it to be. There were three main problems I wanted solved:

To get wide adoption, the Module needs to be available via CPAN

The module needs unicode support to deal with non-English languages

It needs to be easy to edit and tweak a config with the www.xkpasswd.net web interface, and then use it in your scripts.

While solving those problems, I also took the opportunity to tidy up some other odds and ends in the code base. It’s not that code was broken, it just that a few parts of it had a bit of a fishy smell – it seemed like there was probably a better way to do that, and there was!

So, here’s a summary of what’s changed from the the point of view of a user of the Module:

The Packaging – the module has a new name, and is now packaged with Module::Build, so it’s easier to install, and ready for distribution via CPAN.

Unicode Support – if it’s a unicode character, you can use it while generating passwords.

Redesigned Word Sources – more bundled with the module, and easier to create your own.

Redesigned Sources of Randomness – more bundled with the module, a better default, and easier to create you own.

A switch to Named Arguments (in both the constructor and functional interface).

I’ve put a lot of time and effort into developing this entirely free and open source module. If you find it useful, please consider making a donation:

*Download Beta of Crypt::HSXKPasswd via GitHub*

Why The New Name?

The primary motivation for the change was CPAN. You need to respect the CPAN name-space, and that means not taking a root-level name just for the heck of it. Instead, I spent some time reviewing other password generation modules in CPAN to figure out where in the name-space the module would be most at home. In the end I decided on Crypt.

As well as respecting the CPAN name-space, you also need to clearly position your module relative to other similar modules. What makes it different?

There are already password generators inspired by XKCD on CPAN, Crypt::XkcdPassword for example. What makes this module different?

Simply put, this module has two equally ideas at it’s core, Steve Gibson’s Password Hay Stacks, and the famous XKCD web comic about password strength. The old name only referenced half of this important duo of inspirations.

Crypt::HSXKPasswd can create passwords exactly like the famous correct horse battery staple example from the comic, but that’s actually an edge-case. The module is designed to use words as a lattice around which a more secure, but still memorable, password can be built. The idea to use words comes from XKCD, the concept of separators and padding comes from Password Hay Stacks.

New Packaging

Earlier versions of this module were simply distributed as a single .pm file. There was no concept of an installer of any kind – users had to download the code from GitHub, save it to a folder of their choice, and then point their scripts at that folder with the help of a use lib directive. This was always sub-optimal, and proper packaging was always on my to do list. Well, now it’s done!

The module is now packaged with Module::Build, the more modern of the two common Perl packagers. This allows me to generate .tar.gz bundles for distributing the module, which contain the code, as well as the build scripts needed to install it.

One of the big reasons for choosing Module::Build is that it’s compatible with CPAN. Once the module has endured some beta testing, I’ll be uploading it to CPAN, so in future, you’ll be able to install it directly from there.

Installing the Beta

In the mean time, the installation process is quite simple:

Start the installation process by downloading the .tar.gz file for the most recent release from GitHub (Crypt-HSXKPasswd-v3.1.1 BETA 1 at the moment). Once the file is downloaded, extract it, open a Terminal, and change directory into the newly extracted folder. It doesn’t matter where you extract this folder because you can delete it once the module is installed.

In that folder, issue the following commands:

perl Build.PL
sudo ./Build installdeps
./Build
./Build test
sudo ./Build install

More detailed instructions here.

Unicode Support

Previous versions of this module have suffered from the narrow word view that comes from living in an English speaking country. If a word contained a character other than a bare letter between a and z, the module simply skipped the word. No more – the module has broadened its world view with full Unicode support!

By default the module uses UTF-8 for all inputs, but it can use any input encoding supported by Perl.

Unfortunately, there is a price to pay for this new-found international awareness – backwards compatibility. Perl didn’t develop robust unicode support until version 5.16, so this new version of the module is incompatible with all versions of Perl older than Per 5.16. Given that Perl 5.16 was released in May 2012, don’t expect this to cause too many problems.

Support for accented characters in passwords is by no means universal, so the module’s default behaviour is still to avoid generating passwords containing accents. But, that default has become significantly smarter. Rather than rejecting words with accented characters, the module will now happily ingest them and use them in passwords, but, when the password is returned, the accents will be stripped off. That is to say, the module’s default behaviour is to load the word clichÃ© without error, but to render it as cliche in passwords.

If the site you are generating passwords for will happily accept accented characters, then you can override the default behaviour and have the module preserve the accents in the generated passwords. You do this by setting the new config variable allow_accents to 1. All the presets have allow_accents set to 0, but that can be easily override using the new named arguments (described in detail below). For example, to use the XKCD preset with accents enabled, you could do the following:

my $password = hsxkpasswd(
preset => 'XKCD',
preset_override => {allow_accents => 1}
);

Word Sources

Previous versions of the module have been built around a single source of words – dictionary files, i.e. text files with one word per line.

Dictionary files are still supported, but, so are other sources of words. The module can now consume words from an instance of any class the extends Crypt::HSXKPasswd::Dictionary, and implements the method word_list().

The module now includes nine word sources ready for you to use:

Crypt::HSXKPasswd::Dictionary::EN_Default

As it’s name suggests, this is the module’s default word source. It contains the same English words that were in the sample dictionary file that shipped with previous versions of the module, but bundled into a module, making it easier to use.

Crypt::HSXKPasswd::Dictionary::Basic

This word source allows for the aggregation of one or more dictionary files and/or arrays of words into a single dictionary. This is particularly convenient for people who speak multiple languages, and want to generate passwords contains words randomly chosen from any of the languages they speak.

Crypt::HSXKPasswd::Dictionary::System

This dictionary uses the Unix words file as its source of words. It’s obviously only available on Linux and Unix systems that have a words file, including Mac OS X.

Crypt::HSXKPasswd::Dictionary::DE, Crypt::HSXKPasswd::Dictionary::ES, Crypt::HSXKPasswd::Dictionary::FR, Crypt::HSXKPasswd::Dictionary::IT, Crypt::HSXKPasswd::Dictionary::NL & Crypt::HSXKPasswd::Dictionary::PT

German, Spanish, French, Italian, Dutch/Flemish & Portuguese dictionaries, all based on the free and open source dictionary files from the WinEdt project. These dictionaries are new, and need to have the rough edges knocked off them – see the Help Needed! section below for details.

For example, to use the French dictionary and permit accented characters in the generated passwords, you could do something like:

my $password = hsxkpasswd(
dictionary => Crypt::HSXKPasswd::Dictionary::FR->new(),
preset => 'WEB32',
preset_override => {allow_accents => 1}
);

Help Needed!

The German, Spanish, French, Italian, Dutch/Flemish & Portuguese dictionaries are all based on the free and open-source dictionary files for WinEdit. These dictionaries are designed to be used in a word processor, not for use a list of common words. This means that are all too big, and they all contain words that would definitely not be considered memorable by most.

I’m looking for native speakers of each of these languages to help me prune these dictionaries down to just a few thousand truly common words, like I did a few years ago when creating the English dictionary the module uses by default.

You’ll find copies of the dictionary files, with their open source licenses and credits, in the share inside the installer bundle. Anyone who helps me with the files will get credited in them, and the license will remain as it is (BSD in some cases, GPL in others).

Sources of Random Numbers

All versions of this module have made it possible to supply your own random number generator (RNG), but previous versions didn’t make that as easy as it could have been, and shipped with only one standard RNG.

You can now create your own RNGs by extending the class Crypt::HSXKPasswd::RNG, and implementing the method random_numbers().

The module ships with five RNGs ready for your use:

Crypt::HSXKPasswd::RNG::Basic

This RNG uses Perl’s built-in rand() function to generate random numbers. Using rand() makes this RNG fast, and means it doesn’t rely on any non-standard modules. Unfortunately, there is also a significant draw-back – rand() is not actually a very good RNG! I would argue that it’s good enough for most users, but none the less, my recommendation would be to avoid it unless none of the alternatives will work for you.

Crypt::HSXKPasswd::RNG::DevUrandom

Linux and Unix systems (Mac OS X included), come with their own OS-provided source of randomness, the special file /dev/urandom. This RNG uses /dev/urandom to generate random numbers. It’s fast, provides good quality randomness, but is sadly only available on Unix, Linux, and OS X systems.

Crypt::HSXKPasswd::RNG::Math_Random_Secure

As its name implies, this RNG uses the non-standard module Math::Random::Secure to generate random numbers. If you can install Math::Random::Secure on your system, this is the RNG I would recommend, it generates very high quality randomness, and is quite fast.

Crypt::HSXKPasswd::RNG::Data_Entropy

As its name implies, this RNG uses the non-standard module Data::Entropy::Algorithms to generate random numbers. It generates high quality random numbers, but it’s slow. I found it to be about six times slower than perl’s build-in rand() function in my testing.

Crypt::HSXKPasswd::RNG::RandomDotOrg

This RNG uses Random.Org’s HTTP API to source random numbers. As it relies on a web service, it’s inevitably much slower than the other RNGs, but the random numbers are of a very high quality, so it might be a good option for the somewhat paranoid To use this RNG you need to install the following packages which Crypt::HSXKPasswd declares as recommended rather than required: Email::Valid, URI, LWP::UserAgent & Mozilla::CA.

The module’s default behaviour is to use the best RNG available on your system. It will first try Crypt::HSXKPasswd::RNG::Math_Random_Secure, then Crypt::HSXKPasswd::RNG::Data_Entropy, then Crypt::HSXKPasswd::RNG::DevUrandom, and only then fall back to Crypt::HSXKPasswd::RNG::Basic. You’ll know if the module falls back to Crypt::HSXKPasswd::RNG::Basic, because it will issues a warning if it does.

Named Arguments

When creating a single password using the functional interface (hsxkpasswd()), or when creating a Crypt::HSXKPasswd object, you need to be able to easily specify all the customisations you require. Specifically, you want to specify between zero and three of the the following, a word source, a config, and a an RNG. Given that there are multiple ways to specify a word source, and multiple ways to specify a config, there are a lot of possible permutations to cater for. Named arguments are the only way cater for all the possible combinations in a sane.

If you’re not familiar with named arguments, they work by specifying one or more name-value pairs where the name-value pairs are separated by ,s and the names and the values by =>s. For clarity, named arguments are often written one per line, but that’s purely a stylistic choice. Below is an example showing how to generate a password from a custom dictionary file, the APPLEID preset, and the default RNG:

my $password = hsxkpasswd(
dictionary_file => 'my_dict.txt',
preset => 'APPLEID'
);

Note that since there is now a default word source, default, config, and default RNG, you can use both the constructor and the functional interface with no argument at all:

# a single password using the functional interface with all defaults
my $password = hsxkpasswd();

# an object with all defaults
my $hsxkpasswd_obj = Crypt::HSXKPasswd->new();

Let’s look at the three groups of named arguments in detail.

Specifying a Word Source

Words can be loaded from a text file, a reference to an array of words, or an instance of a class that extends Crypt::HSXKPasswd::Dictionary. There is a different named argument available for each of these options:

dictionary

An instance of a class that extends Crypt::HSXKPasswd::Dictionary.

dictionary_list

A reference to an array of words.

dictionary_file

The path to a dictionary file. When using this argument, you can optionally include a second named argument, dictionary_file_encoding, to specify the file’s character encoding. The module defaults to treating input files as UTF-8.

You should only ever use one of dictionary, dictionary_list and dictionary_file in a single function call. If multiple are specified, only the one with the highest priority will be used, and the rest will be ignored. The list above shows the options in descending priority.

If you don’t specify a word source, the module will use an instance of Crypt::HSXKPasswd::Dictionary::EN_Default.

Specifying A Config

The module can accept a configs as a preset name (with optional key overrides), as a config hasherf, or as a JSON string representing a valid config hashref.

The following named arguments are available for specifying a config:

config

A valid config hashref.

config_json

A JSON string representing a valid config hashref. This feature is only available if you have the standard Perl JSON library installed (available via CPAN).

preset

Use one of the presets defined by the module. You can optionally override one or more keys in the preset by passing a hashref using the named argument preset_overrides.

You should only ever use one of config, config_json and preset in a single function call. If multiple are specified, only the one with the highest priority will be used, and the rest will be ignored. The list above shows the options in descending priority.

When you save a config using the Load/Save tab at www.xkpasswd.net, the config is output in JSON format. The config_json named argument makes it easy to build a config you like using the web GUI, and then use that config in your scripts.

By default the module uses the preset DEFAULT.

Specifying an RNG

There is only one named argument for controlling random number generation, rng, and the associated value must be an instance of a class that extends Crypt::HSXKPasswd::RNG.

As described earlier in the article, by default the module will use the most secure RNG available on your system. Unless you write your own custom RNG, or want to use Crypt::HSXKPasswd::RNG::RandomDotOrg, my advice would be to allow the module choose the most secure available RNG for you by not specifying an rng argument.

Finally – Reporting Bugs & Contributing

There are literally thousands of new or edited lines of code in this module. I’ve obviously done my own testing, but it’s almost inevitable that I’ve missed something somewhere. If you stumble into any bugs, please let me know by opening an issue on the project’s GitHub page.

You can also use the GitHub issue tracker to suggest improvements or additions.

Finally for any interested developers out there – pull requests are welcome, provided the code works, the code style is consistent with the rest of the module, and the code passes PerlCritic run using the settings in the .perlcriticrc file included in the module’s GitHub project.