summaryrefslogtreecommitdiff
path: root/lib/Crypt/PRNG.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Crypt/PRNG.pm')
-rw-r--r--lib/Crypt/PRNG.pm283
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