283 lines
7.0 KiB
Perl
283 lines
7.0 KiB
Perl
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> |