diff options
Diffstat (limited to 'lib/Crypt/PRNG.pm')
-rw-r--r-- | lib/Crypt/PRNG.pm | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/lib/Crypt/PRNG.pm b/lib/Crypt/PRNG.pm new file mode 100644 index 00000000..ae14afaf --- /dev/null +++ b/lib/Crypt/PRNG.pm @@ -0,0 +1,283 @@ +package Crypt::PRNG; + +use strict; +use warnings; +our $VERSION = '0.048'; + +use base qw(Exporter); +our %EXPORT_TAGS = ( all => [qw(random_bytes random_bytes_hex random_bytes_b64 random_bytes_b64u random_string random_string_from rand irand)] ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT = qw(); + +#BEWARE: cannot use Crypt::Misc qw(encode_b64 encode_b64u); +use CryptX; + +sub _trans_prng_name { + my $name = shift; + $name =~ s/^Crypt::PRNG:://; + return lc($name); +} + +### METHODS + +sub new { + my $pkg = shift; + my $prng_name = $pkg eq __PACKAGE__ ? _trans_prng_name(shift||'ChaCha20') : _trans_prng_name($pkg); + return _new($$, $prng_name, @_); +} + +sub bytes { return shift->_bytes($$, shift) } + +sub int32 { return shift->_int32($$) } + +sub double { return shift->_double($$, shift) } + +sub bytes_hex { return unpack("H*", shift->bytes(shift)) } + +sub bytes_b64 { return CryptX::_encode_base64(shift->bytes(shift)) } + +sub bytes_b64u { return CryptX::_encode_base64url(shift->bytes(shift)) } + +sub string { + my ($self, $len) = @_; + return $self->string_from("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", $len); +} + +sub string_from { + my ($self, $chars, $len) = @_; + + $len = 20 unless defined $len; + return unless $len > 0; + return unless length($chars) > 0; + + my @ch = split(//, $chars); + my $max_index = $#ch; + return if $max_index > 65535; + + my $mask; + for my $n (1..31) { + $mask = (1<<$n) - 1; + last if $mask >= $max_index; + } + + my $upck = ($max_index > 255) ? "n*" : "C*"; + my $l = $len * 2; + + my $rv = ''; + my @r; + while (length $rv < $len) { + @r = unpack($upck, $self->bytes($l)) if scalar @r == 0; + my $i = (shift @r) & $mask; + next if $i > $max_index; + $rv .= $ch[$i]; + } + return $rv; +} + +sub CLONE_SKIP { 1 } # prevent cloning + +### FUNCTIONS + +{ + ### stolen from Bytes::Random::Secure + # + # Instantiate our random number generator(s) inside of a lexical closure, + # limiting the scope of the RNG object so it can't be tampered with. + my $RNG_object = undef; + my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once. + $RNG_object = Crypt::PRNG->new unless defined $RNG_object && ref($RNG_object) ne 'SCALAR'; + return $RNG_object; + }; + sub rand(;$) { return $fetch_RNG->()->double(@_) } + sub irand() { return $fetch_RNG->()->int32() } + sub random_bytes($) { return $fetch_RNG->()->bytes(@_) } + sub random_bytes_hex($) { return $fetch_RNG->()->bytes_hex(@_) } + sub random_bytes_b64($) { return $fetch_RNG->()->bytes_b64(@_) } + sub random_bytes_b64u($) { return $fetch_RNG->()->bytes_b64u(@_) } + sub random_string_from($;$) { return $fetch_RNG->()->string_from(@_) } + sub random_string(;$) { return $fetch_RNG->()->string(@_) } +} + +1; + +=pod + +=head1 NAME + +Crypt::PRNG - Cryptographically secure random number generator + +=head1 SYNOPSIS + + ### Functional interface: + use Crypt::PRNG qw(random_bytes random_bytes_hex random_bytes_b64 random_bytes_b64u + random_string random_string_from rand irand); + + $octets = random_bytes(45); + $hex_string = random_bytes_hex(45); + $base64_string = random_bytes_b64(45); + $base64url_string = random_bytes_b64u(45); + $alphanumeric_string = random_string(30); + $string = random_string_from('ACGT', 64); + $floating_point_number_0_to_1 = rand; + $floating_point_number_0_to_88 = rand(88); + $unsigned_32bit_int = irand; + + ### OO interface: + use Crypt::PRNG; + + $prng = Crypt::PRNG->new; + #or + $prng = Crypt::PRNG->new("RC4"); + #or + $prng = Crypt::PRNG->new("RC4", "some data used for seeding PRNG"); + + $octets = $prng->bytes(45); + $hex_string = $prng->bytes_hex(45); + $base64_string = $prng->bytes_b64(45); + $base64url_string = $prng->bytes_b64u(45); + $alphanumeric_string = $prng->string(30); + $string = $prng->string_from('ACGT', 64); + $floating_point_number_0_to_1 = $prng->double; + $floating_point_number_0_to_88 = $prng->double(88); + $unsigned_32bit_int = $prng->int32; + +=head1 DESCRIPTION + +Provides an interface to the ChaCha20 based pseudo random number generator (thread-safe and fork-safe). + +=head1 FUNCTIONS + +=head2 random_bytes + + $octets = random_bytes($length); + +Returns C<$length> random octects. + +=head2 random_bytes_hex + + $hex_string = random_bytes_hex($length); + +Returns C<$length> random octects encoded as hexadecimal string. + +=head2 random_bytes_b64 + + $base64_string = random_bytes_b64($length); + +Returns C<$length> random octects Base64 encoded. + +=head2 random_bytes_b64u + + $base64url_string = random_bytes_b64u($length); + +Returns C<$length> random octects Base64 URL Safe (RFC 4648 section 5) encoded. + +=head2 random_string_from + + $string = random_string_from($range, $length); + #e.g. + $string = random_string_from("ABCD", 10); + +Returns a random string made of C<$length> chars randomly chosen from C<$range> string. + +=head2 random_string + + $alphanumeric_string = random_string($length); + #or + $alphanumeric_string = random_string; # default length = 20 + +Similar to random_string_from, only C<$range> is fixed to C<'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'>. + +=head2 rand + + $n = rand; + #or + $n = rand($limit); + +Returns a random floating point number from range C<[0,1)> (if called without param) or C<[0,$limit)>. + +=head2 irand + + $i = irand; + +Returns a random unsigned 32bit integer - range 0 .. 0xFFFFFFFF. + +=head1 METHODS + +=head2 new + + $prng = Crypt::PRNG->new; + #or + $prng = Crypt::PRNG->new($alg); + #or + $prng = Crypt::PRNG->new($alg, $seed); + + # $alg ... algorithm name 'Frotuna' (DEFAULT), 'RC4', 'Sober128' or 'Yarrow' + # $seed ... will be used as an initial entropy for seeding PRNG + +If C<$seed> is not specified the PRNG is automatically seeded with 32bytes random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32) + +=head2 add_entropy + + $prng->add_entropy($random_data); + #or + $prng->add_entropy(); + +If called without parameter it uses 32bytes random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32). + +B<BEWARE:> you probably do not need this function at all as the module does automatic seeding on initialization as well as reseeding after fork and thread creation. + +=head2 bytes + + $octets = $prng->bytes($length); + +See L<random_bytes|/random_bytes> + +=head2 bytes_hex + + $hex_string = $prng->bytes_hex($length); + +See L<random_bytes_hex|/random_bytes_hex> + +=head2 bytes_b64 + + $base64_string = $prng->bytes_b64($length); + +See L<random_bytes_b64|/random_bytes_b64> + +=head2 bytes_b64u + + $base64url_string = $prng->bytes_b64u($length); + +See L<random_bytes_b64u|/random_bytes_b64u> + +=head2 string + + $alphanumeric_string = $prng->string($length); + #or + $alphanumeric_string = $prng->string; + +See L<random_string|/random_string> + +=head2 string_from + + $string = $prng->string_from($range, $length); + +See L<random_string_from|/random_string_from> + +=head2 double + + $n = $prng->double; + #or + $n = $prng->double($limit); + +See L<rand|/rand> + +=head2 int32 + + $i = $prng->int32; + +See L<irand|/irand> + +=head1 SEE ALSO + +L<Crypt::PRNG::Fortuna>, L<Crypt::PRNG::RC4>, L<Crypt::PRNG::Sober128>, L<Crypt::PRNG::Yarrow>
\ No newline at end of file |