package Crypt::PRNG; use strict; use warnings; use Exporter 'import'; 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(); use CryptX; use MIME::Base64 qw(encode_base64); 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||'Fortuna') : _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 encode_base64(shift->bytes(shift), "") } sub bytes_b64u { return _base64url_enc(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(@_) } } # Base64 URL Safe hack as encode_base64url requires MIME::Base64 3.11+ sub _base64url_enc { # RFC 4648 Base64 URL Safe - https://tools.ietf.org/html/rfc4648#page-7 my $data = shift; my $b64 = encode_base64($data, ''); $b64 =~ s/=+\z//; $b64 =~ tr[+/][-_]; return $b64; } 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 Fortuna 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 (UNIX) or C (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 (UNIX) or C (Win32). B 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 =head2 bytes_hex $hex_string = $prng->bytes_hex($length); See L =head2 bytes_b64 $base64_string = $prng->bytes_b64($length); See L =head2 bytes_b64u $base64url_string = $prng->bytes_b64u($length); See L =head2 string $alphanumeric_string = $prng->string($length); #or $alphanumeric_string = $prng->string; See L =head2 string_from $string = $prng->string_from($range, $length); See L =head2 double $n = $prng->double; #or $n = $prng->double($limit); See L =head2 int32 $i = $prng->int32; See L =head1 SEE ALSO L, L, L, L