NAME

Crypt::HSXKPasswd - A secure memorable password generator inspired by Steve Gibson's Passord Haystacks (https://www.grc.com/haystack.htm), and the famous XKCD password cartoon (https://xkcd.com/936/).

VERSION

This documentation refers to Crypt::HSXKPasswd version 3.3.1.

SYNOPSIS

    use Crypt::HSXKPasswd;

    #
    # Functional Interface - a shortcut for generating single passwords
    #
    
    # generate a single password using the default word source, configuration,
    # and random number generator
    my $password = hsxkpasswd();
    
    # the above call is simply a shortcut for the following
    my $password = Crypt::HSXKPasswd->new()->password();
    
    # this function passes all arguments on to Crypt::HSXKPasswd->new()
    # so all the same customisations can be specified, e.g. specifying a
    # config preset:
    my $password = hsxkpasswd(preset => 'XKCD');
    
    #
    # Object Oriented Interface - recommended for generating multiple passwords
    #
    
    # create a new instance with the default dictionary, config, and random
    # number generator
    my $hsxkpasswd_instance = Crypt::HSXKPasswd->new();
    
    # generate a single password
    my $password = $hsxkpasswd_instance->password();
    
    # generate multiple passwords
    my @passwords = $hsxkpasswd_instance->passwords(10);

DESCRIPTION

A secure memorable password generator inspired by the wonderful XKCD webcomic at http://www.xkcd.com/ and Steve Gibson's Password Haystacks page at https://www.grc.com/haystack.htm. This is the Perl module that powers https://www.xkpasswd.net.

PHILOSOPHY

More and more of the things we do on our computer require passwords, and at the same time it seems we hear about organisations or sites losing user database on every day that ends in a y. If we re-use our passwords we expose ourself to an ever greater risk, but we need more passwords than we can possibly remember or invent. Coming up with one good password is easy, but coming up with one good password a week is a lot harder, let alone one a day!

Obviously we need some technological help. We need our computers to help us generate robust password and store them securely. There are many great password managers out there to help us securely store and sync our passwords, including commercial offerings and open-source projects. Many of these managers also offer to generate random passwords for us, usually in the form of a random string of meaningless letters numbers and symbols. These kinds of nonsense passwords are certainly secure, but they are often impractical.

Regardless of how good your chosen password manager is, there will always be times when you need to type in your passwords, and that's when random gibberish passwords become a real pain point. As annoying as it is to have to glance over and back at a small cellphone screen to manually type a gibberish password into a computer, that's nothing compared to the annoyance of trying to communicate such a password to a family member, friend, colleague or customer over the phone.

Surely it would be better to have passwords that are still truly random in the way humans can't be, but are also human-friendly in the way random gibberish never will be? This is the problem this module aims to solve.

Rather than randomly choosing many letters, digits, and symbols from a fairly small alphabet of possible characters, this library chooses a small number of words from a large alphabet of possible words as the basis for passwords. Words are easy to remember, easy to read from a screen, easy to type, and easy to communicate over the telephone.

This module uses words to make up the bulk of the passwords it generates, but it also adds carefully placed symbols and digits to add security without making the passwords difficult to remember, read, type, and speak.

In shot, this module is for people who prefer passwords that look like this:

    !15.play.MAJOR.fresh.FLAT.23!

to passwords that look like this:

    eB8.GJXa@TuM
    

PASSWORD GENERATION ALGORITHM

This module always uses a simple five-step algorithm to generate passwords, but each step can be customised, and many steps can be skipped completely.

It's important to understand the algorithm before trying to create your own custom configurations for this module.

The algorithm is broken in to the following steps:

  1. Pick random words from the dictionary.

  2. Apply transformations to the words.

  3. Create pseudo-words made up for randomly chosen digits and add them as the first and last words.

  4. Insert a copy of the same symbol between each of the words and pseudo-words. This symbol is referred to as the separator character.

  5. Pad the password with multiple instances of the same symbol front and/or back. This symbol is referred to as the padding character.

You can visualise this process as follows:

    correct horse batter staple
    correct HORSE battery staple
    25 correct HORSE battery staple 83
    25*correct*HORSE*battery*staple*83
    ++25*correct*HORSE*battery*staple*83++
    

Each of these steps can be customised in the following ways:

  1. The number of words to be used, and the minimum and maximum lengths of the words can be configured.

  2. The case of the words can be modified in a number of ways, including randomly choosing the case for each word.

    It is also possible to specify so-called 133t-style character substitutions, e.g. replacing all occurrences of the letter e with the digit 3, or all occurrences of the letter s with the symbol $.

  3. The number of digits to add as pseudo words to the front and back of the password can be configured. A length of zero can be specified for both to generate passwords without any randomly chosen digits.

  4. The separator character can be specified directly, or it can be randomly chosen from a list of symbols. It is also possible to specify that no separator should be used.

  5. The padding character can also be specified directly, or remotely chosen from a list of possible symbols. Padding can also be disabled completely. If padding is to be used it can be applied in two modes - fixed, and adaptive.

    With fixed padding a specified number of copies of the separator character are added to the front and back of the password. The fixed padding does not have to be symmetric.

    With adaptive padding the required number of copies of the separator character are added to the back of the password until it reaches a specified length.

THE MATHS

Before examining the password strength of passwords generated with this module we need to lay out the relatively simple maths underlying it all.

Maths Primer

A coin could be used as a very simple password generator. Each character in the password would be the result of a single coin toss. If the coin lands heads up, we add a H to our password, if it lands tails up, we add a T.

If you made a one-letter password in this way there would only be two possibilities, H, or T, or two permutations. If you made a two-letter password in this way there would be four possible combinations, or permutations, HH, HT, TH, and TT. If you made a three-character password in this way there would be 16 permutations, a five character one would have 32 permutations, and so forth.

So, for a coin toss, which has two possible values for each character, the formula for the number of permutations P for a given length of password L is:

    P = 2^L

Or, two to the power of the length of the password.

If we now swapped our coin for a dice, we would go from two possible values per letter, to six possible values per letter. For one dice roll there would be six permutations, for two there would be 36, for three there would be 108 and so on.

This means that for a dice, the number of permutations can be calculated with the formula:

    P = 6^L

When talking about passwords, the set of possible symbols used for each character in the password is referred to as the password's alphabet. So, for the coin toss the alphabet was just H and T, and for the dice it was 1, 2, 3, 4, 5, and 6. The actual characters used in the alphabet make no difference to the strength of the password, all that matters is the size of the alphabet, which we'll call A.

As you can probably infer from the two examples above, the formula for the number of possible permutations P for a password of length L created from an alphabet of size A is:

    P = A^L

In the real world our passwords are generally made up of a mix of letters, digits, and symbols. If we use mixed case that gives us 52 letters alone, then add in the ten digits from 0 to 9 and we're already up to 62 possible characters before we even start on the array of symbols and punctuation characters on our keyboards. It's generally accepted that if you include symbols and punctuation, there are 95 characters available for use in randomly generated passwords. Hence, in the real-world, the value for A is assumed to be 95. When you start raising a number as big as 95 to even low powers the number of permutations quickly rises.

A two character password with alphabet of 95 has 9025 permutations, increasing the length to three characters brings that up to 857,375, and so on. These numbers very quickly become too big to handle. For just an 8 character password we are talking about 6,634,204,312,890,625 permutations, which is a number so big most people couldn't say it (what do you call something a thousand times bigger than a trillion?).

Because the numbers get so astronomically big so quickly, computer scientists use bits of entropy to measure password strength rather than the number of permutations. The formula to turn permutations into bits of entropy E is very simple:

    E = Log(2)P

In other words, the entropy is the log to base two of the permutations. For our eight character example that equates to about 52 bits.

There are two approaches to increasing the number of permutations, and hence the entropy, you can choose more characters, or, you can make the alphabet you are choosing from bigger.

The Entropy of HSXKPasswd Passwords

Exactly how much entropy does a password need? That's the subject of much debate, and the answer ultimately depends on the value of the assets being protected by the password.

Two common recommendations you hear are 8 characters containing a mix of upper and lower case letters, digits, and symbols, or 12 characters with the same composition. These evaluation to approximately 52 bits of entropy and 78 bits of entropy respectively.

When evaluating the entropy of passwords generated by this module, it has to be done from two points of view for the answer to be meaningful. Firstly, a best-case scenario - the attacker has absolutely no knowledge of how the password was generated, and hence must mount a brute-force attack. Then, secondly from the point of view of an attacker with full knowledge of how the password was generated. Not just the knowledge that this module was used, but a copy of the dictionary file used, and, a copy of the configuration settings used.

For the purpose of this documentation, the entropy in the first scenario, the brute force attack, will be referred to as the blind entropy, and the entropy in the second scenario the seen entropy.

The blind entropy is solely determined by the configuration settings, the seen entropy depends on both the settings and the dictionary file used.

Calculating the bind entropy Eb is quite straightforward, we just need to know the size of the alphabet resulting from the configuration A, and the minimum length of passwords generated with the configuration L, and plug those values into this formula:

    Eb = Log(2)(A^L)

Calculating A simply involves determining whether or not the configuration results in a mix of letter cases (26 or 52 characters), the inclusion of at least one symbol (if any one is present, assume the industry standard of a 33 character search space), and the inclusion of at least one digit (10 character). This will result in a value between 26 and 95.

Calculating L is also straightforward. The one minor complication is that some configurations result in a variable length password. In this case, assume the shortest possible length the configuration could produce.

The example password from the "PHILOSOPHY" section (!15.play.MAJOR.fresh.FLAT.23!) was generated using the preset WEB32. This preset uses four words of between four and five letters long, with the case of each word randomly set to all lower or all upper as the basis for the password, it then chooses two pairs of random digits as extra words to go front and back, before separating each word with a copy of a randomly chosen symbol, and padding the front and back of the password with a copy of a different randomly chosen symbol. This results in passwords that contain a mix of cases, digits, and symbols, and are between 27 and 31 characters long. If we add these values into the formula we find that the blind entropy for passwords created with this preset is:

    Eb = Log(2)(95^27) = 163 bits

This is spectacularly secure! And, this is the most likely kind of attack for a password to face. However, to have confidence in the password we must also now calculate the entropy when the attacker knows everything about how the password was generated.

We will calculate the entropy resulting from the same WEB32 config being used to generate a password using the sample library file that ships with the module.

The number of permutations the attacker needs to check is purely the product of possibly results for each random choice made during the assembly of the password.

Lets start with the words that will form the core of the password. The configuration chooses four words of between four and five letters long from the dictionary, and then randomises their case, effectively making it a choice from twice as many words (each word in each case).

The sample dictionary file contains 698 words of the configured length, which doubles to 1396. Choosing four words from that very large alphabet gives a starting point of 1396^4, or 3,797,883,801,856 permutations.

Next we need to calculate the permutations for the separator character. The configuration specifies just nine permitted characters, and we choose just one, so that equates to 9 permutations.

Similarly, the padding character on the end is chosen from 13 permitted symbols giving 13 more permutations.

Finally, there are four randomly chosen digits, giving 10^4, or 10,000 permutations.

The total number of permutations is the product of all these permutations:

    Pseen = 3,797,883,801,856 * 9 * 13 * 10,000 = 2.77x10^17
    

Finally, we convert this to entropy by taking the base 2 log:

    Eseen = Log(2)2.77x10^17 = ~57bits
    

What this means is that most probably, passwords generated with this preset using the sample dictionary file are spectacularly more secure than even 12 randomly chosen characters, and, that in the very unlikely event that an attackers knows absolutely everything about how the password was generated, it is still significantly more secure than 8 randomly chosen characters.

Because the exact strength of the passwords produced by this module depend on the configuration and dictionary file used, the constructor does the above math when creating an HSXKPasswd object, and throws a warning if either the blind entropy falls below 78bits, or the seen entropy falls below 52 bits.

SUBROUTINES/METHODS

MODULE CONFIGURATION

It is possible to tweak the module's behaviour in certain areas by updating the values contained within a set of module configuration keys. The values associated with these keys can be accessed and updated via the class function module_config().

    # get the current debug status
    my $debug_status = Crypt::HSXKPasswd->module_config('DEBUG');
    
    # configure the module to suppress all entropy warnings
    Crypt::HSXKPasswd->module_config('ENTROPY_WARNINGS', 'NONE');

The following module configuration keys exist within the module:

CUSTOM DATA TYPES

This module uses a custom type library created with Type::Library for data validation. It is important to know this for two reasons - firstly, these custom types are mentioned in many error messages, and secondly these custom types are available for developers to use in their own code, either when utilising Crypt::HSXKPasswd, or writing custom word sources by extending Crypt::HSXKPasswd::Dictionary, or when writing custom random number generators by extending Crypt::HSXKPasswd::RNG.

Defined Types

Using the Custom Types

The library of custom types is defined in the package Crypt::HSXKPasswd::Types, and it is a standard Type::Library type library containing Type::Tiny type definitions.

Useful Links:

To use the bare type definitions listed above, import the module as follows:

    use Crypt::HSXKPasswd::Types qw( :types );
    

Each type listed above will now be imported, and become available as a bare word. The Type::Tiny documentation provides a full list of available functions, but the examples below illustrate some of the more useful ones:

    $is_valid = Letter->check('e'); # $is_valid = 1
    $is_valid = Letter->check('-'); # $is_valid = undef
    $err_msg = Letter->validate('e'); # $err_msg = undef
    $err_msg = Letter->validate('-'); # $err_msg = "'-' is not a Letter ...
                    # ... (must be a string containing exactly one letter)"

Type::Library automatically creates an is_TypeName function for each type defined in the library. These are not imported by default. To import them add the export tag :is to the use line. I would recommend the following use line:

    use Crypt::HSXKPasswd::Types qw( :types :is );
    

You can now do things like the following:

    $is_valid = is_Letter('e'); # $is_valid = 1
    $is_valid = is_Letter('-'); # $is_valid = undef
    

Each of the types listed above also contains a custom function using Type::Tiny's new, and still officially experimental, my_methods feature. The custom function is called my_english, and can be used to return an English description of the values considered valid by the type, e.g.:

    print Letter->my_english(); # prints: a string containing exactly one letter
    

As well as the named types listed above, there are also anonymous types defined for each supported configuration key. These can be accessed using the function Crypt::HSXKPasswd-config_key_definitions()>.

If declaring your own Type::Tiny types, you may also find the public subroutine Crypt::HSXKPasswd::Types::var_to_string() useful - it will turn anything passed as a scalar into a meaningful string, truncating any resulting strings longer than 72 characters in nice way. All the custom error messages in all the types defined in Crypt::HSXKPasswd::Types make use of this subroutine.

CONFIGURATION

The module builds passwords using the following process.

First, a set of words are randomly chosen from the word source. Then, two pseudo-words made of one or more digits may added before and/or after the words from. Next, a separator character may be placed between all the words (including the groups of digits), and one or more occurrences of a padding symbol may be added front and/or back.

You can envisage the process as follows:

    correct HORSE BATTERY staple
    34 correct HORSE BATTERY staple 56
    34-correct-HORSE-BATTERY-staple-56
    !!34-correct-HORSE-BATTERY-staple-56!!
    

Many aspects of this password generation process are configurable. You can control the length and number of words chosen, and what, if any, case transformations should be applied to those words, and how accented characters should be treated. How many, if any, digits should be added front and back. What symbol, if any, should be used as a separator. And finally how the password should be padded, if at all, and with what symbol. Passwords can be padded to a given length, or by a given number of symbols front and back.

The symbols used as the separator and for padding can be explicitly specified, or the they can be randomly chosen from a given alphabet of possible symbols. Both symbols can be randomly chosen from the same alphabet, or from two separately specified alphabets.

Every instance of an HSXKPasswd password generator stores its configuration as a set of name-value pairs, referred to as configuration keys throughout this documentation.

Configurations can be specified either as a complete set of configuration keys with values that together form a valid configuration, as a named preset, or, as a named preset accompanied by a list of one or more configuration keys with new values to override those specified by the preset.

The module contains a preset called DEFAULT, and this preset is used if no configuration is specified. The function default_config() will return a copy of this configuration as a reference to a hashtable.

For more details on how to specify configurations, see the documentation for the constructor (the function new()) below.

Password Generator Configuration Keys

Below is a list of all the configuration keys that can be used to customise the password generation algorithm. Each configuration key is accompanied by a description of what aspect of the algorithm they control, and any validation rules that apply to the key.

Note that some keys are always required, and that there are dependencies between keys. For examples, if you specify that the separator symbol should be chosen at random, you must also specify an alphabet from which the symbol should be randomly chosen.

PRESETS

Below is a list of all the presets defined by this module.

This information can be accessed programatically using the functions defined_presets(), presets_to_string(), preset_description(), and preset_config().

FUNCTIONAL INTERFACE

Although the package was primarily designed to be used in an object-oriented way, there is a functional interface too. The functional interface initialises an object internally and then uses that object to generate a single password. If you only need one password, this is no less efficient than the object-oriented interface, however, if you are generating multiple passwords it is much less efficient.

There is only a single function exported by the module:

hsxkpasswd()

    my $password = hsxkpasswd();
    

This function call is equivalent to the following Object-Oriented code:

    my $password =  HSXKPasswd->new()->password();
    

This function passes all arguments it receives through to the constructor, so all arguments that are valid in new() are valid here.

This function Croaks if there is a problem generating the password.

Note that it is inefficient to use this function to generate multiple passwords because the dictionary will be re-loaded, and the entropy stats re-calculated each time the function is called.

CONSTRUCTOR

    # create a new instance with the default dictionary, config, and random
    # number generator
    my $hsxkpasswd_instance = Crypt::HSXKPasswd->new();
    
    # the constructor takes optional named arguments, these can be used to
    # customise the word source, config, and random number source.
    
    # create an instance that uses the UNIX words file as the word source
    my $hsxkpasswd_instance = HSXKPasswd->new(
        dictionary => Crypt::HSXKPasswd::Dictionary::System->new()
    );
    
    # create an instance that uses an array reference as the word source
    my $hsxkpasswd_instance = HSXKPasswd->new(dictionary_list => $array_ref);
    
    # create an instance that uses a dictionary file as the word source
    my $hsxkpasswd_instance = HSXKPasswd->new(
        dictionary_file => 'sample_dict_EN.txt'
    );
    
    # the class Crypt::HSXKPasswd::Dictionary::Basic can be used to aggregate
    # multiple array refs and/or dictionary files into a single word source
    my $dictionary = Crypt::HSXKPasswd::Dictionary::Basic->new();
    $dictionary->add_words('dict1.txt');
    $dictionary->add_words('dict2.txt');
    $dictionary->add_words($array_ref);
    my $hsxkpasswd_instance = HSXKPasswd->new(dictionary => $dictionary);
    
    # create an instance from the preset 'XKCD'
    my $hsxkpasswd_instance = HSXKPasswd->new(preset => 'XKCD');
    
    # create an instance based on the preset 'XKCD' with one customisation
    my $hsxkpasswd_instance = HSXKPasswd->new(
        preset => 'XKCD',
        preset_override => {separator_character => q{ }}
    );
    
    # create an instance from a config based on a preset
    # but with many alterations
    my $config = HSXKPasswd->preset_config('XKCD');
    $config->{separator_character} = q{ };
    $config->{case_transform} = 'INVERT';
    $config->{padding_type} = "FIXED";
    $config->{padding_characters_before} = 1;
    $config->{padding_characters_after} = 1;
    $config->{padding_character} = '*';
    my $hsxkpasswd_instance = HSXKPasswd->new(config => $config);
    
    # create an instance from an entirely custom configuration
    my $config = {
        padding_alphabet => [qw{! @ $ % ^ & * + = : ~ ?}],
        separator_alphabet => [qw{- + = . _ | ~}],
        word_length_min => 6,
        word_length_max => 6,
        num_words => 3,
        separator_character => 'RANDOM',
        padding_digits_before => 2,
        padding_digits_after => 2,
        padding_type => 'FIXED',
        padding_character => 'RANDOM',
        padding_characters_before => 2,
        padding_characters_after => 2,
        case_transform => 'CAPITALISE',
    }
    my $hsxkpasswd_instance = HSXKPasswd->new(config => $config);
    
    # create an instance from an entire custom config passed as a JSON string
    # a convenient way to use configs generated using the web interface at
    # https://xkpasswd.net
    my $config = <<'END_CONF';
    {
     "num_words": 4,
     "word_length_min": 4,
     "word_length_max": 8,
     "case_transform": "RANDOM",
     "separator_character": " ",
     "padding_digits_before": 0,
     "padding_digits_after": 0,
     "padding_type": "NONE",
    }
    END_CONF
    my $hsxkpasswd_instance = HSXKPasswd->new(config_json => $config);
    
    # create an instance which uses /dev/urandom as the RNG
    # (only possible on Linux/Unix only systems)
    my $hsxkpasswd_instance = HSXKPasswd->new(
        rng => Crypt::HSXKPasswd::RNG::DevUrandom->new();
    );
    
    # create an instance which uses Random.Org as the random number generator
    # NOTE - this should be used sparingly, and only by the paranoid. If you
    # abuse this RNG your IP will get blacklisted on Random.Org. You must pass
    # a valid email address to the constructor for
    # Crypt::HSXKPasswd::RNG::RandomDorOrg because Random.Org's usage
    # guidelines request that all invocations to their API contain a contact
    # email in the useragent header, and this module honours that request.
    my $hsxkpasswd_instance = HSXKPasswd->new(
        rng => Crypt::HSXKPasswd::RNG::RandomDorOrg->new('your.email@addre.ss');
    );

The constructor must be called via the package name.

If called with no arguments the constructor will use an instance of Crypt::HSXKPasswd::Dictionary::EN as the word source, the preset DEFAULT, and an instance of the class Crypt::HSXKPasswd::RNG::Basic to generate random numbers.

The function accepts named arguments to allow for custom specification of the word source, config, and random number source.

Specifying Custom Word Sources

Three named arguments can be used to specify a word source, but only one should be specified at a time. If multiple are specified, the one with the highest priority will be used, and the rest ignored. The variables are listed below in descending order of priority:

Specifying Custom Password Generator Configurations

Two primary named arguments can be used to specify the config the instance should use to generate passwords. Only one should be specified at a time. If multiple are specified, the one with the highest priority will be used, and the rest ignored. The variables are listed below in descending order of priority:

Specifying Custom Random Number Generators

A custom RNG can be specified using the named argument rng. The passed value must be an instance of a class that extends Crypt::HSXKPasswd::RNG and overrides the function random_numbers().

INSTANCE METHODS

NOTE - all instance methods must be invoked on a Crypt::HSXKPasswd object or they will croak.

->config()

    my $config = $hsxkpasswd_instance->config(); # getter
    $hsxkpasswd_instance->config($config_hashref); # setter
    $hsxkpasswd_instance->config($config_json_string); # setter

When called with no arguments the function returns a clone of the instance's config hashref.

When called with a single argument the function sets the config of the instance to a clone of the passed config. If present, the argument must be either a hashref containing valid config keys and values, or a JSON string representing a hashref containing valid config keys and values.

The function will croak if an invalid config is passed.

->config_as_json()

    my $config_json_string = $hsxkpasswd_instance->config_as_json();
    

This function returns the content of the instance's loaded config hashref as a JSON string.

The output from this function can be loaded into the web interface at https://xkpasswd.net (using the load/save tab).

->config_as_string()

    my $config_string = $hsxkpasswd_instance->config_as_string();
    

This function returns the content of the instance's loaded config hashref as a scalar string.

->dictionary()

    my $dictionary_clone = $hsxkpasswd_instance->dictionary();
    $hsxkpasswd_instance->dictionary($dictionary_instance);
    $hsxkpasswd_instance->dictionary($array_ref);
    $hsxkpasswd_instance->dictionary('sample_dict_EN.txt');
    $hsxkpasswd_instance->dictionary('sample_dict_EN.txt', 'Latin1');
    

When called with no arguments this function returns a clone of the currently loaded dictionary which will be an instance of a class that extends Crypt::HSXKPasswd::Dictionary.

To load a new dictionary into an instance, call this function with arguments. The first argument argument can be an instance of a class that extends Crypt::HSXKPasswd::Dictionary, a reference to an array of words, or the path to a dictionary file. If either an array reference or a file path are passed, they will be used to instantiate an instance of the class Crypt::HSXKPasswd::Dictionary::Basic, and that new instance will then be loaded into the object. If a file path is passed, it will be assumed to be UTF-8 encoded. If not, an optional second argument can be passed to specify the file's encoding.

->password()

    my $password = $hsxkpasswd_instance->password();
    

This function generates a random password based on the instance's loaded config and returns it as a scalar. The function takes no arguments.

The function croaks if there is an error generating the password. The most likely cause of and error is the random number generation, particularly if the loaded random generation function relies on a cloud service or a non-standard library.

->passwords()

    my @passwords = $hsxkpasswd_instance->passwords(10);
    

This function generates a number of passwords and returns them all as an array.

The function uses password() to generate the passwords, and hence will croak if there is an error generating any of the requested passwords.

->passwords_json()

    my $json_string = $hsxkpasswd_instance->passwords_json(10);
    

This function generates a number of passwords and returns them and the instance's entropy stats as a JSON string representing a hashref containing an array of passwords indexed by passwords, and a hashref of entropy stats indexed by stats. The stats hashref itself is indexed by: password_entropy_blind, password_permutations_blind, password_entropy_blind_min, password_entropy_blind_max, password_permutations_blind_max, password_entropy_seen & password_permutations_seen.

The function uses passwords() to generate the passwords, and hence will croak if there is an error generating any of the requested passwords.

->rng()

    my $rng_instance = $hsxkpasswd_instance->rng();
    $hsxkpasswd_instance->rng($rng_instance);
    

When called with no arguments this function returns currently loaded Random Number Generator (RNG) which will be an instance of a class that extends Crypt::HSXKPasswd::RNG.

To load a new RNG into an instance, call this function with a single argument, an instance of a class that extends Crypt::HSXKPasswd::RNG.

->stats()

    my %stats = $hsxkpasswd_instance->stats();
    

This function generates a hash containing stats about the instance indexed by the following keys:

->status()

    print $hsxkpasswd_instance->status();
    

Generates a string detailing the internal status of the instance. Below is a sample status string:

    *DICTIONARY*
    Source: Crypt::HSXKPasswd::Dictionary::EN
    # words: 1425
    # words of valid length: 1194 (84%)
    
    *CONFIG*
    case_transform: 'ALTERNATE'
    num_words: '3'
    padding_character: 'RANDOM'
    padding_characters_after: '2'
    padding_characters_before: '2'
    padding_digits_after: '2'
    padding_digits_before: '2'
    padding_type: 'FIXED'
    separator_alphabet: ['!', '$', '%', '&', '*', '+', '-', '.', '/', ':', ';', '=', '?', '@', '^', '_', '|', '~']
    separator_character: 'RANDOM'
    symbol_alphabet: ['!', '$', '%', '&', '*', '+', '-', '.', '/', ':', ';', '=', '?', '@', '^', '_', '|', '~']
    word_length_max: '8'
    word_length_min: '4'
    
    *RANDOM NUMBER CACHE*
    Random Number Generator: Crypt::HSXKPasswd::RNG::Basic
    # in cache: 0
    
    *PASSWORD STATISTICS*
    Password length: between 24 & 36
    Permutations (brute-force): between 2.91x10^47 & 1.57x10^71 (average 2.14x10^59)
    Permutations (given dictionary & config): 5.51x10^15
    Entropy (Brute-Force): between 157bits and 236bits (average 197bits)
    Entropy (given dictionary & config): 52bits
    # Random Numbers needed per-password: 9
    Passwords Generated: 0

->update_config()

    $hsxkpasswd_instance->update_config({separator_character => '+'});
    

The function updates the config within an HSXKPasswd instance. A hashref with the config options to be changed must be passed. The function returns a reference to the instance to enable function chaining. The function will croak if the updated config would be invalid in some way. Note that if this happens the running config will not have been altered in any way.

CLASS METHODS

NOTE - All class methods must be invoked via the package name, or they will croak.

clone_config()

    my $clone = Crypt::HSXKPasswd->clone_config($config);
    

This function must be passed a valid config hashref as the first argument or it will croak. The function returns a hashref.

config_key_definition()

    my %key_definition = Crypt::HSXKPasswd->config_key_definition($key_name);
    

A function to return the definition for a config key. The definition is returned as a hash indexed by the following keys:

config_key_definitions()

    my %key_definitions = Crypt::HSXKPasswd->config_key_definitions();
    

A function to return definitions for all defined config keys as a hash indexed by config key names. Each definition is represented as a hash with the same keys as the hashes returned by the function config_key_definition().

config_stats()

    my %stats = Crypt::HSXKPasswd->config_stats($config);
    my %stats = Crypt::HSXKPasswd->config_stats(
        $config,
        suppress_warnings => 1,
    );
    

This function requires one argument, a valid config hashref. It returns a hash of statistics about a given configuration. The hash is indexed by the following:

There is one scenario in which the calculated maximum length will not be reliably accurate, and that's when a character substitution with a length greater than 1 is specified, and padding_type is not set to ADAPTIVE. If the config passed contains such a character substitution, the length will be calculated ignoring the possibility that one or more extra characters could be introduced depending on how many, if any, of the long substitutions get triggered by the randomly chosen words. If this happens the function will also carp with a warning. Such warnings can be suppressed by passing an optional named argument suppress_warnings with the value 1.

config_to_json()

    my $config_json_string = Crypt::HSXKPasswd->config_to_json($config);
    

This function returns a JSON representation of the passed config hashref as a scalar string.

The function must be passed a valid config hashref or it will croak.

config_to_string()

    my $config_string = Crypt::HSXKPasswd->config_to_string($config);
    

This function returns the content of the passed config hashref as a scalar string. The function must be passed a valid config hashref or it will croak.

default_config()

    my $config = Crypt::HSXKPasswd->default_config();

This function returns a hashref containing a config with default values.

This function can optionally be called with a single argument, a hashref containing keys with values to override the defaults with.

    my $config = Crypt::HSXKPasswd->default_config({num_words => 3});
    

When overrides are present, the function will carp if an invalid key or value is passed, and croak if the resulting merged config is invalid.

This function is a shortcut for preset_config(), and the two examples above are equivalent to the following:

    my $config = Crypt::HSXKPasswd->preset_config('DEFAULT');
    my $config = Crypt::HSXKPasswd->preset_config('DEFAULT', {num_words => 3});

defined_config_keys()

    my @config_key_names = Crypt::HSXKPasswd->defined_config_keys();
    

This function returns the list of valid config key names as an array of strings.

defined_presets()

    my @preset_names = Crypt::HSXKPasswd->defined_presets();
    

This function returns the list of defined preset names as an array of strings.

distil_to_config_keys()

    my $dist_hashref = Crypt::HSXKPasswd->distil_to_config_keys($hashref);
    

This function takes a hashref as an argument, and returns a deep clone of that hashref containing only valid config keys with valid values.

By default the function silently drops keys that are not valid config keys, but issues a warning when dropping a key that is a valid config key, but contains an invalid value. The function can also issue warnings when dropping keys that are not valid config keys.

The warnings can be controlled with a pair of optional named arguments that can be added as a second argument:

    # suppress all warnings
    my $dist_hashref = Crypt::HSXKPasswd->distil_to_config_keys(
        $hashref,
        suppress_warnings => 1,
    );
    
    # emit warnings when dropping invalidly named keys
    my $dist_hashref = Crypt::HSXKPasswd->distil_to_config_keys(
        $hashref,
        warn_invalid_key_names => 1,
    );

distil_to_symbol_alphabet()

    my @unique_syms = Crypt::HSXKPasswd->distil_to_symbol_alphabet($arrayref);
    my @unique_syms = Crypt::HSXKPasswd->distil_to_symbol_alphabet(
        $arrayref,
        warn => 1,
    );
    

This function takes reference to an array of strings and returns a new array containing all the valid symbols from the referenced array. The valid symbols are de-duplicated before being returned.

By default the function silently skips over strings that are not valid symbols. The function can be made issue warnings each time a string is skipped by passing a named argument warn with a value of 1 (0 can also be passed to explicitly disable warnings).

distil_to_words()

    my @valid_unique_words = Crypt::HSXKPasswd->distil_to_words($arrayref);
    my @valid_unique_words = Crypt::HSXKPasswd->distil_to_words(
        $arrayref,
        warn => 1,
    );
    

This function takes reference to an array of strings and returns a new array containing all the valid words from the referenced array. The valid words are de-duplicated before being returned.

By default the function silently skips over strings that are not valid words. The function can be made issue warnings each time a string is skipped by passing a named argument warn with a value of 1 (0 can also be passed to explicitly disable warnings).

is_valid_config()

    # determine the validity
    my $is_ok = Crypt::HSXKPasswd->is_valid_config($config);
    
    # assert the validity - will croak if the config is invalid
    Crypt::HSXKPasswd->is_valid_config($config, croak => 1);
    

This function must be passed a hashref to test as the first argument. The function returns 1 if the passed config is valid, and 0 otherwise.

Optionally, a named argument croak can also be passed to control whether or not the function should croak if the config is invalid. The value of this named argument should be 1 or 0.

When calling the function with croak set to 1, the message thrown by croak will explain why the config is invalid.

    use English qw( -no_match_vars );
    eval{
        Crypt::HSXKPasswd->is_valid_config($config, croak => 1);
    }or do{
        print "ERROR - config is invalid because: $EVAL_ERROR\n";
    }
    

module_config()

    my $debug_val = Crypt::HSXKPasswd->module_config('DEBUG'); # getter
    Crypt::HSXKPasswd->module_config('DEBUG', 1); # setter
    

This function is used to access or alter the value of one of the module configuration settings. The first function must always be a valid module configuration key name. If no second argument is provided, the value stored in the module configuration key will not be updated. To update the stored value, pass a new value as a second argument. Regardless of whether or not a second argument is passed, the value stored in the module configuration key is always returned.

The function will croak if called with an invalid module configuration key name, or passed an invalid new value.

For a list of the module configuration keys, see the MODULE CONFIGURATION section of this document.

preset_config()

    my $config = Crypt::HSXKPasswd->preset_config('XKCD');
    

This function returns the config hashref for a given preset. See above for the list of available presets.

The first argument this function accepts is the name of the desired preset as a scalar. If an invalid name is passed, the function will carp. If no preset is passed the preset DEFAULT is assumed.

This function can optionally accept a second argument, a hashref containing keys with values to override the defaults with.

    my $config = Crypt::HSXKPasswd->preset_config(
        'XKCD',
        {case_transform => 'INVERT'}
    );
    

When overrides are present, the function will carp if an invalid key or value is passed, and croak if the resulting merged config is invalid.

preset_definition()

    my %preset_def = Crypt::HSXKPasswd->preset_definition('XKCD');
    

This function returns a hash defining a preset. The hash contains an English description of the preset indexed be description and a config hashref indexed by config.

The function expects to be called with one argument, a valid preset name, but it can be called without arguments, in which case it will return the definition for the preset c<DEFAULT>.

You can see all the defined presets in the PRESETS section of this document, and you can get a list of valid preset names programatically with the function defined_presets().

preset_definitions()

    my %preset_defs = Crypt::HSXKPasswd->preset_definitions();
    

This function returns a hash of all defined presets indexed by preset name. Each preset definition is a hash as returned by preset_definition().

This function does not take any arguments.

presets_json()

    my $json_string = Crypt::HSXKPasswd->presets_json();
    

This function returns a JSON string representing all the defined configs, including their descriptions.

The returned JSON string represents a hashref indexed by three keys: defined_keys contains an array of preset identifiers, presets contains the preset configs indexed by reset identifier, and preset_descriptions contains a hashref of descriptions indexed by preset identifiers.

preset_description()

    my $description = Crypt::HSXKPasswd->preset_description('XKCD');
    

This function returns the description for a given preset. See above for the list of available presets.

The first argument this function accepts is the name of the desired preset as a scalar. If an invalid name is passed, the function will carp. If no preset is passed the preset DEFAULT is assumed.

presets_to_string()

    print Crypt::HSXKPasswd->presets_to_string();
    

This function returns a string containing a description of each defined preset and the configs associated with the presets.

COMMANDLINE INTERFACE

The module ships with a commandline interface to this library, simply called hsxkpasswd.

This interface allows for the generation of multiple passwords at a time, the use of presets and preset overrides, the use of custom password generator configurations, the use of custom word sources, and the use of custom random number generators.

Both preset overrides and password generator configurations must be specified in JSON format.

Examples

Generate a single password using all the default settings:

    hsxkpasswd
    

Generate five passwords using the default settings:

    hsxkpasswd 5
    

Generate five passwords using the XKCD preset:

    hsxkpasswd -p XKCD 5
    

Generate five passwords using the XKCD preset with an overridden password generator configuration key:

    hsxkpasswd -p XKCD -o '{"separator_character" : "*"}' 5

Generate five passwords using a custom password generator configuration stored in a text file in JSON format:

    hsxkpasswd -c my_config.json
    

Further Reading

The examples above are just a sample of what the command can do, for complete documentation, run the command with the -h flag:

    hsxkpasswd -h
    

If you are new to JSON, you may find the following links useful:

ENTROPY CHECKING

For security reasons, this module's default behaviour is to warn (using carp()) when ever the loaded combination of word source and configuration would result in low-entropy passwords. When the constructor is invoked, or when an instance's the word source or config are altered (using dictionary() or config()), the entropy is re-calculated and re-checked against the defined minima.

Entropy is calculated and checked for two scenarios. Firstly, for the best-case scenario, when an attacker has no prior knowledge about the password, and must resort to a brute-force attack. And secondly, for the worst-case scenario, when the attacker is assumed to know that this module was used to generate the password, and, that the attacker has a copy of the word source and config settings used to generate the password.

Entropy checking is controlled via three module configuration variables (which can be accessed and updated using the function module_config()):

Caveats

The entropy calculations make some assumptions which may in some cases lead to the results being inaccurate. In general, an attempt has been made to always round down, meaning that in reality the entropy of the produced passwords may be higher than the values calculated by the package.

When calculating the entropy for brute force attacks on configurations that can result in variable length passwords, the shortest possible password is assumed.

When calculating the entropy for brute force attacks on configurations that contain at least one symbol, it is assumed that an attacker would have to brute-force-check 33 symbols. This is the same value used by Steve Gibson's Password Haystacks calculator (https://www.grc.com/haystack.htm).

When calculating the entropy for worst-case attacks on configurations that contain symbol substitutions where the replacement is more than 1 character long the possible extra length is ignored.

WORD SOURCES (DICTIONARIES)

The abstract class Crypt::HSXKPasswd::Dictionary acts as a base class for sources of words for use by this module. Word sources should extend this base class and implement the function word_list(), which should return an array of words.

In order to produce secure passwords it's important to use a word source that contains a large selection of words with a good mix of different lengths of words.

The module ships with a number of pre-defined word sources:

Crypt::HSXKPasswd::Dictionary::DE

A German word list based on the GPL-licensed German dictionary for WinEdit by Juergen Vierheilig.

Note: This module is licensed under the GPL, not the BSD license used for the majority of this project.

Crypt::HSXKPasswd::Dictionary::EN

A default word list consisting of English words and place names.

Crypt::HSXKPasswd::Dictionary::ES

A Spanish word list based on the BSD-licensed Spanish dictionary for WinEdit by Juan L. Varona from the Universidad de La Rioja.

Crypt::HSXKPasswd::Dictionary::FR

A French word list based on the GPL-licensed French dictionary for WinEdit.

Note: This module is licensed under GPL V2, not the BSD license used for the majority of this project.

Crypt::HSXKPasswd::Dictionary::IT

An Italian word list based on the free-for-non-commerical-use Italian dictionary for WinEdit by Karl Koeller.

Note: This module is licensed under GPL V2, not the BSD license used for the majority of this project.

Crypt::HSXKPasswd::Dictionary::NL

A Dutch/Flemish word list based on the GPL-licensed Dutch dictionary for WinEdit.

Note: This module is licensed under GPL V2, not the BSD license used for the majority of this project.

Crypt::HSXKPasswd::Dictionary::PT

A Portuguese word list based on the GPL-licensed Portuguese dictionary for WinEdit compiled by Bernhard Enders (building on work by Raimundo Santos Moura & Ricardo Ueda Karpischek).

Note: This module is licensed under GPL V2.1, not the BSD license used for the majority of this project.

Crypt::HSXKPasswd::Dictionary::System

This class tries to find and use a Unix words file on the system.

The constructor croaks if no system words file can be found.

Usage

    my $word_source = Crypt::HSXKPasswd::Dictionary::System->new();

Crypt::HSXKPasswd::Dictionary::Basic

This class can be initialised from a words file, or from an array ref containing words.

Usage

    my $word_source = Crypt::HSXKPasswd::Dictionary::Basic->new('file_path');
    my $word_source = Crypt::HSXKPasswd::Dictionary::Basic->new(
        'file_path',
        'Latin1'
    );
    my $word_source = Crypt::HSXKPasswd::Dictionary::Basic->new($array_ref);

The rules for the formatting of dictionary files are simple. Dictionary files must contain one word per line. Words shorter than four letters will be ignored, as will all lines starting with the # symbol. Files are assumed to be UTF-8 encoded, but an optional second argument can be passed specifying a different file encoding.

This format is the same as that of the standard Unix Words file, usually found at /usr/share/dict/words on Unix and Linux operating systems (including OS X).

RANDOM NUMBER SOURCES

In order to minimise the number of non-standard modules this module requires, the default source of randomness is Perl's built-in rand() function. This provides a reasonable level of randomness, and should suffice for most users, however, some users will prefer to make use of one of the many advanced randomisation modules in CPAN, or, reach out to a web service like http://random.org for their random numbers. To facilitate both of these options, this module uses a cache of randomness, and provides an abstract Random Number Generator (RNG) class that can be extended.

The module can use an instance of any class that extends Crypt::HSXKPasswd::RNG as it's source of randomness. Custom RNG classes must implement the method random_numbers() which will be invoked on an instance of the class and passed one argument, the number of random numbers required to generate a single password. The function must return an array of random numbers between 0 and 1. The number of random numbers returned is entirely up to the module to decide. The number required for a single password is passed purely as a guide. The function must always return at least one random number.

The module ships with five standard RNGs (described below).

By default, the module will try to use one of the following four RNGs, listed from most to least preferred, depending on what is available on the system:

  1. Crypt::HSXKPasswd::RNG::Math_Random_Secure (only available if Math::Random::Secure is installed on the system).

  2. Crypt::HSXKPasswd::RNG::Data_Entropy (only available if Data::Entropy::Algorithms is installed on the system).

  3. Crypt::HSXKPasswd::RNG::DevUrandom (only available on Linux/Unix systems with a /dev/urandom).

  4. Crypt::HSXKPasswd::RNG::Basic (available on all systems because it uses Perl's built-in rand() function).

If the constructor is called without specifying an RNG, and if the only available RNG is Crypt::HSXKPasswd::RNG::Basic, a warning will be thrown suggesting installing Math::Random::Secure or Data::Entropy::Algorithms.

The module also ships with a fifth RNG, Crypt::HSXKPasswd::RNG::RandomDotOrg, but this one must be explicitly used, the constructor will never used it by default. As its name suggests, this class uses http://Random.Org/'s HTTP API to generate random numbers.

To explicitly use any particular RNG, create an instance of it, and either pass that instance to the constructor with the named argument rng, or, set the RNG after instantiating the object using the rng() function.

Crypt::HSXKPasswd::RNG::Math_Random_Secure

    my $rng = Crypt::HSXKPasswd::RNG::Math_Random_Secure->new();

This is the preferred RNG because it is both fast and secure, but, it requires the non-standard module Math::Random::Secure (http://search.cpan.org/perldoc?Math%3A%3ARandom%3A%3ASecure) be installed.

Crypt::HSXKPasswd::RNG::Data_Entropy

    my $rng = Crypt::HSXKPasswd::RNG::Data_Entropy->new();

This RNG is secure, but it is quite slow (about six times slower than Crypt::HSXKPasswd::RNG::Math_Random_Secure), and it requires the non-standard module Data::Entropy::Algorithms (http://search.cpan.org/perldoc?Data%3A%3AEntropy%3A%3AAlgorithms) be installed.

Crypt::HSXKPasswd::RNG::DevUrandom

    my $rng = Crypt::HSXKPasswd::RNG::DevUrandom->new();
    

This RNG is secure and relatively fast (faster than Crypt::HSXKPasswd::RNG::Data_Entropy but slower than Crypt::HSXKPasswd::RNG::Math_Random_Secure), but is only available on Linux/Unix systems with a /dev/urandom special file.

Crypt::HSXKPasswd::RNG::Basic

    my $rng = Crypt::HSXKPasswd::RNG::Basic->new();
    

This RNG uses Perl's built-in rand() function as its source of randomness, and this is sub-optimal. The Perl docs warn that rand() is not a particularly good source of random numbers, and advises against its use for cryptography.

This RNG provides a base-line, and should only be used if none of the better RNGs are available. While it is sub-optimal, it will still generate passwords with sufficient entropy in most situations. Ultimately, even using this imperfect RNG, this module will still produce passwords that are much better than those produced by the human imagination!

Crypt::HSXKPasswd::RNG::RandomDotOrg

    my $rng = Crypt::HSXKPasswd::RNG::RandomDotOrg->new('my.address@my.dom');
    my $rng = Crypt::HSXKPasswd::RNG::RandomDotOrg->new('my.address@my.dom',
        timeout => 180,
        num_passwords => 3,
    );

This RNG serves as a usable example of an RNG that queries a web service. As its name suggests, this class uses http://Random.Org/'s HTTP API to generate random numbers.

In order to comply with Random.Org's client guidelines (https://www.random.org/clients/), this module requires that a valid email address be passed as the first argument.

The client guidelines also request that clients use long timeouts, and batch their requests. They prefer to be asked for more number less frequently than less numbers more frequently. For this reason the class's default behaviour is to use a timeout of 180 seconds, and to request enough random numbers to generate three passwords at a time.

These defaults can be overridden by passing named arguments to the constructor after the email address. The following named arguments are supported:

num_passwords and num_absolute should not be used together, but if they are, num_absolute use used, and num_passwords is ignored.

This class requires a number of modules not used by any other classes under Crypt::HSXKPasswd, and not listed in that module's requirements. If all of the following modules are not installed, the constructor will croak:

DIAGNOSTICS

By default this module does all of it's error notification via the functions carp(), croak(), and confess() from the Carp module. Optionally, all error messages can also be printed to a stream. To enable the printing of messages, set the LOG_ERRORS module configuration variable to 1. All error messages will then be printed to the stream defined by the module configuration variable LOG_STREAM, which is set to STDERR by default.

Ordinarily this module produces very little output. To enable more verbose output the module configuration variable DEBUG can be set to 1. Debug message are printed to the stream specified by the module variable LOG_STREAM.

This module produces output at three severity levels:

The value stored in a module configuration variable can be accessed and updated using the function module_config().

CONFIGURATION AND ENVIRONMENT

This module does not currently support configuration files, nor does it currently interact with the environment. It may do so in future versions.

DEPENDENCIES

This module requires the following Perl modules:

The module can also optionally use the following Perl modules:

INCOMPATIBILITIES

This module has no known incompatibilities.

BUGS AND LIMITATIONS

There are no known bugs in this module.

Please report any bugs you may find on the module's GitHub page: https://github.com/bbusschots/xkpasswd.pm.

LICENCE AND COPYRIGHT

Copyright (c) 2014-15, Bart Busschots T/A Bartificer Web Solutions All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The following components of this package are covered by the more restrictive GPL V2 license https://www.gnu.org/licenses/gpl-2.0.html:

AUTHOR

Bart Busschots (mailto:bart@bartificer.net)