1package Crypt::HSXKPasswd; 2 3# import required modules 4use strict; 5use warnings; 6use Carp; # for nicer 'exception' handling for users of the module 7use Fatal qw( :void open close binmode ); # make builtins throw exceptions on failure 8use English qw( -no_match_vars ); # for more readable code 9use Scalar::Util qw( blessed ); # for checking if a reference is blessed 10use Math::Round; # for round() 11use Math::BigInt; # for the massive numbers needed to store the permutations 12use Clone qw( clone ); # for cloning nested data structures - exports clone() 13use Readonly; # for truly constant constants 14use JSON; # for dealing with JSON strings 15use List::MoreUtils qw( uniq ); # for array deduplication 16use Type::Tiny; # for generating anonymous type constraints when needed 17use Type::Params qw( compile multisig ); # for parameter validation with Type::Tiny objects 18use Types::Standard qw( slurpy :types ); # for basic type checking (Int Str etc.) 19use Crypt::HSXKPasswd::Types qw( :types ); # for custom type checking 20use Crypt::HSXKPasswd::Helper; # exports utility functions like _error & _warn 21use Crypt::HSXKPasswd::Dictionary::Basic; 22use Crypt::HSXKPasswd::RNG::Math_Random_Secure; 23use Crypt::HSXKPasswd::RNG::Data_Entropy; 24use Crypt::HSXKPasswd::RNG::DevUrandom; 25use Crypt::HSXKPasswd::RNG::Basic; 26 27# set things up for using UTF-8 28use 5.016; # min Perl for good UTF-8 support, implies feature 'unicode_strings' 29use Encode qw( encode decode ); 30use Text::Unidecode; # for stripping accents from accented characters 31use utf8; 32binmode STDOUT, ':encoding(UTF-8)'; 33 34# import (or not) optional modules 35eval{ 36 # the default dicrionary may not have been geneated using the Util module 37 require Crypt::HSXKPasswd::Dictionary::EN; 38}or do{ 39 carp('WARNING - failed to load Crypt::HSXKPasswd::Dictionary::EN'); 40}; 41 42## no critic (ProhibitAutomaticExportation); 43use base qw( Exporter ); 44our @EXPORT = qw( hsxkpasswd ); 45## use critic 46 47# Copyright (c) 2015, Bart Busschots T/A Bartificer Web Solutions All rights 48# reserved. 49# 50# Code released under the FreeBSD license (included in the POD at the bottom of 51# this file) 52 53#============================================================================== 54# Code 55#============================================================================== 56 57# 58# === Constants and Package Variables =========================================# 59# 60 61# version info 62use version; our $VERSION = qv('3.6'); 63 64# entropy control variables 65my $_ENTROPY_MIN_BLIND = 78; # 78 bits - equivalent to 12 alpha numeric characters with mixed case and symbols 66my $_ENTROPY_MIN_SEEN = 52; # 52 bits - equivalent to 8 alpha numeric characters with mixed case and symbols 67my $_ENTROPY_WARNINGS = 'ALL'; # valid values are 'ALL', 'BLIND', or 'NONE' (invalid values treated like 'ALL') 68 69# utility constants 70Readonly my $_CLASS => __PACKAGE__; 71Readonly my $_TYPES_CLASS => 'Crypt::HSXKPasswd::Types'; 72Readonly my $_DICTIONARY_BASE_CLASS => 'Crypt::HSXKPasswd::Dictionary'; 73Readonly my $_RNG_BASE_CLASS => 'Crypt::HSXKPasswd::RNG'; 74 75# 76# Constructor ----------------------------------------------------------------- 77# 78 79#####-SUB-###################################################################### 80# Type : CONSTRUCTOR (CLASS) 81# Purpose : Instantiate an object of type XKPasswd 82# Returns : An object of type XKPasswd 83# Arguments : This function accepts the following named arguments - all 84# optional: 85# dictionary - an object that inherits from 86# Crypt::HSXKPasswd::Dictionary 87# dictionary_list - an array ref of words to use as a dictionary. 88# dictionary_file - the path to a dictionary file. 89# dictionary_file_encoding - the encoding to use when loading the 90# dictionary file, defaults to UTF-8. 91# preset - the preset to use 92# preset_overrides - a hashref of config options to override. 93# Ignored unless preset is set, and in use. 94# config - a config hashref. 95# config_json - a config as a JSON string (requires that the JSON 96# module be installed) 97# rng - an object that inherits from Crypt::HSXKPasswd::RNG 98# Throws : Croaks if the function is called in an invalid way, called with 99# invalid args, or called with a JSON string when JSON is not 100# installed. 101# Notes : The order of preference for word sources is dictionary, then 102# dictionary_list, then dictionary_file. If none are specified, 103# then an instance of Crypt::HSXKPasswd::Dictionary::EN will be 104# used. 105# The order of preference for the configuration source is config 106# then config_json, then preset. If no configuration source is 107# specified, then the preset 'DEFAULT' is used. 108# If no RNG is passed, _best_available_rng() will be used to 109# instantiate and instance of the most secure RNG usable on the 110# system. 111# See Also : For valid configuarion options see POD documentation below 112sub new{ 113 my @args = @_; 114 my $class = shift @args; 115 _force_class($class); 116 117 # validate args 118 state $args_check = compile(slurpy Dict[ 119 dictionary => Optional[InstanceOf[$_DICTIONARY_BASE_CLASS]], 120 dictionary_list => Optional[ArrayRef[Str]], 121 dictionary_file => Optional[Str], 122 dictionary_file_encoding => Optional[Str], 123 config => Optional[Config], 124 config_json => Optional[Str], 125 preset => Optional[PresetName], 126 preset_overrides => Optional[ConfigOverride], 127 rng => Optional[InstanceOf[$_RNG_BASE_CLASS]], 128 ]); 129 my ($options) = $args_check->(@args); 130 131 # set defaults 132 $options->{dictionary_file_encoding} = 'UTF-8' unless $options->{dictionary_file_encoding}; 133 134 135 # before going any further, check the presets and key definitions if debugging (doing later may cause an error before we test) 136 if($_CLASS->module_config('DEBUG')){ 137 $_CLASS->_check_config_key_definitions(); 138 $_CLASS->_check_preset_definitions(); 139 } 140 141 # process the word source 142 my $dictionary; 143 if($options->{dictionary}){ 144 $dictionary = $options->{dictionary}; 145 }elsif($options->{dictionary_list}){ 146 $dictionary = Crypt::HSXKPasswd::Dictionary::Basic->new($options->{dictionary_list}); 147 }elsif($options->{dictionary_file}){ 148 $dictionary = Crypt::HSXKPasswd::Dictionary::Basic->new($options->{dictionary_file}, $options->{dictionary_file_encoding}); 149 }else{ 150 $dictionary = Crypt::HSXKPasswd::Dictionary::EN->new(); 151 } 152 153 # process the config source 154 my $config = {}; 155 if($options->{config}){ 156 $config = $options->{config}; 157 }elsif($options->{config_json}){ 158 $config = $options->{config_json}; # pass the string on, config() will deal with it 159 }elsif($options->{preset}){ 160 $config = $_CLASS->preset_config($options->{preset}, $options->{preset_overrides}); 161 }else{ 162 $config = $_CLASS->default_config(); 163 } 164 165 # process the random number source 166 my $rng = {}; 167 if($options->{rng}){ 168 $rng = $options->{rng}; 169 }else{ 170 $rng = $_CLASS->_best_available_rng(); 171 } 172 173 # initialise the object 174 my $instance = { 175 # 'public' instance variables (none so far) 176 # 'PRIVATE' internal variables 177 _CONFIG => {}, 178 _DICTIONARY_SOURCE => {}, # the dictionary object to source words from 179 _RNG => {}, # the random number generator 180 _CACHE_DICTIONARY_FULL => [], # a cache of all words found in the dictionary file 181 _CACHE_DICTIONARY_LIMITED => [], # a cache of all the words found in the dictionary file that meet the length criteria 182 _CACHE_CONTAINS_ACCENTS => 0, # a cache of whether or not the filtered word list contains words with accented letters 183 _CACHE_ENTROPYSTATS => {}, # a cache of the entropy stats for the current combination of dictionary and config 184 _CACHE_RANDOM => [], # a cache of random numbers (as floating points between 0 and 1) 185 _PASSWORD_COUNTER => 0, # the number of passwords this instance has generated 186 }; 187 bless $instance, $class; 188 189 # load the config - will croak on invalid config 190 $instance->config($config); 191 192 # load the dictionary (can't be done until the config is loaded) 193 $instance->dictionary($dictionary); 194 195 # load the rng 196 $instance->rng($rng); 197 198 # if debugging, print status 199 _debug("instantiated $_CLASS object with the following details:\n".$instance->status()); 200 201 # return the initialised object 202 return $instance; 203} 204 205# 206# Public Class (Static) functions --------------------------------------------- 207# 208 209#####-SUB-###################################################################### 210# Type : CLASS 211# Purpose : Return or update a module config variable 212# Returns : The value of the specified module config variable 213# Arguments : 1) the name of the module config variable 214# 2) OPTIONAL - a new value for the module config variable 215# Throws : Croaks on invalid invocation, or invalid args 216# Notes : 217# See Also : 218sub module_config{ 219 my @args = @_; 220 my $class = shift @args; 221 _force_class($class); 222 223 # validate args 224 state $args_check = compile( 225 NonEmptyString->plus_coercions(Str, q{uc $_}), ## no critic (RequireInterpolationOfMetachars) 226 Optional[Maybe[Value]], 227 ); 228 my ($config_key, $new_value) = $args_check->(@args); 229 230 231 # figure out which variable we are accessing 232 ## no critic (ProhibitCascadingIfElse); 233 if($config_key eq 'LOG_STREAM'){ 234 # check if we are a setter 235 if(defined $new_value){ 236 # make sure the new value is valid 237 FileHandle->check($new_value) || _error(FileHandle->get_message($new_value)); 238 239 # save the new value 240 $Crypt::HSXKPasswd::Helper::_LOG_STREAM = $new_value; ## no critic (ProtectPrivateVars) 241 } 242 243 #return the value 244 return $Crypt::HSXKPasswd::Helper::_LOG_STREAM; ## no critic (ProtectPrivateVars) 245 } 246 elsif($config_key eq 'LOG_ERRORS'){ 247 # check if we are a setter 248 if(defined $new_value){ 249 # make sure the new value is valid 250 TrueFalse->check($new_value) || _error(TrueFalse->get_message($new_value)); 251 252 # save the new value 253 $Crypt::HSXKPasswd::Helper::_LOG_ERRORS = $new_value; ## no critic (ProtectPrivateVars) 254 } 255 256 #return the value 257 return $Crypt::HSXKPasswd::Helper::_LOG_ERRORS; ## no critic (ProtectPrivateVars) 258 } 259 elsif($config_key eq 'DEBUG'){ 260 # check if we are a setter 261 if(defined $new_value){ 262 # make sure the new value is valid 263 TrueFalse->check($new_value) || _error(TrueFalse->get_message($new_value)); 264 265 # save the new value 266 $Crypt::HSXKPasswd::Helper::_DEBUG = $new_value; ## no critic (ProtectPrivateVars) 267 } 268 269 #return the value 270 return $Crypt::HSXKPasswd::Helper::_DEBUG; ## no critic (ProtectPrivateVars) 271 }elsif($config_key eq 'ENTROPY_MIN_BLIND'){ 272 # check if we are a setter 273 if(defined $new_value){ 274 # make sure the new value is valid 275 PositiveInteger->check($new_value) || _error(PositiveInteger->get_message($new_value)); 276 277 # save the new value 278 $_ENTROPY_MIN_BLIND = $new_value; 279 } 280 281 #return the value 282 return $_ENTROPY_MIN_BLIND; 283 }elsif($config_key eq 'ENTROPY_MIN_SEEN'){ 284 # check if we are a setter 285 if(defined $new_value){ 286 # make sure the new value is valid 287 PositiveInteger->check($new_value) || _error(PositiveInteger->get_message($new_value)); 288 289 # save the new value 290 $_ENTROPY_MIN_SEEN = $new_value; 291 } 292 293 #return the value 294 return $_ENTROPY_MIN_SEEN; 295 }elsif($config_key eq 'ENTROPY_WARNINGS'){ 296 # check if we are a setter 297 if(defined $new_value){ 298 # make sure the new value is valid 299 EntropyWarningLevel->check($new_value) || _error(EntropyWarningLevel->get_message($new_value)); 300 301 # save the new value 302 $_ENTROPY_WARNINGS = $new_value; 303 } 304 305 #return the value 306 return $_ENTROPY_WARNINGS; 307 }else{ 308 # the config key was invalid 309 _error(qq{no package variable '$config_key'}); 310 } 311 ## use critic 312 313 # It's not possible to get here, so return 1 to keep PerlCritic happy 314 return 1; 315} 316 317#####-SUB-###################################################################### 318# Type : CLASS 319# Purpose : Get a list of defined config keys. 320# Returns : An array of strings. 321# Arguments : NONE 322# Throws : NOTHING 323# Notes : 324# See Also : 325sub defined_config_keys{ 326 # gather and return the list of config key names 327 return (sort keys %{$_TYPES_CLASS->_config_keys()}); 328} 329 330#####-SUB-###################################################################### 331# Type : CLASS 332# Purpose : Return the specification for a given config key. 333# Returns : A hash indexed by 'required', 'type', and 'expects'. 334# Arguments : 1) a valid config key name 335# Throws : Croaks on invalid invocation and args 336# Notes : 337# See Also : 338sub config_key_definition{ 339 my @args = @_; 340 my $class = shift @args; 341 _force_class($class); 342 343 # validate args 344 state $args_check = compile(ConfigKeyName); 345 my ($key) = $args_check->(@args); 346 347 # get a referece to the keys hashref from the Types class 348 my $defined_keys = $_TYPES_CLASS->_config_keys(); 349 350 # assemble the hash 351 my %definition = ( 352 required => $defined_keys->{$key}->{required}, 353 type => $defined_keys->{$key}->{type}, 354 expects => $defined_keys->{$key}->{expects}, 355 ); 356 357 # return the hash 358 return %definition; 359} 360 361#####-SUB-###################################################################### 362# Type : CLASS 363# Purpose : Return a hash of all key definitions indexed by key name. 364# Returns : A hash of key defintions as returned by config_key_definition(). 365# Arguments : NONE 366# Throws : NOTHING 367# Notes : 368# See Also : config_key_definition() 369sub config_key_definitions{ 370 # gather the definitions 371 my %definitions = (); 372 foreach my $key ($_CLASS->defined_config_keys()){ 373 $definitions{$key} = $_CLASS->config_key_definition($key); 374 } 375 376 # return the definitions 377 return %definitions; 378} 379 380#####-SUB-###################################################################### 381# Type : CLASS 382# Purpose : generate a config hashref populated with the default values 383# Returns : a hashref 384# Arguments : 1. OPTIONAL - a hashref with config keys to over-ride when 385# assembling the config 386# Throws : Croaks if invoked in an invalid way. If passed overrides also 387# Croaks if the resulting config is invalid, and Carps if passed 388# on each invalid key passed in the overrides hashref. 389# Notes : 390# See Also : 391sub default_config{ 392 my @args = @_; 393 my $class = shift @args; 394 _force_class($class); 395 396 # validate args 397 state $args_check = compile(Optional[ConfigOverride]); 398 my ($overrides) = $args_check->(@args); 399 400 # build and return a default config 401 return $_CLASS->preset_config('DEFAULT', $overrides); 402} 403 404#####-SUB-###################################################################### 405# Type : CLASS 406# Purpose : Return the specification for a given preset. 407# Returns : A hash indexed by 'description', and 'config'. 408# Arguments : 1) OPTIONAL - a valid preset name, defaults to 'DEFAULT' 409# Throws : Croaks on invalid invocation and args 410# Notes : 411# See Also : 412sub preset_definition{ 413 my @args = @_; 414 my $class = shift @args; 415 _force_class($class); 416 417 # validate args 418 state $args_check = compile(Optional[Maybe[PresetName]]); 419 my ($preset_name) = $args_check->(@args); 420 421 # set defaults 422 $preset_name = 'DEFAULT' unless $preset_name; 423 424 # get a referece to the presets hashref from the Types class 425 my $preset_defs = $_TYPES_CLASS->_presets(); 426 427 # assemble the hash 428 my %definition = ( 429 description => $preset_defs->{$preset_name}->{description}, 430 config => $preset_defs->{$preset_name}->{config}, 431 ); 432 433 # return the hash 434 return %definition; 435} 436 437#####-SUB-###################################################################### 438# Type : CLASS 439# Purpose : Return a hash of all preset definitions indexed by name. 440# Returns : A hash of preset defintions as returned by preset_definition(). 441# Arguments : NONE 442# Throws : NOTHING 443# Notes : 444# See Also : preset_definition() 445sub preset_definitions{ 446 # gather the definitions 447 my %definitions = (); 448 foreach my $name ($_CLASS->defined_presets()){ 449 $definitions{$name} = $_CLASS->preset_definition($name); 450 } 451 452 # return the definitions 453 return %definitions; 454} 455 456#####-SUB-###################################################################### 457# Type : CLASS 458# Purpose : generate a config hashref populated using a preset 459# Returns : a hashref 460# Arguments : 1. OPTIONAL - The name of the preset to assemble the config for 461# as a scalar. If no name is passed, the preset 'DEFAULT' is 462# used 463# 2. OPTIONAL - a hashref with config keys to over-ride when 464# assembling the config 465# Throws : Croaks if invoked in an invalid way. If passed overrides also 466# Croaks if the resulting config is invalid, and Carps if passed 467# on each invalid key passed in the overrides hashref. 468# Notes : 469# See Also : 470sub preset_config{ 471 my @args = @_; 472 my $class = shift @args; 473 _force_class($class); 474 475 # validate args 476 state $args_check = compile(Optional[PresetName], Optional[Maybe[ConfigOverride]]); 477 my ($preset_name, $overrides) = $args_check->(@args); 478 479 # default the preset name to 'DEFAULT' 480 $preset_name = 'DEFAULT' unless $preset_name; 481 482 # get a reference to the Presets hashref from the Types class 483 my $preset_defs = $_TYPES_CLASS->_presets(); 484 485 # start by loading the preset 486 my $config = $_CLASS->clone_config($preset_defs->{$preset_name}->{config}); 487 488 # if overrides were passed, apply them and validate 489 if(defined $overrides){ 490 # save the keys into the config 491 foreach my $key (keys %{$overrides}){ 492 $config->{$key} = $overrides->{$key}; 493 } 494 # validate the resulting config 495 unless(Config->check($config)){ 496 _error('The preset combined with the specified overrides produces an invalid config: '.Config->get_message($config)); 497 } 498 } 499 500 # return the config 501 return $config; 502} 503 504#####-SUB-###################################################################### 505# Type : CLASS 506# Purpose : Resturn the presets defined in the Crypt::HSXKPasswd module as a 507# JSON string 508# Returns : A JSON String as a scalar. The JSON string represets a hashref 509# with three keys - 'defined_keys' contains an array of preset 510# identifiers, 'presets' contains the preset configs indexed by 511# preset identifier, and 'preset_descriptions' contains the a 512# hashref of descriptions indexed by preset identifiers 513# Arguments : NONE 514# Throws : If there is a problem converting the objects to JSON. 515# Notes : 516# See Also : 517sub presets_json{ 518 # assemble an object containing the presets with any keys that can't be 519 # converted to JSON removed 520 my @defined_presets = $_CLASS->defined_presets(); 521 my $sanitised_presets = {}; 522 my $preset_descriptions = {}; 523 foreach my $preset_name (@defined_presets){ 524 $sanitised_presets->{$preset_name} = $_CLASS->preset_config($preset_name); 525 $preset_descriptions->{$preset_name} = $_CLASS->preset_description($preset_name); 526 } 527 my $return_object = { 528 defined_presets => [@defined_presets], 529 presets => $sanitised_presets, 530 preset_descriptions => $preset_descriptions, 531 }; 532 533 # try convert the object to a JSON string 534 my $json_string = q{}; 535 eval{ 536 $json_string = JSON->new()->encode($return_object); 537 1; # ensure truthy evaluation on succesful execution 538 }or do{ 539 _error("failed to render presets as JSON string with error: $EVAL_ERROR"); 540 }; 541 542 # return the JSON string 543 return $json_string; 544} 545 546#####-SUB-###################################################################### 547# Type : CLASS 548# Purpose : Clone a config hashref 549# Returns : a hashref 550# Arguments : 1. the config hashref to clone 551# Throws : Croaks if called in an invalid way, or with an invalid config. 552# Notes : This function needs to be updated each time a new non-scalar 553# valid config key is added to the library. 554# See Also : 555sub clone_config{ 556 my @args = @_; 557 my $class = shift @args; 558 _force_class($class); 559 560 # validate args 561 state $args_check = compile(Config); 562 my ($config) = $args_check->(@args); 563 564 return clone($config); 565} 566 567#####-SUB-###################################################################### 568# Type : CLASS 569# Purpose : Remove all keys from a hashref that are not valid config keys 570# Returns : A reference to a hashref 571# Arguments : 1) a hashref 572# 2) OPTIONAL - a list of named arguments: 573# 'warn_invalid_key_names' - must be either 1 or 0. If 1, 574# warnings will be issued for any keys containined in the 575# test hash that are not valid config keys. 576# 'suppress_warnings' takes precedence over this argument. 577# 'suppress_warnings' - must be either 1 or 0. If 1, no warnings 578# will be printed when dropping valid keys with invalid 579# values, or invalid keys. 580# Throws : Croaks on invalid args. Unless configured not to, will warn if 581# a valid key with an invalid value is encountered. 582# Notes : 583# See Also : 584sub distil_to_config_keys{ 585 my @args = @_; 586 my $class = shift @args; 587 _force_class($class); 588 589 # validate args 590 state $args_check = compile(HashRef, 591 slurpy Dict[ 592 suppress_warnings => Optional[TrueFalse], 593 warn_invalid_key_names => Optional[TrueFalse], 594 ], 595 ); 596 my ($hashref, $options) = $args_check->(@args); 597 598 # get a list of all the defined keys 599 my @defined_keys = $_CLASS->defined_config_keys(); 600 601 # if warnings are not suppressed, and if extra warnings are asked for, check for invalid keys 602 if(!$options->{suppress_warnings} && $options->{warn_invalid_key_names}){ 603 # build a lookup table to quickly test if a key exists 604 my %defined_keys_lookup = (); 605 foreach my $key (@defined_keys){ 606 $defined_keys_lookup{$key} = 1; 607 } 608 609 # check each key in the test hash against the lookup table 610 foreach my $test_key (sort keys %{$hashref}){ 611 unless($defined_keys_lookup{$test_key}){ 612 _warn(qq{distilling out undefined config key '$test_key'}); 613 } 614 } 615 } 616 617 # start with a new blank hashref, and copy across only the valid keys 618 my $distilled = {}; 619 foreach my $key (@defined_keys){ 620 if(defined $hashref->{$key}){ 621 if(ConfigKeyAssignment->check({$key => $hashref->{$key}})){ 622 $distilled->{$key} = clone($hashref->{$key}); 623 }else{ 624 _warn("distilling out valid config key '$key' because of invalid value: ".ConfigKeyAssignment->get_message({$key => $hashref->{$key}})) unless $options->{suppress_warnings}; 625 } 626 } 627 } 628 _debug('hashref distilled down from '.(scalar keys %{$hashref}).' to '.(scalar keys %{$distilled}).' keys'); 629 630 # return the distilled hashref 631 return $distilled; 632} 633 634#####-SUB-###################################################################### 635# Type : CLASS 636# Purpose : Distil an array of strings down to a de-duplicated array of only 637# symbols. 638# Returns : An array of strings 639# Arguments : 1) A reference to an array of strings 640# 2) OPTIONAL - a named argument warn with a value of 0 or 1. If 1 641# is passed, warnings will be issued each time an invalid string 642# is skipped over. 643# Throws : Croaks on invalid invocation or args, and warns on request when 644# skipping words. 645# Notes : 646# See Also : 647sub distil_to_symbol_alphabet{ 648 my @args = @_; 649 my $class = shift @args; 650 _force_class($class); 651 652 # validate args 653 state $args_check = compile(ArrayRef[Str], slurpy Dict[warn => Optional[TrueFalse]]); 654 my ($array_ref, $options) = $args_check->(@args); 655 my $warn = $options->{warn} || 0; 656 657 # loop through the array and copy all valid synbols to a new array 658 my @valid_symbols = (); 659 foreach my $potential_symbol (@{$array_ref}){ 660 if(Symbol->check($potential_symbol)){ 661 push @valid_symbols, $potential_symbol; 662 }else{ 663 if($warn || _do_debug()){ 664 my $msg = 'skipping invalid symbol: '.Symbol->get_message($potential_symbol); 665 if($warn){ 666 _warn($msg); 667 }else{ 668 _debug($msg); 669 } 670 } 671 } 672 } 673 674 # de-dupe the valid symbols 675 my @final_alphabet = uniq(@valid_symbols); 676 677 # return the valid symbols 678 return @final_alphabet; 679} 680 681#####-SUB-###################################################################### 682# Type : CLASS 683# Purpose : Distil an array of strings down to a de-duplicated array of only 684# the valid words. 685# Returns : An array of words 686# Arguments : 1) A reference to an array of strings 687# 2) OPTIONAL - a named argument warn with a value of 0 or 1. If 1 688# is passed, warnings will be issued each time an invalid string 689# is skipped over. 690# Throws : Croaks on invalid invocation or args, and warns on request when 691# skipping words. 692# Notes : 693# See Also : 694sub distil_to_words{ 695 my @args = @_; 696 my $class = shift @args; 697 _force_class($class); 698 699 # just pass everything through to the dictionary class 700 return $_DICTIONARY_BASE_CLASS->distil_to_words(@args); 701} 702 703#####-SUB-###################################################################### 704# Type : CLASS 705# Purpose : validate a config hashref 706# Returns : 1 if the config is valid, 0 otherwise 707# Arguments : 1. a hashref to validate 708# 2. OPTIONAL - a named argument croak with a value of 1 or 1 709# Throws : Croaks on invalid args, or on error if second arg is truthy 710# Notes : This function needs to be updated each time a new valid config 711# key is added to the library. 712# See Also : 713sub is_valid_config{ 714 my @args = @_; 715 my $class = shift @args; 716 _force_class($class); 717 718 # validate args 719 state $args_check = compile(Item, slurpy Dict[croak => Optional[TrueFalse]]); 720 my ($config, $options) = $args_check->(@args); 721 722 # validate the config 723 my $is_valid = Config->check($config) || 0; 724 725 # croak if appropriate 726 if(!$is_valid && $options->{croak}){ 727 _error(Config->get_message($config)); 728 } 729 730 # return the result of the validation 731 return $is_valid; 732} 733 734#####-SUB-###################################################################### 735# Type : CLASS 736# Purpose : Convert a config hashref to a JSON String 737# Returns : A scalar 738# Arguments : 1. A config hashref 739# Throws : Croaks on invalid invocation, invalid args, or if the JSON module 740# is not available 741# Notes : 742# See Also : 743sub config_to_json{ 744 my @args = @_; 745 my $class = shift @args; 746 _force_class($class); 747 748 # validate args 749 state $args_check = compile(Config); 750 my ($config) = $args_check->(@args); 751 752 # try render the config to a JSON string 753 my $ans = q{}; 754 eval{ 755 $ans = encode_json($config); 756 1; # ensure a thurthy evaluation on successful execution 757 }or do{ 758 _error("Failed to convert config to JSON stirng with error: $EVAL_ERROR"); 759 }; 760 761 # return the string 762 return $ans; 763} 764 765#####-SUB-###################################################################### 766# Type : CLASS 767# Purpose : Convert a config hashref to a String 768# Returns : A scalar 769# Arguments : 1. A config hashref 770# Throws : Croaks on invalid invocation or with invalid args. Carps if there 771# are problems with the config hashref. 772# Notes : 773# See Also : 774sub config_to_string{ 775 my @args = @_; 776 my $class = shift @args; 777 _force_class($class); 778 779 # validate args 780 state $args_check = compile(Config); 781 my ($config) = $args_check->(@args); 782 783 # get a reference to the key definitions from the types class 784 my $defined_keys = $_TYPES_CLASS->_config_keys(); 785 786 # assemble the string to return 787 my $ans = q{}; 788 CONFIG_KEY: 789 foreach my $key (sort keys %{$defined_keys}){ 790 # skip undefined keys 791 next CONFIG_KEY unless defined $config->{$key}; 792 793 # process the key 794 if(ref $config->{$key} eq q{}){ 795 # the key is a scalar 796 $ans .= $key.q{: '}.$config->{$key}.qq{'\n}; 797 }elsif(ref $config->{$key} eq 'ARRAY'){ 798 # the key is an array ref 799 $ans .= "$key: ["; 800 my @parts = (); 801 foreach my $subval (sort @{$config->{$key}}){ 802 push @parts, "'$subval'"; 803 } 804 $ans .= join q{, }, @parts; 805 $ans .= "]\n"; 806 }elsif(ref $config->{$key} eq 'HASH'){ 807 $ans .= "$key: {"; 808 my @parts = (); 809 foreach my $subkey (sort keys %{$config->{$key}}){ 810 push @parts, "$subkey: '$config->{$key}->{$subkey}'"; 811 } 812 $ans .= join q{, }, @parts; 813 $ans .= "}\n"; 814 }else{ 815 # this should never happen, but just in case, throw a warning 816 _warn("the data for the key '$key' is of an un-expected type (".(ref $config->{$key}).') - skipping key'); 817 } 818 } 819 820 # return the string 821 return $ans; 822} 823 824#####-SUB-###################################################################### 825# Type : CLASS 826# Purpose : Return the description for a given preset 827# Returns : A scalar string 828# Arguments : 1. OPTIONAL - the name of the preset to get the description for, 829# if no name is passed 'DEFAULT' is assumed 830# Throws : Croaks on invalid invocation or invalid args 831# Notes : 832# See Also : 833sub preset_description{ 834 my @args = @_; 835 my $class = shift @args; 836 _force_class($class); 837 838 # validate args 839 state $args_check = compile(Optional[Maybe[PresetName]]); 840 my ($preset) = $args_check->(@args); 841 842 # set defaults 843 $preset = 'DEFAULT' unless $preset; 844 845 # get a reference to the preset definitions from the Types class 846 my $preset_defs = $_TYPES_CLASS->_presets(); 847 848 # return the description by loading the preset 849 return $preset_defs->{$preset}->{description}; 850} 851 852 853#####-SUB-###################################################################### 854# Type : CLASS 855# Purpose : Return a list of all valid preset names 856# Returns : An array of preset names as scalars 857# Arguments : NONE 858# Throws : NOTHING 859# Notes : 860# See Also : 861sub defined_presets{ 862 # return the preset names 863 my @preset_names = sort keys %{$_TYPES_CLASS->_presets()}; 864 return @preset_names; 865} 866 867#####-SUB-###################################################################### 868# Type : CLASS 869# Purpose : Render the defined presets as a string 870# Returns : A scalar 871# Arguments : NONE 872# Throws : NOTHING 873# Notes : 874# See Also : 875sub presets_to_string{ 876 # get a reference to the preset definitions from th Types class 877 my $preset_defs = $_TYPES_CLASS->_presets(); 878 879 # loop through each preset and assemble the result 880 my $ans = q{}; 881 my @preset_names = $_CLASS->defined_presets(); 882 foreach my $preset (@preset_names){ 883 $ans .= $preset."\n===\n"; 884 $ans .= $preset_defs->{$preset}->{description}."\n"; 885 $ans .= "\nConfig:\n---\n"; 886 $ans .= $_CLASS->config_to_string($preset_defs->{$preset}->{config}); 887 $ans .= "\nStatistics:\n---\n"; 888 my %stats = $_CLASS->config_stats($preset_defs->{$preset}->{config}); 889 if($stats{length_min} == $stats{length_max}){ 890 $ans .= "Length (fixed): $stats{length_min} characters\n"; 891 }else{ 892 $ans .= "Length (variable): between $stats{length_min} & $stats{length_max} characters\n"; 893 } 894 $ans .= "Random Numbers Needed Per-Password: $stats{random_numbers_required}\n"; 895 $ans .= "\n"; 896 } 897 898 # return the string 899 return $ans; 900} 901 902#####-SUB-##################################################################### 903# Type : CLASS 904# Purpose : Calculate the number of random numbers needed to genereate a 905# single password with a given config. 906# Returns : An integer 907# Arguments : 1) a valid config hashref 908# Throws : Croaks in invalid invocation, or invalid args 909# Notes : 910# See Also : 911sub config_random_numbers_required{ 912 my @args = @_; 913 my $class = shift @args; 914 _force_class($class); 915 916 # validate args 917 state $args_check = compile(Config); 918 my ($config) = $args_check->(@args); 919 920 # calculate the number of random numbers needed to generate the password 921 my $num_rand = 0; 922 $num_rand += $config->{num_words}; 923 if($config->{case_transform} eq 'RANDOM'){ 924 $num_rand += $config->{num_words}; 925 } 926 if($config->{separator_character} eq 'RANDOM'){ 927 $num_rand++; 928 } 929 if(defined $config->{padding_character} && $config->{padding_character} eq 'RANDOM'){ 930 $num_rand++; 931 } 932 $num_rand += $config->{padding_digits_before}; 933 $num_rand += $config->{padding_digits_after}; 934 935 # return the number 936 return $num_rand; 937} 938 939#####-SUB-###################################################################### 940# Type : CLASS 941# Purpose : Calculate statistics for a given configutration hashref. 942# Returns : A hash of statistics indexed by the following keys: 943# * 'length_min' - the minimum possible length of a password 944# generated by the given config 945# * 'length_max' - the maximum possible length of a password 946# generated by the given config 947# * 'random_numbers_required' - the number of random numbers needed 948# to generate a single password using the given config 949# Arguments : 1. A valid config hashref 950# 2. OPTONAL - a named argument 'suppress_warnings' to indicate that 951# no warnings should be issued if the config is such that there 952# are uncertainties in the calculation. 953# Throws : Croaks on invalid invocation or args, carps if multi-character 954# substitutions are in use when not using adapive padding 955# Notes : This function ignores character replacements, if one or more 956# multi-character replacements are used when padding is not set 957# to adaptive, this function will return an invalid max length. 958# See Also : 959sub config_stats{ 960 my @args = @_; 961 my $class = shift @args; 962 _force_class($class); 963 964 # validate args 965 state $args_check = compile(Config, slurpy Dict[suppress_warnings => Optional[TrueFalse]]); 966 my ($config, $options) = $args_check->(@args); 967 my $suppres_warnings = $options->{suppress_warnings} || 0; 968 969 # calculate the lengths 970 my $len_min = 0; 971 my $len_max = 0; 972 if($config->{padding_type} eq 'ADAPTIVE'){ 973 $len_min = $len_max = $config->{pad_to_length}; 974 }else{ 975 # calcualte the length of everything but the words themselves 976 my $len_base = 0; 977 if($config->{padding_type} eq 'FIXED'){ 978 $len_base += $config->{padding_characters_before}; 979 $len_base += $config->{padding_characters_after}; 980 } 981 if($config->{padding_digits_before} > 0){ 982 $len_base += $config->{padding_digits_before}; 983 if($config->{separator_character} ne 'NONE'){ 984 $len_base++; 985 } 986 } 987 if($config->{padding_digits_after} > 0){ 988 $len_base += $config->{padding_digits_after}; 989 if($config->{separator_character} ne 'NONE'){ 990 $len_base++; 991 } 992 } 993 if($config->{separator_character} ne 'NONE'){ 994 $len_base += $config->{num_words} - 1; 995 } 996 997 # maximise and minimise the word lengths to calculate the final answers 998 $len_min = $len_base + ($config->{num_words} * $config->{word_length_min}); 999 $len_max = $len_base + ($config->{num_words} * $config->{word_length_max}); 1000 } 1001 1002 # calculate the number of random numbers needed to generate the password 1003 my $num_rand = $_CLASS->config_random_numbers_required($config); 1004 1005 # detect whether or not we need to carp about multi-character replacements 1006 if($config->{padding_type} ne 'ADAPTIVE' && !$suppres_warnings){ 1007 if(defined $config->{character_substitutions}){ 1008 CHAR_SUB: 1009 foreach my $char (keys %{$config->{character_substitutions}}){ 1010 if(length $config->{character_substitutions}->{$char} > 1){ 1011 _warn('maximum length may be underestimated. The loaded config contains at least one character substitution which replaces a single character with multiple characters.'); 1012 last CHAR_SUB; 1013 } 1014 } 1015 } 1016 } 1017 1018 # assemble the result and return 1019 my $stats = { 1020 length_min => $len_min, 1021 length_max => $len_max, 1022 random_numbers_required => $num_rand, 1023 }; 1024 return %{$stats}; 1025} 1026 1027# 1028# --- Public Instance functions ----------------------------------------------- 1029# 1030 1031#####-SUB-##################################################################### 1032# Type : INSTANCE 1033# Purpose : Get the currently loaded dictionary object, or, load a new 1034# dictionary 1035# Returns : An clone of the loaded dictionary object, or, a reference to the 1036# instance (to enable function chaining) if called as a setter 1037# Arguments : 1. OPTIONAL - the source for the dictionary, can be: 1038# The path to a dictionary file 1039# -OR- 1040# A reference to an array of words 1041# -OR- 1042# An instance of a sub-class of Crypt::HSXKPasswd::Dictionary 1043# 2. OPTIONAL - the encoding to import the file with. The default 1044# is UTF-8 (ignored if the first argument is not a file path). 1045# Throws : Croaks on invalid invocation, or, if there is a problem loading 1046# a dictionary file. While debugging, also warns when skipping 1047# invalid words. 1048# Notes : 1049# See Also : For description of dictionary file format, see POD documentation 1050# below 1051sub dictionary{ 1052 my @args = @_; 1053 my $self = shift @args; 1054 _force_instance($self); 1055 1056 # validate args 1057 state $args_check = multisig( 1058 [], 1059 [NonEmptyString, Optional[Maybe[NonEmptyString]]], 1060 [InstanceOf[$_DICTIONARY_BASE_CLASS]], 1061 [ArrayRef[Str]], 1062 ); 1063 my ($dictionary_source, $encoding) = $args_check->(@args); 1064 1065 # set defaults 1066 $encoding = 'UTF-8' unless $encoding; 1067 1068 # if we're a getter, just get and return 1069 unless(defined $dictionary_source){ 1070 return $self->{_DICTIONARY_SOURCE}->clone(); 1071 } 1072 1073 # OK, so we're a setter - carry on! 1074 1075 # croak if we are called before the config has been loaded into the instance 1076 unless(defined $self->{_CONFIG}->{word_length_min} && $self->{_CONFIG}->{word_length_max}){ 1077 _error('failed to load word source - config has not been loaded yet'); 1078 } 1079 1080 # get a dictionary instance 1081 my $new_dict; 1082 if(blessed($dictionary_source) && $dictionary_source->isa($_DICTIONARY_BASE_CLASS)){ 1083 $new_dict = $dictionary_source; 1084 }elsif(ref $dictionary_source eq q{} || ref $dictionary_source eq 'ARRAY'){ 1085 $new_dict = Crypt::HSXKPasswd::Dictionary::Basic->new($dictionary_source, $encoding); # could throw an error 1086 }else{ 1087 _error('invalid word source - must be a dictionary object, hashref, or file path'); 1088 } 1089 1090 # load and sanitise the words from the word source 1091 my @cache_full = $_CLASS->distil_to_words(\@{$new_dict->word_list()}); 1092 1093 # generate the cache of appropriate-length words - croaks if too few words left after filtering 1094 my @cache_limited = $_CLASS->_filter_word_list( 1095 \@cache_full, 1096 $self->{_CONFIG}->{word_length_min}, 1097 $self->{_CONFIG}->{word_length_max}, 1098 allow_accents => $self->{_CONFIG}->{allow_accents}, 1099 ); 1100 1101 # if we got here all is well, so save the new path and caches into the object 1102 $self->{_DICTIONARY_SOURCE} = $new_dict; 1103 $self->{_CACHE_DICTIONARY_FULL} = [@cache_full]; 1104 $self->{_CACHE_DICTIONARY_LIMITED} = [@cache_limited]; 1105 if($self->{_CONFIG}->{allow_accents}){ 1106 $self->{_CACHE_CONTAINS_ACCENTS} = $_CLASS->_contains_accented_letters(\@cache_limited); 1107 }else{ 1108 $self->{_CACHE_CONTAINS_ACCENTS} = 0; 1109 } 1110 1111 # update the instance's entropy cache 1112 $self->_update_entropystats_cache(); 1113 1114 # return a reference to self 1115 return $self; 1116} 1117 1118#####-SUB-###################################################################### 1119# Type : INSTANCE 1120# Purpose : Get a clone of the current config from an instance, or load a 1121# new config into the instance. 1122# Returns : A config hashref if called with no arguments, or, the instance 1123# if called with a hashref (to facilitate function chaining) 1124# Arguments : 1. OPTIONAL - a configuration to load as: 1125# A config hashref 1126# -OR- 1127# A JSON string representing a config hashref 1128# Throws : Croaks if the function is called in an invalid way, with invalid 1129# arguments, or with an invalid config. 1130# Notes : Passing a JSON string will cause the function to croak if perl's 1131# JSON module is not installed. 1132# See Also : For valid configuarion options see POD documentation below 1133sub config{ 1134 my @args = @_; 1135 my $self = shift @args; 1136 _force_instance($self); 1137 1138 # validate args 1139 state $args_check = multisig( 1140 [], 1141 [Config], 1142 [NonEmptyString], 1143 ); 1144 my ($config_raw) = $args_check->(@args); 1145 1146 # if we're a getter, just get and return 1147 unless(defined $config_raw){ 1148 return $self->_clone_config(); 1149 } 1150 1151 # OK - so we're a setter - carry on! 1152 1153 # see what kind of argument we were passed, and behave appropriately 1154 my $config = {}; 1155 if(ref $config_raw eq 'HASH'){ 1156 # we received a hashref, so just and pass it on 1157 $config = $config_raw; 1158 }elsif(ref $config_raw eq q{}){ 1159 # we received as string, so treat it as JSON 1160 1161 # try parse the received string as JSON 1162 my $config_from_json = {}; 1163 eval{ 1164 $config_from_json = decode_json($config_raw); 1165 1; # ensure truthy evaluation on successful execution 1166 }or do{ 1167 _error("Failed to parse JSON config string with error: $EVAL_ERROR"); 1168 }; 1169 1170 # strip out any extraneous keys found 1171 $config = $_CLASS->distil_to_config_keys($config_from_json); 1172 1173 # validate the generated config 1174 unless(Config->check($config)){ 1175 _error('Config extracted from JSON string is not valid: '.Config->get_message($config)); 1176 } 1177 }else{ 1178 _error('the config passed must be a hashref or a JSON string'); 1179 } 1180 1181 # distil the alphabets in the new config 1182 $_CLASS->_distil_alphabets_inplace($config); 1183 1184 # save a clone of the passed config into the instance 1185 $self->{_CONFIG} = $_CLASS->clone_config($config); 1186 1187 # update the instance's entropy cache 1188 $self->_update_entropystats_cache(); 1189 1190 # return a reference to self to facilitate function chaining 1191 return $self; 1192} 1193 1194#####-SUB-##################################################################### 1195# Type : INSTANCE 1196# Purpose : Return the config of the currently running instance as a JSON 1197# string. 1198# Returns : A scalar. 1199# Arguments : NONE 1200# Throws : Croaks if invoked in an invalid way. Carps if it meets a key of a 1201# type not accounted for in the code. 1202# Notes : This function will carp if the JSON module is not available 1203# See Also : 1204sub config_as_json{ 1205 my $self = shift; 1206 _force_instance($self); 1207 1208 # assemble and return the JSON string 1209 return $_CLASS->config_to_json($self->{_CONFIG}); # will croak without JSON 1210} 1211 1212#####-SUB-##################################################################### 1213# Type : INSTANCE 1214# Purpose : Return the config of the currently running instance as a string. 1215# Returns : A scalar. 1216# Arguments : NONE 1217# Throws : Croaks if invoked in an invalid way. Carps if it meets a key of a 1218# type not accounted for in the code. 1219# Notes : 1220# See Also : 1221sub config_as_string{ 1222 my $self = shift; 1223 _force_instance($self); 1224 1225 # assemble and return the string 1226 return $_CLASS->config_to_string($self->{_CONFIG}); 1227} 1228 1229#####-SUB-###################################################################### 1230# Type : INSTANCE 1231# Purpose : Alter the running config with new values. 1232# Returns : A reference to the instalce itself to enable function chaining. 1233# Arguments : 1. a hashref containing config keys and values. 1234# Throws : Croaks on invalid invocaiton, invalid args, and, if the resulting 1235# new config is in some way invalid. 1236# Notes : Invalid keys in the new keys hashref will be silently ignored. 1237# See Also : 1238sub update_config{ 1239 my @args = @_; 1240 my $self = shift @args; 1241 _force_instance($self); 1242 1243 # validate args 1244 state $args_check = compile(ConfigOverride); 1245 my ($new_keys) = $args_check->(@args); 1246 1247 # clone the current config as a starting point for the new config 1248 my $new_config = $self->_clone_config(); 1249 1250 # get a reference to the key definitions from the types class 1251 my $defined_keys = $_TYPES_CLASS->_config_keys(); 1252 1253 # merge the new values into the config 1254 my $num_keys_updated = 0; 1255 CONFIG_KEY: 1256 foreach my $key ($_CLASS->defined_config_keys()){ 1257 # skip the key if it's not present in the list of new keys 1258 next CONFIG_KEY unless defined $new_keys->{$key}; 1259 1260 # update the key in the new config 1261 $new_config->{$key} = $new_keys->{$key}; 1262 $num_keys_updated++; 1263 _debug("updated $key to new value"); 1264 } 1265 _debug("updated $num_keys_updated keys"); 1266 1267 # distil the alphabets in the merged config 1268 $_CLASS->_distil_alphabets_inplace($new_config); 1269 1270 # validate the merged config 1271 unless(Config->check($new_config)){ 1272 _error('the updated config is not valid: '.Config->get_message($new_config)); 1273 } 1274 1275 # re-calculate the dictionary cache if needed 1276 my @cache_all = @{$self->{_CACHE_DICTIONARY_FULL}}; 1277 my @cache_limited = @{$self->{_CACHE_DICTIONARY_LIMITED}}; 1278 if($new_config->{word_length_min} ne $self->{_CONFIG}->{word_length_min} || $new_config->{word_length_max} ne $self->{_CONFIG}->{word_length_max}){ 1279 # re-build the cache of valid words - throws an error if too few words are returned 1280 @cache_limited = $_CLASS->_filter_word_list(\@cache_all, $new_config->{word_length_min}, $new_config->{word_length_max}, $new_config->{allow_accents}); 1281 } 1282 1283 # if we got here, all is well with the new config, so add it and the caches to the instance 1284 $self->{_CONFIG} = $new_config; 1285 $self->{_CACHE_DICTIONARY_LIMITED} = [@cache_limited]; 1286 1287 # update the instance's entropy cache 1288 $self->_update_entropystats_cache(); 1289 1290 # return a reference to self 1291 return $self; 1292} 1293 1294#####-SUB-##################################################################### 1295# Type : INSTANCE 1296# Purpose : Get the currently loaded RNG object, or, load a new RNG 1297# Returns : An instance to the loaded RNG object, or, a reference to the 1298# instance (to enable function chaining) if called as a setter 1299# Arguments : 1. OPTIONAL - an object that is a Crypt::HSXKPasswd::RNG 1300# Throws : Croaks on invalid invocation, or invalid args 1301# Notes : 1302# See Also : 1303sub rng{ 1304 my @args = @_; 1305 my $self = shift @args; 1306 _force_instance($self); 1307 1308 # validate args 1309 state $args_check = multisig( 1310 [], 1311 [InstanceOf[$_RNG_BASE_CLASS]], 1312 ); 1313 my ($rng) = $args_check->(@args); 1314 1315 # if we're a getter, just get and return 1316 unless(defined $rng){ 1317 return $self->{_RNG}; 1318 } 1319 1320 # OK - so we're a getter - carry on! 1321 1322 # set the RNG 1323 $self->{_RNG} = $rng; 1324 1325 # empty the random cache 1326 $self->{_CACHE_RANDOM} = []; 1327 1328 # return a reference to self 1329 return $self; 1330} 1331 1332#####-SUB-###################################################################### 1333# Type : INSTANCE 1334# Purpose : Return the status of the internal caches within the instnace. 1335# Returns : A string 1336# Arguments : NONE 1337# Throws : Croaks in invalid invocation 1338# Notes : 1339# See Also : 1340sub caches_state{ 1341 my $self = shift; 1342 _force_instance($self); 1343 1344 # generate the string 1345 my $ans = q{}; 1346 $ans .= 'Loaded Words: '.(scalar @{$self->{_CACHE_DICTIONARY_LIMITED}}).' (out of '.(scalar @{$self->{_CACHE_DICTIONARY_FULL}}).' loaded from the file)'.qq{\n}; 1347 $ans .= 'Cached Random Numbers: '.(scalar @{$self->{_CACHE_RANDOM}}).qq{\n}; 1348 1349 # return it 1350 return $ans; 1351} 1352 1353#####-SUB-###################################################################### 1354# Type : INSTANCE 1355# Purpose : Generaete a random password based on the object's loaded config 1356# Returns : a passowrd as a scalar 1357# Arguments : NONE 1358# Throws : Croaks on invalid invocation or on error generating the password 1359# Notes : 1360# See Also : 1361sub password{ 1362 my $self = shift; 1363 _force_instance($self); 1364 1365 # 1366 # Generate the password 1367 # 1368 my $password = q{}; 1369 eval{ 1370 # 1371 # start by generating the needed parts of the password 1372 # 1373 _debug('starting to generate random words'); 1374 my @words = $self->_random_words(); 1375 _debug('got random words='.(join q{, }, @words)); 1376 $self->_transform_case(\@words); 1377 $self->_substitute_characters(\@words); # TO DO 1378 my $separator = $self->_separator(); 1379 _debug("got separator=$separator"); 1380 my $pad_char = $self->_padding_char($separator); 1381 _debug("got pad_char=$pad_char"); 1382 1383 # 1384 # Then assemble the finished password 1385 # 1386 1387 # start with the words and the separator 1388 $password = join $separator, @words; 1389 _debug("assembled base password: $password"); 1390 1391 # next add the numbers front and back 1392 if($self->{_CONFIG}->{padding_digits_before} > 0){ 1393 $password = $self->_random_digits($self->{_CONFIG}->{padding_digits_before}).$separator.$password; 1394 } 1395 if($self->{_CONFIG}->{padding_digits_after} > 0){ 1396 $password = $password.$separator.$self->_random_digits($self->{_CONFIG}->{padding_digits_after}); 1397 } 1398 _debug("added random digits (as configured): $password"); 1399 1400 1401 # then finally add the padding characters 1402 if($self->{_CONFIG}->{padding_type} eq 'FIXED'){ 1403 # simple fixed padding 1404 if($self->{_CONFIG}->{padding_characters_before} > 0){ 1405 foreach my $c (1..$self->{_CONFIG}->{padding_characters_before}){ 1406 $password = $pad_char.$password; 1407 } 1408 } 1409 if($self->{_CONFIG}->{padding_characters_after} > 0){ 1410 foreach my $c (1..$self->{_CONFIG}->{padding_characters_after}){ 1411 $password .= $pad_char; 1412 } 1413 } 1414 }elsif($self->{_CONFIG}->{padding_type} eq 'ADAPTIVE'){ 1415 # adaptive padding 1416 my $pwlen = length $password; 1417 if($pwlen < $self->{_CONFIG}->{pad_to_length}){ 1418 # if the password is shorter than the target length, padd it out 1419 while((length $password) < $self->{_CONFIG}->{pad_to_length}){ 1420 $password .= $pad_char; 1421 } 1422 }elsif($pwlen > $self->{_CONFIG}->{pad_to_length}){ 1423 # if the password is too long, trim it 1424 $password = substr $password, 0, $self->{_CONFIG}->{pad_to_length}; 1425 } 1426 } 1427 _debug("added padding (as configured): $password"); 1428 1; # ensure true evaluation on successful execution 1429 }or do{ 1430 _error("Failed to generate password with the following error: $EVAL_ERROR"); 1431 }; 1432 1433 # increment the passwords generated counter 1434 $self->{_PASSWORD_COUNTER}++; 1435 1436 # return the finished password 1437 return $password; 1438} 1439 1440#####-SUB-###################################################################### 1441# Type : INSTANCE 1442# Purpose : Generate multiple passwords 1443# Returns : An array of passwords as scalars 1444# Arguments : 1. the number of passwords to generate as a scalar 1445# Throws : Croaks on invalid invocation or invalid args 1446# Notes : 1447# See Also : 1448sub passwords{ 1449 my @args = @_; 1450 my $self = shift @args; 1451 _force_instance($self); 1452 1453 # validate args 1454 state $args_check = compile(NonZeroPositiveInteger); 1455 my ($num_pws) = $args_check->(@args); 1456 1457 # generate the needed passwords 1458 my @passwords = (); 1459 my $num_to_do = $num_pws; 1460 while($num_to_do > 0){ 1461 push @passwords, $self->password(); # could croak 1462 $num_to_do--; 1463 } 1464 1465 # return the passwords 1466 return @passwords; 1467} 1468 1469#####-SUB-###################################################################### 1470# Type : INSTANCE 1471# Purpose : Generate n passwords and return them, and the entropy stats as a 1472# JSON string. 1473# Returns : A JSON string as a scalar representing a hashref contianing an 1474# array of passwords indexed by 'passwords', and a hashref of 1475# entropy stats indexed by 'stats'. The stats hashref itself is 1476# indexed by: 'password_entropy_blind', 1477# 'password_permutations_blind', 'password_entropy_blind_min', 1478# 'password_entropy_blind_max', 'password_permutations_blind_max', 1479# 'password_entropy_seen' & 'password_permutations_seen' 1480# Arguments : 1. the number of passwords to generate 1481# Throws : Croaks on invalid invocation, invalid args, if there is a 1482# problem generating the passwords, statistics, or converting the 1483# results to a JSON string, or if the JSON module is not 1484# available. 1485# Notes : 1486# See Also : 1487sub passwords_json{ 1488 my @args = @_; 1489 my $self = shift @args; 1490 _force_instance($self); 1491 1492 # validate args 1493 state $args_check = compile(NonZeroPositiveInteger); 1494 my ($num_pws) = $args_check->(@args); 1495 1496 # try generate the passwords and stats - could croak 1497 my @passwords = $self->passwords($num_pws); 1498 my %stats = $self->stats(); 1499 1500 # generate the hashref containing the results 1501 my $response_obj = { 1502 passwords => [@passwords], 1503 stats => { 1504 password_entropy_blind => $stats{password_entropy_blind}, 1505 password_permutations_blind => $_CLASS->_render_bigint($stats{password_permutations_blind}), 1506 password_entropy_blind_min => $stats{password_entropy_blind_min}, 1507 password_permutations_blind_min => $_CLASS->_render_bigint($stats{password_permutations_blind_min}), 1508 password_entropy_blind_max => $stats{password_entropy_blind_max}, 1509 password_permutations_blind_max => $_CLASS->_render_bigint($stats{password_permutations_blind_max}), 1510 password_entropy_seen => $stats{password_entropy_seen}, 1511 password_permutations_seen => $_CLASS->_render_bigint($stats{password_permutations_seen}), 1512 }, 1513 }; 1514 1515 # try generate the JSON string to return 1516 my $json_string = q{}; 1517 eval{ 1518 $json_string = JSON->new()->encode($response_obj); 1519 1; # ensure truthy evaluation on succesful execution 1520 }or do{ 1521 _error("Failed to render hashref as JSON string with error: $EVAL_ERROR"); 1522 }; 1523 1524 # return the JSON string 1525 return $json_string; 1526} 1527 1528#####-SUB-###################################################################### 1529# Type : INSTANCE 1530# Purpose : Return statistics about the instance 1531# Returns : A hash of statistics indexed by the following keys: 1532# * 'dictionary_source' - the source of the word list 1533# * 'dictionary_words_total' - the total number of words loaded 1534# from the dictionary file 1535# * 'dictionary_words_filtered' - the number of words loaded from 1536# the dictionary file that meet the lenght criteria set in the 1537# loaded config 1538# * 'dictionary_words_percent_available' - the percentage of the 1539# total dictionary that is avialable for use with the loaded 1540# config 1541# * 'dictionary_filter_length_min' - the minimum length world 1542# permitted by the filter 1543# * 'dictionary_filter_length_max' - the maximum length world 1544# permitted by the filter 1545# * 'dictionary_contains_accents' - whether or not the filtered 1546# list contains accented letters 1547# * 'password_entropy_blind_min' - the entropy of the shortest 1548# password this config can generate from the point of view of a 1549# brute-force attacker in bits 1550# * 'password_entropy_blind_max' - the entropy of the longest 1551# password this config can generate from the point of view of a 1552# brute-force attacker in bits 1553# * 'password_entropy_blind' - the entropy of the average length 1554# of password generated by this configuration from the point of 1555# view of a brute-force attacker in bits 1556# * 'password_entropy_seen' - the true entropy of passwords 1557# generated by this instance assuming the dictionary and config 1558# are known to the attacker in bits 1559# * 'password_length_min' - the minimum length of passwords 1560# generated with this instance's config 1561# * 'password_length_max' - the maximum length of passwords 1562# generated with this instance's config 1563# * 'password_permutations_blind_min' - the number of permutations 1564# a brute-froce attacker would have to try to be sure of success 1565# on the shortest possible passwords geneated by this instance 1566# as a Math::BigInt object 1567# * 'password_permutations_blind_max' - the number of permutations 1568# a brute-froce attacker would have to try to be sure of success 1569# on the longest possible passwords geneated by this instance as 1570# a Math::BigInt object 1571# * 'password_permutations_blind' - the number of permutations 1572# a brute-froce attacker would have to try to be sure of success 1573# on the average length password geneated by this instance as a 1574# Math::BigInt object 1575# * 'password_permutations_seen' - the number of permutations an 1576# attacker with a copy of the dictionary and config would need to 1577# try to be sure of cracking a password generated by this 1578# instance as a Math::BigInt object 1579# * 'password_random_numbers_required' - the number of random 1580# numbers needed to generate a single password using the loaded 1581# config 1582# * 'passwords_generated' - the number of passwords this instance 1583# has generated 1584# * 'randomnumbers_cached' - the number of random numbers 1585# currently cached within the instance 1586# * 'randomnumbers_cache_increment' - the number of random numbers 1587# generated at once to re-plenish the cache when it's empty 1588# * 'randomnumbers_source' - the name of the class used to 1589# generate random numbers 1590# Arguments : NONE 1591# Throws : Croaks on invalid invocation 1592# Notes : 1593# See Also : 1594sub stats{ 1595 my $self = shift; 1596 _force_instance($self); 1597 1598 # create a hash to assemble all the stats into 1599 my %stats = (); 1600 1601 # deal with the config-specific stats 1602 my %config_stats = $_CLASS->config_stats($self->{_CONFIG}); 1603 $stats{password_length_min} = $config_stats{length_min}; 1604 $stats{password_length_max} = $config_stats{length_max}; 1605 $stats{password_random_numbers_required} = $config_stats{random_numbers_required}; 1606 1607 # deal with the dictionary file 1608 my %dict_stats = $self->_calcualte_dictionary_stats(); 1609 $stats{dictionary_source} = $dict_stats{source}; 1610 $stats{dictionary_words_total} = $dict_stats{num_words_total}; 1611 $stats{dictionary_words_filtered} = $dict_stats{num_words_filtered}; 1612 $stats{dictionary_words_percent_available} = $dict_stats{percent_words_available}; 1613 $stats{dictionary_filter_length_min} = $dict_stats{filter_length_min}; 1614 $stats{dictionary_filter_length_max} = $dict_stats{filter_length_max}; 1615 $stats{dictionary_contains_accents} = $dict_stats{contains_accents}; 1616 1617 # deal with the entropy stats 1618 $stats{password_entropy_blind_min} = $self->{_CACHE_ENTROPYSTATS}->{entropy_blind_min}; 1619 $stats{password_entropy_blind_max} = $self->{_CACHE_ENTROPYSTATS}->{entropy_blind_max}; 1620 $stats{password_entropy_blind} = $self->{_CACHE_ENTROPYSTATS}->{entropy_blind}; 1621 $stats{password_entropy_seen} = $self->{_CACHE_ENTROPYSTATS}->{entropy_seen}; 1622 $stats{password_permutations_blind_min} = $self->{_CACHE_ENTROPYSTATS}->{permutations_blind_min}; 1623 $stats{password_permutations_blind_max} = $self->{_CACHE_ENTROPYSTATS}->{permutations_blind_max}; 1624 $stats{password_permutations_blind} = $self->{_CACHE_ENTROPYSTATS}->{permutations_blind}; 1625 $stats{password_permutations_seen} = $self->{_CACHE_ENTROPYSTATS}->{permutations_seen}; 1626 1627 # deal with password counter 1628 $stats{passwords_generated} = $self->{_PASSWORD_COUNTER}; 1629 1630 # deal with the random number generator 1631 $stats{randomnumbers_cached} = scalar @{$self->{_CACHE_RANDOM}}; 1632 $stats{randomnumbers_source} = blessed($self->{_RNG}); 1633 1634 # return the stats 1635 return %stats; 1636} 1637 1638#####-SUB-###################################################################### 1639# Type : INSTANCE 1640# Purpose : Represent the current state of the instance as a string. 1641# Returns : Returns a multi-line string as as scalar containing details of the 1642# loaded dictionary file, config, and caches 1643# Arguments : NONE 1644# Throws : Croaks on invalid invocation 1645# Notes : 1646# See Also : 1647sub status{ 1648 my $self = shift; 1649 _force_instance($self); 1650 1651 # assemble the response 1652 my %stats = $self->stats(); 1653 my $status = q{}; 1654 1655 # the dictionary 1656 $status .= "*DICTIONARY*\n"; 1657 $status .= "Source: $stats{dictionary_source}\n"; 1658 $status .= "# words: $stats{dictionary_words_total}\n"; 1659 $status .= "# words of valid length: $stats{dictionary_words_filtered} ($stats{dictionary_words_percent_available}%)\n"; 1660 $status .= 'Contains Accented Characters: '.($stats{dictionary_contains_accents} ? 'YES' : 'NO')."\n"; 1661 1662 # the config 1663 $status .= "\n*CONFIG*\n"; 1664 $status .= $self->config_as_string(); 1665 1666 # the random number cache 1667 $status .= "\n*RANDOM NUMBER CACHE*\n"; 1668 $status .= "Random Number Generator: $stats{randomnumbers_source}\n"; 1669 $status .= "# in cache: $stats{randomnumbers_cached}\n"; 1670 1671 # password statistics 1672 $status .= "\n*PASSWORD STATISTICS*\n"; 1673 if($stats{password_length_min} == $stats{password_length_max}){ 1674 $status .= "Password length: $stats{password_length_max}\n"; 1675 $status .= 'Permutations (brute-force): '.$_CLASS->_render_bigint($stats{password_permutations_blind_max})."\n"; 1676 }else{ 1677 $status .= "Password length: between $stats{password_length_min} & $stats{password_length_max}\n"; 1678 $status .= 'Permutations (brute-force): between '.$_CLASS->_render_bigint($stats{password_permutations_blind_min}).q{ & }.$_CLASS->_render_bigint($stats{password_permutations_blind_max}).q{ (average }.$_CLASS->_render_bigint($stats{password_permutations_blind}).")\n"; 1679 } 1680 $status .= 'Permutations (given dictionary & config): '.$_CLASS->_render_bigint($stats{password_permutations_seen})."\n"; 1681 if($stats{password_length_min} == $stats{password_length_max}){ 1682 $status .= "Entropy (brute-force): $stats{password_entropy_blind_max}bits\n"; 1683 }else{ 1684 $status .= "Entropy (Brute-Force): between $stats{password_entropy_blind_min}bits and $stats{password_entropy_blind_max}bits (average $stats{password_entropy_blind}bits)\n"; 1685 } 1686 $status .= "Entropy (given dictionary & config): $stats{password_entropy_seen}bits\n"; 1687 $status .= "# Random Numbers needed per-password: $stats{password_random_numbers_required}\n"; 1688 $status .= "Passwords Generated: $stats{passwords_generated}\n"; 1689 1690 # debug-only 1691 if($_Types_CLASS::_DEBUG){ ## no critic (ProtectPrivateVars) 1692 $status .= "\n*DEBUG INFO*\n"; 1693 if($_Types_CLASS::_CAN_STACK_TRACE){ ## no critic (ProtectPrivateVars) 1694 $status .= "Devel::StackTrace IS installed\n"; 1695 }else{ 1696 $status .= "Devel::StackTrace is NOT installed\n"; 1697 } 1698 } 1699 1700 # return the status 1701 return $status; 1702} 1703 1704# 1705# Regular Subs----------------------------------------------------------------- 1706# 1707 1708#####-SUB-###################################################################### 1709# Type : SUBROUTINE 1710# Purpose : A functional interface to this library (exported) 1711# Returns : A random password as a scalar 1712# Arguments : See the constructor 1713# Throws : Croaks on error 1714# Notes : See the Constructor 1715# See Also : For valid configuarion options see POD documentation below 1716sub hsxkpasswd{ 1717 my @constructor_args = @_; 1718 1719 # try initialise an xkpasswd object 1720 my $hsxkpasswd; 1721 eval{ 1722 $hsxkpasswd = $_CLASS->new(@constructor_args); 1723 1; # ensure truthy evaliation on successful execution 1724 } or do { 1725 _error("Failed to generate password with the following error: $EVAL_ERROR"); 1726 }; 1727 1728 # genereate and return a password - could croak 1729 return $hsxkpasswd->password(); 1730} 1731 1732# 1733# 'Private' functions --------------------------------------------------------- 1734# 1735 1736#####-SUB-###################################################################### 1737# Type : INSTANCE ('PRIVATE') 1738# Purpose : Clone the instance's config hashref 1739# Returns : a hashref 1740# Arguments : NONE 1741# Throws : Croaks if called in an invalid way 1742# Notes : 1743# See Also : 1744sub _clone_config{ 1745 my $self = shift; 1746 _force_instance($self); 1747 1748 # build the clone 1749 my $clone = $_CLASS->clone_config($self->{_CONFIG}); 1750 1751 # if, and only if, debugging, validate the cloned config so errors in the 1752 # cloning code will trigger an exception 1753 if($self->{debug}){ 1754 Config->check($clone) || _error('cloning error - clone is invalid: '.Config->get_message($clone)); 1755 } 1756 1757 # return the clone 1758 return $clone; 1759} 1760 1761#####-SUB-###################################################################### 1762# Type : CLASS 1763# Purpose : Distil all alphabets in a config hashref 1764# Returns : always returns 1 (to keep perlcritic happy) 1765# Arguments : 1) a config hashref 1766# Throws : Croaks on invalid invocation or args 1767# Notes : 1768# See Also : 1769sub _distil_alphabets_inplace{ 1770 my @args = @_; 1771 my $class = shift @args; 1772 _force_class($class); 1773 1774 # validate args 1775 state $args_check = compile(Config); 1776 my ($config) = $args_check->(@args); 1777 1778 # distil all three possible alphabet keys, if pressent 1779 if($config->{symbol_alphabet}){ 1780 $config->{symbol_alphabet} = [$_CLASS->distil_to_symbol_alphabet($config->{symbol_alphabet})]; 1781 } 1782 if($config->{padding_alphabet}){ 1783 $config->{padding_alphabet} = [$_CLASS->distil_to_symbol_alphabet($config->{padding_alphabet})]; 1784 } 1785 if($config->{separator_alphabet}){ 1786 $config->{separator_alphabet} = [$_CLASS->distil_to_symbol_alphabet($config->{separator_alphabet})]; 1787 } 1788 1789 # an explicit return 1790 return 1; 1791} 1792 1793#####-SUB-###################################################################### 1794# Type : CLASS (PRIVATE) 1795# Purpose : Filter a word list based on word length 1796# Returns : An array of words as scalars. 1797# Arguments : 1. a reference to the array of words to filter. 1798# 2. the minimum allowed word length 1799# 3. the maximum allowed word length 1800# 4. OPTIONAL - named argument allow_accents with a value of 0 or 1801# 1. If 1 is passed, accents will not be stripped from words, 1802# otherwise they will. 1803# Throws : Croaks on invalid invocation, or if too few matching words found. 1804# Notes : Unless the fourth argument is a truthy value, accents will be 1805# stripped from the words. 1806# See Also : 1807sub _filter_word_list{ 1808 my @args = @_; 1809 my $class = shift @args; 1810 _force_class($class); 1811 1812 # validate args 1813 state $args_check = compile( 1814 ArrayRef[Str], 1815 WordLength, 1816 WordLength, 1817 slurpy Dict[allow_accents => Optional[TrueFalse]] 1818 ); 1819 my ($word_list_ref, $min_len, $max_len, $options) = $args_check->(@args); 1820 my $allow_accents = $options->{allow_accents} || 0; 1821 unless($max_len >= $min_len){ 1822 _error("minimum length (recived $min_len) cannot be greater than maximum length (received $max_len)"); 1823 } 1824 1825 #build the array of words of appropriate length 1826 my @ans = (); 1827 WORD: 1828 foreach my $word (@{$word_list_ref}){ 1829 # calcualte the grapheme length 1830 my $grapheme_length = $_CLASS->_grapheme_length($word); 1831 1832 # skip words shorter than the minimum 1833 next WORD if $grapheme_length < $min_len; 1834 1835 # skip words longer than the maximum 1836 next WORD if $grapheme_length > $max_len; 1837 1838 # strip accents unless they are explicitly allowed by the config 1839 unless($allow_accents){ 1840 $word = unidecode($word); 1841 } 1842 1843 # store the word in the filtered list 1844 push @ans, $word; 1845 } 1846 1847 # return the list 1848 return @ans; 1849} 1850 1851#####-SUB-###################################################################### 1852# Type : CLASS (PRIVATE) 1853# Purpose : Determine whether a word list contains accented characters 1854# Returns : 1 if the word list does contain accented characters, and 0 if it 1855# does not. 1856# Arguments : 1. A reference to an array of words to test 1857# Throws : NOTHING 1858# Notes : 1859# See Also : 1860sub _contains_accented_letters{ 1861 my @args = @_; 1862 my $class = shift @args; 1863 _force_class($class); 1864 1865 # validate args 1866 state $args_check = compile(ArrayRef[Str]); 1867 my ($word_list_ref) = $args_check->(@args); 1868 1869 # assume no accented characters, test until 1 is found 1870 my $accent_found = 0; 1871 WORD: 1872 foreach my $word (@{$word_list_ref}){ 1873 # check for accents by stripping accents and comparing to original 1874 my $word_accents_stripped = unidecode($word); 1875 unless($word eq $word_accents_stripped){ 1876 $accent_found = 1; 1877 last WORD; 1878 } 1879 } 1880 1881 # return the list 1882 return $accent_found; 1883} 1884 1885#####-SUB-###################################################################### 1886# Type : INSTANCE (PRIVATE) 1887# Purpose : Generate a random integer greater than 0 and less than a given 1888# maximum value. 1889# Returns : A random integer as a scalar. 1890# Arguments : 1. the min value for the random number (as a positive integer) 1891# Throws : Croaks if invoked in an invalid way, with invalid args, of if 1892# there is a problem generating random numbers (should the cache) 1893# be empty. 1894# Notes : The random cache is used as the source for the randomness. If the 1895# random pool is empty, this function will replenish it. 1896# See Also : 1897sub _random_int{ 1898 my @args = @_; 1899 my $self = shift @args; 1900 _force_instance($self); 1901 1902 # validate args 1903 state $args_check = compile(NonZeroPositiveInteger); 1904 my ($max) = $args_check->(@args); 1905 1906 # calculate the random number 1907 my $ans = ($self->_rand() * 1_000_000) % $max; 1908 1909 # return it 1910 _debug("returning $ans (max=$max)"); 1911 return $ans; 1912} 1913 1914#####-SUB-###################################################################### 1915# Type : INSTANCE (PRIVATE) 1916# Purpose : Generate a number of random integers. 1917# Returns : A scalar containing a number of random integers. 1918# Arguments : 1. The number of random integers to generate 1919# Throws : Croaks on invalid invocation, or if there is a problem generating 1920# the needed randomness. 1921# Notes : 1922# See Also : 1923sub _random_digits{ 1924 my @args = @_; 1925 my $self = shift @args; 1926 _force_instance($self); 1927 1928 # validate args 1929 state $args_check = compile(NonZeroPositiveInteger); 1930 my ($num) = $args_check->(@args); 1931 1932 # assemble the response 1933 my $ans = q{}; 1934 foreach my $n (1..$num){ 1935 $ans .= $self->_random_int(10); 1936 } 1937 1938 # return the response 1939 return $ans; 1940} 1941 1942#####-SUB-###################################################################### 1943# Type : INSTANCE (PRIVATE) 1944# Purpose : Return the next random number in the cache, and if needed, 1945# replenish it. 1946# Returns : A decimal number between 0 and 1 1947# Arguments : NONE 1948# Throws : Croaks if invoked in an invalid way, or if there is problem 1949# replenishing the random cache. 1950# Notes : 1951# See Also : 1952sub _rand{ 1953 my $self = shift; 1954 _force_instance($self); 1955 1956 # get the next random number from the cache 1957 my $num = shift @{$self->{_CACHE_RANDOM}}; 1958 if(!defined $num){ 1959 # the cache was empty - so try top up the random cache - could croak 1960 _debug('random cache empty - attempting to replenish'); 1961 $self->_increment_random_cache(); 1962 1963 # try shift again 1964 $num = shift @{$self->{_CACHE_RANDOM}}; 1965 } 1966 1967 # make sure we got a valid random number 1968 unless(defined $num && $num =~ m/^\d+([.]\d+)?$/sx && $num >= 0 && $num <= 1){ 1969 _error('found invalid entry in random cache'); 1970 } 1971 1972 # return the random number 1973 _debug("returning $num (".(scalar @{$self->{_CACHE_RANDOM}}).' remaining in cache)'); 1974 return $num; 1975} 1976 1977#####-SUB-###################################################################### 1978# Type : INSTANCE (PRIVATE) 1979# Purpose : Append random numbers to the cache. 1980# Returns : Always returns 1. 1981# Arguments : NONE 1982# Throws : Croaks if incorrectly invoked or if the random generating 1983# function fails to produce random numbers. 1984# Notes : 1985# See Also : 1986sub _increment_random_cache{ 1987 my $self = shift; 1988 _force_instance($self); 1989 1990 # genereate the random numbers 1991 my @random_numbers = $self->{_RNG}->random_numbers($_CLASS->config_random_numbers_required($self->{_CONFIG})); 1992 _debug('generated '.(scalar @random_numbers).' random numbers ('.(join q{, }, @random_numbers).')'); 1993 1994 # validate them 1995 unless(scalar @random_numbers){ 1996 _error('random function did not return any random numbers'); 1997 } 1998 foreach my $num (@random_numbers){ 1999 unless($num =~ m/^1|(0([.]\d+)?)$/sx){ 2000 _error("random function returned and invalid value ($num)"); 2001 } 2002 } 2003 2004 # add them to the cache 2005 foreach my $num (@random_numbers){ 2006 push @{$self->{_CACHE_RANDOM}}, $num; 2007 } 2008 2009 # always return 1 (to keep PerlCritic happy) 2010 return 1; 2011} 2012 2013#####-SUB-###################################################################### 2014# Type : INSTANCE (PRIVATE) 2015# Purpose : Get the required number of random words from the loaded words 2016# file 2017# Returns : An array of words 2018# Arguments : NONE 2019# Throws : Croaks on invalid invocation or error generating random numbers 2020# Notes : The number of words generated is determined by the num_words 2021# config key. 2022# See Also : 2023sub _random_words{ 2024 my $self = shift; 2025 _force_instance($self); 2026 2027 # get the random words 2028 my @ans = (); 2029 _debug('about to generate '.$self->{_CONFIG}->{num_words}.' words'); 2030 while ((scalar @ans) < $self->{_CONFIG}->{num_words}){ 2031 my $word = $self->{_CACHE_DICTIONARY_LIMITED}->[$self->_random_int(scalar @{$self->{_CACHE_DICTIONARY_LIMITED}})]; 2032 _debug("generate word=$word"); 2033 push @ans, $word; 2034 } 2035 2036 # return the list of random words 2037 _debug('returning '.(scalar @ans).' words'); 2038 return @ans; 2039} 2040 2041#####-SUB-###################################################################### 2042# Type : INSTANCE (PRIVATE) 2043# Purpose : Get the separator character to use based on the loaded config. 2044# Returns : A scalar containing the separator, which could be an empty string. 2045# Arguments : NONE 2046# Throws : Croaks on invalid invocation, or if there is a problem generating 2047# any needed random numbers. 2048# Notes : The character returned is controlled by the config variable 2049# separator_character 2050# See Also : 2051sub _separator{ 2052 my $self = shift; 2053 _force_instance($self); 2054 2055 # figure out the separator character 2056 my $sep = $self->{_CONFIG}->{separator_character}; 2057 if ($sep eq 'NONE'){ 2058 $sep = q{}; 2059 }elsif($sep eq 'RANDOM'){ 2060 if(defined $self->{_CONFIG}->{separator_alphabet}){ 2061 $sep = $self->{_CONFIG}->{separator_alphabet}->[$self->_random_int(scalar @{$self->{_CONFIG}->{separator_alphabet}})]; 2062 }else{ 2063 $sep = $self->{_CONFIG}->{symbol_alphabet}->[$self->_random_int(scalar @{$self->{_CONFIG}->{symbol_alphabet}})]; 2064 } 2065 } 2066 2067 # return the separator character 2068 return $sep 2069} 2070 2071#####-SUB-###################################################################### 2072# Type : INSTANCE (PRIVATE) 2073# Purpose : Return the padding character based on the loaded config. 2074# Returns : A scalar containing the padding character, which could be an 2075# empty string. 2076# Arguments : 1. the separator character being used to generate the password 2077# Throws : Croaks on invalid invocation, or if there is a problem geneating 2078# any needed random numbers. 2079# Notes : The character returned is determined by a combination of the 2080# padding_type & padding_character config variables. 2081# See Also : 2082sub _padding_char{ 2083 my $self = shift; 2084 my $sep = shift; 2085 2086 2087 # validate args - doing it the old-fassioned way because the separator will 2088 # be an empty string if the separator is set to 'NONE' 2089 _force_instance($self); 2090 if($sep){ 2091 unless(Symbol->check($sep)){ 2092 _error('first argument must be an empty string or a valid Symbol'); 2093 } 2094 } 2095 2096 # if there is no padding character needed, return an empty string 2097 if($self->{_CONFIG}->{padding_type} eq 'NONE'){ 2098 return q{}; 2099 } 2100 2101 # if we got here we do need a character, so generate one as appropriate 2102 my $padc = $self->{_CONFIG}->{padding_character}; 2103 if($padc eq 'SEPARATOR'){ 2104 $padc = $sep; 2105 }elsif($padc eq 'RANDOM'){ 2106 if(defined $self->{_CONFIG}->{padding_alphabet}){ 2107 $padc = $self->{_CONFIG}->{padding_alphabet}->[$self->_random_int(scalar @{$self->{_CONFIG}->{padding_alphabet}})]; 2108 }else{ 2109 $padc = $self->{_CONFIG}->{symbol_alphabet}->[$self->_random_int(scalar @{$self->{_CONFIG}->{symbol_alphabet}})]; 2110 } 2111 } 2112 2113 # return the padding character 2114 return $padc; 2115} 2116 2117#####-SUB-###################################################################### 2118# Type : INSTANCE (PRIVATE) 2119# Purpose : Apply the case transform (if any) specified in the loaded config. 2120# Returns : Always returns 1 (to keep PerlCritic happy) 2121# Arguments : 1. A reference to the array contianing the words to be 2122# transformed. 2123# Throws : Croaks on invalid invocation or if there is a problem generating 2124# any needed random numbers. 2125# Notes : The transformations applied are controlled by the case_transform 2126# config variable. 2127# See Also : 2128sub _transform_case{ 2129 my @args = @_; 2130 my $self = shift @args; 2131 _force_instance($self); 2132 2133 # validate args 2134 state $args_check = compile(ArrayRef[Str]); 2135 my ($words_ref) = $args_check->(@args); 2136 2137 # if the transform is set to nothing, then just return 2138 if($self->{_CONFIG}->{case_transform} eq 'NONE'){ 2139 return 1; 2140 } 2141 2142 # apply the appropriate transform 2143 ## no critic (ProhibitCascadingIfElse); 2144 if($self->{_CONFIG}->{case_transform} eq 'UPPER'){ 2145 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2146 $words_ref->[$i] = uc $words_ref->[$i]; 2147 } 2148 }elsif($self->{_CONFIG}->{case_transform} eq 'LOWER'){ 2149 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2150 $words_ref->[$i] = lc $words_ref->[$i]; 2151 } 2152 }elsif($self->{_CONFIG}->{case_transform} eq 'CAPITALISE'){ 2153 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2154 $words_ref->[$i] = ucfirst lc $words_ref->[$i]; 2155 } 2156 }elsif($self->{_CONFIG}->{case_transform} eq 'INVERT'){ 2157 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2158 $words_ref->[$i] = lcfirst uc $words_ref->[$i]; 2159 } 2160 }elsif($self->{_CONFIG}->{case_transform} eq 'ALTERNATE'){ 2161 # randomly decide whether to capitalise on odd or even 2162 my $rand_bias = ($self->_random_int(2) % 2 == 0) ? 1 : 0; 2163 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2164 my $word = $words_ref->[$i]; 2165 if(($i + $rand_bias) % 2 == 0){ 2166 $word = lc $word; 2167 }else{ 2168 $word = uc $word; 2169 } 2170 $words_ref->[$i] = $word; 2171 } 2172 }elsif($self->{_CONFIG}->{case_transform} eq 'RANDOM'){ 2173 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2174 my $word = $words_ref->[$i]; 2175 if($self->_random_int(2) % 2 == 0){ 2176 $word = uc $word; 2177 }else{ 2178 $word = lc $word; 2179 } 2180 $words_ref->[$i] = $word; 2181 } 2182 } 2183 ## use critic 2184 2185 return 1; # just to to keep PerlCritic happy 2186} 2187 2188#####-SUB-###################################################################### 2189# Type : INSTANCE (PRIVATE) 2190# Purpose : Apply any case transforms specified in the loaded config. 2191# Returns : Always returns 1 (to keep PerlCritic happy) 2192# Arguments : 1. a reference to an array containing the words that will make up 2193# the password. 2194# Throws : Croaks on invalid invocation or invalid args. 2195# Notes : The substitutions that will be applied are specified in the 2196# character_substitutions config variable. 2197# See Also : 2198sub _substitute_characters{ 2199 my @args = @_; 2200 my $self = shift @args; 2201 _force_instance($self); 2202 2203 # validate args 2204 state $args_check = compile(ArrayRef[Str]); 2205 my ($words_ref) = $args_check->(@args); 2206 2207 # if no substitutions are defined, do nothing 2208 unless(defined $self->{_CONFIG}->{character_substitutions} && (scalar keys %{$self->{_CONFIG}->{character_substitutions}})){ 2209 return 1; 2210 } 2211 2212 # If we got here, go ahead and apply the substitutions 2213 foreach my $i (0..((scalar @{$words_ref}) - 1)){ 2214 my $word = $words_ref->[$i]; 2215 foreach my $char (keys %{$self->{_CONFIG}->{character_substitutions}}){ 2216 my $sub = $self->{_CONFIG}->{character_substitutions}->{$char}; 2217 $word =~ s/$char/$sub/sxg; 2218 } 2219 $words_ref->[$i] = $word; 2220 } 2221 2222 # always return 1 to keep PerlCritic happy 2223 return 1; 2224} 2225 2226#####-SUB-###################################################################### 2227# Type : CLASS (PRIVATE) 2228# Purpose : Perform sanity checks on all the config key definitions 2229# Returns : Always returns 1 (to keep PerlCritic happy) 2230# Arguments : NONE 2231# Throws : Croaks if there is a problem with a key definition. 2232# Notes : The function is designed to be called from the constructor when 2233# in debug mode, so it prints information on what it's doing 2234# to STDERR. 2235# See Also : 2236sub _check_config_key_definitions{ 2237 # get a reference to the config key definitions form the Types class 2238 my $key_definitions = $_TYPES_CLASS->_config_keys(); 2239 2240 # loop through each key definition and do some sanity checks 2241 my $num_problems = 0; 2242 foreach my $key_name ($_CLASS->defined_config_keys()){ 2243 _debug("checking config key '$key_name'"); 2244 unless(ConfigKeyDefinition->check($key_definitions->{$key_name})){ 2245 _warn(ConfigKeyDefinition->get_message($key_definitions->{$key_name})); 2246 $num_problems++; 2247 } 2248 } 2249 if($num_problems == 0){ 2250 _debug('all config key definitions OK'); 2251 }else{ 2252 _error("there are errors in $num_problems config key definitions - fix these before continuing"); 2253 } 2254 2255 # to keep perlcritic happy 2256 return 1; 2257} 2258 2259#####-SUB-###################################################################### 2260# Type : CLASS (PRIVATE) 2261# Purpose : Perform sanity checks on all defined presets 2262# Returns : Always returns 1 (to keep perlcritic happy) 2263# Arguments : NONE 2264# Throws : Croaks if there is a problem with a preset. 2265# Notes : The function is designed to be called from the constructor when 2266# in debug mode, so it prints information on what it's doing 2267# to STDERR. 2268# See Also : 2269sub _check_preset_definitions{ 2270 # get a reference to the preset definitions from the types class 2271 my $preset_defs = $_TYPES_CLASS->_presets(); 2272 2273 # loop through all presets and perform sanity checks 2274 my $num_problems = 0; 2275 foreach my $preset_name (sort keys %{$preset_defs}){ 2276 _debug("checking preset '$preset_name'"); 2277 unless(PresetDefinition->check($preset_defs->{$preset_name})){ 2278 _warn(PresetDefinition->get_message($preset_defs->{$preset_name})); 2279 $num_problems++; 2280 } 2281 } 2282 if($num_problems == 0){ 2283 _debug('all presets OK'); 2284 }else{ 2285 _error("there are errors in $num_problems presets - fix these before continuing"); 2286 } 2287 2288 # to keep perlcritic happy 2289 return 1; 2290} 2291 2292#####-SUB-###################################################################### 2293# Type : CLASS (PRIVATE) 2294# Purpose : Create an RNG object that is as secure as possible. 2295# Returns : An instance of a class that extends Crypt::HSXKPasswd::RNG. 2296# Arguments : NONE 2297# Throws : This function issues a warning if it has to fall back to 2298# Crypt::HSXKPasswd::RNG::Basic. 2299# Notes : This function works its way through the constructurs for the 2300# following RNG classes in the following order, returing the first 2301# successfully instantiate object: 2302# 1) Crypt::HSXKPasswd::RNG::Math_Random_Secure (using 2303# Math::Random::Secure) 2304# 2) Crypt::HSXKPasswd::RNG::Data_Entropy (using 2305# Data::Entropy::Algorithms) 2306# 3) Crypt::HSXKPasswd::RNG::DevUrandom (reads from /dev/urandom) 2307# 4) Crypt::HSXKPasswd::RNG::Basic (using Perl's built-in rand()) 2308# This ordering is based on security and speed - all but Basic are 2309# good from a secutrity point of view, but Math::Random::Secure is 2310# over six times faster than Data::Entropy::Algorithms, so it is 2311# reduced to second place. Speed tested wth the commands: 2312# time perl -MMath::Random::Secure -e "foreach my \$n (0..1000000){Math::Random::Secure::rand();}" 2313# time perl -MData::Entropy::Algorithms -e "foreach my \$n (0..1000000){Data::Entropy::Algorithms::rand();}" 2314# See Also : 2315sub _best_available_rng{ 2316 # try the good entropy sources in order 2317 my $rng; 2318 eval{ 2319 $rng = Crypt::HSXKPasswd::RNG::Math_Random_Secure->new(); # will return a truthy value on success 2320 }or do{ 2321 _debug("Failed to instantiate a Crypt::HSXKPasswd::RNG::Math_Random_Secure object with error: $EVAL_ERROR"); 2322 }; 2323 return $rng if $rng; 2324 eval{ 2325 $rng = Crypt::HSXKPasswd::RNG::Data_Entropy->new(); # will return a truthy value on success 2326 }or do{ 2327 _debug("Failed to instantiate a Crypt::HSXKPasswd::RNG::Data_Entropy object with error: $EVAL_ERROR"); 2328 }; 2329 return $rng if $rng; 2330 eval{ 2331 $rng = Crypt::HSXKPasswd::RNG::DevUrandom->new(); # will return a truthy value on success 2332 }or do{ 2333 _debug("Failed to instantiate a Crypt::HSXKPasswd::RNG::DevUrandom object with error: $EVAL_ERROR"); 2334 }; 2335 return $rng if $rng; 2336 2337 # if we got here, no secure RNGs were avaialable, so warn, then return an instance of the basic RNG 2338 _warn(q{using Perl's built-in rand() function for random number generation. This is secure enough for most users, but you can get more secure random numbers by installing Math::Random::Secure or Data::Entropy::Algorithms}); 2339 return Crypt::HSXKPasswd::RNG::Basic->new(); 2340} 2341 2342#####-SUB-###################################################################### 2343# Type : INSTANCE (PRIVATE) 2344# Purpose : Gather entropy stats for the combination of the loaded config 2345# and dictionary. 2346# Returns : A hash of stats indexed by: 2347# * 'permutations_blind_min' - the number of permutations to be 2348# tested by an attacker with no knowledge of the dictionary file 2349# used, or the config used, assuming the minimum possible 2350# password length from the given config (as BigInt) 2351# * 'permutations_blind_max' - the number of permutations to be 2352# tested by an attacker with no knowledge of the dictionary file 2353# used, or the cofig file used, assuming the maximum possible 2354# password length fom the given config (as BigInt) 2355# * 'permutations_blind' - the number of permutations for the 2356# average password length for the given config (as BigInt) 2357# * 'permutations_seen' - the number of permutations to be tested 2358# by an attacker with full knowledge of the dictionary file and 2359# configuration used (as BigInt) 2360# * 'entropy_blind_min' - permutations_blind_min converted to bits 2361# * 'entropy_blind_max' - permutations_blind_max converted to bits 2362# * 'entropy_blind' - permutations_blind converted to bits 2363# * 'entropy_seen' - permutations_seen converted to bits 2364# Arguments : NONE 2365# Throws : Croaks on invalid invocation 2366# Notes : This function uses config_stats() to determined the longest and 2367# shortest password lengths, so the caveat that function has 2368# when it comes to multi-character substitutions applies here too. 2369# This function assumes no accented characters (at least for now). 2370# For the blind calculations, if any single symbol is present, a 2371# search-space of 33 symbols is assumed (same as password 2372# haystacks page) 2373# See Also : config_stats() 2374sub _calculate_entropy_stats{ 2375 my $self = shift; 2376 _force_instance($self); 2377 2378 my %ans = (); 2379 2380 # get the password length details for the config 2381 my %config_stats = $_CLASS->config_stats($self->{_CONFIG}, suppress_warnings => 1); 2382 my $b_length_min = Math::BigInt->new($config_stats{length_min}); 2383 my $b_length_max = Math::BigInt->new($config_stats{length_max}); 2384 2385 # calculate the blind permutations - (based purely on length and alphabet) 2386 my $alphabet_count = 26; # all passwords have at least one case of letters 2387 if($self->{_CONFIG}->{case_transform} =~ m/^(ALTERNATE)|(CAPITALISE)|(INVERT)|(RANDOM)$/sx){ 2388 $alphabet_count += 26; # these configs guarantee a mix of cases 2389 } 2390 if($self->{_CONFIG}->{padding_digits_before} > 0 || $self->{_CONFIG}->{padding_digits_after} > 0){ 2391 $alphabet_count += 10; # these configs guarantee digits in the mix 2392 } 2393 if($self->_passwords_will_contain_symbol() || $self->{_CACHE_CONTAINS_ACCENTS}){ 2394 $alphabet_count += 33; # the config almost certainly includes a symbol, so add 33 to the alphabet (like password haystacks does) 2395 } 2396 my $b_alphabet_count = Math::BigInt->new($alphabet_count); 2397 my $length_avg = round(($config_stats{length_min} + $config_stats{length_max})/2); 2398 $ans{permutations_blind_min} = $b_alphabet_count->copy()->bpow($b_length_min); #$alphabet_count ** $length_min; 2399 _debug('got permutations_blind_min='.$ans{permutations_blind_min}); 2400 $ans{permutations_blind_max} = $b_alphabet_count->copy()->bpow($b_length_max); #$alphabet_count ** $length_max; 2401 _debug('got permutations_blind_max='.$ans{permutations_blind_max}); 2402 $ans{permutations_blind} = $b_alphabet_count->copy()->bpow(Math::BigInt->new($length_avg)); #$alphabet_count ** $length_avg; 2403 _debug('got permutations_blind='.$ans{permutations_blind}); 2404 2405 # calculate the seen permutations 2406 my $num_words = scalar @{$self->{_CACHE_DICTIONARY_LIMITED}}; 2407 my $b_num_words = Math::BigInt->new($num_words); 2408 my $b_seen_perms = Math::BigInt->new('0'); 2409 # start with the permutations from the chosen words 2410 $b_seen_perms->badd($b_num_words->copy()->bpow(Math::BigInt->new($self->{_CONFIG}->{num_words}))); # += $num_words ** $self->{_CONFIG}->{num_words}; 2411 # then add the extra randomness from the case transformations (if any) 2412 if($self->{_CONFIG}->{case_transform} eq 'RANDOM'){ 2413 # multiply by two for each word 2414 for my $n (1..$self->{_CONFIG}->{num_words}){ 2415 $b_seen_perms->bmul(Math::BigInt->new(2)); 2416 } 2417 }elsif($self->{_CONFIG}->{case_transform} eq 'ALTERNATE'){ 2418 # multiply by two for the one random decision about whether or capitalise the odd or even words 2419 $b_seen_perms->bmul(Math::BigInt->new(2)); 2420 } 2421 # multiply in the permutations from the separator (if any - i.e. if it's randomly chosen) 2422 if($self->{_CONFIG}->{separator_character} eq 'RANDOM'){ 2423 if(defined $self->{_CONFIG}->{separator_alphabet}){ 2424 $b_seen_perms->bmul(Math::BigInt->new(scalar @{$self->{_CONFIG}->{separator_alphabet}})); 2425 }else{ 2426 $b_seen_perms->bmul(Math::BigInt->new(scalar @{$self->{_CONFIG}->{symbol_alphabet}})); 2427 } 2428 } 2429 # multiply in the permutations from the padding character (if any - i.e. if it's randomly chosen) 2430 if($self->{_CONFIG}->{padding_type} ne 'NONE' && $self->{_CONFIG}->{padding_character} eq 'RANDOM'){ 2431 if(defined $self->{_CONFIG}->{padding_alphabet}){ 2432 $b_seen_perms->bmul(Math::BigInt->new(scalar @{$self->{_CONFIG}->{padding_alphabet}})); 2433 }else{ 2434 $b_seen_perms->bmul(Math::BigInt->new(scalar @{$self->{_CONFIG}->{symbol_alphabet}})); 2435 } 2436 } 2437 # multiply in the permutations from the padding digits (if any) 2438 my $num_padding_digits = $self->{_CONFIG}->{padding_digits_before} + $self->{_CONFIG}->{padding_digits_after}; 2439 while($num_padding_digits > 0){ 2440 $b_seen_perms->bmul(Math::BigInt->new('10')); 2441 $num_padding_digits--; 2442 } 2443 $ans{permutations_seen} = $b_seen_perms; 2444 _debug('got permutations_seen='.$ans{permutations_seen}); 2445 2446 # calculate the entropy values based on the permutations 2447 $ans{entropy_blind_min} = $ans{permutations_blind_min}->copy()->blog(2)->numify(); 2448 _debug('got entropy_blind_min='.$ans{entropy_blind_min}); 2449 $ans{entropy_blind_max} = $ans{permutations_blind_max}->copy()->blog(2)->numify(); 2450 _debug('got entropy_blind_max='.$ans{entropy_blind_max}); 2451 $ans{entropy_blind} = $ans{permutations_blind}->copy()->blog(2)->numify(); 2452 _debug('got entropy_blind='.$ans{entropy_blind}); 2453 $ans{entropy_seen} = $ans{permutations_seen}->copy()->blog(2)->numify(); 2454 _debug('got entropy_seen='.$ans{entropy_seen}); 2455 2456 # return the stats 2457 return %ans; 2458} 2459 2460#####-SUB-###################################################################### 2461# Type : INSTANCE (PRIVATE) 2462# Purpose : Calculate statistics on the loaded dictionary file 2463# Returns : A hash of statistics indexed by: 2464# * 'source' - the source for the word list 2465# * 'filter_length_min' - the minimum allowed word length 2466# * 'filter_length_max' - the maximum allowed word length 2467# * 'num_words_total' - the number of words in the un-filtered 2468# dictionary file 2469# * 'num_words_filtered' - the number of words after filtering on 2470# size limitations 2471# * 'percent_words_available' - the percentage of the un-filtered 2472# words remaining in the filtered words list 2473# * 'contains_accents' - whether or not the filtered word list 2474# contains accented letter 2475# Arguments : NONE 2476# Throws : Croaks on invalid invocation 2477# Notes : 2478# See Also : 2479sub _calcualte_dictionary_stats{ 2480 my $self = shift; 2481 _force_instance($self); 2482 2483 # create a hash to aggregate the stats into 2484 my %ans = (); 2485 2486 # deal with agregate numbers first 2487 $ans{source} = $self->{_DICTIONARY_SOURCE}->source(); 2488 $ans{num_words_total} = scalar @{$self->{_CACHE_DICTIONARY_FULL}}; 2489 $ans{num_words_filtered} = scalar @{$self->{_CACHE_DICTIONARY_LIMITED}}; 2490 $ans{percent_words_available} = round(($ans{num_words_filtered}/$ans{num_words_total}) * 100); 2491 $ans{filter_length_min} = $self->{_CONFIG}->{word_length_min}; 2492 $ans{filter_length_max} = $self->{_CONFIG}->{word_length_max}; 2493 $ans{contains_accents} = $self->{_CACHE_CONTAINS_ACCENTS}; 2494 2495 # return the stats 2496 return %ans; 2497} 2498 2499#####-SUB-###################################################################### 2500# Type : INSTANCE (PRIVATE) 2501# Purpose : A function to check if passwords genereated with the loaded 2502# config would contian a symbol 2503# Returns : 1 if the config will produce passwords with a symbol, or 0 2504# otherwise 2505# Arguments : NONE 2506# Throws : Croaks on invalid invocation 2507# Notes : This function is used by _calculate_entropy_stats() to figure out 2508# whether or not there are symbols in the alphabet when calculating 2509# the brute-force entropy. 2510# See Also : _calculate_entropy_stats() 2511sub _passwords_will_contain_symbol{ 2512 my $self = shift; 2513 _force_instance($self); 2514 2515 # assume no symbol, if we find one, set to 1 2516 my $symbol_used = 0; 2517 2518 ## no critic (ProhibitEnumeratedClasses); 2519 # first check the padding 2520 if($self->{_CONFIG}->{padding_type} ne 'NONE'){ 2521 if($self->{_CONFIG}->{padding_character} eq 'RANDOM'){ 2522 if(defined $self->{_CONFIG}->{padding_alphabet}){ 2523 my $all_pad_chars = join q{}, @{$self->{_CONFIG}->{padding_alphabet}}; 2524 if($all_pad_chars =~ m/[^0-9a-zA-Z]/sx){ # if we have just one non-word character 2525 $symbol_used = 1; 2526 } 2527 }else{ 2528 my $all_pad_chars = join q{}, @{$self->{_CONFIG}->{symbol_alphabet}}; 2529 if($all_pad_chars =~ m/[^0-9a-zA-Z]/sx){ # if we have just one non-word character 2530 $symbol_used = 1; 2531 } 2532 } 2533 }else{ 2534 if($self->{_CONFIG}->{padding_character} =~ m/[^0-9a-zA-Z]/sx){ # the padding character is not a word character 2535 $symbol_used = 1; 2536 } 2537 } 2538 } 2539 2540 # then check the separator 2541 if($self->{_CONFIG}->{separator_character} ne 'NONE'){ 2542 if($self->{_CONFIG}->{separator_character} eq 'RANDOM'){ 2543 if(defined $self->{_CONFIG}->{separator_alphabet}){ 2544 my $all_sep_chars = join q{}, @{$self->{_CONFIG}->{separator_alphabet}}; 2545 if($all_sep_chars =~ m/[^0-9a-zA-Z]/sx){ # if we have just one non-word character 2546 $symbol_used = 1; 2547 } 2548 }else{ 2549 my $all_sep_chars = join q{}, @{$self->{_CONFIG}->{symbol_alphabet}}; 2550 if($all_sep_chars =~ m/[^0-9a-zA-Z]/sx){ # if we have just one non-word character 2551 $symbol_used = 1; 2552 } 2553 } 2554 }else{ 2555 if($self->{_CONFIG}->{separator_character} =~ m/[^0-9a-zA-Z]/sx){ # the separator is not a word character 2556 $symbol_used = 1; 2557 } 2558 } 2559 } 2560 ## use critic 2561 2562 # return 2563 return $symbol_used; 2564} 2565 2566#####-SUB-###################################################################### 2567# Type : INSTANCE (PRIVATE) 2568# Purpose : Update the entropy stats cache (and warn of low entropy if 2569# appropriate) 2570# Returns : always returns 1 (to keep perlcritic happy) 2571# Arguments : NONE 2572# Throws : Croaks on invalid invocation 2573# Notes : This function should only be called from config() or dictionary(). 2574# The entropy is calculated with _calculate_entropy_stats(), and a 2575# reference to the hash returned from that function is stored in 2576# $self->{_CACHE_ENTROPYSTATS}. 2577# See Also : _calculate_entropy_stats(), config() & dictionary() 2578sub _update_entropystats_cache{ 2579 my $self = shift; 2580 _force_instance($self); 2581 2582 # do nothing if the dictionary has not been loaded yet (should only happen while the constructor is building an instance) 2583 return 1 unless($self->{_DICTIONARY_SOURCE} && blessed($self->{_DICTIONARY_SOURCE}) && $self->{_DICTIONARY_SOURCE}->isa($_DICTIONARY_BASE_CLASS)); 2584 2585 # calculate and store the entropy stats 2586 my %stats = $self->_calculate_entropy_stats(); 2587 $self->{_CACHE_ENTROPYSTATS} = \%stats; 2588 2589 # warn if we need to 2590 unless(uc $_ENTROPY_WARNINGS eq 'NONE'){ 2591 # blind warnings are always needed if the level is not 'NONE' 2592 if($self->{_CACHE_ENTROPYSTATS}->{entropy_blind_min} < $_ENTROPY_MIN_BLIND){ 2593 _warn('for brute force attacks, the combination of the loaded config and dictionary produces an entropy of '.$self->{_CACHE_ENTROPYSTATS}->{entropy_blind_min}.'bits, below the minimum recommended '.$_ENTROPY_MIN_BLIND.'bits'); 2594 } 2595 2596 # seen warnings if the cut-off is not 'BLIND' 2597 unless(uc $_ENTROPY_WARNINGS eq 'BLIND'){ 2598 if($self->{_CACHE_ENTROPYSTATS}->{entropy_seen} < $_ENTROPY_MIN_SEEN){ 2599 _warn('for attacks assuming full knowledge, the combination of the loaded config and dictionary produces an entropy of '.$self->{_CACHE_ENTROPYSTATS}->{entropy_seen}.'bits, below the minimum recommended '.$_ENTROPY_MIN_SEEN.'bits'); 2600 } 2601 } 2602 } 2603 2604 # to keep perl critic happy 2605 return 1; 2606} 2607 2608#####-SUB-###################################################################### 2609# Type : CLASS (PRIVATE) 2610# Purpose : To nicely print a Math::BigInt object 2611# Returns : a string representing the object's value in scientific notation 2612# with 1 digit before the decimal and 2 after 2613# Arguments : 1. a Math::BigInt object 2614# Throws : Croaks on invalid invocation or args 2615# Notes : 2616# See Also : 2617sub _render_bigint{ 2618 my @args = @_; 2619 my $class = shift @args; 2620 _force_class($class); 2621 2622 # validate args 2623 state $args_check = compile(InstanceOf['Math::BigInt']); 2624 my ($bigint) = $args_check->(@args); 2625 2626 # convert the bigint to an array of characters 2627 my @chars = split //sx, "$bigint"; 2628 2629 # render nicely 2630 if(scalar @chars < 3){ 2631 return q{}.join q{}, @chars; 2632 } 2633 # start with the three most signifficant digits (as a decimal) 2634 my $ans = q{}.$chars[0].q{.}.$chars[1].$chars[2]; 2635 # then add the scientific notation bit 2636 $ans .= 'x10^'.(scalar @chars - 1); 2637 2638 # return the result 2639 return $ans; 2640} 2641 2642#####-SUB-###################################################################### 2643# Type : CLASS (PRIVATE) 2644# Purpose : Get the so-called 'grapheme length' of a unicode string, that is 2645# to say, the length of a word where a letter with an accent counts 2646# as a single letter. 2647# Returns : An integer 2648# Arguments : 1) the string to get the length of 2649# Throws : Croaks on invalid invocation and invalid args 2650# Notes : Perl, by default, will consider accented letters as having a 2651# length of two. This function uses a very common algorythm 2652# recommended all over the internet, including in the Perl Unicode 2653# cookbook: http://search.cpan.org/~shay/perl-5.20.2/pod/perlunicook.pod 2654# Before resorting to this technique, I tried to use the 2655# grapheme_length function from Unicode::Util, but it proved 2656# unacceptably slow. 2657# See Also : 2658sub _grapheme_length{ 2659 my @args = @_; 2660 my $class = shift @args; 2661 _force_class($class); 2662 2663 # validate args 2664 state $args_check = compile(Str); 2665 my ($string) = $args_check->(@args); 2666 2667 # do the calculation 2668 my $grapheme_length = 0; 2669 while($string =~ /\X/gsx){$grapheme_length++}; 2670 2671 # return the result 2672 return $grapheme_length; 2673} 2674 26751; # because Perl is just a little bit odd :) 2676__END__ 2677 2678#============================================================================== 2679# User Documentation 2680#============================================================================== 2681 2682=head1 NAME 2683 2684C<Crypt::HSXKPasswd> - A secure memorable password generator inspired by Steve 2685Gibson's Passord Haystacks (L<https://www.grc.com/haystack.htm>), and the 2686famous XKCD password cartoon (L<https://xkcd.com/936/>). 2687 2688=head1 VERSION 2689 2690This documentation refers to C<Crypt::HSXKPasswd> version 3.6. 2691 2692=head1 SYNOPSIS 2693 2694 use Crypt::HSXKPasswd; 2695 2696 # 2697 # Functional Interface - a shortcut for generating single passwords 2698 # 2699 2700 # generate a single password using the default word source, configuration, 2701 # and random number generator 2702 my $password = hsxkpasswd(); 2703 2704 # the above call is simply a shortcut for the following 2705 my $password = Crypt::HSXKPasswd->new()->password(); 2706 2707 # this function passes all arguments on to Crypt::HSXKPasswd->new() 2708 # so all the same customisations can be specified, e.g. specifying a 2709 # config preset: 2710 my $password = hsxkpasswd(preset => 'XKCD'); 2711 2712 # 2713 # Object Oriented Interface - recommended for generating multiple passwords 2714 # 2715 2716 # create a new instance with the default dictionary, config, and random 2717 # number generator 2718 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(); 2719 2720 # generate a single password 2721 my $password = $hsxkpasswd_instance->password(); 2722 2723 # generate multiple passwords 2724 my @passwords = $hsxkpasswd_instance->passwords(10); 2725 2726=head1 DESCRIPTION 2727 2728A secure memorable password generator inspired by the wonderful XKCD webcomic 2729at L<http://www.xkcd.com/> and Steve Gibson's Password Haystacks page at 2730L<https://www.grc.com/haystack.htm>. This is the Perl module that powers 2731L<https://www.xkpasswd.net>. 2732 2733=head2 PHILOSOPHY 2734 2735More and more of the things we do on our computer require passwords, and at the 2736same time it seems we hear about organisations or sites losing user database on 2737every day that ends in a I<y>. If we re-use our passwords we expose ourself to 2738an ever greater risk, but we need more passwords than we can possibly remember 2739or invent. Coming up with one good password is easy, but coming up with one 2740good password a week is a lot harder, let alone one a day! 2741 2742Obviously we need some technological help. We need our computers to help us 2743generate robust password and store them securely. There are many great password 2744managers out there to help us securely store and sync our passwords, including 2745commercial offerings and open-source projects. Many of these managers also offer 2746to generate random passwords for us, usually in the form of a random string of 2747meaningless letters numbers and symbols. These kinds of nonsense passwords are 2748certainly secure, but they are often impractical. 2749 2750Regardless of how good your chosen password manager is, there will always be 2751times when you need to type in your passwords, and that's when random gibberish 2752passwords become a real pain point. As annoying as it is to have to glance over 2753and back at a small cellphone screen to manually type a gibberish password into 2754a computer, that's nothing compared to the annoyance of trying to communicate 2755such a password to a family member, friend, colleague or customer over the phone. 2756 2757Surely it would be better to have passwords that are still truly random in the 2758way humans can't be, but are also human-friendly in the way random gibberish 2759never will be? This is the problem this module aims to solve. 2760 2761Rather than randomly choosing many letters, digits, and symbols from a fairly 2762small alphabet of possible characters, this library chooses a small number of 2763words from a large I<alphabet> of possible words as the basis for passwords. 2764Words are easy to remember, easy to read from a screen, easy to type, and easy 2765to communicate over the telephone. 2766 2767This module uses words to make up the bulk of the passwords it generates, but 2768it also adds carefully placed symbols and digits to add security without making 2769the passwords difficult to remember, read, type, and speak. 2770 2771In shot, this module is for people who prefer passwords that look like this: 2772 2773 !15.play.MAJOR.fresh.FLAT.23! 2774 2775to passwords that look like this: 2776 2777 eB8.GJXa@TuM 2778 2779=head2 PASSWORD GENERATION ALGORITHM 2780 2781This module always uses a simple five-step algorithm to generate passwords, but 2782each step can be customised, and many steps can be skipped completely. 2783 2784It's important to understand the algorithm before trying to create your own 2785custom configurations for this module. 2786 2787The algorithm is broken in to the following steps: 2788 2789=over 4 2790 2791=item 1 2792 2793Pick random words from the dictionary. 2794 2795=item 2 2796 2797Apply transformations to the words. 2798 2799=item 3 2800 2801Create pseudo-words made up for randomly chosen digits and add them as the first 2802and last words. 2803 2804=item 4 2805 2806Insert a copy of the same symbol between each of the words and pseudo-words. 2807This symbol is referred to as the I<separator character>. 2808 2809=item 5 2810 2811Pad the password with multiple instances of the same symbol front and/or back. 2812This symbol is referred to as the I<padding character>. 2813 2814=back 2815 2816You can visualise this process as follows: 2817 2818 correct horse batter staple 2819 correct HORSE battery staple 2820 25 correct HORSE battery staple 83 2821 25*correct*HORSE*battery*staple*83 2822 ++25*correct*HORSE*battery*staple*83++ 2823 2824Each of these steps can be customised in the following ways: 2825 2826=over 4 2827 2828=item 1 2829 2830The number of words to be used, and the minimum and maximum lengths of the words 2831can be configured. 2832 2833=item 2 2834 2835The case of the words can be modified in a number of ways, including randomly 2836choosing the case for each word. 2837 2838It is also possible to specify so-called I<133t-style> character substitutions, 2839e.g. replacing all occurrences of the letter C<e> with the digit C<3>, or all 2840occurrences of the letter C<s> with the symbol C<$>. 2841 2842=item 3 2843 2844The number of digits to add as pseudo words to the front and back of the 2845password can be configured. A length of zero can be specified for both to 2846generate passwords without any randomly chosen digits. 2847 2848=item 4 2849 2850The separator character can be specified directly, or it can be randomly chosen 2851from a list of symbols. It is also possible to specify that no separator should 2852be used. 2853 2854=item 5 2855 2856The padding character can also be specified directly, or remotely chosen from a 2857list of possible symbols. Padding can also be disabled completely. If padding is 2858to be used it can be applied in two modes - fixed, and adaptive. 2859 2860With fixed padding a specified number of copies of the separator character are 2861added to the front and back of the password. The fixed padding does not have to 2862be symmetric. 2863 2864With adaptive padding the required number of copies of the separator character 2865are added to the back of the password until it reaches a specified length. 2866 2867=back 2868 2869=head2 THE MATHS 2870 2871Before examining the password strength of passwords generated with this module 2872we need to lay out the relatively simple maths underlying it all. 2873 2874=head3 Maths Primer 2875 2876A coin could be used as a very simple password generator. Each character in 2877the password would be the result of a single coin toss. If the coin lands 2878heads up, we add a C<H> to our password, if it lands tails up, we add a C<T>. 2879 2880If you made a one-letter password in this way there would only be two 2881possibilities, C<H>, or C<T>, or two permutations. If you made a two-letter 2882password in this way there would be four possible combinations, or 2883permutations, C<HH>, C<HT>, C<TH>, and C<TT>. If you made a three-character 2884password in this way there would be 16 permutations, a five character one 2885would have 32 permutations, and so forth. 2886 2887So, for a coin toss, which has two possible values for each character, the 2888formula for the number of permutations C<P> for a given length of password C<L> 2889is: 2890 2891 P = 2^L 2892 2893Or, two to the power of the length of the password. 2894 2895If we now swapped our coin for a dice, we would go from two possible values 2896per letter, to six possible values per letter. For one dice roll there would 2897be six permutations, for two there would be 36, for three there would be 108 2898and so on. 2899 2900This means that for a dice, the number of permutations can be calculated with 2901the formula: 2902 2903 P = 6^L 2904 2905When talking about passwords, the set of possible symbols used for each 2906character in the password is referred to as the password's I<alphabet>. So, 2907for the coin toss the alphabet was just C<H> and C<T>, and for the dice it 2908was C<1>, C<2>, C<3>, C<4>, C<5>, and C<6>. The actual characters used in 2909the alphabet make no difference to the strength of the password, all that 2910matters is the size of the alphabet, which we'll call C<A>. 2911 2912As you can probably infer from the two examples above, the formula for the 2913number of possible permutations C<P> for a password of length C<L> created from 2914an alphabet of size C<A> is: 2915 2916 P = A^L 2917 2918In the real world our passwords are generally made up of a mix of letters, 2919digits, and symbols. If we use mixed case that gives us 52 letters alone, 2920then add in the ten digits from C<0> to C<9> and we're already up to 62 2921possible characters before we even start on the array of symbols and 2922punctuation characters on our keyboards. It's generally accepted that if you 2923include symbols and punctuation, there are 95 characters available for use in 2924randomly generated passwords. Hence, in the real-world, the value for C<A> is 2925assumed to be 95. When you start raising a number as big as 95 to even low 2926powers the number of permutations quickly rises. 2927 2928A two character password with alphabet of 95 has 9025 permutations, increasing 2929the length to three characters brings that up to 857,375, and so on. These 2930numbers very quickly become too big to handle. For just an 8 character password 2931we are talking about 6,634,204,312,890,625 permutations, which is a number 2932so big most people couldn't say it (what do you call something a thousand times 2933bigger than a trillion?). 2934 2935Because the numbers get so astronomically big so quickly, computer scientists 2936use bits of entropy to measure password strength rather than the number of 2937permutations. The formula to turn permutations into bits of entropy C<E> is very 2938simple: 2939 2940 E = Log(2)P 2941 2942In other words, the entropy is the log to base two of the permutations. For our 2943eight character example that equates to about 52 bits. 2944 2945There are two approaches to increasing the number of permutations, and hence 2946the entropy, you can choose more characters, or, you can make the alphabet you 2947are choosing from bigger. 2948 2949=head3 The Entropy of HSXKPasswd Passwords 2950 2951Exactly how much entropy does a password need? That's the subject of much 2952debate, and the answer ultimately depends on the value of the assets being 2953protected by the password. 2954 2955Two common recommendations you hear are 8 characters containing a mix of upper 2956and lower case letters, digits, and symbols, or 12 characters with the same 2957composition. These evaluation to approximately 52 bits of entropy and 78 bits 2958of entropy respectively. 2959 2960When evaluating the entropy of passwords generated by this module, it has to be 2961done from two points of view for the answer to be meaningful. Firstly, a 2962best-case scenario - the attacker has absolutely no knowledge of how the 2963password was generated, and hence must mount a brute-force attack. Then, 2964secondly from the point of view of an attacker with full knowledge of how the 2965password was generated. Not just the knowledge that this module was used, but 2966a copy of the dictionary file used, and, a copy of the configuration settings 2967used. 2968 2969For the purpose of this documentation, the entropy in the first scenario, the 2970brute force attack, will be referred to as the blind entropy, and the entropy 2971in the second scenario the seen entropy. 2972 2973The blind entropy is solely determined by the configuration settings, the seen 2974entropy depends on both the settings and the dictionary file used. 2975 2976Calculating the bind entropy C<Eb> is quite straightforward, we just need to 2977know the size of the alphabet resulting from the configuration C<A>, and the 2978minimum length of passwords generated with the configuration C<L>, and plug 2979those values into this formula: 2980 2981 Eb = Log(2)(A^L) 2982 2983Calculating C<A> simply involves determining whether or not the configuration 2984results in a mix of letter cases (26 or 52 characters), the inclusion of at 2985least one symbol (if any one is present, assume the industry standard of a 33 2986character search space), and the inclusion of at least one digit 2987(10 character). This will result in a value between 26 and 95. 2988 2989Calculating C<L> is also straightforward. The one minor complication is that 2990some configurations result in a variable length password. In this case, 2991assume the shortest possible length the configuration could produce. 2992 2993The example password from the L</PHILOSOPHY> section 2994(C<!15.play.MAJOR.fresh.FLAT.23!>) was generated using the preset C<WEB32>. 2995This preset uses four words of between four and five letters long, with the 2996case of each word randomly set to all lower or all upper as the 2997basis for the password, it then chooses two pairs of random digits as extra 2998words to go front and back, before separating each word with a copy of a 2999randomly chosen symbol, and padding the front and back of the password with 3000a copy of a different randomly chosen symbol. This results in passwords that 3001contain a mix of cases, digits, and symbols, and are between 27 and 31 3002characters long. If we add these values into the formula we find that the 3003blind entropy for passwords created with this preset is: 3004 3005 Eb = Log(2)(95^27) = 163 bits 3006 3007This is spectacularly secure! And, this is the most likely kind of attack for 3008a password to face. However, to have confidence in the password we must also 3009now calculate the entropy when the attacker knows everything about how the 3010password was generated. 3011 3012We will calculate the entropy resulting from the same C<WEB32> config being 3013used to generate a password using the sample library file that ships with 3014the module. 3015 3016The number of permutations the attacker needs to check is purely the product 3017of possibly results for each random choice made during the assembly of the 3018password. 3019 3020Lets start with the words that will form the core of the password. The 3021configuration chooses four words of between four and five letters long from 3022the dictionary, and then randomises their case, effectively making it a 3023choice from twice as many words (each word in each case). 3024 3025The sample dictionary file contains 698 words of the configured length, which 3026doubles to 1396. Choosing four words from that very large alphabet gives a 3027starting point of C<1396^4>, or 3,797,883,801,856 permutations. 3028 3029Next we need to calculate the permutations for the separator character. The 3030configuration specifies just nine permitted characters, and we choose just one, 3031so that equates to 9 permutations. 3032 3033Similarly, the padding character on the end is chosen from 13 permitted symbols 3034giving 13 more permutations. 3035 3036Finally, there are four randomly chosen digits, giving C<10^4>, or 10,000 3037permutations. 3038 3039The total number of permutations is the product of all these permutations: 3040 3041 Pseen = 3,797,883,801,856 * 9 * 13 * 10,000 = 2.77x10^17 3042 3043Finally, we convert this to entropy by taking the base 2 log: 3044 3045 Eseen = Log(2)2.77x10^17 = ~57bits 3046 3047What this means is that most probably, passwords generated with this preset 3048using the sample dictionary file are spectacularly more secure than even 304912 randomly chosen characters, and, that in the very unlikely event that an 3050attackers knows absolutely everything about how the password was generated, 3051it is still significantly more secure than 8 randomly chosen characters. 3052 3053Because the exact strength of the passwords produced by this module depend on 3054the configuration and dictionary file used, the constructor does the above 3055math when creating an HSXKPasswd object, and throws a warning if either the 3056blind entropy falls below 78bits, or the seen entropy falls below 52 bits. 3057 3058=head1 SUBROUTINES/METHODS 3059 3060=head2 MODULE CONFIGURATION 3061 3062It is possible to tweak the module's behaviour in certain areas by updating the 3063values contained within a set of module configuration keys. The values 3064associated with these keys can be accessed and updated via the class function 3065C<module_config()>. 3066 3067 # get the current debug status 3068 my $debug_status = Crypt::HSXKPasswd->module_config('DEBUG'); 3069 3070 # configure the module to suppress all entropy warnings 3071 Crypt::HSXKPasswd->module_config('ENTROPY_WARNINGS', 'NONE'); 3072 3073The following module configuration keys exist within the module: 3074 3075=over 4 3076 3077=item * 3078 3079C<DEBUG> - A True/False value denoting whether or not the module should print 3080debug messages. The default is not to print debug messages. 3081 3082For more details see the DIAGNOSTICS section of this document. 3083 3084=item * 3085 3086C<LOG_ERRORS> - A True/False value denoting whether or not errors should be 3087logged. The default is not to log. 3088 3089For more details see the DIAGNOSTICS section of this document. 3090 3091=item * 3092 3093C<LOG_STREAM> - the stream to which debug messages should be printed if 3094debugging is enabled, and log messages should be printed when error logging is 3095enabled. The default is to print to C<STDERR>. 3096 3097For more details see the DIAGNOSTICS section of this document. 3098 3099=item * 3100 3101C<ENTROPY_MIN_BLIND> - the minimum allowable entropy against brute force attacks 3102in bits. The default is 78 bits. 3103 3104For more details see the ENTROPY CHECKING section of this document. 3105 3106=item * 3107 3108C<ENTROPY_MIN_SEEN> - the minimum allowable entropy against an attacker with 3109full knowledge. The default is 52 bits. 3110 3111For more details see the ENTROPY CHECKING section of this document. 3112 3113=item * 3114 3115C<ENTROPY_WARNINGS> - control the emission of entropy warnings. The value must 3116be one of C<ALL>, C<BLIND>, or C<NONE>. The default value is C<ALL>. 3117 3118For more details see the ENTROPY CHECKING section of this document. 3119 3120=back 3121 3122=head2 CUSTOM DATA TYPES 3123 3124This module uses a custom type library created with C<Type::Library> for data 3125validation. It is important to know this for two reasons - firstly, these 3126custom types are mentioned in many error messages, and secondly these custom 3127types are available for developers to use in their own code, either when 3128utilising C<Crypt::HSXKPasswd>, or writing custom word sources by extending 3129C<Crypt::HSXKPasswd::Dictionary>, or when writing custom random number 3130generators by extending C<Crypt::HSXKPasswd::RNG>. 3131 3132=head3 Defined Types 3133 3134=over 4 3135 3136=item * 3137 3138C<NonEmptyString> - a string containing at least one character. 3139 3140=item * 3141 3142C<PositiveInteger> - a whole number greater than or equal to zero. 3143 3144=item * 3145 3146C<NonZeroPositiveInteger> - a whole number greater than zero. 3147 3148=item * 3149 3150C<TrueFalse> - a reasonable boolean value, specifically, C<undef>, and empty 3151string, or 0 to indicate false, and a 1 to indicate true. 3152 3153=item * 3154 3155C<PerlPackageName> - string representing a valid Perl package name like 3156C<Crypt::HSXKPasswd::Dictionary::NL>. 3157 3158=item * 3159 3160C<Letter> - a string containing a single letter. Because this module is 3161Unicode-aware, it should be noted that a letter is defined as a single Unicode 3162grapheme with the Unicode property C<Letter>. What this means is that accented 3163letters like C<E<eacute>> are considered valid, as are ligatures like 3164C<E<aelig>>. 3165 3166=item * 3167 3168C<Symbol> - a string containing a single non-letter character. Because this 3169module is Unicode-aware, should be noted that a non-letter character is defined 3170as a single Unicode grapheme that does not have the Unicode property C<Letter>. 3171What this means is that neither letters, accented characters, nor ligatures can 3172be used as symbols, but just about every other Unicode character can, including 3173punctuation symbols, mathematical symbols, and even emoji! 3174 3175=item * 3176 3177C<Word> - a string containing only letters (as defined by the type C<Letter>), 3178and at least four long. 3179 3180=item * 3181 3182C<SymbolAlphabet> - a symbol alphabet is a reference to an array that contains 3183at least two distinct symbols (as defined by the type C<Symbol>), and no values 3184that are not symbols. 3185 3186=item * 3187 3188C<WordLength> - a valid value when specifying the length of a word, 3189specifically, a whole number greater than or equal to four. 3190 3191=item * 3192 3193C<ConfigKeyDefinition> - a valid configuration key definition. A reference to a 3194hash mapping C<required> to a true/false value, C<expects> to a non-empty 3195string, and C<type> to a C<Type::Tiny> object. 3196 3197=item * 3198 3199C<ConfigKeyName> - a valid configuration key name, see the CONFIGURATION section 3200of this document for a description of each configuration key supported by this 3201module. You can get a list of valid configuration key names programatically by 3202calling the function C<Crypt::HSXKPasswd->defined_config_keys()>. 3203 3204=item * 3205 3206C<ConfigKeyAssignment> - a mapping between a valid configuration key name and a 3207valid value for that configuration key. 3208 3209=item * 3210 3211C<ConfigOverride> - a reference to hash containing one or more configuration key 3212assignments as defined by the type C<ConfigKeyAssignment>. 3213 3214=item * 3215 3216C<Config> - a reference to a hash that contains a complete and valid set of 3217mappings between configuration key names and values. For a config to be 3218considered valid it must contain only valid valid configuration key assignments 3219as defined by the type C<ConfigKeyAssignment>, must contain a configuration key 3220assignment for each required configuration key and all interdependencies between 3221the specified configuration key assignments must be fulfilled. 3222 3223See the CONFIG section of this document for a detailed description of each of 3224the defined configuration keys and their various interdependencies. 3225 3226=item * 3227 3228C<PresetDefinition> - a valid preset definition. A reference to a hash mapping 3229C<description> to a non-empty string, and C<config> to a valid Config. 3230 3231=item * 3232 3233C<PresetName> - a valid preset name, see the PRESETS section of this document 3234for a description of each preset supported by this module. You can get a list of 3235valid preset names programatically by calling the function 3236C<Crypt::HSXKPasswd->defined_presets()>. 3237 3238=back 3239 3240=head3 Using the Custom Types 3241 3242The library of custom types is defined in the package 3243C<Crypt::HSXKPasswd::Types>, and it is a standard C<Type::Library> type library 3244containing C<Type::Tiny> type definitions. 3245 3246Useful Links: 3247 3248=over 4 3249 3250=item * 3251 3252The documentation for C<Type::Tiny> - 3253L<http://search.cpan.org/perldoc?Type%3A%3ATiny> 3254 3255=item * 3256 3257The documentation for C<Type::Library> - 3258L<http://search.cpan.org/perldoc?Type%3A%3ALibrary> 3259 3260=back 3261 3262To use the bare type definitions listed above, import the module as follows: 3263 3264 use Crypt::HSXKPasswd::Types qw( :types ); 3265 3266Each type listed above will now be imported, and become available as a bare 3267word. The C<Type::Tiny> documentation provides a full list of available 3268functions, but the examples below illustrate some of the more useful ones: 3269 3270 $is_valid = Letter->check('e'); # $is_valid = 1 3271 $is_valid = Letter->check('-'); # $is_valid = undef 3272 $err_msg = Letter->validate('e'); # $err_msg = undef 3273 $err_msg = Letter->validate('-'); # $err_msg = "'-' is not a Letter ... 3274 # ... (must be a string containing exactly one letter)" 3275 3276C<Type::Library> automatically creates an C<is_TypeName> function for each type 3277defined in the library. These are not imported by default. To import them add 3278the export tag C<:is> to the C<use> line. I would recommend the following C<use> 3279line: 3280 3281 use Crypt::HSXKPasswd::Types qw( :types :is ); 3282 3283You can now do things like the following: 3284 3285 $is_valid = is_Letter('e'); # $is_valid = 1 3286 $is_valid = is_Letter('-'); # $is_valid = undef 3287 3288Each of the types listed above also contains a custom function using 3289C<Type::Tiny>'s new, and still officially experimental, C<my_methods> feature. 3290The custom function is called C<my_english>, and can be used to return an 3291English description of the values considered valid by the type, e.g.: 3292 3293 print Letter->my_english(); # prints: a string containing exactly one letter 3294 3295As well as the named types listed above, there are also anonymous types defined 3296for each supported configuration key. These can be accessed using the function 3297C<Crypt::HSXKPasswd->config_key_definitions()>. 3298 3299If declaring your own C<Type::Tiny> types, you may also find the public 3300subroutine C<Crypt::HSXKPasswd::Types::var_to_string()> useful - it will turn 3301anything passed as a scalar into a meaningful string, truncating any resulting 3302strings longer than 72 characters in nice way. All the custom error messages in 3303all the types defined in C<Crypt::HSXKPasswd::Types> make use of this 3304subroutine. 3305 3306=head2 CONFIGURATION 3307 3308The module builds passwords using the following process. 3309 3310First, a set of words are randomly chosen from the word source. Then, two 3311pseudo-words made of one or more digits may added before and/or after the words 3312from. Next, a separator character may be placed between all the words 3313(including the groups of digits), and one or more occurrences of a padding 3314symbol may be added front and/or back. 3315 3316You can envisage the process as follows: 3317 3318 correct HORSE BATTERY staple 3319 34 correct HORSE BATTERY staple 56 3320 34-correct-HORSE-BATTERY-staple-56 3321 !!34-correct-HORSE-BATTERY-staple-56!! 3322 3323Many aspects of this password generation process are configurable. You can 3324control the length and number of words chosen, and what, if any, case 3325transformations should be applied to those words, and how accented characters 3326should be treated. How many, if any, digits should be added front and back. 3327What symbol, if any, should be used as a separator. And finally how the 3328password should be padded, if at all, and with what symbol. Passwords can be 3329padded to a given length, or by a given number of symbols front and back. 3330 3331The symbols used as the separator and for padding can be explicitly specified, 3332or the they can be randomly chosen from a given alphabet of possible symbols. 3333Both symbols can be randomly chosen from the same alphabet, or from two 3334separately specified alphabets. 3335 3336Every instance of an HSXKPasswd password generator stores its configuration as 3337a set of name-value pairs, referred to as I<configuration keys> throughout this 3338documentation. 3339 3340Configurations can be specified either as a complete set of configuration keys 3341with values that together form a valid configuration, as a named preset, or, as 3342a named preset accompanied by a list of one or more configuration keys 3343with new values to override those specified by the preset. 3344 3345The module contains a preset called C<DEFAULT>, and this preset is used if no 3346configuration is specified. The function C<default_config()> will return a copy 3347of this configuration as a reference to a hashtable. 3348 3349For more details on how to specify configurations, see the documentation for 3350the constructor (the function C<new()>) below. 3351 3352=head3 Password Generator Configuration Keys 3353 3354Below is a list of all the configuration keys that can be used to customise the 3355password generation algorithm. Each configuration key is accompanied by a 3356description of what aspect of the algorithm they control, and any validation 3357rules that apply to the key. 3358 3359Note that some keys are always required, and that there are dependencies 3360between keys. For examples, if you specify that the separator symbol should be 3361chosen at random, you must also specify an alphabet from which the symbol should 3362be randomly chosen. 3363 3364=over 4 3365 3366=item * 3367 3368C<allow_accents> (optional) - if not specified, or if a falsy value is 3369specified, accents will be removed from letters in the generated passwords. 3370E.g. C<E<eacute>> becomes C<e>. If a truthy value is specified, accents will 3371be preserved, and appear in the generated passwords. 3372 3373=item * 3374 3375C<case_transform> (required) - the transformations, if any, that should be 3376applied to the words that appear in the generated passwords. The value specified 3377must be one of the following: 3378 3379=over 4 3380 3381=item * 3382 3383C<ALTERNATE> - each alternate word will be converted to all upper case and 3384all lower case. The case of the first word is chosen at random. 3385 3386=item * 3387 3388C<CAPITALISE> - the first letter in every word will be converted to upper case, 3389all other letters will be converted to lower case. 3390 3391=item * 3392 3393C<INVERT> - the first letter in every word will be converted to lower case, 3394all other letters will be converted to upper case. 3395 3396=item * 3397 3398C<LOWER> - all letters in all the words will be converted to lower case. B<Use 3399of this option is strongly discouraged for security reasons.> 3400 3401=item * 3402 3403C<NONE> - the case of the letters that make up the words will not be altered 3404from how they were specified in the original word source. 3405 3406=item * 3407 3408C<RANDOM> - each word will be randomly converted to all upper case or all lower 3409case. 3410 3411=item * 3412 3413C<UPPER> - all letters in all the words will be converted to upper case. B<Use 3414of this option is strongly discouraged for security reasons.> 3415 3416=back 3417 3418The function C<default_config()> returns a value of C<CAPITALISE> for this key. 3419 3420=item * 3421 3422C<character_substitutions> (optional) - a reference to a hashtable containing 3423containing zero or more character substitutions to be applied to the randomly 3424chosen words when generating passwords. The keys in the hashtable must be 3425single letters. The substitutions can contain multiple characters. Specifying 3426one or more substitutions with a length greater than one could lead to 3427passwords being longer than expected, and to entropy calculations being under 3428estimated. The module will issue a warning when such a config is loaded. 3429 3430=item * 3431 3432C<num_words> (required) - the number of words to randomly choose from the word 3433source as the basis for the generated passwords. 3434 3435The function C<default_config()> returns a value of C<3> for this key. 3436 3437=item * 3438 3439C<pad_to_length> (conditionally required) - the length generated passwords must 3440be padded to when using adaptive padding, i.e. when C<padding_type> is set to 3441C<ADAPTIVE>). The value must be an integer greater than or equal to 12. Lengths 3442of less than 12 are not permitted for security reasons. 3443 3444=item * 3445 3446C<padding_alphabet> (optional) - this key is ignored unless the configuration 3447specifies that the padding character should be randomly chosen, i.e. unless 3448C<padding_character> is set to C<RANDOM>. 3449 3450When the padding character is set to be randomly chosen, the module will check 3451for the presence of this key. If it is specified, the padding character will 3452be randomly chosen from the set of symbols defined by this key. If this key is 3453not set, the module will use the set of symbols specified by the key 3454C<symbol_alphabet>. If neither this key nor C<symbol_alphabet> are specified, 3455then the configuration will be considered invalid. 3456 3457If specified, this key must be a reference to an array of single-character 3458strings. 3459 3460=item * 3461 3462C<padding_character> (conditionally required) - this key is unless the key 3463C<padding_type> is set to C<NONE>. It specifies the padding symbol to be used 3464when generating passwords. 3465 3466If specified, they key's value must be a single character string, or one of the 3467following special values: 3468 3469=over 4 3470 3471=item * 3472 3473C<RANDOM> - the character should be randomly chosen from the set of characters 3474specified by the key C<padding_alphabet> or C<symbol_alphabet>. If specified, 3475C<padding_alphabet> takes precedence over C<symbol_alphabet>. If this value 3476is specified for C<padding_character>, and neither C<padding_alphabet> nor 3477C<symbol_alphabet> are specified, the configuration will be considered invalid. 3478 3479=item * 3480 3481C<SEPARATOR> - pad the password with the same symbol that is used to separate 3482the words. The key C<padding_character> cannot be set to C<SEPARATOR> when the 3483key C<separator_character> is set to C<NONE>. 3484 3485 3486=back 3487 3488 3489The function C<default_config> return the value C<RANDOM> for this key. 3490 3491=item * 3492 3493C<padding_characters_before> & C<padding_characters_after> (conditionally 3494required) - both of these keys are required if the key C<padding_type> is set 3495to C<FIXED>. 3496 3497These keys specify the number of padding symbols that should be added to the 3498front and back of the password. 3499 3500Both keys require that the specified value be an integer greater than or equal 3501to zero. 3502 3503The function C<default_config()> returns a value of C<2> for both of these 3504keys. 3505 3506=item * 3507 3508C<padding_digits_before> & C<padding_digits_after> (required) - the number of 3509random digits to include before and after the randomly chosen words when 3510generating passwords. 3511 3512Both keys require that the specified value be an integer greater than or equal 3513to zero. 3514 3515The function C<default_config()> returns a value of C<2> for both of these 3516keys. 3517 3518=item * 3519 3520C<padding_type> (required) - the way in which padding symbols should be added 3521when generating passwords. 3522 3523Only the following values are valid for this key: 3524 3525=over 4 3526 3527=item * 3528 3529C<NONE> - do not add any padding symbols when generating passwords. 3530 3531=item * 3532 3533C<FIXED> - add an exactly specified number of copies of the padding symbol to 3534the front and back of generated passwords. 3535 3536When they key C<padding_type> is set to C<FIXED>, the three keys 3537C<padding_character>, C<padding_characters_before> & 3538C<padding_characters_after> become required. 3539 3540=item * 3541 3542C<ADAPTIVE> - add no copies of the padding symbol will be added to the front 3543of the generated passwords, and copies of the padding character will be added 3544to the end of the generated passwords until the total length of the password is 3545equal to the value specified for the key C<pad_to_length>. 3546 3547Note that If the password is longer than the value specified by the key 3548C<pad_to_length> before any copies of the padding symbol are added, the 3549password will be truncated to the length specified by the key C<pad_to_length>. 3550 3551When they key C<padding_type> is set to C<ADAPTIVE>, the three keys 3552C<padding_character>, C<padding_characters_before> & 3553C<padding_characters_after> become required. 3554 3555=back 3556 3557The function C<default_config()> returns a value of C<FIXED> for this key. 3558 3559=item * 3560 3561C<separator_alphabet> (optional) - this key is ignored unless the configuration 3562specifies that the separator character should be randomly chosen, i.e. unless 3563C<separator_character> is set to C<RANDOM>. 3564 3565When the separator character is set to be randomly chosen, the module will 3566check for the presence of this key. If it is specified, the separator character 3567will be randomly chosen from the set of symbols defined by this key. If this 3568key is not set, the module will use the set of symbols specified by the key 3569C<symbol_alphabet>. If neither this key nor C<symbol_alphabet> are specified, 3570then the configuration will be considered invalid. 3571 3572If specified, this key must be a reference to an array of single-character 3573strings. 3574 3575=item * 3576 3577C<separator_character> (required) - the symbol to use to separate the words 3578when generating passwords. 3579 3580The value specified for this key must be a single-character string, or one of 3581the following special values: 3582 3583=over 4 3584 3585=item * 3586 3587C<NONE> - no separator character will be used. I.e. the words, and the groups 3588of digits before and after the words, if any, will be directly joined together. 3589 3590C<RANDOM> - a single character will be randomly chosen from the list of symbols 3591specified by one of the keys C<separator_alphabet> or C<symbol_alphabet>. If 3592both keys are set, C<separator_alphabet> takes precedence. 3593 3594=back 3595 3596The function C<default_config()> returns a value of C<RANDOM> for this key. 3597 3598=item * 3599 3600C<symbol_alphabet> (optional) - this key specifies a default alphabet of 3601symbols that can be used when either or both the separator character and the 3602padding character are set to be chosen at random. I.e. when either or both of 3603the keys C<separator_character> and C<padding_character> are set to C<RANDOM>. 3604 3605Note that the keys C<separator_alphabet> and C<padding_alphabet> take 3606precedence over this key if specified. 3607 3608The value specified for this key must be a reference to an array of 3609single-character strings. 3610 3611The function C<default_config()> returns a value of 3612C<['!', '@', '$', '%', '^', '&', '*', '-', '_', 3613'+', '=', ':', '|', '~', '?', '/', '.', ';']> 3614for this key. 3615 3616=item * 3617 3618C<word_length_min> & C<word_length_max> (required) - the minimum and maximum 3619length of the words that will form the basis of the generated passwords. 3620 3621The values specified for both keys must be integers greater than three, and 3622the value specified for C<word_length_max> must be greater than or equal to 3623the value specified for C<word_length_min>. 3624 3625The function C<default_config()> returns values of C<4> and C<8> for these keys. 3626 3627=back 3628 3629=head2 PRESETS 3630 3631Below is a list of all the presets defined by this module. 3632 3633This information can be accessed programatically using the functions 3634C<defined_presets()>, C<presets_to_string()>, C<preset_description()>, and 3635C<preset_config()>. 3636 3637=over 4 3638 3639=item * 3640 3641C<APPLEID> - a preset respecting the many prerequisites Apple places on Apple 3642ID passwords. Apple's official password policy cam be found at the following 3643URL: L<http://support.apple.com/kb/ht4232>. Note that Apple's knowledge base 3644article omits to mention that passwords can't be longer than 32 characters. 3645This preset is also configured to use only characters that are easy to type on 3646the standard iOS keyboard, i.e. those appearing on the letters keyboard 3647(C<ABC>) or the numbers keyboard C<.?123>, and not those on the harder to reach 3648symbols keyboard C<#+=>. 3649 3650Sample Password: 3651 3652 -25,favor,MANY,BEAR,53- 3653 3654Preset Definition: 3655 3656 { 3657 padding_alphabet => [qw{- : . ! ? @ &}], 3658 separator_alphabet => [qw{- : . @}, q{,}, q{ }], 3659 word_length_min => 4, 3660 word_length_max => 7, 3661 num_words => 3, 3662 separator_character => 'RANDOM', 3663 padding_digits_before => 2, 3664 padding_digits_after => 2, 3665 padding_type => 'FIXED', 3666 padding_character => 'RANDOM', 3667 padding_characters_before => 1, 3668 padding_characters_after => 1, 3669 case_transform => 'RANDOM', 3670 allow_accents => 0, 3671 } 3672 3673=item * 3674 3675C<DEFAULT> - the default configuration. 3676 3677Sample Password: 3678 3679 ~~12:settle:SUCCEED:summer:48~~ 3680 3681Preset Definition: 3682 3683 { 3684 symbol_alphabet => [qw{! @ $ % ^ & * - _ + = : | ~ ? / . ;}], 3685 word_length_min => 4, 3686 word_length_max => 8, 3687 num_words => 3, 3688 separator_character => 'RANDOM', 3689 padding_digits_before => 2, 3690 padding_digits_after => 2, 3691 padding_type => 'FIXED', 3692 padding_character => 'RANDOM', 3693 padding_characters_before => 2, 3694 padding_characters_after => 2, 3695 case_transform => 'ALTERNATE', 3696 allow_accents => 0, 3697 } 3698 3699=item * 3700 3701C<NTLM> - a preset for 14 character NTMLv1 (NTLM Version 1) passwords. B<ONLY 3702USE THIS PRESET IF YOU MUST!> The 14 character limit does not allow for 3703sufficient entropy in scenarios where the attacker knows the dictionary and 3704config used to generate the password. Use of this preset will generate a low 3705entropy warning. 3706 3707Sample Password: 3708 3709 0=mAYAN=sCART@ 3710 3711Preset Definition: 3712 3713 { 3714 padding_alphabet => [qw{! @ $ % ^ & * + = : | ~ ?}], 3715 separator_alphabet => [qw{- + = . * _ | ~}, q{,}], 3716 word_length_min => 5, 3717 word_length_max => 5, 3718 num_words => 2, 3719 separator_character => 'RANDOM', 3720 padding_digits_before => 1, 3721 padding_digits_after => 0, 3722 padding_type => 'FIXED', 3723 padding_character => 'RANDOM', 3724 padding_characters_before => 0, 3725 padding_characters_after => 1, 3726 case_transform => 'INVERT', 3727 allow_accents => 0, 3728 } 3729 3730=item * 3731 3732C<SECURITYQ> - a preset for creating fake answers to security questions. This 3733preset generates long nonsense sentences ending in C<.> C<!> or C<?>. 3734 3735Sample 'Password': 3736 3737 Wales outside full month minutes gentle? 3738 3739Preset Definition: 3740 3741 { 3742 word_length_min => 4, 3743 word_length_max => 8, 3744 num_words => 6, 3745 separator_character => q{ }, 3746 padding_digits_before => 0, 3747 padding_digits_after => 0, 3748 padding_type => 'FIXED', 3749 padding_character => 'RANDOM', 3750 padding_alphabet => [qw{. ! ?}], 3751 padding_characters_before => 0, 3752 padding_characters_after => 1, 3753 case_transform => 'NONE', 3754 allow_accents => 0, 3755 } 3756 3757=item * 3758 3759C<WEB16> - a preset for websites that don't allow passwords to be longer than 376016 characters. B<ONLY USE THIS PRESET IF YOU MUST!> The 14 character limit does 3761not allow for sufficient entropy in scenarios where the attacker knows the 3762dictionary and config used to generate the password. Use of this preset will 3763generate a low entropy warning. 3764 3765Sample Password: 3766 3767 tube+NICE+iron+02 3768 3769Preset Definition: 3770 3771 { 3772 symbol_alphabet => [qw{! @ $ % ^ & * - _ + = : | ~ ? / . ;}], 3773 word_length_min => 4, 3774 word_length_max => 4, 3775 num_words => 3, 3776 separator_character => 'RANDOM', 3777 padding_digits_before => 0, 3778 padding_digits_after => 2, 3779 padding_type => 'NONE', 3780 case_transform => 'RANDOM', 3781 allow_accents => 0, 3782 } 3783 3784=item * 3785 3786C<WEB32> - a preset for websites that don't allow passwords to be longer than 378732 characters. 3788 3789Sample Password: 3790 3791 +93-took-CASE-money-AHEAD-31+ 3792 3793Preset Definition: 3794 3795 { 3796 padding_alphabet => [qw{! @ $ % ^ & * + = : | ~ ?}], 3797 separator_alphabet => [qw{- + = . * _ | ~}, q{,}], 3798 word_length_min => 4, 3799 word_length_max => 5, 3800 num_words => 4, 3801 separator_character => 'RANDOM', 3802 padding_digits_before => 2, 3803 padding_digits_after => 2, 3804 padding_type => 'FIXED', 3805 padding_character => 'RANDOM', 3806 padding_characters_before => 1, 3807 padding_characters_after => 1, 3808 case_transform => 'ALTERNATE', 3809 allow_accents => 0, 3810 } 3811 3812=item * 3813 3814C<WIFI> - a preset for generating 63 character long WPA2 keys (most routers 3815allow 64 characters, but some only allow 63, hence the somewhat unexpected 3816length). 3817 3818Sample Password: 3819 3820 2736_ITSELF_PARTIAL_QUICKLY_SCOTLAND_wild_people_7441!!!!!!!!!! 3821 3822Preset Definition: 3823 3824 { 3825 padding_alphabet => [qw{! @ $ % ^ & * + = : | ~ ?}], 3826 separator_alphabet => [qw{- + = . * _ | ~}, q{,}], 3827 word_length_min => 4, 3828 word_length_max => 8, 3829 num_words => 6, 3830 separator_character => 'RANDOM', 3831 padding_digits_before => 4, 3832 padding_digits_after => 4, 3833 padding_type => 'ADAPTIVE', 3834 padding_character => 'RANDOM', 3835 pad_to_length => 63, 3836 case_transform => 'RANDOM', 3837 allow_accents => 0, 3838 } 3839 3840=item * 3841 3842C<XKCD> - a preset inspired by the original XKCD comic 3843(L<http://xkcd.com/936/>), but with some alterations to provide sufficient 3844entropy to avoid low entropy warnings. 3845 3846Sample Password: 3847 3848 quiet-children-OCTOBER-today-HOPE 3849 3850Preset Definition: 3851 3852 { 3853 word_length_min => 4, 3854 word_length_max => 8, 3855 num_words => 5, 3856 separator_character => q{-}, 3857 padding_digits_before => 0, 3858 padding_digits_after => 0, 3859 padding_type => 'NONE', 3860 case_transform => 'RANDOM', 3861 allow_accents => 0, 3862 } 3863 3864=back 3865 3866=head2 FUNCTIONAL INTERFACE 3867 3868Although the package was primarily designed to be used in an object-oriented 3869way, there is a functional interface too. The functional interface initialises 3870an object internally and then uses that object to generate a single password. 3871If you only need one password, this is no less efficient than the 3872object-oriented interface, however, if you are generating multiple passwords it 3873is much less efficient. 3874 3875There is only a single function exported by the module: 3876 3877=head3 hsxkpasswd() 3878 3879 my $password = hsxkpasswd(); 3880 3881This function call is equivalent to the following Object-Oriented code: 3882 3883 my $password = Crypt::HSXKPasswd->new()->password(); 3884 3885This function passes all arguments it receives through to the constructor, so all 3886arguments that are valid in C<new()> are valid here. 3887 3888This function Croaks if there is a problem generating the password. 3889 3890Note that it is inefficient to use this function to generate multiple passwords 3891because the dictionary will be re-loaded, and the entropy stats re-calculated 3892each time the function is called. 3893 3894=head2 CONSTRUCTOR 3895 3896 # create a new instance with the default dictionary, config, and random 3897 # number generator 3898 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(); 3899 3900 # the constructor takes optional named arguments, these can be used to 3901 # customise the word source, config, and random number source. 3902 3903 # create an instance that uses the UNIX words file as the word source 3904 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new( 3905 dictionary => Crypt::HSXKPasswd::Dictionary::System->new() 3906 ); 3907 3908 # create an instance that uses an array reference as the word source 3909 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(dictionary_list => $array_ref); 3910 3911 # create an instance that uses a dictionary file as the word source 3912 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new( 3913 dictionary_file => 'sample_dict_EN.txt' 3914 ); 3915 3916 # the class Crypt::HSXKPasswd::Dictionary::Basic can be used to aggregate 3917 # multiple array refs and/or dictionary files into a single word source 3918 my $dictionary = Crypt::HSXKPasswd::Dictionary::Basic->new(); 3919 $dictionary->add_words('dict1.txt'); 3920 $dictionary->add_words('dict2.txt'); 3921 $dictionary->add_words($array_ref); 3922 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(dictionary => $dictionary); 3923 3924 # create an instance from the preset 'XKCD' 3925 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(preset => 'XKCD'); 3926 3927 # create an instance based on the preset 'XKCD' with one customisation 3928 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new( 3929 preset => 'XKCD', 3930 preset_override => {separator_character => q{ }} 3931 ); 3932 3933 # create an instance from a config based on a preset 3934 # but with many alterations 3935 my $config = Crypt::HSXKPasswd->preset_config('XKCD'); 3936 $config->{separator_character} = q{ }; 3937 $config->{case_transform} = 'INVERT'; 3938 $config->{padding_type} = "FIXED"; 3939 $config->{padding_characters_before} = 1; 3940 $config->{padding_characters_after} = 1; 3941 $config->{padding_character} = '*'; 3942 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(config => $config); 3943 3944 # create an instance from an entirely custom configuration 3945 my $config = { 3946 padding_alphabet => [qw{! @ $ % ^ & * + = : ~ ?}], 3947 separator_alphabet => [qw{- + = . _ | ~}], 3948 word_length_min => 6, 3949 word_length_max => 6, 3950 num_words => 3, 3951 separator_character => 'RANDOM', 3952 padding_digits_before => 2, 3953 padding_digits_after => 2, 3954 padding_type => 'FIXED', 3955 padding_character => 'RANDOM', 3956 padding_characters_before => 2, 3957 padding_characters_after => 2, 3958 case_transform => 'CAPITALISE', 3959 } 3960 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(config => $config); 3961 3962 # create an instance from an entire custom config passed as a JSON string 3963 # a convenient way to use configs generated using the web interface at 3964 # https://xkpasswd.net 3965 my $config = <<'END_CONF'; 3966 { 3967 "num_words": 4, 3968 "word_length_min": 4, 3969 "word_length_max": 8, 3970 "case_transform": "RANDOM", 3971 "separator_character": " ", 3972 "padding_digits_before": 0, 3973 "padding_digits_after": 0, 3974 "padding_type": "NONE", 3975 } 3976 END_CONF 3977 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new(config_json => $config); 3978 3979 # create an instance which uses /dev/urandom as the RNG 3980 # (only possible on Linux/Unix only systems) 3981 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new( 3982 rng => Crypt::HSXKPasswd::RNG::DevUrandom->new(); 3983 ); 3984 3985 # create an instance which uses Random.Org as the random number generator 3986 # NOTE - this should be used sparingly, and only by the paranoid. If you 3987 # abuse this RNG your IP will get blacklisted on Random.Org. You must pass 3988 # a valid email address to the constructor for 3989 # Crypt::HSXKPasswd::RNG::RandomDorOrg because Random.Org's usage 3990 # guidelines request that all invocations to their API contain a contact 3991 # email in the useragent header, and this module honours that request. 3992 my $hsxkpasswd_instance = Crypt::HSXKPasswd->new( 3993 rng => Crypt::HSXKPasswd::RNG::RandomDorOrg->new('your.email@addre.ss'); 3994 ); 3995 3996The constructor must be called via the package name. 3997 3998If called with no arguments the constructor will use an instance of 3999C<Crypt::HSXKPasswd::Dictionary::EN> as the word source, the preset C<DEFAULT>, 4000and an instance of the class C<Crypt::HSXKPasswd::RNG::Basic> to generate random 4001numbers. 4002 4003The function accepts named arguments to allow for custom specification of the 4004word source, config, and random number source. 4005 4006=head3 Specifying Custom Word Sources 4007 4008Three named arguments can be used to specify a word source, but only one should 4009be specified at a time. If multiple are specified, the one with the highest 4010priority will be used, and the rest ignored. The variables are listed below in 4011descending order of priority: 4012 4013=over 4 4014 4015=item * 4016 4017C<dictionary> - an instance of a class that extends 4018C<Crypt::HSXKPasswd::Dictionary>. 4019 4020=item * 4021 4022C<dictionary_list> - a reference to an array containing words as scalars. 4023 4024=item * 4025 4026C<dictionary_file> - the path to a dictionary file. Dictionary files should 4027contain one word per. Lines starting with a # symbol will be ignored. It is 4028assumed files will be UTF-8 encoded. If not, a second named argument, 4029C<dictionary_file_encoding>, can be used to specify another encoding. 4030 4031=back 4032 4033=head3 Specifying Custom Password Generator Configurations 4034 4035Two primary named arguments can be used to specify the config the instance 4036should use to generate passwords. Only one should be specified at a time. If 4037multiple are specified, the one with the highest priority will be used, and the 4038rest ignored. The variables are listed below in descending order of priority: 4039 4040=over 4 4041 4042=item * 4043 4044C<config> - a valid config hashref. 4045 4046=item * 4047 4048C<config_json> - a JSON string representing a valid config hashref. 4049 4050This named argument provides a convenient way to use configs generated using 4051the web interface at L<https://xkpasswd.net/>. The Save/Load tab in that 4052interface saves and loads configs in JSON format. 4053 4054=item * 4055 4056C<preset> - a valid preset name. If this variable is used, then any desired 4057config overrides can be passed as a hashref using the variable 4058C<preset_overrides>. 4059 4060=back 4061 4062=head3 Specifying Custom Random Number Generators 4063 4064A custom RNG can be specified using the named argument C<rng>. The passed value 4065must be an instance of a class that extends C<Crypt::HSXKPasswd::RNG> and 4066overrides the function C<random_numbers()>. 4067 4068=head2 INSTANCE METHODS 4069 4070B<NOTE> - all instance methods must be invoked on a Crypt::HSXKPasswd object or 4071they will croak. 4072 4073=head3 ->config() 4074 4075 my $config = $hsxkpasswd_instance->config(); # getter 4076 $hsxkpasswd_instance->config($config_hashref); # setter 4077 $hsxkpasswd_instance->config($config_json_string); # setter 4078 4079When called with no arguments the function returns a clone of the instance's 4080config hashref. 4081 4082When called with a single argument the function sets the config of the instance 4083to a clone of the passed config. If present, the argument must be either a 4084hashref containing valid config keys and values, or a JSON string representing 4085a hashref containing valid config keys and values. 4086 4087The function will croak if an invalid config is passed. 4088 4089=head3 ->config_as_json() 4090 4091 my $config_json_string = $hsxkpasswd_instance->config_as_json(); 4092 4093This function returns the content of the instance's loaded config hashref as a 4094JSON string. 4095 4096The output from this function can be loaded into the web interface at 4097L<https://xkpasswd.net> (using the load/save tab). 4098 4099=head3 ->config_as_string() 4100 4101 my $config_string = $hsxkpasswd_instance->config_as_string(); 4102 4103This function returns the content of the instance's loaded config hashref as a 4104scalar string. 4105 4106=head3 ->dictionary() 4107 4108 my $dictionary_clone = $hsxkpasswd_instance->dictionary(); 4109 $hsxkpasswd_instance->dictionary($dictionary_instance); 4110 $hsxkpasswd_instance->dictionary($array_ref); 4111 $hsxkpasswd_instance->dictionary('sample_dict_EN.txt'); 4112 $hsxkpasswd_instance->dictionary('sample_dict_EN.txt', 'Latin1'); 4113 4114When called with no arguments this function returns a clone of the currently 4115loaded dictionary which will be an instance of a class that extends 4116C<Crypt::HSXKPasswd::Dictionary>. 4117 4118To load a new dictionary into an instance, call this function with arguments. 4119The first argument argument can be an instance of a class that extends 4120C<Crypt::HSXKPasswd::Dictionary>, a reference to an array of words, or the 4121path to a dictionary file. If either an array reference or a file path are 4122passed, they will be used to instantiate an instance of the class 4123C<Crypt::HSXKPasswd::Dictionary::Basic>, and that new instance will then be 4124loaded into the object. If a file path is passed, it will be assumed to be 4125UTF-8 encoded. If not, an optional second argument can be passed to specify the 4126file's encoding. 4127 4128=head3 ->password() 4129 4130 my $password = $hsxkpasswd_instance->password(); 4131 4132This function generates a random password based on the instance's loaded config 4133and returns it as a scalar. The function takes no arguments. 4134 4135The function croaks if there is an error generating the password. The most 4136likely cause of and error is the random number generation, particularly if the 4137loaded random generation function relies on a cloud service or a non-standard 4138library. 4139 4140=head3 ->passwords() 4141 4142 my @passwords = $hsxkpasswd_instance->passwords(10); 4143 4144This function generates a number of passwords and returns them all as an array. 4145 4146The function uses C<password()> to generate the passwords, and hence will 4147croak if there is an error generating any of the requested passwords. 4148 4149=head3 ->passwords_json() 4150 4151 my $json_string = $hsxkpasswd_instance->passwords_json(10); 4152 4153This function generates a number of passwords and returns them and the 4154instance's entropy stats as a JSON string representing a hashref containing an 4155array of passwords indexed by C<passwords>, and a hashref of entropy stats 4156indexed by C<stats>. The stats hashref itself is indexed by: 4157C<password_entropy_blind>, C<password_permutations_blind>, 4158C<password_entropy_blind_min>, C<password_entropy_blind_max>, 4159C<password_permutations_blind_max>, C<password_entropy_seen> & 4160C<password_permutations_seen>. 4161 4162The function uses C<passwords()> to generate the passwords, and hence will 4163croak if there is an error generating any of the requested passwords. 4164 4165=head3 ->rng() 4166 4167 my $rng_instance = $hsxkpasswd_instance->rng(); 4168 $hsxkpasswd_instance->rng($rng_instance); 4169 4170When called with no arguments this function returns currently loaded Random 4171Number Generator (RNG) which will be an instance of a class that extends 4172C<Crypt::HSXKPasswd::RNG>. 4173 4174To load a new RNG into an instance, call this function with a single 4175argument, an instance of a class that extends 4176C<Crypt::HSXKPasswd::RNG>. 4177 4178=head3 ->stats() 4179 4180 my %stats = $hsxkpasswd_instance->stats(); 4181 4182This function generates a hash containing stats about the instance indexed by 4183the following keys: 4184 4185=over 4 4186 4187=item * 4188 4189C<dictionary_contains_accents> - 1 if the filtered word list contains accented 4190letters, 0 otherwise. 4191 4192=item * 4193 4194C<dictionary_filter_length_min> & C<dictionary_filter_length_max> - the minimum 4195and maximum word lengths allowed by the dictionary filter (defined by config 4196keys C<word_length_min> and C<word_length_max>) 4197 4198=item * 4199 4200C<dictionary_source> - the source of the word list loaded into the instance. 4201 4202=item * 4203 4204C<dictionary_words_filtered> - the number of words loaded from the dictionary 4205file that meet the criteria defined by the loaded config. 4206 4207=item * 4208 4209C<dictionary_words_percent_available> - the percentage of the words in the 4210dictionary file that are available for use with the loaded config. 4211 4212=item * 4213 4214C<dictionary_words_total> - the total number of words loaded from the 4215dictionary file. 4216 4217=item * 4218 4219C<password_entropy_blind_min> - the entropy (in bits) of the shortest password 4220the loaded config can generate from the point of view of a brute-force 4221attacker. 4222 4223=item * 4224 4225C<password_entropy_blind_max> - the entropy (in bits) of the longest password 4226the loaded config can generate from the point of view of a brute-force 4227attacker. 4228 4229=item * 4230 4231C<password_entropy_blind> - the entropy (in bits) of the average length 4232of passwords the loaded config can generate from the point of view of a 4233brute-force attacker. 4234 4235=item * 4236 4237C<password_entropy_seen> - the entropy (in bits) of passwords generated by the 4238instance assuming the dictionary and config are known to the attacker. 4239 4240=item * 4241 4242C<password_length_min> - the minimum length of passwords generated by the 4243loaded config. 4244 4245=item * 4246 4247C<password_length_max> - the maximum length of passwords generated by the 4248loaded config. 4249 4250=item * 4251 4252C<password_permutations_blind_min> - the number of permutations a brute-force 4253attacker would have to try to be sure of cracking the shortest possible 4254passwords generated by this instance. Because this number can be very big, it's 4255returned as a C<Math::BigInt> object. 4256 4257=item * 4258 4259C<password_permutations_blind_max> - the number of permutations a brute-force 4260attacker would have to try to be sure of cracking the longest possible 4261passwords generated by this instance. Because this number can be very big, it's 4262returned as a C<Math::BigInt> object. 4263 4264=item * 4265 4266C<password_permutations_blind> - the number of permutations a brute-force 4267attacker would have to try to be sure of cracking an average length password 4268generated by this instance. Because this number can be very big, it's returned 4269as a C<Math::BigInt> object. 4270 4271=item * 4272 4273C<password_permutations_seen> - the number of permutations an attacker with a 4274copy of the dictionary and config would need to try to be sure of cracking a 4275password generated by this instance. Because this number can be very big, it's 4276returned as a C<Math::BigInt> object. 4277 4278=item * 4279 4280C<passwords_generated> - the number of passwords this instance has generated. 4281 4282=item * 4283 4284C<password_random_numbers_required> - the number of random numbers needed to 4285generate a single password using the loaded config. 4286 4287=item * 4288 4289C<randomnumbers_cached> - the number of random numbers currently cached within 4290the instance. 4291 4292=item * 4293 4294C<randomnumbers_cache_increment> - the number of random numbers generated at 4295once to replenish the cache when it's empty. 4296 4297=item * 4298 4299C<randomnumbers_source> - the class used by the instance to generate random 4300numbers. 4301 4302=back 4303 4304=head3 ->status() 4305 4306 print $hsxkpasswd_instance->status(); 4307 4308Generates a string detailing the internal status of the instance. Below is a 4309sample status string: 4310 4311 *DICTIONARY* 4312 Source: Crypt::HSXKPasswd::Dictionary::EN 4313 # words: 1425 4314 # words of valid length: 1194 (84%) 4315 4316 *CONFIG* 4317 case_transform: 'ALTERNATE' 4318 num_words: '3' 4319 padding_character: 'RANDOM' 4320 padding_characters_after: '2' 4321 padding_characters_before: '2' 4322 padding_digits_after: '2' 4323 padding_digits_before: '2' 4324 padding_type: 'FIXED' 4325 separator_alphabet: ['!', '$', '%', '&', '*', '+', '-', '.', '/', ':', ';', '=', '?', '@', '^', '_', '|', '~'] 4326 separator_character: 'RANDOM' 4327 symbol_alphabet: ['!', '$', '%', '&', '*', '+', '-', '.', '/', ':', ';', '=', '?', '@', '^', '_', '|', '~'] 4328 word_length_max: '8' 4329 word_length_min: '4' 4330 4331 *RANDOM NUMBER CACHE* 4332 Random Number Generator: Crypt::HSXKPasswd::RNG::Basic 4333 # in cache: 0 4334 4335 *PASSWORD STATISTICS* 4336 Password length: between 24 & 36 4337 Permutations (brute-force): between 2.91x10^47 & 1.57x10^71 (average 2.14x10^59) 4338 Permutations (given dictionary & config): 5.51x10^15 4339 Entropy (Brute-Force): between 157bits and 236bits (average 197bits) 4340 Entropy (given dictionary & config): 52bits 4341 # Random Numbers needed per-password: 9 4342 Passwords Generated: 0 4343 4344=head3 ->update_config() 4345 4346 $hsxkpasswd_instance->update_config({separator_character => '+'}); 4347 4348The function updates the config within an HSXKPasswd instance. A hashref with 4349the config options to be changed must be passed. The function returns a 4350reference to the instance to enable function chaining. The function will croak 4351if the updated config would be invalid in some way. Note that if this happens 4352the running config will not have been altered in any way. 4353 4354=head2 CLASS METHODS 4355 4356B<NOTE> - All class methods must be invoked via the package name, or they will 4357croak. 4358 4359=head3 clone_config() 4360 4361 my $clone = Crypt::HSXKPasswd->clone_config($config); 4362 4363This function must be passed a valid config hashref as the first argument or it 4364will croak. The function returns a hashref. 4365 4366=head3 config_key_definition() 4367 4368 my %key_definition = Crypt::HSXKPasswd->config_key_definition($key_name); 4369 4370A function to return the definition for a config key. The definition is returned 4371as a hash indexed by the following keys: 4372 4373=over 4 4374 4375=item * 4376 4377C<required> - 1 if the key is a required key, and 0 otherwise. 4378 4379=item * 4380 4381C<type> - a C<Type::Tiny> object representing the valid data type for the key. 4382 4383=item * 4384 4385C<expects> - an English description of valid values for the key. 4386 4387=back 4388 4389=head3 config_key_definitions() 4390 4391 my %key_definitions = Crypt::HSXKPasswd->config_key_definitions(); 4392 4393A function to return definitions for all defined config keys as a hash indexed 4394by config key names. Each definition is represented as a hash with the same keys 4395as the hashes returned by the function C<config_key_definition()>. 4396 4397=head3 config_stats() 4398 4399 my %stats = Crypt::HSXKPasswd->config_stats($config); 4400 my %stats = Crypt::HSXKPasswd->config_stats( 4401 $config, 4402 suppress_warnings => 1, 4403 ); 4404 4405This function requires one argument, a valid config hashref. It returns a hash 4406of statistics about a given configuration. The hash is indexed by the 4407following: 4408 4409=over 4 4410 4411=item * 4412 4413C<length_min> - the minimum length a password generated with the given 4414config could be. 4415 4416=item * 4417 4418C<length_max> - the maximum length a password generated with the given 4419config could be. (see caveat below) 4420 4421=item * 4422 4423C<random_numbers_required> - the amount of random numbers needed to generate a 4424password using the given config. 4425 4426=back 4427 4428There is one scenario in which the calculated maximum length will not be 4429reliably accurate, and that's when a character substitution with a length 4430greater than 1 is specified, and C<padding_type> is not set to C<ADAPTIVE>. If 4431the config passed contains such a character substitution, the length will be 4432calculated ignoring the possibility that one or more extra characters could 4433be introduced depending on how many, if any, of the long substitutions get 4434triggered by the randomly chosen words. If this happens the function will also 4435carp with a warning. Such warnings can be suppressed by passing an optional 4436named argument C<suppress_warnings> with the value C<1>. 4437 4438=head3 config_to_json() 4439 4440 my $config_json_string = Crypt::HSXKPasswd->config_to_json($config); 4441 4442This function returns a JSON representation of the passed config hashref as a 4443scalar string. 4444 4445The function must be passed a valid config hashref or it will 4446croak. 4447 4448=head3 config_to_string() 4449 4450 my $config_string = Crypt::HSXKPasswd->config_to_string($config); 4451 4452This function returns the content of the passed config hashref as a scalar 4453string. The function must be passed a valid config hashref or it will croak. 4454 4455=head3 default_config() 4456 4457 my $config = Crypt::HSXKPasswd->default_config(); 4458 4459This function returns a hashref containing a config with default values. 4460 4461This function can optionally be called with a single argument, a hashref 4462containing keys with values to override the defaults with. 4463 4464 my $config = Crypt::HSXKPasswd->default_config({num_words => 3}); 4465 4466When overrides are present, the function will carp if an invalid key or value 4467is passed, and croak if the resulting merged config is invalid. 4468 4469This function is a shortcut for C<preset_config()>, and the two examples above 4470are equivalent to the following: 4471 4472 my $config = Crypt::HSXKPasswd->preset_config('DEFAULT'); 4473 my $config = Crypt::HSXKPasswd->preset_config('DEFAULT', {num_words => 3}); 4474 4475=head3 defined_config_keys() 4476 4477 my @config_key_names = Crypt::HSXKPasswd->defined_config_keys(); 4478 4479This function returns the list of valid config key names as an array of strings. 4480 4481=head3 defined_presets() 4482 4483 my @preset_names = Crypt::HSXKPasswd->defined_presets(); 4484 4485This function returns the list of defined preset names as an array of strings. 4486 4487=head3 distil_to_config_keys() 4488 4489 my $dist_hashref = Crypt::HSXKPasswd->distil_to_config_keys($hashref); 4490 4491This function takes a hashref as an argument, and returns a deep clone of that 4492hashref containing only valid config keys with valid values. 4493 4494By default the function silently drops keys that are not valid config keys, but 4495issues a warning when dropping a key that is a valid config key, but contains an 4496invalid value. The function can also issue warnings when dropping keys that are 4497not valid config keys. 4498 4499The warnings can be controlled with a pair of optional named arguments that can 4500be added as a second argument: 4501 4502 # suppress all warnings 4503 my $dist_hashref = Crypt::HSXKPasswd->distil_to_config_keys( 4504 $hashref, 4505 suppress_warnings => 1, 4506 ); 4507 4508 # emit warnings when dropping invalidly named keys 4509 my $dist_hashref = Crypt::HSXKPasswd->distil_to_config_keys( 4510 $hashref, 4511 warn_invalid_key_names => 1, 4512 ); 4513 4514=head3 distil_to_symbol_alphabet() 4515 4516 my @unique_syms = Crypt::HSXKPasswd->distil_to_symbol_alphabet($arrayref); 4517 my @unique_syms = Crypt::HSXKPasswd->distil_to_symbol_alphabet( 4518 $arrayref, 4519 warn => 1, 4520 ); 4521 4522This function takes reference to an array of strings and returns a new array 4523containing all the valid symbols from the referenced array. The valid symbols 4524are de-duplicated before being returned. 4525 4526By default the function silently skips over strings that are not valid symbols. 4527The function can be made issue warnings each time a string is skipped by passing 4528a named argument C<warn> with a value of C<1> (C<0> can also be passed to 4529explicitly disable warnings). 4530 4531=head3 distil_to_words() 4532 4533 my @valid_unique_words = Crypt::HSXKPasswd->distil_to_words($arrayref); 4534 my @valid_unique_words = Crypt::HSXKPasswd->distil_to_words( 4535 $arrayref, 4536 warn => 1, 4537 ); 4538 4539This function takes reference to an array of strings and returns a new array 4540containing all the valid words from the referenced array. The valid words are 4541de-duplicated before being returned. 4542 4543By default the function silently skips over strings that are not valid words. 4544The function can be made issue warnings each time a string is skipped by passing 4545a named argument C<warn> with a value of C<1> (C<0> can also be passed to 4546explicitly disable warnings). 4547 4548=head3 is_valid_config() 4549 4550 # determine the validity 4551 my $is_ok = Crypt::HSXKPasswd->is_valid_config($config); 4552 4553 # assert the validity - will croak if the config is invalid 4554 Crypt::HSXKPasswd->is_valid_config($config, croak => 1); 4555 4556This function must be passed a hashref to test as the first argument. The 4557function returns 1 if the passed config is valid, and 0 otherwise. 4558 4559Optionally, a named argument C<croak> can also be passed to control whether or 4560not the function should croak if the config is invalid. The value of this named 4561argument should be C<1> or C<0>. 4562 4563When calling the function with C<croak> set to C<1>, the message thrown by croak 4564will explain why the config is invalid. 4565 4566 use English qw( -no_match_vars ); 4567 eval{ 4568 Crypt::HSXKPasswd->is_valid_config($config, croak => 1); 4569 }or do{ 4570 print "ERROR - config is invalid because: $EVAL_ERROR\n"; 4571 } 4572 4573=head3 module_config() 4574 4575 my $debug_val = Crypt::HSXKPasswd->module_config('DEBUG'); # getter 4576 Crypt::HSXKPasswd->module_config('DEBUG', 1); # setter 4577 4578This function is used to access or alter the value of one of the module 4579configuration settings. The first function must always be a valid module 4580configuration key name. If no second argument is provided, the value stored 4581in the module configuration key will not be updated. To update the stored value, 4582pass a new value as a second argument. Regardless of whether or not a second 4583argument is passed, the value stored in the module configuration key is always 4584returned. 4585 4586The function will croak if called with an invalid module configuration key name, 4587or passed an invalid new value. 4588 4589For a list of the module configuration keys, see the MODULE CONFIGURATION 4590section of this document. 4591 4592=head3 preset_config() 4593 4594 my $config = Crypt::HSXKPasswd->preset_config('XKCD'); 4595 4596This function returns the config hashref for a given preset. See above for the 4597list of available presets. 4598 4599The first argument this function accepts is the name of the desired preset as a 4600scalar. If an invalid name is passed, the function will carp. If no preset is 4601passed the preset C<DEFAULT> is assumed. 4602 4603This function can optionally accept a second argument, a hashref 4604containing keys with values to override the defaults with. 4605 4606 my $config = Crypt::HSXKPasswd->preset_config( 4607 'XKCD', 4608 {case_transform => 'INVERT'} 4609 ); 4610 4611When overrides are present, the function will carp if an invalid key or value is 4612passed, and croak if the resulting merged config is invalid. 4613 4614=head3 preset_definition() 4615 4616 my %preset_def = Crypt::HSXKPasswd->preset_definition('XKCD'); 4617 4618This function returns a hash defining a preset. The hash contains 4619an English description of the preset indexed be C<description> and 4620a config hashref indexed by C<config>. 4621 4622The function expects to be called with one argument, a valid preset name, but it 4623can be called without arguments, in which case it will return the definition for 4624the preset c<DEFAULT>. 4625 4626You can see all the defined presets in the PRESETS section of this document, and 4627you can get a list of valid preset names programatically with the function 4628C<defined_presets()>. 4629 4630=head3 preset_definitions() 4631 4632 my %preset_defs = Crypt::HSXKPasswd->preset_definitions(); 4633 4634This function returns a hash of all defined presets indexed by preset name. Each 4635preset definition is a hash as returned by C<preset_definition()>. 4636 4637This function does not take any arguments. 4638 4639=head3 presets_json() 4640 4641 my $json_string = Crypt::HSXKPasswd->presets_json(); 4642 4643This function returns a JSON string representing all the defined configs, 4644including their descriptions. 4645 4646The returned JSON string represents a hashref indexed by three keys: 4647C<defined_keys> contains an array of preset identifiers, C<presets> contains the 4648preset configs indexed by reset identifier, and C<preset_descriptions> contains 4649a hashref of descriptions indexed by preset identifiers. 4650 4651=head3 preset_description() 4652 4653 my $description = Crypt::HSXKPasswd->preset_description('XKCD'); 4654 4655This function returns the description for a given preset. See above for the 4656list of available presets. 4657 4658The first argument this function accepts is the name of the desired preset as a 4659scalar. If an invalid name is passed, the function will carp. If no preset is 4660passed the preset C<DEFAULT> is assumed. 4661 4662=head3 presets_to_string() 4663 4664 print Crypt::HSXKPasswd->presets_to_string(); 4665 4666This function returns a string containing a description of each defined preset 4667and the configs associated with the presets. 4668 4669=head2 COMMANDLINE INTERFACE 4670 4671The module ships with a commandline interface to this library, simply called 4672C<hsxkpasswd>. 4673 4674This interface allows for the generation of multiple passwords at a time, the 4675use of presets and preset overrides, the use of custom password generator 4676configurations, the use of custom word sources, and the use of custom random 4677number generators. 4678 4679Both preset overrides and password generator configurations must be specified 4680in JSON format. 4681 4682=head3 Examples 4683 4684Generate a single password using all the default settings: 4685 4686 hsxkpasswd 4687 4688Generate five passwords using the default settings: 4689 4690 hsxkpasswd 5 4691 4692Generate five passwords using the C<XKCD> preset: 4693 4694 hsxkpasswd -p XKCD 5 4695 4696Generate five passwords using the C<XKCD> preset with an overridden password 4697generator configuration key: 4698 4699 hsxkpasswd -p XKCD -o '{"separator_character" : "*"}' 5 4700 4701Generate five passwords using a custom password generator configuration stored 4702in a text file in JSON format: 4703 4704 hsxkpasswd -c my_config.json 4705 4706=head3 Further Reading 4707 4708The examples above are just a sample of what the command can do, for complete 4709documentation, run the command with the -h flag: 4710 4711 hsxkpasswd -h 4712 4713If you are new to JSON, you may find the following links useful: 4714 4715=over 4 4716 4717=item * 4718 4719JSON on Wikipedia - L<http://en.wikipedia.org/wiki/JSON> 4720 4721=item * 4722 4723A free online JSON validator - L<http://jsonformatter.curiousconcept.com> 4724 4725=item * 4726 4727A JSON tutorial from W3Schools - L<http://www.w3schools.com/json/> 4728 4729=back 4730 4731=head2 ENTROPY CHECKING 4732 4733For security reasons, this module's default behaviour is to warn (using 4734C<carp()>) when ever the loaded combination of word source and configuration 4735would result in low-entropy passwords. When the constructor is invoked, or when 4736an instance's the word source or config are altered (using C<dictionary()> or 4737C<config()>), the entropy is re-calculated and re-checked against the defined 4738minima. 4739 4740Entropy is calculated and checked for two scenarios. Firstly, for the best-case 4741scenario, when an attacker has no prior knowledge about the password, and must 4742resort to a brute-force attack. And secondly, for the worst-case scenario, when 4743the attacker is assumed to know that this module was used to generate the 4744password, and, that the attacker has a copy of the word source and config 4745settings used to generate the password. 4746 4747Entropy checking is controlled via three module configuration variables (which 4748can be accessed and updated using the function C<module_config()>): 4749 4750=over 4 4751 4752=item * 4753 4754C<ENTROPY_MIN_BLIND> - the minimum acceptable entropy in bits for a brute-force 4755attack. The default value is 78bits, the equivalent to a 12 character password 4756consisting of mixed-case letters, digits, and symbols. 4757 4758=item * 4759 4760C<ENTROPY_MIN_SEEN> - the minimum acceptable entropy in bits for a worst-case 4761scenario (where the word source and config are known). The default value is 476252bits, equivalent to an 8 character password consisting of mixed-case letters, 4763digits, and symbols. 4764 4765=item * 4766 4767C<ENTROPY_WARNINGS> - this variable can be used to control the emission of 4768entropy warnings. The following values are valid: 4769 4770=over 4 4771 4772=item * 4773 4774C<ALL> - all entropy warnings are emitted. This is the default value. 4775 4776=item * 4777 4778C<BLIND> - only warnings for the best-case scenario are emitted. I.e. warnings 4779for the worst-case scenario (attacker has full knowledge) are suppressed. 4780 4781=item * 4782 4783C<NONE> - all entropy warnings are suppressed. 4784 4785=back 4786 4787=back 4788 4789=head3 Caveats 4790 4791The entropy calculations make some assumptions which may in some cases lead to 4792the results being inaccurate. In general, an attempt has been made to always 4793round down, meaning that in reality the entropy of the produced passwords may 4794be higher than the values calculated by the package. 4795 4796When calculating the entropy for brute force attacks on configurations that can 4797result in variable length passwords, the shortest possible password is assumed. 4798 4799When calculating the entropy for brute force attacks on configurations that 4800contain at least one symbol, it is assumed that an attacker would have to 4801brute-force-check 33 symbols. This is the same value used by Steve Gibson's 4802I<Password Haystacks> calculator (L<https://www.grc.com/haystack.htm>). 4803 4804When calculating the entropy for worst-case attacks on configurations that 4805contain symbol substitutions where the replacement is more than 1 character 4806long the possible extra length is ignored. 4807 4808=head2 WORD SOURCES (DICTIONARIES) 4809 4810The abstract class C<Crypt::HSXKPasswd::Dictionary> acts as a base class for 4811sources of words for use by this module. Word sources should extend this base 4812class and implement the function C<word_list()>, which should return an array 4813of words. 4814 4815In order to produce secure passwords it's important to use a word source that 4816contains a large selection of words with a good mix of different lengths of 4817words. 4818 4819The module ships with a number of pre-defined word sources: 4820 4821=head3 C<Crypt::HSXKPasswd::Dictionary::DE> 4822 4823A German word list based on the GPL-licensed German dictionary for WinEdit by 4824Juergen Vierheilig. 4825 4826B<Note:> This module is licensed under the GPL, not the BSD license used for the 4827majority of this project. 4828 4829=head3 C<Crypt::HSXKPasswd::Dictionary::EN> 4830 4831A default word list consisting of English words and place names. 4832 4833=head3 C<Crypt::HSXKPasswd::Dictionary::ES> 4834 4835A Spanish word list based on the BSD-licensed Spanish dictionary for WinEdit by 4836Juan L. Varona from the Universidad de La Rioja. 4837 4838=head3 C<Crypt::HSXKPasswd::Dictionary::FR> 4839 4840A French word list based on the GPL-licensed French dictionary for WinEdit. 4841 4842B<Note:> This module is licensed under GPL V2, not the BSD license used for the 4843majority of this project. 4844 4845=head3 C<Crypt::HSXKPasswd::Dictionary::IT> 4846 4847An Italian word list based on the free-for-non-commerical-use Italian dictionary 4848for WinEdit by Karl Koeller. 4849 4850B<Note:> This module is licensed under GPL V2, not the BSD license used for the 4851majority of this project. 4852 4853=head3 C<Crypt::HSXKPasswd::Dictionary::NL> 4854 4855A Dutch/Flemish word list based on the GPL-licensed Dutch dictionary for WinEdit. 4856 4857B<Note:> This module is licensed under GPL V2, not the BSD license used for the 4858majority of this project. 4859 4860=head3 C<Crypt::HSXKPasswd::Dictionary::PT> 4861 4862A Portuguese word list based on the GPL-licensed Portuguese dictionary for 4863WinEdit compiled by Bernhard Enders (building on work by Raimundo Santos Moura & 4864Ricardo Ueda Karpischek). 4865 4866B<Note:> This module is licensed under GPL V2.1, not the BSD license used for 4867the majority of this project. 4868 4869=head3 C<Crypt::HSXKPasswd::Dictionary::System> 4870 4871This class tries to find and use a Unix words file on the system. 4872 4873The constructor croaks if no system words file can be found. 4874 4875=head4 Usage 4876 4877 my $word_source = Crypt::HSXKPasswd::Dictionary::System->new(); 4878 4879=head3 C<Crypt::HSXKPasswd::Dictionary::Basic> 4880 4881This class can be initialised from a words file, or from an array ref 4882containing words. 4883 4884=head4 Usage 4885 4886 my $word_source = Crypt::HSXKPasswd::Dictionary::Basic->new('file_path'); 4887 my $word_source = Crypt::HSXKPasswd::Dictionary::Basic->new( 4888 'file_path', 4889 'Latin1' 4890 ); 4891 my $word_source = Crypt::HSXKPasswd::Dictionary::Basic->new($array_ref); 4892 4893 4894The rules for the formatting of dictionary files are simple. Dictionary 4895files must contain one word per line. Words shorter than four letters will be 4896ignored, as will all lines starting with the # symbol. Files are assumed to be 4897UTF-8 encoded, but an optional second argument can be passed specifying a 4898different file encoding. 4899 4900This format is the same as that of the standard Unix Words file, usually found 4901at C</usr/share/dict/words> on Unix and Linux operating systems (including OS 4902X). 4903 4904=head2 RANDOM NUMBER SOURCES 4905 4906In order to minimise the number of non-standard modules this module requires, 4907the default source of randomness is Perl's built-in C<rand()> function. This 4908provides a reasonable level of randomness, and should suffice for most users, 4909however, some users will prefer to make use of one of the many advanced 4910randomisation modules in CPAN, or, reach out to a web service like 4911L<http://random.org> for their random numbers. To facilitate both of these 4912options, this module uses a cache of randomness, and provides an abstract 4913Random Number Generator (RNG) class that can be extended. 4914 4915The module can use an instance of any class that extends 4916C<Crypt::HSXKPasswd::RNG> as it's source of randomness. Custom RNG classes 4917must implement the method C<random_numbers()> which will be invoked on an 4918instance of the class and passed one argument, the number of random numbers 4919required to generate a single password. The function must return an array 4920of random numbers between 0 and 1. The number of random numbers returned is 4921entirely up to the module to decide. The number required for a single password 4922is passed purely as a guide. The function must always return at least one 4923random number. 4924 4925The module ships with five standard RNGs (described below). 4926 4927By default, the module will try to use one of the following four RNGs, listed 4928from most to least preferred, depending on what is available on the system: 4929 4930=over 4 4931 4932=item 1 4933 4934C<Crypt::HSXKPasswd::RNG::Math_Random_Secure> (only available if 4935C<Math::Random::Secure> is installed on the system). 4936 4937=item 2 4938 4939C<Crypt::HSXKPasswd::RNG::Data_Entropy> (only available if 4940C<Data::Entropy::Algorithms> is installed on the system). 4941 4942=item 3 4943 4944C<Crypt::HSXKPasswd::RNG::DevUrandom> (only available on Linux/Unix systems 4945with a C</dev/urandom>). 4946 4947=item 4 4948 4949C<Crypt::HSXKPasswd::RNG::Basic> (available on all systems because it uses 4950Perl's built-in C<rand()> function). 4951 4952=back 4953 4954If the constructor is called without specifying an RNG, and if the only 4955available RNG is C<Crypt::HSXKPasswd::RNG::Basic>, a warning will be thrown 4956suggesting installing C<Math::Random::Secure> or C<Data::Entropy::Algorithms>. 4957 4958The module also ships with a fifth RNG, C<Crypt::HSXKPasswd::RNG::RandomDotOrg>, 4959but this one must be explicitly used, the constructor will never used it by 4960default. As its name suggests, this class uses L<http://Random.Org/>'s HTTP API 4961to generate random numbers. 4962 4963To explicitly use any particular RNG, create an instance of it, and either pass 4964that instance to the constructor with the named argument C<rng>, or, set the RNG 4965after instantiating the object using the C<rng()> function. 4966 4967=head3 Crypt::HSXKPasswd::RNG::Math_Random_Secure 4968 4969 my $rng = Crypt::HSXKPasswd::RNG::Math_Random_Secure->new(); 4970 4971This is the preferred RNG because it is both fast and secure, but, it requires 4972the non-standard module C<Math::Random::Secure> 4973(L<http://search.cpan.org/perldoc?Math%3A%3ARandom%3A%3ASecure>) be installed. 4974 4975=head3 Crypt::HSXKPasswd::RNG::Data_Entropy 4976 4977 my $rng = Crypt::HSXKPasswd::RNG::Data_Entropy->new(); 4978 4979This RNG is secure, but it is quite slow (about six times slower than 4980C<Crypt::HSXKPasswd::RNG::Math_Random_Secure>), and it requires 4981the non-standard module C<Data::Entropy::Algorithms> 4982(L<http://search.cpan.org/perldoc?Data%3A%3AEntropy%3A%3AAlgorithms>) be 4983installed. 4984 4985=head3 Crypt::HSXKPasswd::RNG::DevUrandom 4986 4987 my $rng = Crypt::HSXKPasswd::RNG::DevUrandom->new(); 4988 4989This RNG is secure and relatively fast (faster than 4990C<Crypt::HSXKPasswd::RNG::Data_Entropy> but slower than 4991C<Crypt::HSXKPasswd::RNG::Math_Random_Secure>), but is only available on 4992Linux/Unix systems with a C</dev/urandom> special file. 4993 4994=head3 Crypt::HSXKPasswd::RNG::Basic 4995 4996 my $rng = Crypt::HSXKPasswd::RNG::Basic->new(); 4997 4998This RNG uses Perl's built-in C<rand()> function as its source of randomness, 4999and this is sub-optimal. The Perl docs warn that C<rand()> is not a particularly 5000good source of random numbers, and advises against its use for cryptography. 5001 5002This RNG provides a base-line, and should only be used if none of the better 5003RNGs are available. While it is sub-optimal, it will still generate passwords 5004with sufficient entropy in most situations. Ultimately, even using this 5005imperfect RNG, this module will still produce passwords that are much better 5006than those produced by the human imagination! 5007 5008=head3 Crypt::HSXKPasswd::RNG::RandomDotOrg 5009 5010 my $rng = Crypt::HSXKPasswd::RNG::RandomDotOrg->new('my.address@my.dom'); 5011 my $rng = Crypt::HSXKPasswd::RNG::RandomDotOrg->new('my.address@my.dom', 5012 timeout => 180, 5013 num_passwords => 3, 5014 ); 5015 5016This RNG serves as a usable example of an RNG that queries a web service. As its 5017name suggests, this class uses L<http://Random.Org/>'s HTTP API to generate 5018random numbers. 5019 5020In order to comply with Random.Org's client guidelines 5021(L<https://www.random.org/clients/>), this module requires that a valid email 5022address be passed as the first argument. 5023 5024The client guidelines also request that clients use long timeouts, and batch 5025their requests. They prefer to be asked for more number less frequently than 5026less numbers more frequently. For this reason the class's default behaviour is 5027to use a timeout of 180 seconds, and to request enough random numbers to 5028generate three passwords at a time. 5029 5030These defaults can be overridden by passing named arguments to the constructor 5031after the email address. The following named arguments are supported: 5032 5033=over 4 5034 5035=item * 5036 5037C<timeout> - the timeout to use when making HTTP requests to Random.Org in 5038seconds (the default is 180). 5039 5040=item * 5041 5042C<num_passwords> - the number of password generations to fetch random numbers 5043for per request from Random.org. This value is in effect a multiplier for the 5044value passed to the C<random_numbers()> function by C<Crypt::HSXKPasswd>. 5045 5046C<num_absolute> - the absolute number of random numbers to fetch per request 5047to Random.Org. This argument takes precedence over C<num_passwords>. 5048 5049=back 5050 5051C<num_passwords> and C<num_absolute> should not be used together, but if they 5052are, C<num_absolute> use used, and C<num_passwords> is ignored. 5053 5054This class requires a number of modules not used by any other classes under 5055C<Crypt::HSXKPasswd>, and not listed in that module's requirements. If all of 5056the following modules are not installed, the constructor will croak: 5057 5058=over 4 5059 5060=item * 5061 5062C<Email::Valid> 5063 5064=item * 5065 5066C<LWP::UserAgent> 5067 5068=item * 5069 5070C<Mozilla::CA> 5071 5072=item * 5073 5074C<URI> 5075 5076=back 5077 5078=head1 DIAGNOSTICS 5079 5080By default this module does all of it's error notification via the functions 5081C<carp()>, C<croak()>, and C<confess()> from the C<Carp> module. Optionally, 5082all error messages can also be printed to a stream. To enable the printing of 5083messages, set the C<LOG_ERRORS> module configuration variable to C<1>. All 5084error messages will then be printed to the stream defined by the module 5085configuration variable C<LOG_STREAM>, which is set to C<STDERR> by default. 5086 5087Ordinarily this module produces very little output. To enable more verbose 5088output the module configuration variable C<DEBUG> can be set to C<1>. Debug 5089message are printed to the stream specified by the module variable 5090C<LOG_STREAM>. 5091 5092This module produces output at three severity levels: 5093 5094=over 4 5095 5096=item * 5097 5098C<DEBUG> - this output is completely suppressed unless the module configuration 5099variable C<DEBUG> is set to C<1>. All debug messages are printed to the stream 5100defined in the module configuration variable C<LOG_STREAM> (regardless of the 5101the value of the module configuration variable C<LOG_ERRORS>). 5102 5103=item * 5104 5105C<WARNING> - warning messages are always thrown with C<carp()>, and also printed 5106to the stream specified by the module configuration variable C<LOG_STREAM> if 5107the module configuration variable C<LOG_ERRORS> is set to C<1>. 5108 5109=item * 5110 5111C<ERROR> - error messages are usually thrown with C<croak()>, but will be thrown 5112with C<confess()> if the module configuration variable C<DEBUG> is set to C<1>. 5113If the module configuration variable C<LOG_ERRORS> is set to C<1> errors are 5114also printed to the stream defined by the module configuration variable 5115C<LOG_STREAM>, including a stack trace if the module configuration variable 5116C<DEBUG> is set to C<1> and the module C<Devel::StackTrace> is installed. 5117 5118=back 5119 5120The value stored in a module configuration variable can be accessed and updated 5121using the function C<module_config()>. 5122 5123=head1 CONFIGURATION AND ENVIRONMENT 5124 5125This module does not currently support configuration files, nor does it 5126currently interact with the environment. It may do so in future versions. 5127 5128=head1 DEPENDENCIES 5129 5130This module requires the following Perl modules: 5131 5132=over 4 5133 5134=item * 5135 5136C<Carp> - L<http://search.cpan.org/perldoc?Carp> 5137 5138=item * 5139 5140C<Clone> - L<http://search.cpan.org/perldoc?Clone> 5141 5142=item * 5143 5144C<DateTime> - L<http://search.cpan.org/perldoc?DateTime> 5145 5146=item * 5147 5148C<English> - L<http://search.cpan.org/perldoc?English> 5149 5150=item * 5151 5152C<Fatal> - L<http://search.cpan.org/perldoc?Fatal> 5153 5154=item * 5155 5156C<File::HomeDir> - L<http://search.cpan.org/perldoc?File%3A%3AHomeDir> 5157 5158=item * 5159 5160C<Getopt::Long> - L<http://search.cpan.org/perldoc?Getopt%3A%3ALong> 5161 5162=item * 5163 5164C<JSON> - L<http://search.cpan.org/perldoc?JSON> 5165 5166=item * 5167 5168C<List::MoreUtils> - L<http://search.cpan.org/perldoc?List%3A%3AMoreUtils> 5169 5170=item * 5171 5172C<Math::BigInt> - L<http://search.cpan.org/perldoc?Math%3A%3ABigInt> 5173 5174=item * 5175 5176C<Math::Round> - L<http://search.cpan.org/perldoc?Math%3A%3ARound> 5177 5178=item * 5179 5180C<Module::Load> - L<http://search.cpan.org/perldoc?Module%3A%3ALoad> 5181 5182=item * 5183 5184C<Pod::Usage> - L<http://search.cpan.org/perldoc?Pod%3A%3AUsage> 5185 5186=item * 5187 5188C<Readonly> - L<http://search.cpan.org/perldoc?Readonly> 5189 5190=item * 5191 5192C<Scalar::Util> - L<http://search.cpan.org/perldoc?Scalar%3A%3AUtil> 5193 5194=item * 5195 5196C<strict> - L<http://search.cpan.org/perldoc?strict> 5197 5198=item * 5199 5200C<Text::Unidecode> - L<http://search.cpan.org/perldoc?Text%3A%3AUnidecode> 5201 5202=item * 5203 5204C<Type::Library> - L<http://search.cpan.org/perldoc?Type%3A%3ALibrary> 5205 5206=item * 5207 5208C<Type::Params> - L<http://search.cpan.org/perldoc?Type%3A%3AParams> 5209 5210=item * 5211 5212C<Type::Tiny> - L<http://search.cpan.org/perldoc?Type%3A%3ATiny> 5213 5214=item * 5215 5216C<Types::Standard> - L<http://search.cpan.org/perldoc?Types%3A%3AStandard> 5217 5218=item * 5219 5220C<warnings> - L<http://search.cpan.org/perldoc?warnings> 5221 5222=back 5223 5224The module can also optionally use the following Perl modules: 5225 5226=over 4 5227 5228=item * 5229 5230C<Data::Entropy::Algorithms> - L<http://search.cpan.org/perldoc?Data%3A%3AEntropy%3A%3AAlgorithms> 5231 5232Used by the RNG class C<Crypt::HSXKPasswd::RNG::Data_Entropy>. 5233 5234=item * 5235 5236C<Devel::StackTrace> - L<http://search.cpan.org/perldoc?Devel%3A%3AStackTrace> 5237 5238Used for printing stack traces with error messages if 5239C<$XKPasswd::DEBUG> and C<$XKPasswd::LOG_ERRORS> both evaluate to true. If the 5240module is not installed the stack traces will be omitted from the log messages. 5241 5242=item * 5243 5244C<Email::Valid> - L<http://search.cpan.org/perldoc?Email%3A%3AValid> 5245 5246Used by the Random.Org RNG class C<Crypt::HSXKPasswd::RNG::RandomDotOrg>. 5247 5248=item * 5249 5250C<LWP::UserAgent> - L<http://search.cpan.org/perldoc?LWP%3A%3AUserAgent> 5251 5252Used by the Random.Org RNG class C<Crypt::HSXKPasswd::RNG::RandomDotOrg>. 5253 5254=item * 5255 5256C<Math::Random::Secure> - L<http://search.cpan.org/perldoc?Math%3A%3ARandom%3A%3ASecure> 5257 5258Used by the RNG class C<Crypt::HSXKPasswd::RNG::Math_Random_Secure>. 5259 5260=item * 5261 5262C<Mozilla::CA> - L<http://search.cpan.org/perldoc?Mozilla%3A%3ACA> 5263 5264Indirectly required by the Random.Org RNG class 5265C<Crypt::HSXKPasswd::RNG::RandomDotOrg> because without it C<LWP::UserAgent> 5266can't use HTTPS, and the Random.Org API uses HTTPS. 5267 5268=item * 5269 5270C<URI> - L<http://search.cpan.org/perldoc?URI> 5271 5272Used by the Random.Org RNG class C<Crypt::HSXKPasswd::RNG::RandomDotOrg>. 5273 5274=back 5275 5276=head1 INCOMPATIBILITIES 5277 5278This module has no known incompatibilities. 5279 5280=head1 BUGS AND LIMITATIONS 5281 5282There are no known bugs in this module. 5283 5284Please report any bugs you may find on the module's GitHub page: 5285L<https://github.com/bbusschots/xkpasswd.pm>. 5286 5287=head1 LICENCE AND COPYRIGHT 5288 5289Copyright (c) 2014-15, Bart Busschots T/A Bartificer Web Solutions 5290All rights reserved. 5291 5292Redistribution and use in source and binary forms, with or without 5293modification, are permitted provided that the following conditions are met: 5294 5295=over 4 5296 5297=item 1. 5298 5299Redistributions of source code must retain the above copyright notice, this 5300list of conditions and the following disclaimer. 5301 5302=item 2. 5303 5304Redistributions in binary form must reproduce the above copyright notice, 5305this list of conditions and the following disclaimer in the documentation 5306and/or other materials provided with the distribution. 5307 5308=back 5309 5310THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 5311ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 5312WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 5313DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 5314ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 5315(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 5316LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 5317ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 5318(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 5319SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 5320 5321The following components of this package are covered by the more restrictive 5322GPL V2 license L<https://www.gnu.org/licenses/gpl-2.0.html>: 5323 5324=over 4 5325 5326=item * 5327 5328The C<share/sample_dict_DE.txt> text file. 5329 5330=item * 5331 5332The C<Crypt::HSXKPasswd::Dictionary::DE> Perl module. 5333 5334=item * 5335 5336The C<share/sample_dict_FR.txt> text file. 5337 5338=item * 5339 5340The C<Crypt::HSXKPasswd::Dictionary::FR> Perl module. 5341 5342=item * 5343 5344The C<share/sample_dict_IT.txt> text file. 5345 5346=item * 5347 5348The C<Crypt::HSXKPasswd::Dictionary::IT> Perl module. 5349 5350=item * 5351 5352The C<share/sample_dict_NL.txt> text file. 5353 5354=item * 5355 5356The C<Crypt::HSXKPasswd::Dictionary::NL> Perl module. 5357 5358=item * 5359 5360The C<share/sample_dict_PT.txt> text file. 5361 5362=item * 5363 5364The C<Crypt::HSXKPasswd::Dictionary::PT> Perl module. 5365 5366=back 5367 5368=head1 AUTHOR 5369 5370Bart Busschots (L<mailto:bart@bartificer.net>)