Crypt-HSXKPasswd
view release on metacpan or search on metacpan
lib/Crypt/HSXKPasswd.pm view on Meta::CPAN
package Crypt::HSXKPasswd;
# import required modules
use strict;
use warnings;
use Carp; # for nicer 'exception' handling for users of the module
use Fatal qw( :void open close binmode ); # make builtins throw exceptions on failure
use English qw( -no_match_vars ); # for more readable code
use Scalar::Util qw( blessed ); # for checking if a reference is blessed
use Math::Round; # for round()
use Math::BigInt; # for the massive numbers needed to store the permutations
use Clone qw( clone ); # for cloning nested data structures - exports clone()
use Readonly; # for truly constant constants
use JSON; # for dealing with JSON strings
use List::MoreUtils qw( uniq ); # for array deduplication
use Type::Tiny; # for generating anonymous type constraints when needed
use Type::Params qw( compile multisig ); # for parameter validation with Type::Tiny objects
use Types::Standard qw( slurpy :types ); # for basic type checking (Int Str etc.)
use Crypt::HSXKPasswd::Types qw( :types ); # for custom type checking
use Crypt::HSXKPasswd::Helper; # exports utility functions like _error & _warn
use Crypt::HSXKPasswd::Dictionary::Basic;
use Crypt::HSXKPasswd::RNG::Math_Random_Secure;
use Crypt::HSXKPasswd::RNG::Data_Entropy;
use Crypt::HSXKPasswd::RNG::DevUrandom;
use Crypt::HSXKPasswd::RNG::Basic;
# set things up for using UTF-8
use 5.016; # min Perl for good UTF-8 support, implies feature 'unicode_strings'
use Encode qw( encode decode );
use Text::Unidecode; # for stripping accents from accented characters
use utf8;
binmode STDOUT, ':encoding(UTF-8)';
# import (or not) optional modules
eval{
# the default dicrionary may not have been geneated using the Util module
require Crypt::HSXKPasswd::Dictionary::EN;
}or do{
carp('WARNING - failed to load Crypt::HSXKPasswd::Dictionary::EN');
};
## no critic (ProhibitAutomaticExportation);
use base qw( Exporter );
our @EXPORT = qw( hsxkpasswd );
## use critic
# Copyright (c) 2015, Bart Busschots T/A Bartificer Web Solutions All rights
# reserved.
#
# Code released under the FreeBSD license (included in the POD at the bottom of
# this file)
#==============================================================================
# Code
#==============================================================================
#
# === Constants and Package Variables =========================================#
#
# version info
use version; our $VERSION = qv('3.6');
# entropy control variables
my $_ENTROPY_MIN_BLIND = 78; # 78 bits - equivalent to 12 alpha numeric characters with mixed case and symbols
my $_ENTROPY_MIN_SEEN = 52; # 52 bits - equivalent to 8 alpha numeric characters with mixed case and symbols
my $_ENTROPY_WARNINGS = 'ALL'; # valid values are 'ALL', 'BLIND', or 'NONE' (invalid values treated like 'ALL')
# utility constants
Readonly my $_CLASS => __PACKAGE__;
Readonly my $_TYPES_CLASS => 'Crypt::HSXKPasswd::Types';
Readonly my $_DICTIONARY_BASE_CLASS => 'Crypt::HSXKPasswd::Dictionary';
Readonly my $_RNG_BASE_CLASS => 'Crypt::HSXKPasswd::RNG';
#
# Constructor -----------------------------------------------------------------
#
#####-SUB-######################################################################
# Type : CONSTRUCTOR (CLASS)
# Purpose : Instantiate an object of type XKPasswd
# Returns : An object of type XKPasswd
# Arguments : This function accepts the following named arguments - all
# optional:
# dictionary - an object that inherits from
# Crypt::HSXKPasswd::Dictionary
# dictionary_list - an array ref of words to use as a dictionary.
# dictionary_file - the path to a dictionary file.
lib/Crypt/HSXKPasswd.pm view on Meta::CPAN
# calculate and store the entropy stats
my %stats = $self->_calculate_entropy_stats();
$self->{_CACHE_ENTROPYSTATS} = \%stats;
# warn if we need to
unless(uc $_ENTROPY_WARNINGS eq 'NONE'){
# blind warnings are always needed if the level is not 'NONE'
if($self->{_CACHE_ENTROPYSTATS}->{entropy_blind_min} < $_ENTROPY_MIN_BLIND){
_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');
}
# seen warnings if the cut-off is not 'BLIND'
unless(uc $_ENTROPY_WARNINGS eq 'BLIND'){
if($self->{_CACHE_ENTROPYSTATS}->{entropy_seen} < $_ENTROPY_MIN_SEEN){
_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');
}
}
}
# to keep perl critic happy
return 1;
}
#####-SUB-######################################################################
# Type : CLASS (PRIVATE)
# Purpose : To nicely print a Math::BigInt object
# Returns : a string representing the object's value in scientific notation
# with 1 digit before the decimal and 2 after
# Arguments : 1. a Math::BigInt object
# Throws : Croaks on invalid invocation or args
# Notes :
# See Also :
sub _render_bigint{
my @args = @_;
my $class = shift @args;
_force_class($class);
# validate args
state $args_check = compile(InstanceOf['Math::BigInt']);
my ($bigint) = $args_check->(@args);
# convert the bigint to an array of characters
my @chars = split //sx, "$bigint";
# render nicely
if(scalar @chars < 3){
return q{}.join q{}, @chars;
}
# start with the three most signifficant digits (as a decimal)
my $ans = q{}.$chars[0].q{.}.$chars[1].$chars[2];
# then add the scientific notation bit
$ans .= 'x10^'.(scalar @chars - 1);
# return the result
return $ans;
}
#####-SUB-######################################################################
# Type : CLASS (PRIVATE)
# Purpose : Get the so-called 'grapheme length' of a unicode string, that is
# to say, the length of a word where a letter with an accent counts
# as a single letter.
# Returns : An integer
# Arguments : 1) the string to get the length of
# Throws : Croaks on invalid invocation and invalid args
# Notes : Perl, by default, will consider accented letters as having a
# length of two. This function uses a very common algorythm
# recommended all over the internet, including in the Perl Unicode
# cookbook: http://search.cpan.org/~shay/perl-5.20.2/pod/perlunicook.pod
# Before resorting to this technique, I tried to use the
# grapheme_length function from Unicode::Util, but it proved
# unacceptably slow.
# See Also :
sub _grapheme_length{
my @args = @_;
my $class = shift @args;
_force_class($class);
# validate args
state $args_check = compile(Str);
my ($string) = $args_check->(@args);
# do the calculation
my $grapheme_length = 0;
while($string =~ /\X/gsx){$grapheme_length++};
# return the result
return $grapheme_length;
}
1; # because Perl is just a little bit odd :)
__END__
#==============================================================================
# User Documentation
#==============================================================================
=head1 NAME
C<Crypt::HSXKPasswd> - A secure memorable password generator inspired by Steve
Gibson's Passord Haystacks (L<https://www.grc.com/haystack.htm>), and the
famous XKCD password cartoon (L<https://xkcd.com/936/>).
=head1 VERSION
This documentation refers to C<Crypt::HSXKPasswd> version 3.6.
=head1 SYNOPSIS
use Crypt::HSXKPasswd;
#
# Functional Interface - a shortcut for generating single passwords
#
# generate a single password using the default word source, configuration,
# and random number generator
my $password = hsxkpasswd();
# the above call is simply a shortcut for the following
( run in 1.093 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )