summaryrefslogtreecommitdiff
path: root/lib/Crypt
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Crypt')
-rw-r--r--lib/Crypt/AuthEnc.pm17
-rw-r--r--lib/Crypt/AuthEnc/CCM.pm100
-rw-r--r--lib/Crypt/AuthEnc/ChaCha20Poly1305.pm164
-rw-r--r--lib/Crypt/AuthEnc/EAX.pm179
-rw-r--r--lib/Crypt/AuthEnc/GCM.pm179
-rw-r--r--lib/Crypt/AuthEnc/OCB.pm174
-rw-r--r--lib/Crypt/Checksum.pm197
-rw-r--r--lib/Crypt/Checksum/Adler32.pm121
-rw-r--r--lib/Crypt/Checksum/CRC32.pm121
-rw-r--r--lib/Crypt/Cipher.pm217
-rw-r--r--lib/Crypt/Cipher/AES.pm121
-rw-r--r--lib/Crypt/Cipher/Anubis.pm121
-rw-r--r--lib/Crypt/Cipher/Blowfish.pm121
-rw-r--r--lib/Crypt/Cipher/CAST5.pm121
-rw-r--r--lib/Crypt/Cipher/Camellia.pm121
-rw-r--r--lib/Crypt/Cipher/DES.pm121
-rw-r--r--lib/Crypt/Cipher/DES_EDE.pm121
-rw-r--r--lib/Crypt/Cipher/KASUMI.pm121
-rw-r--r--lib/Crypt/Cipher/Khazad.pm121
-rw-r--r--lib/Crypt/Cipher/MULTI2.pm121
-rw-r--r--lib/Crypt/Cipher/Noekeon.pm121
-rw-r--r--lib/Crypt/Cipher/RC2.pm121
-rw-r--r--lib/Crypt/Cipher/RC5.pm121
-rw-r--r--lib/Crypt/Cipher/RC6.pm121
-rw-r--r--lib/Crypt/Cipher/SAFERP.pm121
-rw-r--r--lib/Crypt/Cipher/SAFER_K128.pm121
-rw-r--r--lib/Crypt/Cipher/SAFER_K64.pm121
-rw-r--r--lib/Crypt/Cipher/SAFER_SK128.pm121
-rw-r--r--lib/Crypt/Cipher/SAFER_SK64.pm121
-rw-r--r--lib/Crypt/Cipher/SEED.pm121
-rw-r--r--lib/Crypt/Cipher/Skipjack.pm121
-rw-r--r--lib/Crypt/Cipher/Twofish.pm121
-rw-r--r--lib/Crypt/Cipher/XTEA.pm121
-rw-r--r--lib/Crypt/Digest.pm382
-rw-r--r--lib/Crypt/Digest/BLAKE2b_160.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2b_256.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2b_384.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2b_512.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2s_128.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2s_160.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2s_224.pm229
-rw-r--r--lib/Crypt/Digest/BLAKE2s_256.pm229
-rw-r--r--lib/Crypt/Digest/CHAES.pm227
-rw-r--r--lib/Crypt/Digest/MD2.pm227
-rw-r--r--lib/Crypt/Digest/MD4.pm227
-rw-r--r--lib/Crypt/Digest/MD5.pm227
-rw-r--r--lib/Crypt/Digest/RIPEMD128.pm227
-rw-r--r--lib/Crypt/Digest/RIPEMD160.pm227
-rw-r--r--lib/Crypt/Digest/RIPEMD256.pm227
-rw-r--r--lib/Crypt/Digest/RIPEMD320.pm227
-rw-r--r--lib/Crypt/Digest/SHA1.pm227
-rw-r--r--lib/Crypt/Digest/SHA224.pm227
-rw-r--r--lib/Crypt/Digest/SHA256.pm227
-rw-r--r--lib/Crypt/Digest/SHA384.pm227
-rw-r--r--lib/Crypt/Digest/SHA3_224.pm227
-rw-r--r--lib/Crypt/Digest/SHA3_256.pm227
-rw-r--r--lib/Crypt/Digest/SHA3_384.pm227
-rw-r--r--lib/Crypt/Digest/SHA3_512.pm227
-rw-r--r--lib/Crypt/Digest/SHA512.pm227
-rw-r--r--lib/Crypt/Digest/SHA512_224.pm227
-rw-r--r--lib/Crypt/Digest/SHA512_256.pm227
-rw-r--r--lib/Crypt/Digest/SHAKE.pm106
-rw-r--r--lib/Crypt/Digest/Tiger192.pm227
-rw-r--r--lib/Crypt/Digest/Whirlpool.pm227
-rw-r--r--lib/Crypt/KeyDerivation.pm167
-rw-r--r--lib/Crypt/Mac.pm53
-rw-r--r--lib/Crypt/Mac/BLAKE2b.pm156
-rw-r--r--lib/Crypt/Mac/BLAKE2s.pm156
-rw-r--r--lib/Crypt/Mac/F9.pm156
-rw-r--r--lib/Crypt/Mac/HMAC.pm160
-rw-r--r--lib/Crypt/Mac/OMAC.pm158
-rw-r--r--lib/Crypt/Mac/PMAC.pm158
-rw-r--r--lib/Crypt/Mac/Pelican.pm156
-rw-r--r--lib/Crypt/Mac/Poly1305.pm156
-rw-r--r--lib/Crypt/Mac/XCBC.pm158
-rw-r--r--lib/Crypt/Misc.pm363
-rw-r--r--lib/Crypt/Mode.pm72
-rw-r--r--lib/Crypt/Mode/CBC.pm108
-rw-r--r--lib/Crypt/Mode/CFB.pm99
-rw-r--r--lib/Crypt/Mode/CTR.pm106
-rw-r--r--lib/Crypt/Mode/ECB.pm109
-rw-r--r--lib/Crypt/Mode/OFB.pm99
-rw-r--r--lib/Crypt/PK.pm33
-rw-r--r--lib/Crypt/PK/DH.pm643
-rw-r--r--lib/Crypt/PK/DSA.pm617
-rw-r--r--lib/Crypt/PK/ECC.pm1398
-rw-r--r--lib/Crypt/PK/RSA.pm939
-rw-r--r--lib/Crypt/PRNG.pm283
-rw-r--r--lib/Crypt/PRNG/ChaCha20.pm159
-rw-r--r--lib/Crypt/PRNG/Fortuna.pm160
-rw-r--r--lib/Crypt/PRNG/RC4.pm159
-rw-r--r--lib/Crypt/PRNG/Sober128.pm159
-rw-r--r--lib/Crypt/PRNG/Yarrow.pm158
-rw-r--r--lib/Crypt/Stream/ChaCha.pm76
-rw-r--r--lib/Crypt/Stream/RC4.pm68
-rw-r--r--lib/Crypt/Stream/Sober128.pm71
96 files changed, 18852 insertions, 0 deletions
diff --git a/lib/Crypt/AuthEnc.pm b/lib/Crypt/AuthEnc.pm
new file mode 100644
index 00000000..b5960786
--- /dev/null
+++ b/lib/Crypt/AuthEnc.pm
@@ -0,0 +1,17 @@
+package Crypt::AuthEnc;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+__END__
+
+=head1 NAME
+
+Crypt::AuthEnc - [internal only]
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/AuthEnc/CCM.pm b/lib/Crypt/AuthEnc/CCM.pm
new file mode 100644
index 00000000..0ba3e398
--- /dev/null
+++ b/lib/Crypt/AuthEnc/CCM.pm
@@ -0,0 +1,100 @@
+package Crypt::AuthEnc::CCM;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::AuthEnc Exporter);
+our %EXPORT_TAGS = ( all => [qw( ccm_encrypt_authenticate ccm_decrypt_verify )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+### the following functions are implemented in XS:
+# - _memory_encrypt
+# - _memory_decrypt
+
+sub ccm_encrypt_authenticate {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $nonce = shift;
+ my $adata = shift;
+ my $tag_len = shift;
+ my $plaintext = shift;
+ return _memory_encrypt(Crypt::Cipher::_trans_cipher_name($cipher_name), $key, $nonce, $adata, $tag_len, $plaintext);
+}
+
+sub ccm_decrypt_verify {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $nonce = shift;
+ my $adata = shift;
+ my $ciphertext = shift;
+ my $tag = shift;
+ return _memory_decrypt(Crypt::Cipher::_trans_cipher_name($cipher_name), $key, $nonce, $adata, $ciphertext, $tag);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::AuthEnc::CCM - Authenticated encryption in CCM mode
+
+=head1 SYNOPSIS
+
+ ### functional interface
+ use Crypt::AuthEnc::CCM qw(ccm_encrypt_authenticate ccm_decrypt_verify);
+
+ my ($ciphertext, $tag) = ccm_encrypt_authenticate('AES', $key, $nonce, $adata, $tag_len, $plaintext);
+ my $plaintext = ccm_decrypt_verify('AES', $key, $nonce, $adata, $ciphertext, $tag);
+
+=head1 DESCRIPTION
+
+CCM is a encrypt+authenticate mode that is centered around using AES (or any 16-byte cipher) as aprimitive.
+Unlike EAX and OCB mode, it is only meant for packet mode where the length of the input is known in advance.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::AuthEnc::CCM qw(ccm_encrypt_authenticate ccm_decrypt_verify);
+
+=head1 FUNCTIONS
+
+=head2 ccm_encrypt_authenticate
+
+ my ($ciphertext, $tag) = ccm_encrypt_authenticate($cipher, $key, $nonce, $adata, $tag_len, $plaintext);
+
+ # $cipher .. 'AES' or name of any other cipher with 16-byte block len
+ # $key ..... key of proper length (e.g. 128/192/256bits for AES)
+ # $nonce ... unique nonce/salt (no need to keep it secret)
+ # $adata ... additional authenticated data
+ # $tag_len . required length of output tag
+
+CCM parameters should follow L<http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38c.pdf>
+
+ # tag length: 4, 6, 8, 10, 12, 14, 16 (reasonable minimum is 8)
+ # nonce length: 7, 8, 9, 10, 11, 12, 13 (if you are not sure, use 11)
+ # BEWARE nonce length determines max. enc/dec data size: max_data_size = 2^(8*(15-nonce_len))
+
+=head2 ccm_decrypt_verify
+
+ my $plaintext = ccm_decrypt_verify($cipher, $key, $nonce, $adata, $ciphertext, $tag);
+
+ # on error returns undef
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::AuthEnc::EAX|Crypt::AuthEnc::EAX>, L<Crypt::AuthEnc::GCM|Crypt::AuthEnc::GCM>, L<Crypt::AuthEnc::OCB|Crypt::AuthEnc::OCB>
+
+=item * L<https://en.wikipedia.org/wiki/CCM_mode|https://en.wikipedia.org/wiki/CCM_mode>
+
+=back
diff --git a/lib/Crypt/AuthEnc/ChaCha20Poly1305.pm b/lib/Crypt/AuthEnc/ChaCha20Poly1305.pm
new file mode 100644
index 00000000..c134a545
--- /dev/null
+++ b/lib/Crypt/AuthEnc/ChaCha20Poly1305.pm
@@ -0,0 +1,164 @@
+package Crypt::AuthEnc::ChaCha20Poly1305;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::AuthEnc Exporter);
+our %EXPORT_TAGS = ( all => [qw( chacha20poly1305_encrypt_authenticate chacha20poly1305_decrypt_verify )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+
+sub new { my $class = shift; _new(@_) }
+
+sub chacha20poly1305_encrypt_authenticate {
+ my $key = shift;
+ my $iv = shift;
+ my $adata = shift;
+ my $plaintext = shift;
+
+ my $m = Crypt::AuthEnc::ChaCha20Poly1305->new($key, $iv);
+ $m->adata_add(defined $adata ? $adata : ''); #XXX-TODO if no aad we have to pass empty string
+ my $ct = $m->encrypt_add($plaintext);
+ my $tag = $m->encrypt_done;
+ return ($ct, $tag);
+}
+
+sub chacha20poly1305_decrypt_verify {
+ my $key = shift;
+ my $iv = shift;
+ my $adata = shift;
+ my $ciphertext = shift;
+ my $tag = shift;
+
+ my $m = Crypt::AuthEnc::ChaCha20Poly1305->new($key, $iv);
+ $m->adata_add(defined $adata ? $adata : ''); #XXX-TODO if no aad we have to pass empty string
+ my $ct = $m->decrypt_add($ciphertext);
+ return $m->decrypt_done($tag) ? $ct : undef;
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::AuthEnc::ChaCha20Poly1305 - Authenticated encryption in ChaCha20Poly1305 mode
+
+=head1 SYNOPSIS
+
+ ### OO interface
+ use Crypt::AuthEnc::ChaCha20Poly1305;
+
+ # encrypt and authenticate
+ my $ae = Crypt::AuthEnc::ChaCha20Poly1305->new($key, $iv);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $ct = $ae->encrypt_add('data1');
+ $ct = $ae->encrypt_add('data2');
+ $ct = $ae->encrypt_add('data3');
+ $tag = $ae->encrypt_done();
+
+ # decrypt and verify
+ my $ae = Crypt::AuthEnc::ChaCha20Poly1305->new($key, $iv);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $pt = $ae->decrypt_add('ciphertext1');
+ $pt = $ae->decrypt_add('ciphertext2');
+ $pt = $ae->decrypt_add('ciphertext3');
+ $tag = $ae->decrypt_done();
+ die "decrypt failed" unless $tag eq $expected_tag;
+
+ #or
+ my $result = $ae->decrypt_done($expected_tag) die "decrypt failed";
+
+ ### functional interface
+ use Crypt::AuthEnc::ChaCha20Poly1305 qw(chacha20poly1305_encrypt_authenticate chacha20poly1305_decrypt_verify);
+
+ my ($ciphertext, $tag) = chacha20poly1305_encrypt_authenticate($key, $iv, $adata, $plaintext);
+ my $plaintext = chacha20poly1305_decrypt_verify($key, $iv, $adata, $ciphertext, $tag);
+
+=head1 DESCRIPTION
+
+Provides encryption and authentication based on ChaCha20 + Poly1305 as defined in RFC 7539 - L<https://tools.ietf.org/html/rfc7539>
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::AuthEnc::ChaCha20Poly1305 qw(chacha20poly1305_encrypt_authenticate chacha20poly1305_decrypt_verify);
+
+=head1 FUNCTIONS
+
+=head2 chacha20poly1305_encrypt_authenticate
+
+ my ($ciphertext, $tag) = chacha20poly1305_encrypt_authenticate($key, $iv, $adata, $plaintext);
+
+ # $key ..... key of proper length (128 or 256 bits / 16 or 32 bytes)
+ # $iv ...... initialization vector (64 or 96 bits / 8 or 12 bytes)
+ # $adata ... additional authenticated data (optional)
+
+=head2 chacha20poly1305_decrypt_verify
+
+ my $plaintext = chacha20poly1305_decrypt_verify($key, $iv, $adata, $ciphertext, $tag);
+
+ # on error returns undef
+
+=head1 METHODS
+
+=head2 new
+
+ my $ae = Crypt::AuthEnc::ChaCha20Poly1305->new($key, $iv);
+
+ # $key ..... encryption key of proper length (128 or 256 bits / 16 or 32 bytes)
+ # $iv ...... initialization vector (64 or 96 bits / 8 or 12 bytes)
+
+=head2 aad_add
+
+Can be called before the first C<encrypt_add> or C<decrypt_add>;
+
+ $ae->aad_add($aad_data); #can be called multiple times
+
+=head2 encrypt_add
+
+ $ciphertext = $ae->encrypt_add($data); #can be called multiple times
+
+=head2 encrypt_done
+
+ $tag = $ae->encrypt_done();
+
+=head2 decrypt_add
+
+ $plaintext = $ae->decrypt_add($ciphertext); #can be called multiple times
+
+=head2 decrypt_done
+
+ my $result = $ae->decrypt_done($tag); # returns 1 (success) or 0 (failure)
+ #or
+ my $tag = $ae->decrypt_done; # returns $tag value
+
+=head2 clone
+
+ my $ae_new = $ae->clone;
+
+=head2 set_iv
+
+ $ae->set_iv($iv);
+
+=head2 set_iv_rfc7905
+
+ $ae->set_iv_rfc7905($iv, $seqnum);
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::AuthEnc::GCM|Crypt::AuthEnc::GCM>, L<Crypt::AuthEnc::CCM|Crypt::AuthEnc::CCM>, L<Crypt::AuthEnc::EAX|Crypt::AuthEnc::EAX>, L<Crypt::AuthEnc::OCB|Crypt::AuthEnc::OCB>
+
+=item * L<https://tools.ietf.org/html/rfc7539>
+
+=back
diff --git a/lib/Crypt/AuthEnc/EAX.pm b/lib/Crypt/AuthEnc/EAX.pm
new file mode 100644
index 00000000..e5e7095d
--- /dev/null
+++ b/lib/Crypt/AuthEnc/EAX.pm
@@ -0,0 +1,179 @@
+package Crypt::AuthEnc::EAX;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::AuthEnc Exporter);
+our %EXPORT_TAGS = ( all => [qw( eax_encrypt_authenticate eax_decrypt_verify )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+### the following methods/functions are implemented in XS:
+# - _new
+# - DESTROY
+# - clone
+# - encrypt_add
+# - encrypt_done
+# - decrypt_add
+# - decrypt_done
+# - aad_add
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+sub eax_encrypt_authenticate {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $iv = shift;
+ my $adata = shift;
+ my $plaintext = shift;
+
+ my $m = Crypt::AuthEnc::EAX->new($cipher_name, $key, $iv);
+ $m->aad_add($adata) if defined $adata;
+ my $ct = $m->encrypt_add($plaintext);
+ my $tag = $m->encrypt_done;
+ return ($ct, $tag);
+}
+
+sub eax_decrypt_verify {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $iv = shift;
+ my $adata = shift;
+ my $ciphertext = shift;
+ my $tag = shift;
+
+ my $m = Crypt::AuthEnc::EAX->new($cipher_name, $key, $iv);
+ $m->aad_add($adata) if defined $adata;
+ my $ct = $m->decrypt_add($ciphertext);
+ return $m->decrypt_done($tag) ? $ct : undef;
+}
+
+sub header_add {
+ # obsolete, only for backwards compatibility
+ shift->aad_add(@_);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::AuthEnc::EAX - Authenticated encryption in EAX mode
+
+=head1 SYNOPSIS
+
+ ### OO interface
+ use Crypt::AuthEnc::EAX;
+
+ # encrypt and authenticate
+ my $ae = Crypt::AuthEnc::EAX->new("AES", $key, $iv);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $ct = $ae->encrypt_add('data1');
+ $ct = $ae->encrypt_add('data2');
+ $ct = $ae->encrypt_add('data3');
+ $tag = $ae->encrypt_done();
+
+ # decrypt and verify
+ my $ae = Crypt::AuthEnc::EAX->new("AES", $key, $iv);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $pt = $ae->decrypt_add('ciphertext1');
+ $pt = $ae->decrypt_add('ciphertext2');
+ $pt = $ae->decrypt_add('ciphertext3');
+ $tag = $ae->decrypt_done();
+ die "decrypt failed" unless $tag eq $expected_tag;
+
+ #or
+ my $result = $ae->decrypt_done($expected_tag) die "decrypt failed";
+
+ ### functional interface
+ use Crypt::AuthEnc::EAX qw(eax_encrypt_authenticate eax_decrypt_verify);
+
+ my ($ciphertext, $tag) = eax_encrypt_authenticate('AES', $key, $iv, $adata, $plaintext);
+ my $plaintext = eax_decrypt_verify('AES', $key, $iv, $adata, $ciphertext, $tag);
+
+=head1 DESCRIPTION
+
+EAX is a mode that requires a cipher, CTR and OMAC support and provides encryption and authentication.
+It is initialized with a random IV that can be shared publicly, additional authenticated data which can
+be fixed and public, and a random secret symmetric key.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::AuthEnc::EAX qw(eax_encrypt_authenticate eax_decrypt_verify);
+
+=head1 FUNCTIONS
+
+=head2 eax_encrypt_authenticate
+
+ my ($ciphertext, $tag) = eax_encrypt_authenticate($cipher, $key, $iv, $adata, $plaintext);
+
+ # $cipher .. 'AES' or name of any other cipher with 16-byte block len
+ # $key ..... AES key of proper length (128/192/256bits)
+ # $iv ...... unique initialization vector (no need to keep it secret)
+ # $adata ... additional authenticated data
+
+=head2 eax_decrypt_verify
+
+ my $plaintext = eax_decrypt_verify($cipher, $key, $iv, $adata, $ciphertext, $tag);
+
+ # on error returns undef
+
+=head1 METHODS
+
+=head2 new
+
+ my $ae = Crypt::AuthEnc::EAX->new($cipher, $key, $iv);
+ #or
+ my $ae = Crypt::AuthEnc::EAX->new($cipher, $key, $iv, $adata);
+
+ # $cipher .. 'AES' or name of any other cipher with 16-byte block len
+ # $key ..... AES key of proper length (128/192/256bits)
+ # $iv ...... unique initialization vector (no need to keep it secret)
+ # $adata ... additional authenticated data (optional)
+
+=head2 aad_add
+
+ $ae->aad_add($adata); #can be called multiple times
+
+=head2 encrypt_add
+
+ $ciphertext = $ae->encrypt_add($data); #can be called multiple times
+
+=head2 encrypt_done
+
+ $tag = $ae->encrypt_done();
+
+=head2 decrypt_add
+
+ $plaintext = $ae->decrypt_add($ciphertext); #can be called multiple times
+
+=head2 decrypt_done
+
+ my $result = $ae->decrypt_done($tag); # returns 1 (success) or 0 (failure)
+ #or
+ my $tag = $ae->decrypt_done; # returns $tag value
+
+=head2 clone
+
+ my $ae_new = $ae->clone;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::AuthEnc::CCM|Crypt::AuthEnc::CCM>, L<Crypt::AuthEnc::GCM|Crypt::AuthEnc::GCM>, L<Crypt::AuthEnc::OCB|Crypt::AuthEnc::OCB>
+
+=item * L<https://en.wikipedia.org/wiki/EAX_mode|https://en.wikipedia.org/wiki/EAX_mode>
+
+=back
diff --git a/lib/Crypt/AuthEnc/GCM.pm b/lib/Crypt/AuthEnc/GCM.pm
new file mode 100644
index 00000000..4d9b98f6
--- /dev/null
+++ b/lib/Crypt/AuthEnc/GCM.pm
@@ -0,0 +1,179 @@
+package Crypt::AuthEnc::GCM;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::AuthEnc Exporter);
+our %EXPORT_TAGS = ( all => [qw( gcm_encrypt_authenticate gcm_decrypt_verify )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+sub new {
+ my ($class, $cipher, $key, $iv) = @_;
+ my $self = _new(Crypt::Cipher::_trans_cipher_name($cipher), $key);
+ # for backwards compatibility the $iv is optional
+ $self->iv_add($iv) if defined $iv;
+ return $self;
+}
+
+sub gcm_encrypt_authenticate {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $iv = shift;
+ my $adata = shift;
+ my $plaintext = shift;
+
+ my $m = Crypt::AuthEnc::GCM->new($cipher_name, $key);
+ $m->iv_add($iv);
+ $m->adata_add(defined $adata ? $adata : ''); #XXX-TODO if no aad we have to pass empty string
+ my $ct = $m->encrypt_add($plaintext);
+ my $tag = $m->encrypt_done;
+ return ($ct, $tag);
+}
+
+sub gcm_decrypt_verify {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $iv = shift;
+ my $adata = shift;
+ my $ciphertext = shift;
+ my $tag = shift;
+
+ my $m = Crypt::AuthEnc::GCM->new($cipher_name, $key);
+ $m->iv_add($iv);
+ $m->adata_add(defined $adata ? $adata : ''); #XXX-TODO if no aad we have to pass empty string
+ my $ct = $m->decrypt_add($ciphertext);
+ return $m->decrypt_done($tag) ? $ct : undef;
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::AuthEnc::GCM - Authenticated encryption in GCM mode
+
+=head1 SYNOPSIS
+
+ ### OO interface
+ use Crypt::AuthEnc::GCM;
+
+ # encrypt and authenticate
+ my $ae = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $ct = $ae->encrypt_add('data1');
+ $ct = $ae->encrypt_add('data2');
+ $ct = $ae->encrypt_add('data3');
+ $tag = $ae->encrypt_done();
+
+ # decrypt and verify
+ my $ae = Crypt::AuthEnc::GCM->new("AES", $key, $iv);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $pt = $ae->decrypt_add('ciphertext1');
+ $pt = $ae->decrypt_add('ciphertext2');
+ $pt = $ae->decrypt_add('ciphertext3');
+ $tag = $ae->decrypt_done();
+ die "decrypt failed" unless $tag eq $expected_tag;
+
+ #or
+ my $result = $ae->decrypt_done($expected_tag) die "decrypt failed";
+
+ ### functional interface
+ use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
+
+ my ($ciphertext, $tag) = gcm_encrypt_authenticate('AES', $key, $iv, $adata, $plaintext);
+ my $plaintext = gcm_decrypt_verify('AES', $key, $iv, $adata, $ciphertext, $tag);
+
+=head1 DESCRIPTION
+
+Galois/Counter Mode (GCM) - provides encryption and authentication.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
+
+=head1 FUNCTIONS
+
+=head2 gcm_encrypt_authenticate
+
+ my ($ciphertext, $tag) = gcm_encrypt_authenticate($cipher, $key, $iv, $adata, $plaintext);
+
+ # $cipher .. 'AES' or name of any other cipher with 16-byte block len
+ # $key ..... AES key of proper length (128/192/256bits)
+ # $iv ...... initialization vector
+ # $adata ... additional authenticated data
+
+=head2 gcm_decrypt_verify
+
+ my $plaintext = gcm_decrypt_verify($cipher, $key, $iv, $adata, $ciphertext, $tag);
+
+ # on error returns undef
+
+=head1 METHODS
+
+=head2 new
+
+ my $ae = Crypt::AuthEnc::GCM->new($cipher, $key);
+ #or
+ my $ae = Crypt::AuthEnc::GCM->new($cipher, $key, $iv);
+
+ # $cipher .. 'AES' or name of any other cipher
+ # $key ..... encryption key of proper length
+ # $iv ...... initialization vector (optional, you can set it later via iv_add method)
+
+=head2 iv_add
+
+ $ae->iv_add($iv_data); #can be called multiple times
+
+=head2 aad_add
+
+Can be called B<after> all C<iv_add> calls but before the first C<encrypt_add> or C<decrypt_add>;
+
+ $ae->aad_add($aad_data); #can be called multiple times
+
+=head2 encrypt_add
+
+ $ciphertext = $ae->encrypt_add($data); #can be called multiple times
+
+=head2 encrypt_done
+
+ $tag = $ae->encrypt_done();
+
+=head2 decrypt_add
+
+ $plaintext = $ae->decrypt_add($ciphertext); #can be called multiple times
+
+=head2 decrypt_done
+
+ my $result = $ae->decrypt_done($tag); # returns 1 (success) or 0 (failure)
+ #or
+ my $tag = $ae->decrypt_done; # returns $tag value
+
+=head2 reset
+
+ $ae->reset;
+
+=head2 clone
+
+ my $ae_new = $ae->clone;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::AuthEnc::CCM|Crypt::AuthEnc::CCM>, L<Crypt::AuthEnc::EAX|Crypt::AuthEnc::EAX>, L<Crypt::AuthEnc::OCB|Crypt::AuthEnc::OCB>
+
+=item * L<https://en.wikipedia.org/wiki/Galois/Counter_Mode>
+
+=back
diff --git a/lib/Crypt/AuthEnc/OCB.pm b/lib/Crypt/AuthEnc/OCB.pm
new file mode 100644
index 00000000..aabab364
--- /dev/null
+++ b/lib/Crypt/AuthEnc/OCB.pm
@@ -0,0 +1,174 @@
+package Crypt::AuthEnc::OCB;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::AuthEnc Exporter);
+our %EXPORT_TAGS = ( all => [qw( ocb_encrypt_authenticate ocb_decrypt_verify )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+sub ocb_encrypt_authenticate {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $nonce = shift;
+ my $adata = shift;
+ my $plaintext = shift;
+
+ my $m = Crypt::AuthEnc::OCB->new($cipher_name, $key, $nonce);
+ $m->aad_add($adata) if defined $adata;
+ my $ct = $m->encrypt_last($plaintext);
+ my $tag = $m->encrypt_done;
+ return ($ct, $tag);
+}
+
+sub ocb_decrypt_verify {
+ my $cipher_name = shift;
+ my $key = shift;
+ my $nonce = shift;
+ my $adata = shift;
+ my $ciphertext = shift;
+ my $tag = shift;
+
+ my $m = Crypt::AuthEnc::OCB->new($cipher_name, $key, $nonce);
+ $m->aad_add($adata) if defined $adata;
+ my $ct = $m->decrypt_last($ciphertext);
+ return $m->decrypt_done($tag) ? $ct : undef;
+}
+
+sub adata_add {
+ # obsolete, only for backwards compatibility
+ shift->aad_add(@_);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::AuthEnc::OCB - Authenticated encryption in OCBv3 mode
+
+=head1 SYNOPSIS
+
+ ### OO interface
+ use Crypt::AuthEnc::OCB;
+
+ # encrypt and authenticate
+ my $ae = Crypt::AuthEnc::OCB->new("AES", $key, $nonce);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $ct = $ae->encrypt_add('data1');
+ $ct = $ae->encrypt_add('data2');
+ $ct = $ae->encrypt_add('data3');
+ $ct = $ae->encrypt_last('rest of data');
+ ($ct,$tag) = $ae->encrypt_done();
+
+ # decrypt and verify
+ my $ae = Crypt::AuthEnc::OCB->new("AES", $key, $nonce);
+ $ae->aad_add('additional_authenticated_data1');
+ $ae->aad_add('additional_authenticated_data2');
+ $pt = $ae->decrypt_add('ciphertext1');
+ $pt = $ae->decrypt_add('ciphertext2');
+ $pt = $ae->decrypt_add('ciphertext3');
+ $pt = $ae->decrypt_last('rest of data');
+ ($pt,$tag) = $ae->decrypt_done();
+
+ ### functional interface
+ use Crypt::AuthEnc::OCB qw(ocb_encrypt_authenticate ocb_decrypt_verify);
+
+ my ($ciphertext, $tag) = ocb_encrypt_authenticate('AES', $key, $nonce, $adata, $plaintext);
+ my $plaintext = ocb_decrypt_verify('AES', $key, $nonce, $adata, $ciphertext, $tag);
+
+=head1 DESCRIPTION
+
+This module implements OCB version 3 according http://datatracker.ietf.org/doc/draft-irtf-cfrg-ocb/
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::AuthEnc::OCB qw(ocb_encrypt_authenticate ocb_decrypt_verify);
+
+=head1 FUNCTIONS
+
+=head2 ocb_encrypt_authenticate
+
+ my ($ciphertext, $tag) = ocb_encrypt_authenticate($cipher, $key, $nonce, $adata, $plaintext);
+
+ # $cipher .. 'AES' or name of any other cipher with 16-byte block len
+ # $key ..... AES key of proper length (128/192/256bits)
+ # $nonce ... unique nonce/salt (no need to keep it secret)
+ # $adata ... additional authenticated data
+
+=head2 ocb_decrypt_verify
+
+ my $plaintext = ocb_decrypt_verify($cipher, $key, $nonce, $adata, $ciphertext, $tag);
+
+ # on error returns undef
+
+=head1 METHODS
+
+=head2 new
+
+ my $ae = Crypt::AuthEnc::OCB->new($cipher, $key, $nonce);
+
+ # $cipher .. 'AES' or name of any other cipher with 16-byte block len
+ # $key ..... AES key of proper length (128/192/256bits)
+ # $nonce ... unique nonce/salt (no need to keep it secret)
+
+=head2 aad_add
+
+ $ae->aad_add($adata); #can be called multiple times
+
+=head2 encrypt_add
+
+ $ciphertext = $ae->encrypt_add($data); #can be called multiple times
+
+ #BEWARE: size of $data has to be multiple of blocklen (16 for AES)
+
+=head2 encrypt_last
+
+ $ciphertext = $ae->encrypt_last($data);
+
+=head2 encrypt_done
+
+ $tag = $ae->encrypt_done();
+
+=head2 decrypt_add
+
+ $plaintext = $ae->decrypt_add($ciphertext); #can be called multiple times
+
+ #BEWARE: size of $ciphertext has to be multiple of blocklen (16 for AES)
+
+=head2 encrypt_last
+
+ $plaintext = $ae->decrypt_last($data);
+
+=head2 decrypt_done
+
+ my $result = $ae->decrypt_done($tag); # returns 1 (success) or 0 (failure)
+ #or
+ my $tag = $ae->decrypt_done; # returns $tag value
+
+=head2 clone
+
+ my $ae_new = $ae->clone;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::AuthEnc::CCM|Crypt::AuthEnc::CCM>, L<Crypt::AuthEnc::GCM|Crypt::AuthEnc::GCM>, L<Crypt::AuthEnc::EAX|Crypt::AuthEnc::EAX>
+
+=item * L<https://en.wikipedia.org/wiki/OCB_mode|https://en.wikipedia.org/wiki/OCB_mode>
+
+=back \ No newline at end of file
diff --git a/lib/Crypt/Checksum.pm b/lib/Crypt/Checksum.pm
new file mode 100644
index 00000000..23d23519
--- /dev/null
+++ b/lib/Crypt/Checksum.pm
@@ -0,0 +1,197 @@
+package Crypt::Checksum;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw/
+ adler32_data adler32_data_hex adler32_data_int adler32_file adler32_file_hex adler32_file_int
+ crc32_data crc32_data_hex crc32_data_int crc32_file crc32_file_hex crc32_file_int
+ /] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+use Crypt::Checksum::Adler32;
+use Crypt::Checksum::CRC32;
+
+sub adler32_data { Crypt::Checksum::Adler32->new->add(@_)->digest }
+sub adler32_data_hex { Crypt::Checksum::Adler32->new->add(@_)->hexdigest }
+sub adler32_data_int { unpack("N", Crypt::Checksum::Adler32->new->add(@_)->digest) }
+sub adler32_file { Crypt::Checksum::Adler32->new->addfile(@_)->digest }
+sub adler32_file_hex { Crypt::Checksum::Adler32->new->addfile(@_)->hexdigest }
+sub adler32_file_int { unpack("N", Crypt::Checksum::Adler32->new->addfile(@_)->digest) }
+sub crc32_data { Crypt::Checksum::CRC32->new->add(@_)->digest }
+sub crc32_data_hex { Crypt::Checksum::CRC32->new->add(@_)->hexdigest }
+sub crc32_data_int { unpack("N", Crypt::Checksum::CRC32->new->add(@_)->digest) }
+sub crc32_file { Crypt::Checksum::CRC32->new->addfile(@_)->digest }
+sub crc32_file_hex { Crypt::Checksum::CRC32->new->addfile(@_)->hexdigest }
+sub crc32_file_int { unpack("N", Crypt::Checksum::CRC32->new->addfile(@_)->digest) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Checksum - functional interface to CRC32 and Adler32 checksums
+
+=head1 SYNOPSIS
+
+ use Crypt::Checksum ':all';
+
+ # calculate Adler32 checksum from string/buffer
+ $checksum_raw = adler32_data($data);
+ $checksum_hex = adler32_data_hex($data);
+
+ # calculate Adler32 checksum from file
+ $checksum_raw = adler32_file('filename.dat');
+ $checksum_hex = adler32_file_hex('filename.dat');
+
+ # calculate Adler32 checksum from filehandle
+ $checksum_raw = adler32_file(*FILEHANDLE);
+ $checksum_hex = adler32_file_hex(*FILEHANDLE);
+
+ # calculate CRC32 checksum from string/buffer
+ $checksum_raw = crc32_data($data);
+ $checksum_hex = crc32_data_hex($data);
+
+ # calculate CRC32 checksum from file
+ $checksum_raw = crc32_file('filename.dat');
+ $checksum_hex = crc32_file_hex('filename.dat');
+
+ # calculate CRC32 checksum from filehandle
+ $checksum_raw = crc32_file(*FILEHANDLE);
+ $checksum_hex = crc32_file_hex(*FILEHANDLE);
+
+=head1 DESCRIPTION
+
+Calculating CRC32 and Adler32 checksums (functional interface);
+
+I<Since: CryptX-0.032>
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Checksum qw( adler32_data adler32_data_hex adler32_file adler32_file_hex
+ crc32_data crc32_data_hex crc32_file crc32_file_hex );
+
+Or all of them at once:
+
+ use Crypt::Checksum ':all';
+
+=head1 FUNCTIONS
+
+=head2 adler32_data
+
+Returns checksum as raw octects.
+
+ $checksum_raw = adler32_data('data string');
+ #or
+ $checksum_raw = adler32_data('any data', 'more data', 'even more data');
+
+=head2 adler32_data_hex
+
+Returns checksum as a hexadecimal string.
+
+ $checksum_hex = adler32_data_hex('data string');
+ #or
+ $checksum_hex = adler32_data_hex('any data', 'more data', 'even more data');
+
+=head2 adler32_data_int
+
+Returns checksum as unsingned 32bit integer.
+
+ $checksum_hex = adler32_data_int('data string');
+ #or
+ $checksum_hex = adler32_data_int('any data', 'more data', 'even more data');
+
+=head2 adler32_file
+
+Returns checksum as raw octects.
+
+ $checksum_raw = adler32_file('filename.dat');
+ #or
+ $checksum_raw = adler32_file(*FILEHANDLE);
+
+=head2 adler32_file_hex
+
+Returns checksum as a hexadecimal string.
+
+ $checksum_hex = adler32_file_hex('filename.dat');
+ #or
+ $checksum_hex = adler32_file_hex(*FILEHANDLE);
+
+=head2 adler32_file_int
+
+Returns checksum as unsingned 32bit integer.
+
+ $checksum_hex = adler32_file_int('data string');
+ #or
+ $checksum_hex = adler32_file_int('any data', 'more data', 'even more data');
+
+=head2 crc32_data
+
+Returns checksum as raw octects.
+
+ $checksum_raw = crc32_data('data string');
+ #or
+ $checksum_raw = crc32_data('any data', 'more data', 'even more data');
+
+=head2 crc32_data_hex
+
+Returns checksum as a hexadecimal string.
+
+ $checksum_hex = crc32_data_hex('data string');
+ #or
+ $checksum_hex = crc32_data_hex('any data', 'more data', 'even more data');
+
+=head2 crc32_data_int
+
+Returns checksum as unsingned 32bit integer.
+
+ $checksum_hex = crc32_data_int('data string');
+ #or
+ $checksum_hex = crc32_data_int('any data', 'more data', 'even more data');
+
+=head2 crc32_file
+
+Returns checksum as raw octects.
+
+ $checksum_raw = crc32_file('filename.dat');
+ #or
+ $checksum_raw = crc32_file(*FILEHANDLE);
+
+=head2 crc32_file_hex
+
+Returns checksum as a hexadecimal string.
+
+ $checksum_hex = crc32_file_hex('filename.dat');
+ #or
+ $checksum_hex = crc32_file_hex(*FILEHANDLE);
+
+=head2 crc32_file_int
+
+Returns checksum as unsingned 32bit integer.
+
+ $checksum_hex = crc32_file_int('data string');
+ #or
+ $checksum_hex = crc32_file_int('any data', 'more data', 'even more data');
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Checksum::Adler32>, L<Crypt::Checksum::CRC32>
+
+=item * L<https://en.wikipedia.org/wiki/Adler-32>
+
+=item * L<https://en.wikipedia.org/wiki/Cyclic_redundancy_check>
+
+=back
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/Checksum/Adler32.pm b/lib/Crypt/Checksum/Adler32.pm
new file mode 100644
index 00000000..5691805f
--- /dev/null
+++ b/lib/Crypt/Checksum/Adler32.pm
@@ -0,0 +1,121 @@
+package Crypt::Checksum::Adler32;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+use Carp;
+use CryptX;
+
+sub addfile {
+ my ($self, $file) = @_;
+
+ my $handle;
+ if (ref(\$file) eq 'SCALAR') { #filename
+ open($handle, "<", $file) || croak "FATAL: cannot open '$file': $!";
+ binmode($handle);
+ }
+ else { #handle
+ $handle = $file
+ }
+ croak "FATAL: invalid handle" unless defined $handle;
+
+ my $n;
+ my $buf = "";
+ while (($n = read($handle, $buf, 32*1024))) {
+ $self->add($buf)
+ }
+ croak "FATAL: read failed: $!" unless defined $n;
+
+ return $self;
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Checksum::Adler32 - Compute Adler32 checksum
+
+=head1 SYNOPSIS
+
+ use Crypt::Checksum::Adler32;
+
+ $d = Crypt::Checksum::Adler32->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $checksum_raw = $d->digest; # raw bytes
+ $checksum_hex = $d->hexdigest; # hexadecimal form
+
+=head1 DESCRIPTION
+
+Calculating Adler32 checksums (OO interface);
+
+I<Since: CryptX-0.032>
+
+=head1 METHODS
+
+=head2 new
+
+Constructor, returns a reference to the checksum object.
+
+ $d = Crypt::Checksum::Adler32->new;
+
+=head2 clone
+
+Creates a copy of the checksum object state and returns a reference to the copy.
+
+ $d->clone();
+
+=head2 reset
+
+Reinitialize the checksum object state and returns a reference to the checksum object.
+
+ $d->reset();
+
+=head2 add
+
+All arguments are appended to the message we calculate checksum for.
+The return value is the checksum object itself.
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+The content of the file (or filehandle) is appended to the message we calculate checksum for.
+The return value is the checksum object itself.
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 digest
+
+Returns the binary checksum (raw bytes).
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+Returns the checksum encoded as a hexadecimal string.
+
+ $result_hex = $d->hexdigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Checksum>
+
+=item * L<https://en.wikipedia.org/wiki/Adler-32>
+
+=back
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/Checksum/CRC32.pm b/lib/Crypt/Checksum/CRC32.pm
new file mode 100644
index 00000000..72cd6624
--- /dev/null
+++ b/lib/Crypt/Checksum/CRC32.pm
@@ -0,0 +1,121 @@
+package Crypt::Checksum::CRC32;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+use Carp;
+use CryptX;
+
+sub addfile {
+ my ($self, $file) = @_;
+
+ my $handle;
+ if (ref(\$file) eq 'SCALAR') { #filename
+ open($handle, "<", $file) || croak "FATAL: cannot open '$file': $!";
+ binmode($handle);
+ }
+ else { #handle
+ $handle = $file
+ }
+ croak "FATAL: invalid handle" unless defined $handle;
+
+ my $n;
+ my $buf = "";
+ while (($n = read($handle, $buf, 32*1024))) {
+ $self->add($buf)
+ }
+ croak "FATAL: read failed: $!" unless defined $n;
+
+ return $self;
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Checksum::CRC32 - Compute CRC32 checksum
+
+=head1 SYNOPSIS
+
+ use Crypt::Checksum::CRC32;
+
+ $d = Crypt::Checksum::CRC32->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $checksum_raw = $d->digest; # raw bytes
+ $checksum_hex = $d->hexdigest; # hexadecimal form
+
+=head1 DESCRIPTION
+
+Calculating CRC32 checksums (OO interface);
+
+I<Since: CryptX-0.032>
+
+=head1 METHODS
+
+=head2 new
+
+Constructor, returns a reference to the checksum object.
+
+ $d = Crypt::Checksum::CRC32->new;
+
+=head2 clone
+
+Creates a copy of the checksum object state and returns a reference to the copy.
+
+ $d->clone();
+
+=head2 reset
+
+Reinitialize the checksum object state and returns a reference to the checksum object.
+
+ $d->reset();
+
+=head2 add
+
+All arguments are appended to the message we calculate checksum for.
+The return value is the checksum object itself.
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+The content of the file (or filehandle) is appended to the message we calculate checksum for.
+The return value is the checksum object itself.
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 digest
+
+Returns the binary checksum (raw bytes).
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+Returns the checksum encoded as a hexadecimal string.
+
+ $result_hex = $d->hexdigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Checksum>
+
+=item * L<https://en.wikipedia.org/wiki/Cyclic_redundancy_check>
+
+=back
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/Cipher.pm b/lib/Crypt/Cipher.pm
new file mode 100644
index 00000000..686780f8
--- /dev/null
+++ b/lib/Crypt/Cipher.pm
@@ -0,0 +1,217 @@
+package Crypt::Cipher;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+use CryptX;
+
+### the following methods/functions are implemented in XS:
+# - _new
+# - DESTROY
+# - _keysize
+# - _max_keysize
+# - _min_keysize
+# - _blocksize
+# - _default_rounds
+# - encrypt
+# - decrypt
+#functions, not methods:
+# - _block_length_by_name
+# - _min_key_length_by_name
+# - _max_key_length_by_name
+# - _default_rounds_by_name
+
+sub _trans_cipher_name {
+ my $name = shift;
+ my %trans = (
+ DES_EDE => '3des',
+ SAFERP => 'safer+',
+ SAFER_K128 => 'safer-k128',
+ SAFER_K64 => 'safer-k64',
+ SAFER_SK128 => 'safer-sk128',
+ SAFER_SK64 => 'safer-sk64',
+ );
+ $name =~ s/^Crypt::Cipher:://;
+ return $trans{uc($name)} if defined $trans{uc($name)};
+ return lc($name);
+}
+
+### METHODS
+
+sub new {
+ my $pkg = shift;
+ my $cipher_name = $pkg eq __PACKAGE__ ? _trans_cipher_name(shift) : _trans_cipher_name($pkg);
+ return _new($cipher_name, @_);
+}
+
+sub blocksize {
+ my $self = shift;
+ return $self->_blocksize if ref($self);
+ $self = _trans_cipher_name(shift) if $self eq __PACKAGE__;
+ return _block_length_by_name(_trans_cipher_name($self));
+}
+
+sub keysize {
+ max_keysize(@_);
+}
+
+sub max_keysize
+{
+ my $self = shift;
+ return unless defined $self;
+ return $self->_max_keysize if ref($self);
+ $self = _trans_cipher_name(shift) if $self eq __PACKAGE__;
+ return _max_key_length_by_name(_trans_cipher_name($self));
+}
+
+sub min_keysize {
+ my $self = shift;
+ return unless defined $self;
+ return $self->_min_keysize if ref($self);
+ $self = _trans_cipher_name(shift) if $self eq __PACKAGE__;
+ return _min_key_length_by_name(_trans_cipher_name($self));
+}
+
+sub default_rounds {
+ my $self = shift;
+ return unless defined $self;
+ return $self->_default_rounds if ref($self);
+ $self = _trans_cipher_name(shift) if $self eq __PACKAGE__;
+ return _default_rounds_by_name(_trans_cipher_name($self));
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher - Generic interface to cipher functions
+
+=head1 SYNOPSIS
+
+ #### example 1 (encrypting single block)
+ use Crypt::Cipher;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $c = Crypt::Cipher->new('AES', $key);
+ my $blocksize = $c->blocksize;
+ my $ciphertext = $c->encrypt('plain text block'); #encrypt 1 block
+ my $plaintext = $c->decrypt($ciphertext); #decrypt 1 block
+
+ ### example 2 (using CBC mode)
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('AES');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ #### example 3 (compatibility with Crypt::CBC)
+ use Crypt::CBC;
+ use Crypt::Cipher;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cipher = Crypt::Cipher('AES', $key);
+ my $cbc = Crypt::CBC->new( -cipher=>$cipher, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+Provides an interface to various symetric cipher algorithms.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+Constructor, returns a reference to the cipher object.
+
+ ## basic scenario
+ $d = Crypt::Cipher->new($name, $key);
+ # $name = one of 'AES', 'Anubis', 'Blowfish', 'CAST5', 'Camellia', 'DES', 'DES_EDE',
+ # 'KASUMI', 'Khazad', 'MULTI2', 'Noekeon', 'RC2', 'RC5', 'RC6',
+ # 'SAFERP', 'SAFER_K128', 'SAFER_K64', 'SAFER_SK128', 'SAFER_SK64',
+ # 'SEED', 'Skipjack', 'Twofish', 'XTEA'
+ # simply any <CNAME> for which there exists Crypt::Cipher::<NAME>
+ # $key = binary key (keysize should comply with selected cipher requirements)
+
+ ## some of the ciphers (e.g. MULTI2, RC5, SAFER) allows to set number of rounds
+ $d = Crypt::Cipher->new('MULTI2', $key, $rounds);
+ # $rounds = positive integer (should comply with selected cipher requirements)
+
+=head2 encrypt
+
+Encrypts $plaintext and returns the $ciphertext where $plaintext and $ciphertext should be of B<blocksize> bytes.
+
+ $ciphertext = $d->encrypt($plaintext);
+
+=head2 decrypt
+
+Decrypts $ciphertext and returns the $plaintext where $plaintext and $ciphertext should be of B<blocksize> bytes.
+
+ $plaintext = $d->encrypt($ciphertext);
+
+=head2 keysize
+
+Just an alias for B<max_keysize> (needed for L<Crypt::CBC|Crypt::CBC> compatibility).
+
+=head2 max_keysize
+
+Returns the maximal allowed key size (in bytes) for given cipher.
+
+ $d->max_keysize;
+ #or
+ Crypt::Cipher->max_keysize('AES');
+ #or
+ Crypt::Cipher::max_keysize('AES');
+
+=head2 min_keysize
+
+Returns the minimal allowed key size (in bytes) for given cipher.
+
+ $d->min_keysize;
+ #or
+ Crypt::Cipher->min_keysize('AES');
+ #or
+ Crypt::Cipher::min_keysize('AES');
+
+=head2 blocksize
+
+Returns block size (in bytes) for given cipher.
+
+ $d->blocksize;
+ #or
+ Crypt::Cipher->blocksize('AES');
+ #or
+ Crypt::Cipher::blocksize('AES');
+
+=head2 default_rounds
+
+Returns default number of rounds for given cipher. NOTE: only some cipher (e.g. MULTI2, RC5, SAFER) allows to set number of rounds via new().
+
+ $d->default_rounds;
+ #or
+ Crypt::Cipher->default_rounds('AES');
+ #or
+ Crypt::Cipher::default_rounds('AES');
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * Check subclasses like L<Crypt::Cipher::AES|Crypt::Cipher::AES>, L<Crypt::Cipher::Blowfish|Crypt::Cipher::Blowfish>, ...
+
+=back
+
+=cut
+
+__END__
diff --git a/lib/Crypt/Cipher/AES.pm b/lib/Crypt/Cipher/AES.pm
new file mode 100644
index 00000000..1d4e97dc
--- /dev/null
+++ b/lib/Crypt/Cipher/AES.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::AES;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::AES - Symetric cipher AES (aka Rijndael), key size: 128/192/256 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('AES');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::AES;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::AES', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the AES cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::AES->new($key);
+ #or
+ $c = Crypt::Cipher::AES->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::AES->keysize;
+ #or
+ Crypt::Cipher::AES::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::AES->blocksize;
+ #or
+ Crypt::Cipher::AES::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::AES->max_keysize;
+ #or
+ Crypt::Cipher::AES::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::AES->min_keysize;
+ #or
+ Crypt::Cipher::AES::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::AES->default_rounds;
+ #or
+ Crypt::Cipher::AES::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Advanced_Encryption_Standard|http://en.wikipedia.org/wiki/Advanced_Encryption_Standard>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Anubis.pm b/lib/Crypt/Cipher/Anubis.pm
new file mode 100644
index 00000000..e06b5cd1
--- /dev/null
+++ b/lib/Crypt/Cipher/Anubis.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Anubis;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Anubis - Symetric cipher Anubis, key size: 128-320 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Anubis');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Anubis;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Anubis', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Anubis cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Anubis->new($key);
+ #or
+ $c = Crypt::Cipher::Anubis->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Anubis->keysize;
+ #or
+ Crypt::Cipher::Anubis::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Anubis->blocksize;
+ #or
+ Crypt::Cipher::Anubis::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Anubis->max_keysize;
+ #or
+ Crypt::Cipher::Anubis::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Anubis->min_keysize;
+ #or
+ Crypt::Cipher::Anubis::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Anubis->default_rounds;
+ #or
+ Crypt::Cipher::Anubis::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Anubis_(cipher)|http://en.wikipedia.org/wiki/Anubis_(cipher)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Blowfish.pm b/lib/Crypt/Cipher/Blowfish.pm
new file mode 100644
index 00000000..0e98a2cd
--- /dev/null
+++ b/lib/Crypt/Cipher/Blowfish.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Blowfish;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Blowfish - Symetric cipher Blowfish, key size: 64-448 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Blowfish');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Blowfish;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Blowfish', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Blowfish cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Blowfish->new($key);
+ #or
+ $c = Crypt::Cipher::Blowfish->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Blowfish->keysize;
+ #or
+ Crypt::Cipher::Blowfish::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Blowfish->blocksize;
+ #or
+ Crypt::Cipher::Blowfish::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Blowfish->max_keysize;
+ #or
+ Crypt::Cipher::Blowfish::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Blowfish->min_keysize;
+ #or
+ Crypt::Cipher::Blowfish::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Blowfish->default_rounds;
+ #or
+ Crypt::Cipher::Blowfish::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Blowfish_(cipher)|http://en.wikipedia.org/wiki/Blowfish_(cipher)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/CAST5.pm b/lib/Crypt/Cipher/CAST5.pm
new file mode 100644
index 00000000..fbfd240c
--- /dev/null
+++ b/lib/Crypt/Cipher/CAST5.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::CAST5;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::CAST5 - Symetric cipher CAST5 (aka CAST-128), key size: 40-128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('CAST5');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::CAST5;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::CAST5', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the CAST5 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::CAST5->new($key);
+ #or
+ $c = Crypt::Cipher::CAST5->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::CAST5->keysize;
+ #or
+ Crypt::Cipher::CAST5::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::CAST5->blocksize;
+ #or
+ Crypt::Cipher::CAST5::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::CAST5->max_keysize;
+ #or
+ Crypt::Cipher::CAST5::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::CAST5->min_keysize;
+ #or
+ Crypt::Cipher::CAST5::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::CAST5->default_rounds;
+ #or
+ Crypt::Cipher::CAST5::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/CAST-128|http://en.wikipedia.org/wiki/CAST-128>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Camellia.pm b/lib/Crypt/Cipher/Camellia.pm
new file mode 100644
index 00000000..0415ef26
--- /dev/null
+++ b/lib/Crypt/Cipher/Camellia.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Camellia;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Camellia - Symetric cipher Camellia, key size: 128/192/256 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Camellia');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Camellia;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Camellia', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Camellia cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Camellia->new($key);
+ #or
+ $c = Crypt::Cipher::Camellia->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Camellia->keysize;
+ #or
+ Crypt::Cipher::Camellia::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Camellia->blocksize;
+ #or
+ Crypt::Cipher::Camellia::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Camellia->max_keysize;
+ #or
+ Crypt::Cipher::Camellia::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Camellia->min_keysize;
+ #or
+ Crypt::Cipher::Camellia::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Camellia->default_rounds;
+ #or
+ Crypt::Cipher::Camellia::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Camellia_(cipher)|http://en.wikipedia.org/wiki/Camellia_(cipher)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/DES.pm b/lib/Crypt/Cipher/DES.pm
new file mode 100644
index 00000000..486cae84
--- /dev/null
+++ b/lib/Crypt/Cipher/DES.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::DES;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::DES - Symetric cipher DES, key size: 64[56] bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('DES');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::DES;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::DES', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the DES cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::DES->new($key);
+ #or
+ $c = Crypt::Cipher::DES->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::DES->keysize;
+ #or
+ Crypt::Cipher::DES::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::DES->blocksize;
+ #or
+ Crypt::Cipher::DES::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::DES->max_keysize;
+ #or
+ Crypt::Cipher::DES::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::DES->min_keysize;
+ #or
+ Crypt::Cipher::DES::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::DES->default_rounds;
+ #or
+ Crypt::Cipher::DES::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Data_Encryption_Standard|http://en.wikipedia.org/wiki/Data_Encryption_Standard>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/DES_EDE.pm b/lib/Crypt/Cipher/DES_EDE.pm
new file mode 100644
index 00000000..ea4b31d9
--- /dev/null
+++ b/lib/Crypt/Cipher/DES_EDE.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::DES_EDE;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::DES_EDE - Symetric cipher DES_EDE (aka Tripple-DES, 3DES), key size: 192[168] bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('DES_EDE');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::DES_EDE;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::DES_EDE', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the DES_EDE cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::DES_EDE->new($key);
+ #or
+ $c = Crypt::Cipher::DES_EDE->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::DES_EDE->keysize;
+ #or
+ Crypt::Cipher::DES_EDE::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::DES_EDE->blocksize;
+ #or
+ Crypt::Cipher::DES_EDE::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::DES_EDE->max_keysize;
+ #or
+ Crypt::Cipher::DES_EDE::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::DES_EDE->min_keysize;
+ #or
+ Crypt::Cipher::DES_EDE::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::DES_EDE->default_rounds;
+ #or
+ Crypt::Cipher::DES_EDE::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Triple_DES|http://en.wikipedia.org/wiki/Triple_DES>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/KASUMI.pm b/lib/Crypt/Cipher/KASUMI.pm
new file mode 100644
index 00000000..f1dc727e
--- /dev/null
+++ b/lib/Crypt/Cipher/KASUMI.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::KASUMI;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::KASUMI - Symetric cipher KASUMI, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('KASUMI');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::KASUMI;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::KASUMI', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the KASUMI cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::KASUMI->new($key);
+ #or
+ $c = Crypt::Cipher::KASUMI->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::KASUMI->keysize;
+ #or
+ Crypt::Cipher::KASUMI::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::KASUMI->blocksize;
+ #or
+ Crypt::Cipher::KASUMI::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::KASUMI->max_keysize;
+ #or
+ Crypt::Cipher::KASUMI::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::KASUMI->min_keysize;
+ #or
+ Crypt::Cipher::KASUMI::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::KASUMI->default_rounds;
+ #or
+ Crypt::Cipher::KASUMI::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/KASUMI_(block_cipher)|http://en.wikipedia.org/wiki/KASUMI_(block_cipher)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Khazad.pm b/lib/Crypt/Cipher/Khazad.pm
new file mode 100644
index 00000000..bc6217f0
--- /dev/null
+++ b/lib/Crypt/Cipher/Khazad.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Khazad;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Khazad - Symetric cipher Khazad, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Khazad');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Khazad;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Khazad', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Khazad cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Khazad->new($key);
+ #or
+ $c = Crypt::Cipher::Khazad->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Khazad->keysize;
+ #or
+ Crypt::Cipher::Khazad::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Khazad->blocksize;
+ #or
+ Crypt::Cipher::Khazad::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Khazad->max_keysize;
+ #or
+ Crypt::Cipher::Khazad::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Khazad->min_keysize;
+ #or
+ Crypt::Cipher::Khazad::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Khazad->default_rounds;
+ #or
+ Crypt::Cipher::Khazad::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/KHAZAD|http://en.wikipedia.org/wiki/KHAZAD>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/MULTI2.pm b/lib/Crypt/Cipher/MULTI2.pm
new file mode 100644
index 00000000..895b2420
--- /dev/null
+++ b/lib/Crypt/Cipher/MULTI2.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::MULTI2;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::MULTI2 - Symetric cipher MULTI2, key size: 320 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('MULTI2');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::MULTI2;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::MULTI2', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the MULTI2 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::MULTI2->new($key);
+ #or
+ $c = Crypt::Cipher::MULTI2->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::MULTI2->keysize;
+ #or
+ Crypt::Cipher::MULTI2::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::MULTI2->blocksize;
+ #or
+ Crypt::Cipher::MULTI2::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::MULTI2->max_keysize;
+ #or
+ Crypt::Cipher::MULTI2::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::MULTI2->min_keysize;
+ #or
+ Crypt::Cipher::MULTI2::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::MULTI2->default_rounds;
+ #or
+ Crypt::Cipher::MULTI2::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/MULTI2|http://en.wikipedia.org/wiki/MULTI2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Noekeon.pm b/lib/Crypt/Cipher/Noekeon.pm
new file mode 100644
index 00000000..4a73159e
--- /dev/null
+++ b/lib/Crypt/Cipher/Noekeon.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Noekeon;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Noekeon - Symetric cipher Noekeon, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Noekeon');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Noekeon;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Noekeon', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Noekeon cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Noekeon->new($key);
+ #or
+ $c = Crypt::Cipher::Noekeon->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Noekeon->keysize;
+ #or
+ Crypt::Cipher::Noekeon::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Noekeon->blocksize;
+ #or
+ Crypt::Cipher::Noekeon::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Noekeon->max_keysize;
+ #or
+ Crypt::Cipher::Noekeon::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Noekeon->min_keysize;
+ #or
+ Crypt::Cipher::Noekeon::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Noekeon->default_rounds;
+ #or
+ Crypt::Cipher::Noekeon::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/NOEKEON|http://en.wikipedia.org/wiki/NOEKEON>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/RC2.pm b/lib/Crypt/Cipher/RC2.pm
new file mode 100644
index 00000000..8529a4b6
--- /dev/null
+++ b/lib/Crypt/Cipher/RC2.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::RC2;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::RC2 - Symetric cipher RC2, key size: 64-1024 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('RC2');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::RC2;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::RC2', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the RC2 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::RC2->new($key);
+ #or
+ $c = Crypt::Cipher::RC2->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::RC2->keysize;
+ #or
+ Crypt::Cipher::RC2::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::RC2->blocksize;
+ #or
+ Crypt::Cipher::RC2::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::RC2->max_keysize;
+ #or
+ Crypt::Cipher::RC2::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::RC2->min_keysize;
+ #or
+ Crypt::Cipher::RC2::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::RC2->default_rounds;
+ #or
+ Crypt::Cipher::RC2::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/RC2|http://en.wikipedia.org/wiki/RC2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/RC5.pm b/lib/Crypt/Cipher/RC5.pm
new file mode 100644
index 00000000..f358c25a
--- /dev/null
+++ b/lib/Crypt/Cipher/RC5.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::RC5;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::RC5 - Symetric cipher RC5, key size: 64-1024 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('RC5');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::RC5;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::RC5', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the RC5 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::RC5->new($key);
+ #or
+ $c = Crypt::Cipher::RC5->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::RC5->keysize;
+ #or
+ Crypt::Cipher::RC5::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::RC5->blocksize;
+ #or
+ Crypt::Cipher::RC5::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::RC5->max_keysize;
+ #or
+ Crypt::Cipher::RC5::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::RC5->min_keysize;
+ #or
+ Crypt::Cipher::RC5::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::RC5->default_rounds;
+ #or
+ Crypt::Cipher::RC5::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/RC5|http://en.wikipedia.org/wiki/RC5>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/RC6.pm b/lib/Crypt/Cipher/RC6.pm
new file mode 100644
index 00000000..f185f3b9
--- /dev/null
+++ b/lib/Crypt/Cipher/RC6.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::RC6;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::RC6 - Symetric cipher RC6, key size: 64-1024 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('RC6');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::RC6;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::RC6', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the RC6 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::RC6->new($key);
+ #or
+ $c = Crypt::Cipher::RC6->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::RC6->keysize;
+ #or
+ Crypt::Cipher::RC6::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::RC6->blocksize;
+ #or
+ Crypt::Cipher::RC6::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::RC6->max_keysize;
+ #or
+ Crypt::Cipher::RC6::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::RC6->min_keysize;
+ #or
+ Crypt::Cipher::RC6::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::RC6->default_rounds;
+ #or
+ Crypt::Cipher::RC6::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/RC6|http://en.wikipedia.org/wiki/RC6>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/SAFERP.pm b/lib/Crypt/Cipher/SAFERP.pm
new file mode 100644
index 00000000..05f989f5
--- /dev/null
+++ b/lib/Crypt/Cipher/SAFERP.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::SAFERP;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::SAFERP - Symetric cipher SAFER+, key size: 128/192/256 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('SAFERP');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::SAFERP;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::SAFERP', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the SAFERP cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::SAFERP->new($key);
+ #or
+ $c = Crypt::Cipher::SAFERP->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::SAFERP->keysize;
+ #or
+ Crypt::Cipher::SAFERP::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::SAFERP->blocksize;
+ #or
+ Crypt::Cipher::SAFERP::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::SAFERP->max_keysize;
+ #or
+ Crypt::Cipher::SAFERP::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::SAFERP->min_keysize;
+ #or
+ Crypt::Cipher::SAFERP::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::SAFERP->default_rounds;
+ #or
+ Crypt::Cipher::SAFERP::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/SAFER|http://en.wikipedia.org/wiki/SAFER>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/SAFER_K128.pm b/lib/Crypt/Cipher/SAFER_K128.pm
new file mode 100644
index 00000000..c3732607
--- /dev/null
+++ b/lib/Crypt/Cipher/SAFER_K128.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::SAFER_K128;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::SAFER_K128 - Symetric cipher SAFER_K128, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('SAFER_K128');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::SAFER_K128;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::SAFER_K128', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the SAFER_K128 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::SAFER_K128->new($key);
+ #or
+ $c = Crypt::Cipher::SAFER_K128->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::SAFER_K128->keysize;
+ #or
+ Crypt::Cipher::SAFER_K128::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::SAFER_K128->blocksize;
+ #or
+ Crypt::Cipher::SAFER_K128::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_K128->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_K128::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_K128->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_K128::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_K128->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_K128::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/SAFER|http://en.wikipedia.org/wiki/SAFER>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/SAFER_K64.pm b/lib/Crypt/Cipher/SAFER_K64.pm
new file mode 100644
index 00000000..52741af6
--- /dev/null
+++ b/lib/Crypt/Cipher/SAFER_K64.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::SAFER_K64;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::SAFER_K64 - Symetric cipher SAFER_K64, key size: 64 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('SAFER_K64');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::SAFER_K64;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::SAFER_K64', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the SAFER_K64 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::SAFER_K64->new($key);
+ #or
+ $c = Crypt::Cipher::SAFER_K64->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::SAFER_K64->keysize;
+ #or
+ Crypt::Cipher::SAFER_K64::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::SAFER_K64->blocksize;
+ #or
+ Crypt::Cipher::SAFER_K64::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_K64->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_K64::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_K64->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_K64::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_K64->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_K64::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/SAFER|http://en.wikipedia.org/wiki/SAFER>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/SAFER_SK128.pm b/lib/Crypt/Cipher/SAFER_SK128.pm
new file mode 100644
index 00000000..32193ff1
--- /dev/null
+++ b/lib/Crypt/Cipher/SAFER_SK128.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::SAFER_SK128;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::SAFER_SK128 - Symetric cipher SAFER_SK128, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('SAFER_SK128');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::SAFER_SK128;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::SAFER_SK128', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the SAFER_SK128 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::SAFER_SK128->new($key);
+ #or
+ $c = Crypt::Cipher::SAFER_SK128->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::SAFER_SK128->keysize;
+ #or
+ Crypt::Cipher::SAFER_SK128::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::SAFER_SK128->blocksize;
+ #or
+ Crypt::Cipher::SAFER_SK128::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK128->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK128::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK128->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK128::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_SK128->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_SK128::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/SAFER|http://en.wikipedia.org/wiki/SAFER>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/SAFER_SK64.pm b/lib/Crypt/Cipher/SAFER_SK64.pm
new file mode 100644
index 00000000..73ac3715
--- /dev/null
+++ b/lib/Crypt/Cipher/SAFER_SK64.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::SAFER_SK64;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::SAFER_SK64 - Symetric cipher SAFER_SK64, key size: 64 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('SAFER_SK64');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::SAFER_SK64;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::SAFER_SK64', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the SAFER_SK64 cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::SAFER_SK64->new($key);
+ #or
+ $c = Crypt::Cipher::SAFER_SK64->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::SAFER_SK64->keysize;
+ #or
+ Crypt::Cipher::SAFER_SK64::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::SAFER_SK64->blocksize;
+ #or
+ Crypt::Cipher::SAFER_SK64::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK64->max_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK64::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK64->min_keysize;
+ #or
+ Crypt::Cipher::SAFER_SK64::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_SK64->default_rounds;
+ #or
+ Crypt::Cipher::SAFER_SK64::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/SAFER|http://en.wikipedia.org/wiki/SAFER>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/SEED.pm b/lib/Crypt/Cipher/SEED.pm
new file mode 100644
index 00000000..c81553b0
--- /dev/null
+++ b/lib/Crypt/Cipher/SEED.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::SEED;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::SEED - Symetric cipher SEED, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('SEED');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::SEED;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::SEED', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the SEED cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::SEED->new($key);
+ #or
+ $c = Crypt::Cipher::SEED->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::SEED->keysize;
+ #or
+ Crypt::Cipher::SEED::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::SEED->blocksize;
+ #or
+ Crypt::Cipher::SEED::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::SEED->max_keysize;
+ #or
+ Crypt::Cipher::SEED::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::SEED->min_keysize;
+ #or
+ Crypt::Cipher::SEED::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::SEED->default_rounds;
+ #or
+ Crypt::Cipher::SEED::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/SEED|http://en.wikipedia.org/wiki/SEED>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Skipjack.pm b/lib/Crypt/Cipher/Skipjack.pm
new file mode 100644
index 00000000..41f6f2cb
--- /dev/null
+++ b/lib/Crypt/Cipher/Skipjack.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Skipjack;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Skipjack - Symetric cipher Skipjack, key size: 80 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Skipjack');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Skipjack;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Skipjack', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Skipjack cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Skipjack->new($key);
+ #or
+ $c = Crypt::Cipher::Skipjack->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Skipjack->keysize;
+ #or
+ Crypt::Cipher::Skipjack::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Skipjack->blocksize;
+ #or
+ Crypt::Cipher::Skipjack::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Skipjack->max_keysize;
+ #or
+ Crypt::Cipher::Skipjack::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Skipjack->min_keysize;
+ #or
+ Crypt::Cipher::Skipjack::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Skipjack->default_rounds;
+ #or
+ Crypt::Cipher::Skipjack::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Skipjack_(cipher)|http://en.wikipedia.org/wiki/Skipjack_(cipher)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/Twofish.pm b/lib/Crypt/Cipher/Twofish.pm
new file mode 100644
index 00000000..85a20d2a
--- /dev/null
+++ b/lib/Crypt/Cipher/Twofish.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::Twofish;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::Twofish - Symetric cipher Twofish, key size: 128/192/256 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('Twofish');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::Twofish;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::Twofish', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the Twofish cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::Twofish->new($key);
+ #or
+ $c = Crypt::Cipher::Twofish->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::Twofish->keysize;
+ #or
+ Crypt::Cipher::Twofish::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::Twofish->blocksize;
+ #or
+ Crypt::Cipher::Twofish::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::Twofish->max_keysize;
+ #or
+ Crypt::Cipher::Twofish::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::Twofish->min_keysize;
+ #or
+ Crypt::Cipher::Twofish::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::Twofish->default_rounds;
+ #or
+ Crypt::Cipher::Twofish::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/Twofish|http://en.wikipedia.org/wiki/Twofish>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Cipher/XTEA.pm b/lib/Crypt/Cipher/XTEA.pm
new file mode 100644
index 00000000..ce325ba3
--- /dev/null
+++ b/lib/Crypt/Cipher/XTEA.pm
@@ -0,0 +1,121 @@
+package Crypt::Cipher::XTEA;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+use base 'Crypt::Cipher';
+
+sub blocksize { Crypt::Cipher::blocksize(__PACKAGE__) }
+sub keysize { Crypt::Cipher::keysize(__PACKAGE__) }
+sub max_keysize { Crypt::Cipher::max_keysize(__PACKAGE__) }
+sub min_keysize { Crypt::Cipher::min_keysize(__PACKAGE__) }
+sub default_rounds { Crypt::Cipher::default_rounds(__PACKAGE__) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Cipher::XTEA - Symetric cipher XTEA, key size: 128 bits (Crypt::CBC compliant)
+
+=head1 SYNOPSIS
+
+ ### example 1
+ use Crypt::Mode::CBC;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::Mode::CBC->new('XTEA');
+ my $ciphertext = $cbc->encrypt("secret data", $key, $iv);
+
+ ### example 2
+ use Crypt::CBC;
+ use Crypt::Cipher::XTEA;
+
+ my $key = '...'; # length has to be valid key size for this cipher
+ my $iv = '...'; # 16 bytes
+ my $cbc = Crypt::CBC->new( -cipher=>'Cipher::XTEA', -key=>$key, -iv=>$iv );
+ my $ciphertext = $cbc->encrypt("secret data");
+
+=head1 DESCRIPTION
+
+This module implements the XTEA cipher. Provided interface is compliant with L<Crypt::CBC|Crypt::CBC> module.
+
+B<BEWARE:> This module implements just elementary "one-block-(en|de)cryption" operation - if you want to
+encrypt/decrypt generic data you have to use some of the cipher block modes - check for example
+L<Crypt::Mode::CBC|Crypt::Mode::CBC>, L<Crypt::Mode::CTR|Crypt::Mode::CTR> or L<Crypt::CBC|Crypt::CBC> (which will be slower).
+
+=head1 METHODS
+
+=head2 new
+
+ $c = Crypt::Cipher::XTEA->new($key);
+ #or
+ $c = Crypt::Cipher::XTEA->new($key, $rounds);
+
+=head2 encrypt
+
+ $ciphertext = $c->encrypt($plaintext);
+
+=head2 decrypt
+
+ $plaintext = $c->decrypt($ciphertext);
+
+=head2 keysize
+
+ $c->keysize;
+ #or
+ Crypt::Cipher::XTEA->keysize;
+ #or
+ Crypt::Cipher::XTEA::keysize;
+
+=head2 blocksize
+
+ $c->blocksize;
+ #or
+ Crypt::Cipher::XTEA->blocksize;
+ #or
+ Crypt::Cipher::XTEA::blocksize;
+
+=head2 max_keysize
+
+ $c->max_keysize;
+ #or
+ Crypt::Cipher::XTEA->max_keysize;
+ #or
+ Crypt::Cipher::XTEA::max_keysize;
+
+=head2 min_keysize
+
+ $c->min_keysize;
+ #or
+ Crypt::Cipher::XTEA->min_keysize;
+ #or
+ Crypt::Cipher::XTEA::min_keysize;
+
+=head2 default_rounds
+
+ $c->default_rounds;
+ #or
+ Crypt::Cipher::XTEA->default_rounds;
+ #or
+ Crypt::Cipher::XTEA::default_rounds;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<http://en.wikipedia.org/wiki/XTEA|http://en.wikipedia.org/wiki/XTEA>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest.pm b/lib/Crypt/Digest.pm
new file mode 100644
index 00000000..a1ec914c
--- /dev/null
+++ b/lib/Crypt/Digest.pm
@@ -0,0 +1,382 @@
+package Crypt::Digest;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw( digest_data digest_data_hex digest_data_b64 digest_data_b64u digest_file digest_file_hex digest_file_b64 digest_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+### the following methods/functions are implemented in XS:
+# - _new
+# - _hashsize
+# - _hashsize_by_name (function, not method)
+# - clone
+# - reset
+# - digest
+# - hexdigest
+# - b64digest
+# - add
+# - DESTROY
+
+sub _trans_digest_name {
+ my $name = shift;
+ my %trans = (
+ CHAES => 'chc_hash',
+ RIPEMD128 => 'rmd128',
+ RIPEMD160 => 'rmd160',
+ RIPEMD256 => 'rmd256',
+ RIPEMD320 => 'rmd320',
+ TIGER192 => 'tiger',
+ SHA512_224 => 'sha512-224',
+ SHA512_256 => 'sha512-256',
+ SHA3_224 => 'sha3-224',
+ SHA3_256 => 'sha3-256',
+ SHA3_384 => 'sha3-384',
+ SHA3_512 => 'sha3-512',
+ BLAKE2B_160 => 'blake2b-160',
+ BLAKE2B_256 => 'blake2b-256',
+ BLAKE2B_384 => 'blake2b-384',
+ BLAKE2B_512 => 'blake2b-512',
+ BLAKE2S_128 => 'blake2s-128',
+ BLAKE2S_160 => 'blake2s-160',
+ BLAKE2S_224 => 'blake2s-224',
+ BLAKE2S_256 => 'blake2s-256',
+ );
+ $name =~ s/^Crypt::Digest:://i;
+ return $trans{uc($name)} if defined $trans{uc($name)};
+ return lc($name);
+}
+
+### METHODS
+
+sub new {
+ my $pkg = shift;
+ unshift @_, ($pkg eq 'Crypt::Digest' ? _trans_digest_name(shift) : _trans_digest_name($pkg));
+ ###return _new(@_);
+ goto \&_new; # keep the real caller for croak()
+}
+
+sub hashsize {
+ return unless defined $_[0];
+
+ if (ref $_[0]) {
+ ###return _hashsize(@_);
+ goto \&_hashsize if ref $_[0]; # keep the real caller for croak()
+ }
+ else {
+ my $pkg = shift;
+ unshift @_, ($pkg eq 'Crypt::Digest' ? _trans_digest_name(shift) : _trans_digest_name($pkg));
+ ###return _hashsize_by_name(@_);
+ goto \&_hashsize_by_name; # keep the real caller for croak()
+ }
+}
+
+sub addfile {
+ my ($self, $file) = @_;
+
+ my $handle;
+ if (ref(\$file) eq 'SCALAR') { #filename
+ open($handle, "<", $file) || croak "FATAL: cannot open '$file': $!";
+ binmode($handle);
+ }
+ else { #handle
+ $handle = $file
+ }
+ croak "FATAL: invalid handle" unless defined $handle;
+
+ my $n;
+ my $buf = "";
+ while (($n = read($handle, $buf, 32*1024))) {
+ $self->add($buf)
+ }
+ croak "FATAL: read failed: $!" unless defined $n;
+
+ return $self;
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+### FUNCTIONS
+
+sub digest_data { my $rv = eval {Crypt::Digest->new(shift)->add(@_)->digest}; _croak($@); $rv }
+sub digest_data_hex { my $rv = eval {Crypt::Digest->new(shift)->add(@_)->hexdigest}; _croak($@); $rv }
+sub digest_data_b64 { my $rv = eval {Crypt::Digest->new(shift)->add(@_)->b64digest}; _croak($@); $rv }
+sub digest_data_b64u { my $rv = eval {Crypt::Digest->new(shift)->add(@_)->b64udigest}; _croak($@); $rv }
+
+sub digest_file { my $rv = eval {Crypt::Digest->new(shift)->addfile(@_)->digest}; _croak($@); $rv }
+sub digest_file_hex { my $rv = eval {Crypt::Digest->new(shift)->addfile(@_)->hexdigest}; _croak($@); $rv }
+sub digest_file_b64 { my $rv = eval {Crypt::Digest->new(shift)->addfile(@_)->b64digest}; _croak($@); $rv }
+sub digest_file_b64u { my $rv = eval {Crypt::Digest->new(shift)->addfile(@_)->b64udigest}; _croak($@); $rv }
+
+sub _croak { #XXX-FIXME ugly hack for reporting real caller from XS croaks
+ if ($_[0]) {
+ $_[0] =~ s/ at .*?\.pm line \d+.[\n\r]*$//g;
+ croak $_[0];
+ }
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest - Generic interface to hash/digest functions
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest qw( digest_data digest_data_hex digest_data_b64 digest_data_b64u
+ digest_file digest_file_hex digest_file_b64 digest_file_b64u );
+
+ # calculate digest from string/buffer
+ $digest_raw = digest_data('SHA1', 'data string');
+ $digest_hex = digest_data_hex('SHA1', 'data string');
+ $digest_b64 = digest_data_b64('SHA1', 'data string');
+ $digest_b64u = digest_data_b64u('SHA1', 'data string');
+ # calculate digest from file
+ $digest_raw = digest_file('SHA1', 'filename.dat');
+ $digest_hex = digest_file_hex('SHA1', 'filename.dat');
+ $digest_b64 = digest_file_b64('SHA1', 'filename.dat');
+ $digest_b64u = digest_file_b64u('SHA1', 'filename.dat');
+ # calculate digest from filehandle
+ $digest_raw = digest_file('SHA1', *FILEHANDLE);
+ $digest_hex = digest_file_hex('SHA1', *FILEHANDLE);
+ $digest_b64 = digest_file_b64('SHA1', *FILEHANDLE);
+ $digest_b64u = digest_file_b64u('SHA1', *FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest;
+
+ $d = Crypt::Digest->new('SHA1');
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to various hash/digest algorithms.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest qw( digest_data digest_data_hex digest_data_b64 digest_data_b64u
+ digest_file digest_file_hex digest_file_b64 digest_file_b64u );
+
+Or all of them at once:
+
+ use Crypt::Digest ':all';
+
+=head1 FUNCTIONS
+
+Please note that all functions take as its first argument the algoritm name, supported values are:
+
+ 'CHAES', 'MD2', 'MD4', 'MD5', 'RIPEMD128', 'RIPEMD160',
+ 'RIPEMD256', 'RIPEMD320', 'SHA1', 'SHA224', 'SHA256',
+ 'SHA384', 'SHA512', 'SHA512_224', 'SHA512_256', 'Tiger192', 'Whirlpool',
+ 'SHA3_224', 'SHA3_256', 'SHA3_384', 'SHA3_512'
+
+ (simply any <FUNCNAME> for which there is Crypt::Digest::<FUNCNAME> module)
+
+=head2 digest_data
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a binary string.
+
+ $digest_raw = digest_data('SHA1', 'data string');
+ #or
+ $digest_raw = digest_data('SHA1', 'any data', 'more data', 'even more data');
+
+=head2 digest_data_hex
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a hexadecimal string.
+
+ $digest_hex = digest_data_hex('SHA1', 'data string');
+ #or
+ $digest_hex = digest_data_hex('SHA1', 'any data', 'more data', 'even more data');
+
+=head2 digest_data_b64
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $digest_b64 = digest_data_b64('SHA1', 'data string');
+ #or
+ $digest_b64 = digest_data_b64('SHA1', 'any data', 'more data', 'even more data');
+
+=head2 digest_data_b64u
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $digest_b64url = digest_data_b64u('SHA1', 'data string');
+ #or
+ $digest_b64url = digest_data_b64u('SHA1', 'any data', 'more data', 'even more data');
+
+=head2 digest_file
+
+Reads file (defined by filename or filehandle) content, and returns its digest encoded as a binary string.
+
+ $digest_raw = digest_file('SHA1', 'filename.dat');
+ #or
+ $digest_raw = digest_file('SHA1', *FILEHANDLE);
+
+=head2 digest_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its digest encoded as a hexadecimal string.
+
+ $digest_hex = digest_file_hex('SHA1', 'filename.dat');
+ #or
+ $digest_hex = digest_file_hex('SHA1', *FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 digest_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $digest_b64 = digest_file_b64('SHA1', 'filename.dat');
+ #or
+ $digest_b64 = digest_file_b64('SHA1', *FILEHANDLE);
+
+=head2 digest_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $digest_b64url = digest_file_b64u('SHA1', 'filename.dat');
+ #or
+ $digest_b64url = digest_file_b64u('SHA1', *FILEHANDLE);
+
+=head1 METHODS
+
+=head2 new
+
+Constructor, returns a reference to the digest object.
+
+ $d = Crypt::Digest->new($name);
+ # $name could be: 'CHAES', 'MD2', 'MD4', 'MD5', 'RIPEMD128', 'RIPEMD160',
+ # 'RIPEMD256', 'RIPEMD320', 'SHA1', 'SHA224', 'SHA256', 'SHA384',
+ # 'SHA512', 'SHA512_224', 'SHA512_256', 'Tiger192', 'Whirlpool'
+ #
+ # simply any <FUNCNAME> for which there is Crypt::Digest::<FUNCNAME> module
+
+=head2 clone
+
+Creates a copy of the digest object state and returns a reference to the copy.
+
+ $d->clone();
+
+=head2 reset
+
+Reinitialize the digest object state and returns a reference to the digest object.
+
+ $d->reset();
+
+=head2 add
+
+All arguments are appended to the message we calculate digest for.
+The return value is the digest object itself.
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+Note that all the following cases are equivalent:
+
+ # case 1
+ $d->add('aa', 'bb', 'cc');
+
+ # case 2
+ $d->add('aa');
+ $d->add('bb');
+ $d->add('cc');
+
+ # case 3
+ $d->add('aabbcc');
+
+ # case 4
+ $d->add('aa')->add('bb')->add('cc');
+
+=head2 addfile
+
+The content of the file (or filehandle) is appended to the message we calculate digest for.
+The return value is the digest object itself.
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 add_bits
+
+This method is available mostly for compatibility with other Digest::SOMETHING modules on CPAN, you are very unlikely to need it.
+The return value is the digest object itself.
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+B<BEWARE:> It is not possible to add bits that are not a multiple of 8.
+
+=head2 hashsize
+
+Returns the length of calculated digest in bytes (e.g. 32 for SHA-256).
+
+ $d->hashsize;
+ #or
+ Crypt::Digest->hashsize('SHA1');
+ #or
+ Crypt::Digest::hashsize('SHA1');
+
+=head2 digest
+
+Returns the binary digest (raw bytes).
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+Returns the digest encoded as a hexadecimal string.
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+Returns the digest encoded as a Base64 string, B<with> trailing '=' padding (B<BEWARE:> this padding
+style might differ from other Digest::SOMETHING modules on CPAN).
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+Returns the digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<Crypt::Digest|Crypt::Digest> tries to be compatible with L<Digest|Digest> interface.
+
+=item * Check subclasses like L<Crypt::Digest::SHA1|Crypt::Digest::SHA1>, L<Crypt::Digest::MD5|Crypt::Digest::MD5>, ...
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2b_160.pm b/lib/Crypt/Digest/BLAKE2b_160.pm
new file mode 100644
index 00000000..bb829e16
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2b_160.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2b_160;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2b_160 blake2b_160_hex blake2b_160_b64 blake2b_160_b64u blake2b_160_file blake2b_160_file_hex blake2b_160_file_b64 blake2b_160_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2b_160 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2b_160_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2b_160_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2b_160_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2b_160_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2b_160_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2b_160_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2b_160_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2b_160 - Hash function BLAKE2b [size: 160 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2b_160 qw( blake2b_160 blake2b_160_hex blake2b_160_b64 blake2b_160_b64u
+ blake2b_160_file blake2b_160_file_hex blake2b_160_file_b64 blake2b_160_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2b_160_raw = blake2b_160('data string');
+ $blake2b_160_hex = blake2b_160_hex('data string');
+ $blake2b_160_b64 = blake2b_160_b64('data string');
+ $blake2b_160_b64u = blake2b_160_b64u('data string');
+ # calculate digest from file
+ $blake2b_160_raw = blake2b_160_file('filename.dat');
+ $blake2b_160_hex = blake2b_160_file_hex('filename.dat');
+ $blake2b_160_b64 = blake2b_160_file_b64('filename.dat');
+ $blake2b_160_b64u = blake2b_160_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2b_160_raw = blake2b_160_file(*FILEHANDLE);
+ $blake2b_160_hex = blake2b_160_file_hex(*FILEHANDLE);
+ $blake2b_160_b64 = blake2b_160_file_b64(*FILEHANDLE);
+ $blake2b_160_b64u = blake2b_160_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2b_160;
+
+ $d = Crypt::Digest::BLAKE2b_160->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2b_160 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2b_160 qw(blake2b_160 blake2b_160_hex blake2b_160_b64 blake2b_160_b64u
+ blake2b_160_file blake2b_160_file_hex blake2b_160_file_b64 blake2b_160_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2b_160 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2b_160
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_160 digest encoded as a binary string.
+
+ $blake2b_160_raw = blake2b_160('data string');
+ #or
+ $blake2b_160_raw = blake2b_160('any data', 'more data', 'even more data');
+
+=head2 blake2b_160_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_160 digest encoded as a hexadecimal string.
+
+ $blake2b_160_hex = blake2b_160_hex('data string');
+ #or
+ $blake2b_160_hex = blake2b_160_hex('any data', 'more data', 'even more data');
+
+=head2 blake2b_160_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_160 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_160_b64 = blake2b_160_b64('data string');
+ #or
+ $blake2b_160_b64 = blake2b_160_b64('any data', 'more data', 'even more data');
+
+=head2 blake2b_160_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_160 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_160_b64url = blake2b_160_b64u('data string');
+ #or
+ $blake2b_160_b64url = blake2b_160_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2b_160_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_160 digest encoded as a binary string.
+
+ $blake2b_160_raw = blake2b_160_file('filename.dat');
+ #or
+ $blake2b_160_raw = blake2b_160_file(*FILEHANDLE);
+
+=head2 blake2b_160_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_160 digest encoded as a hexadecimal string.
+
+ $blake2b_160_hex = blake2b_160_file_hex('filename.dat');
+ #or
+ $blake2b_160_hex = blake2b_160_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2b_160_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_160 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_160_b64 = blake2b_160_file_b64('filename.dat');
+ #or
+ $blake2b_160_b64 = blake2b_160_file_b64(*FILEHANDLE);
+
+=head2 blake2b_160_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_160 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_160_b64url = blake2b_160_file_b64u('filename.dat');
+ #or
+ $blake2b_160_b64url = blake2b_160_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2b_160->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2b_160->hashsize();
+ #or
+ Crypt::Digest::BLAKE2b_160::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2b_256.pm b/lib/Crypt/Digest/BLAKE2b_256.pm
new file mode 100644
index 00000000..c9a5a2ae
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2b_256.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2b_256;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2b_256 blake2b_256_hex blake2b_256_b64 blake2b_256_b64u blake2b_256_file blake2b_256_file_hex blake2b_256_file_b64 blake2b_256_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2b_256 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2b_256_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2b_256_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2b_256_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2b_256_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2b_256_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2b_256_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2b_256_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2b_256 - Hash function BLAKE2b [size: 256 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2b_256 qw( blake2b_256 blake2b_256_hex blake2b_256_b64 blake2b_256_b64u
+ blake2b_256_file blake2b_256_file_hex blake2b_256_file_b64 blake2b_256_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2b_256_raw = blake2b_256('data string');
+ $blake2b_256_hex = blake2b_256_hex('data string');
+ $blake2b_256_b64 = blake2b_256_b64('data string');
+ $blake2b_256_b64u = blake2b_256_b64u('data string');
+ # calculate digest from file
+ $blake2b_256_raw = blake2b_256_file('filename.dat');
+ $blake2b_256_hex = blake2b_256_file_hex('filename.dat');
+ $blake2b_256_b64 = blake2b_256_file_b64('filename.dat');
+ $blake2b_256_b64u = blake2b_256_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2b_256_raw = blake2b_256_file(*FILEHANDLE);
+ $blake2b_256_hex = blake2b_256_file_hex(*FILEHANDLE);
+ $blake2b_256_b64 = blake2b_256_file_b64(*FILEHANDLE);
+ $blake2b_256_b64u = blake2b_256_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2b_256;
+
+ $d = Crypt::Digest::BLAKE2b_256->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2b_256 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2b_256 qw(blake2b_256 blake2b_256_hex blake2b_256_b64 blake2b_256_b64u
+ blake2b_256_file blake2b_256_file_hex blake2b_256_file_b64 blake2b_256_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2b_256 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2b_256
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_256 digest encoded as a binary string.
+
+ $blake2b_256_raw = blake2b_256('data string');
+ #or
+ $blake2b_256_raw = blake2b_256('any data', 'more data', 'even more data');
+
+=head2 blake2b_256_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_256 digest encoded as a hexadecimal string.
+
+ $blake2b_256_hex = blake2b_256_hex('data string');
+ #or
+ $blake2b_256_hex = blake2b_256_hex('any data', 'more data', 'even more data');
+
+=head2 blake2b_256_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_256_b64 = blake2b_256_b64('data string');
+ #or
+ $blake2b_256_b64 = blake2b_256_b64('any data', 'more data', 'even more data');
+
+=head2 blake2b_256_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_256_b64url = blake2b_256_b64u('data string');
+ #or
+ $blake2b_256_b64url = blake2b_256_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2b_256_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_256 digest encoded as a binary string.
+
+ $blake2b_256_raw = blake2b_256_file('filename.dat');
+ #or
+ $blake2b_256_raw = blake2b_256_file(*FILEHANDLE);
+
+=head2 blake2b_256_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_256 digest encoded as a hexadecimal string.
+
+ $blake2b_256_hex = blake2b_256_file_hex('filename.dat');
+ #or
+ $blake2b_256_hex = blake2b_256_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2b_256_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_256_b64 = blake2b_256_file_b64('filename.dat');
+ #or
+ $blake2b_256_b64 = blake2b_256_file_b64(*FILEHANDLE);
+
+=head2 blake2b_256_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_256_b64url = blake2b_256_file_b64u('filename.dat');
+ #or
+ $blake2b_256_b64url = blake2b_256_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2b_256->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2b_256->hashsize();
+ #or
+ Crypt::Digest::BLAKE2b_256::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2b_384.pm b/lib/Crypt/Digest/BLAKE2b_384.pm
new file mode 100644
index 00000000..3657a358
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2b_384.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2b_384;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2b_384 blake2b_384_hex blake2b_384_b64 blake2b_384_b64u blake2b_384_file blake2b_384_file_hex blake2b_384_file_b64 blake2b_384_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2b_384 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2b_384_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2b_384_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2b_384_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2b_384_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2b_384_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2b_384_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2b_384_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2b_384 - Hash function BLAKE2b [size: 384 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2b_384 qw( blake2b_384 blake2b_384_hex blake2b_384_b64 blake2b_384_b64u
+ blake2b_384_file blake2b_384_file_hex blake2b_384_file_b64 blake2b_384_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2b_384_raw = blake2b_384('data string');
+ $blake2b_384_hex = blake2b_384_hex('data string');
+ $blake2b_384_b64 = blake2b_384_b64('data string');
+ $blake2b_384_b64u = blake2b_384_b64u('data string');
+ # calculate digest from file
+ $blake2b_384_raw = blake2b_384_file('filename.dat');
+ $blake2b_384_hex = blake2b_384_file_hex('filename.dat');
+ $blake2b_384_b64 = blake2b_384_file_b64('filename.dat');
+ $blake2b_384_b64u = blake2b_384_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2b_384_raw = blake2b_384_file(*FILEHANDLE);
+ $blake2b_384_hex = blake2b_384_file_hex(*FILEHANDLE);
+ $blake2b_384_b64 = blake2b_384_file_b64(*FILEHANDLE);
+ $blake2b_384_b64u = blake2b_384_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2b_384;
+
+ $d = Crypt::Digest::BLAKE2b_384->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2b_384 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2b_384 qw(blake2b_384 blake2b_384_hex blake2b_384_b64 blake2b_384_b64u
+ blake2b_384_file blake2b_384_file_hex blake2b_384_file_b64 blake2b_384_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2b_384 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2b_384
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_384 digest encoded as a binary string.
+
+ $blake2b_384_raw = blake2b_384('data string');
+ #or
+ $blake2b_384_raw = blake2b_384('any data', 'more data', 'even more data');
+
+=head2 blake2b_384_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_384 digest encoded as a hexadecimal string.
+
+ $blake2b_384_hex = blake2b_384_hex('data string');
+ #or
+ $blake2b_384_hex = blake2b_384_hex('any data', 'more data', 'even more data');
+
+=head2 blake2b_384_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_384 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_384_b64 = blake2b_384_b64('data string');
+ #or
+ $blake2b_384_b64 = blake2b_384_b64('any data', 'more data', 'even more data');
+
+=head2 blake2b_384_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_384 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_384_b64url = blake2b_384_b64u('data string');
+ #or
+ $blake2b_384_b64url = blake2b_384_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2b_384_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_384 digest encoded as a binary string.
+
+ $blake2b_384_raw = blake2b_384_file('filename.dat');
+ #or
+ $blake2b_384_raw = blake2b_384_file(*FILEHANDLE);
+
+=head2 blake2b_384_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_384 digest encoded as a hexadecimal string.
+
+ $blake2b_384_hex = blake2b_384_file_hex('filename.dat');
+ #or
+ $blake2b_384_hex = blake2b_384_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2b_384_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_384 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_384_b64 = blake2b_384_file_b64('filename.dat');
+ #or
+ $blake2b_384_b64 = blake2b_384_file_b64(*FILEHANDLE);
+
+=head2 blake2b_384_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_384 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_384_b64url = blake2b_384_file_b64u('filename.dat');
+ #or
+ $blake2b_384_b64url = blake2b_384_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2b_384->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2b_384->hashsize();
+ #or
+ Crypt::Digest::BLAKE2b_384::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2b_512.pm b/lib/Crypt/Digest/BLAKE2b_512.pm
new file mode 100644
index 00000000..fd067846
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2b_512.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2b_512;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2b_512 blake2b_512_hex blake2b_512_b64 blake2b_512_b64u blake2b_512_file blake2b_512_file_hex blake2b_512_file_b64 blake2b_512_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2b_512 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2b_512_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2b_512_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2b_512_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2b_512_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2b_512_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2b_512_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2b_512_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2b_512 - Hash function BLAKE2b [size: 512 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2b_512 qw( blake2b_512 blake2b_512_hex blake2b_512_b64 blake2b_512_b64u
+ blake2b_512_file blake2b_512_file_hex blake2b_512_file_b64 blake2b_512_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2b_512_raw = blake2b_512('data string');
+ $blake2b_512_hex = blake2b_512_hex('data string');
+ $blake2b_512_b64 = blake2b_512_b64('data string');
+ $blake2b_512_b64u = blake2b_512_b64u('data string');
+ # calculate digest from file
+ $blake2b_512_raw = blake2b_512_file('filename.dat');
+ $blake2b_512_hex = blake2b_512_file_hex('filename.dat');
+ $blake2b_512_b64 = blake2b_512_file_b64('filename.dat');
+ $blake2b_512_b64u = blake2b_512_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2b_512_raw = blake2b_512_file(*FILEHANDLE);
+ $blake2b_512_hex = blake2b_512_file_hex(*FILEHANDLE);
+ $blake2b_512_b64 = blake2b_512_file_b64(*FILEHANDLE);
+ $blake2b_512_b64u = blake2b_512_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2b_512;
+
+ $d = Crypt::Digest::BLAKE2b_512->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2b_512 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2b_512 qw(blake2b_512 blake2b_512_hex blake2b_512_b64 blake2b_512_b64u
+ blake2b_512_file blake2b_512_file_hex blake2b_512_file_b64 blake2b_512_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2b_512 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2b_512
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_512 digest encoded as a binary string.
+
+ $blake2b_512_raw = blake2b_512('data string');
+ #or
+ $blake2b_512_raw = blake2b_512('any data', 'more data', 'even more data');
+
+=head2 blake2b_512_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_512 digest encoded as a hexadecimal string.
+
+ $blake2b_512_hex = blake2b_512_hex('data string');
+ #or
+ $blake2b_512_hex = blake2b_512_hex('any data', 'more data', 'even more data');
+
+=head2 blake2b_512_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_512 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_512_b64 = blake2b_512_b64('data string');
+ #or
+ $blake2b_512_b64 = blake2b_512_b64('any data', 'more data', 'even more data');
+
+=head2 blake2b_512_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2b_512 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_512_b64url = blake2b_512_b64u('data string');
+ #or
+ $blake2b_512_b64url = blake2b_512_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2b_512_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_512 digest encoded as a binary string.
+
+ $blake2b_512_raw = blake2b_512_file('filename.dat');
+ #or
+ $blake2b_512_raw = blake2b_512_file(*FILEHANDLE);
+
+=head2 blake2b_512_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_512 digest encoded as a hexadecimal string.
+
+ $blake2b_512_hex = blake2b_512_file_hex('filename.dat');
+ #or
+ $blake2b_512_hex = blake2b_512_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2b_512_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_512 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2b_512_b64 = blake2b_512_file_b64('filename.dat');
+ #or
+ $blake2b_512_b64 = blake2b_512_file_b64(*FILEHANDLE);
+
+=head2 blake2b_512_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2b_512 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_512_b64url = blake2b_512_file_b64u('filename.dat');
+ #or
+ $blake2b_512_b64url = blake2b_512_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2b_512->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2b_512->hashsize();
+ #or
+ Crypt::Digest::BLAKE2b_512::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2s_128.pm b/lib/Crypt/Digest/BLAKE2s_128.pm
new file mode 100644
index 00000000..54b873c5
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2s_128.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2s_128;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2s_128 blake2s_128_hex blake2s_128_b64 blake2s_128_b64u blake2s_128_file blake2s_128_file_hex blake2s_128_file_b64 blake2s_128_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2s_128 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2s_128_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2s_128_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2s_128_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2s_128_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2s_128_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2s_128_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2s_128_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2s_128 - Hash function BLAKE2s [size: 128 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2s_128 qw( blake2s_128 blake2s_128_hex blake2s_128_b64 blake2s_128_b64u
+ blake2s_128_file blake2s_128_file_hex blake2s_128_file_b64 blake2s_128_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2s_128_raw = blake2s_128('data string');
+ $blake2s_128_hex = blake2s_128_hex('data string');
+ $blake2s_128_b64 = blake2s_128_b64('data string');
+ $blake2s_128_b64u = blake2s_128_b64u('data string');
+ # calculate digest from file
+ $blake2s_128_raw = blake2s_128_file('filename.dat');
+ $blake2s_128_hex = blake2s_128_file_hex('filename.dat');
+ $blake2s_128_b64 = blake2s_128_file_b64('filename.dat');
+ $blake2s_128_b64u = blake2s_128_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2s_128_raw = blake2s_128_file(*FILEHANDLE);
+ $blake2s_128_hex = blake2s_128_file_hex(*FILEHANDLE);
+ $blake2s_128_b64 = blake2s_128_file_b64(*FILEHANDLE);
+ $blake2s_128_b64u = blake2s_128_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2s_128;
+
+ $d = Crypt::Digest::BLAKE2s_128->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2s_128 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2s_128 qw(blake2s_128 blake2s_128_hex blake2s_128_b64 blake2s_128_b64u
+ blake2s_128_file blake2s_128_file_hex blake2s_128_file_b64 blake2s_128_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2s_128 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2s_128
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_128 digest encoded as a binary string.
+
+ $blake2s_128_raw = blake2s_128('data string');
+ #or
+ $blake2s_128_raw = blake2s_128('any data', 'more data', 'even more data');
+
+=head2 blake2s_128_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_128 digest encoded as a hexadecimal string.
+
+ $blake2s_128_hex = blake2s_128_hex('data string');
+ #or
+ $blake2s_128_hex = blake2s_128_hex('any data', 'more data', 'even more data');
+
+=head2 blake2s_128_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_128 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_128_b64 = blake2s_128_b64('data string');
+ #or
+ $blake2s_128_b64 = blake2s_128_b64('any data', 'more data', 'even more data');
+
+=head2 blake2s_128_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_128 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_128_b64url = blake2s_128_b64u('data string');
+ #or
+ $blake2s_128_b64url = blake2s_128_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2s_128_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_128 digest encoded as a binary string.
+
+ $blake2s_128_raw = blake2s_128_file('filename.dat');
+ #or
+ $blake2s_128_raw = blake2s_128_file(*FILEHANDLE);
+
+=head2 blake2s_128_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_128 digest encoded as a hexadecimal string.
+
+ $blake2s_128_hex = blake2s_128_file_hex('filename.dat');
+ #or
+ $blake2s_128_hex = blake2s_128_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2s_128_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_128 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_128_b64 = blake2s_128_file_b64('filename.dat');
+ #or
+ $blake2s_128_b64 = blake2s_128_file_b64(*FILEHANDLE);
+
+=head2 blake2s_128_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_128 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_128_b64url = blake2s_128_file_b64u('filename.dat');
+ #or
+ $blake2s_128_b64url = blake2s_128_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2s_128->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2s_128->hashsize();
+ #or
+ Crypt::Digest::BLAKE2s_128::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2s_160.pm b/lib/Crypt/Digest/BLAKE2s_160.pm
new file mode 100644
index 00000000..97c33b45
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2s_160.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2s_160;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2s_160 blake2s_160_hex blake2s_160_b64 blake2s_160_b64u blake2s_160_file blake2s_160_file_hex blake2s_160_file_b64 blake2s_160_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2s_160 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2s_160_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2s_160_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2s_160_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2s_160_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2s_160_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2s_160_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2s_160_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2s_160 - Hash function BLAKE2s [size: 160 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2s_160 qw( blake2s_160 blake2s_160_hex blake2s_160_b64 blake2s_160_b64u
+ blake2s_160_file blake2s_160_file_hex blake2s_160_file_b64 blake2s_160_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2s_160_raw = blake2s_160('data string');
+ $blake2s_160_hex = blake2s_160_hex('data string');
+ $blake2s_160_b64 = blake2s_160_b64('data string');
+ $blake2s_160_b64u = blake2s_160_b64u('data string');
+ # calculate digest from file
+ $blake2s_160_raw = blake2s_160_file('filename.dat');
+ $blake2s_160_hex = blake2s_160_file_hex('filename.dat');
+ $blake2s_160_b64 = blake2s_160_file_b64('filename.dat');
+ $blake2s_160_b64u = blake2s_160_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2s_160_raw = blake2s_160_file(*FILEHANDLE);
+ $blake2s_160_hex = blake2s_160_file_hex(*FILEHANDLE);
+ $blake2s_160_b64 = blake2s_160_file_b64(*FILEHANDLE);
+ $blake2s_160_b64u = blake2s_160_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2s_160;
+
+ $d = Crypt::Digest::BLAKE2s_160->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2s_160 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2s_160 qw(blake2s_160 blake2s_160_hex blake2s_160_b64 blake2s_160_b64u
+ blake2s_160_file blake2s_160_file_hex blake2s_160_file_b64 blake2s_160_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2s_160 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2s_160
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_160 digest encoded as a binary string.
+
+ $blake2s_160_raw = blake2s_160('data string');
+ #or
+ $blake2s_160_raw = blake2s_160('any data', 'more data', 'even more data');
+
+=head2 blake2s_160_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_160 digest encoded as a hexadecimal string.
+
+ $blake2s_160_hex = blake2s_160_hex('data string');
+ #or
+ $blake2s_160_hex = blake2s_160_hex('any data', 'more data', 'even more data');
+
+=head2 blake2s_160_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_160 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_160_b64 = blake2s_160_b64('data string');
+ #or
+ $blake2s_160_b64 = blake2s_160_b64('any data', 'more data', 'even more data');
+
+=head2 blake2s_160_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_160 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_160_b64url = blake2s_160_b64u('data string');
+ #or
+ $blake2s_160_b64url = blake2s_160_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2s_160_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_160 digest encoded as a binary string.
+
+ $blake2s_160_raw = blake2s_160_file('filename.dat');
+ #or
+ $blake2s_160_raw = blake2s_160_file(*FILEHANDLE);
+
+=head2 blake2s_160_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_160 digest encoded as a hexadecimal string.
+
+ $blake2s_160_hex = blake2s_160_file_hex('filename.dat');
+ #or
+ $blake2s_160_hex = blake2s_160_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2s_160_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_160 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_160_b64 = blake2s_160_file_b64('filename.dat');
+ #or
+ $blake2s_160_b64 = blake2s_160_file_b64(*FILEHANDLE);
+
+=head2 blake2s_160_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_160 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_160_b64url = blake2s_160_file_b64u('filename.dat');
+ #or
+ $blake2s_160_b64url = blake2s_160_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2s_160->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2s_160->hashsize();
+ #or
+ Crypt::Digest::BLAKE2s_160::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2s_224.pm b/lib/Crypt/Digest/BLAKE2s_224.pm
new file mode 100644
index 00000000..c47e8107
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2s_224.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2s_224;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2s_224 blake2s_224_hex blake2s_224_b64 blake2s_224_b64u blake2s_224_file blake2s_224_file_hex blake2s_224_file_b64 blake2s_224_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2s_224 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2s_224_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2s_224_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2s_224_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2s_224_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2s_224_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2s_224_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2s_224_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2s_224 - Hash function BLAKE2s [size: 224 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2s_224 qw( blake2s_224 blake2s_224_hex blake2s_224_b64 blake2s_224_b64u
+ blake2s_224_file blake2s_224_file_hex blake2s_224_file_b64 blake2s_224_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2s_224_raw = blake2s_224('data string');
+ $blake2s_224_hex = blake2s_224_hex('data string');
+ $blake2s_224_b64 = blake2s_224_b64('data string');
+ $blake2s_224_b64u = blake2s_224_b64u('data string');
+ # calculate digest from file
+ $blake2s_224_raw = blake2s_224_file('filename.dat');
+ $blake2s_224_hex = blake2s_224_file_hex('filename.dat');
+ $blake2s_224_b64 = blake2s_224_file_b64('filename.dat');
+ $blake2s_224_b64u = blake2s_224_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2s_224_raw = blake2s_224_file(*FILEHANDLE);
+ $blake2s_224_hex = blake2s_224_file_hex(*FILEHANDLE);
+ $blake2s_224_b64 = blake2s_224_file_b64(*FILEHANDLE);
+ $blake2s_224_b64u = blake2s_224_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2s_224;
+
+ $d = Crypt::Digest::BLAKE2s_224->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2s_224 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2s_224 qw(blake2s_224 blake2s_224_hex blake2s_224_b64 blake2s_224_b64u
+ blake2s_224_file blake2s_224_file_hex blake2s_224_file_b64 blake2s_224_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2s_224 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2s_224
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_224 digest encoded as a binary string.
+
+ $blake2s_224_raw = blake2s_224('data string');
+ #or
+ $blake2s_224_raw = blake2s_224('any data', 'more data', 'even more data');
+
+=head2 blake2s_224_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_224 digest encoded as a hexadecimal string.
+
+ $blake2s_224_hex = blake2s_224_hex('data string');
+ #or
+ $blake2s_224_hex = blake2s_224_hex('any data', 'more data', 'even more data');
+
+=head2 blake2s_224_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_224_b64 = blake2s_224_b64('data string');
+ #or
+ $blake2s_224_b64 = blake2s_224_b64('any data', 'more data', 'even more data');
+
+=head2 blake2s_224_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_224_b64url = blake2s_224_b64u('data string');
+ #or
+ $blake2s_224_b64url = blake2s_224_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2s_224_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_224 digest encoded as a binary string.
+
+ $blake2s_224_raw = blake2s_224_file('filename.dat');
+ #or
+ $blake2s_224_raw = blake2s_224_file(*FILEHANDLE);
+
+=head2 blake2s_224_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_224 digest encoded as a hexadecimal string.
+
+ $blake2s_224_hex = blake2s_224_file_hex('filename.dat');
+ #or
+ $blake2s_224_hex = blake2s_224_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2s_224_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_224_b64 = blake2s_224_file_b64('filename.dat');
+ #or
+ $blake2s_224_b64 = blake2s_224_file_b64(*FILEHANDLE);
+
+=head2 blake2s_224_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_224_b64url = blake2s_224_file_b64u('filename.dat');
+ #or
+ $blake2s_224_b64url = blake2s_224_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2s_224->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2s_224->hashsize();
+ #or
+ Crypt::Digest::BLAKE2s_224::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/BLAKE2s_256.pm b/lib/Crypt/Digest/BLAKE2s_256.pm
new file mode 100644
index 00000000..5c15cfd7
--- /dev/null
+++ b/lib/Crypt/Digest/BLAKE2s_256.pm
@@ -0,0 +1,229 @@
+package Crypt::Digest::BLAKE2s_256;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2s_256 blake2s_256_hex blake2s_256_b64 blake2s_256_b64u blake2s_256_file blake2s_256_file_hex blake2s_256_file_b64 blake2s_256_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub blake2s_256 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub blake2s_256_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub blake2s_256_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub blake2s_256_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub blake2s_256_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub blake2s_256_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub blake2s_256_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub blake2s_256_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::BLAKE2s_256 - Hash function BLAKE2s [size: 256 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::BLAKE2s_256 qw( blake2s_256 blake2s_256_hex blake2s_256_b64 blake2s_256_b64u
+ blake2s_256_file blake2s_256_file_hex blake2s_256_file_b64 blake2s_256_file_b64u );
+
+ # calculate digest from string/buffer
+ $blake2s_256_raw = blake2s_256('data string');
+ $blake2s_256_hex = blake2s_256_hex('data string');
+ $blake2s_256_b64 = blake2s_256_b64('data string');
+ $blake2s_256_b64u = blake2s_256_b64u('data string');
+ # calculate digest from file
+ $blake2s_256_raw = blake2s_256_file('filename.dat');
+ $blake2s_256_hex = blake2s_256_file_hex('filename.dat');
+ $blake2s_256_b64 = blake2s_256_file_b64('filename.dat');
+ $blake2s_256_b64u = blake2s_256_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $blake2s_256_raw = blake2s_256_file(*FILEHANDLE);
+ $blake2s_256_hex = blake2s_256_file_hex(*FILEHANDLE);
+ $blake2s_256_b64 = blake2s_256_file_b64(*FILEHANDLE);
+ $blake2s_256_b64u = blake2s_256_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::BLAKE2s_256;
+
+ $d = Crypt::Digest::BLAKE2s_256->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2s_256 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::BLAKE2s_256 qw(blake2s_256 blake2s_256_hex blake2s_256_b64 blake2s_256_b64u
+ blake2s_256_file blake2s_256_file_hex blake2s_256_file_b64 blake2s_256_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::BLAKE2s_256 ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2s_256
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_256 digest encoded as a binary string.
+
+ $blake2s_256_raw = blake2s_256('data string');
+ #or
+ $blake2s_256_raw = blake2s_256('any data', 'more data', 'even more data');
+
+=head2 blake2s_256_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_256 digest encoded as a hexadecimal string.
+
+ $blake2s_256_hex = blake2s_256_hex('data string');
+ #or
+ $blake2s_256_hex = blake2s_256_hex('any data', 'more data', 'even more data');
+
+=head2 blake2s_256_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_256_b64 = blake2s_256_b64('data string');
+ #or
+ $blake2s_256_b64 = blake2s_256_b64('any data', 'more data', 'even more data');
+
+=head2 blake2s_256_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2s_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_256_b64url = blake2s_256_b64u('data string');
+ #or
+ $blake2s_256_b64url = blake2s_256_b64u('any data', 'more data', 'even more data');
+
+=head2 blake2s_256_file
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_256 digest encoded as a binary string.
+
+ $blake2s_256_raw = blake2s_256_file('filename.dat');
+ #or
+ $blake2s_256_raw = blake2s_256_file(*FILEHANDLE);
+
+=head2 blake2s_256_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_256 digest encoded as a hexadecimal string.
+
+ $blake2s_256_hex = blake2s_256_file_hex('filename.dat');
+ #or
+ $blake2s_256_hex = blake2s_256_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 blake2s_256_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $blake2s_256_b64 = blake2s_256_file_b64('filename.dat');
+ #or
+ $blake2s_256_b64 = blake2s_256_file_b64(*FILEHANDLE);
+
+=head2 blake2s_256_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its BLAKE2s_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_256_b64url = blake2s_256_file_b64u('filename.dat');
+ #or
+ $blake2s_256_b64url = blake2s_256_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::BLAKE2s_256->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::BLAKE2s_256->hashsize();
+ #or
+ Crypt::Digest::BLAKE2s_256::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<https://blake2.net/|https://blake2.net/>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/CHAES.pm b/lib/Crypt/Digest/CHAES.pm
new file mode 100644
index 00000000..7ae167e0
--- /dev/null
+++ b/lib/Crypt/Digest/CHAES.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::CHAES;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( chaes chaes_hex chaes_b64 chaes_b64u chaes_file chaes_file_hex chaes_file_b64 chaes_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub chaes { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub chaes_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub chaes_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub chaes_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub chaes_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub chaes_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub chaes_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub chaes_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::CHAES - Hash function - CipherHash based on AES [size: 128 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::CHAES qw( chaes chaes_hex chaes_b64 chaes_b64u
+ chaes_file chaes_file_hex chaes_file_b64 chaes_file_b64u );
+
+ # calculate digest from string/buffer
+ $chaes_raw = chaes('data string');
+ $chaes_hex = chaes_hex('data string');
+ $chaes_b64 = chaes_b64('data string');
+ $chaes_b64u = chaes_b64u('data string');
+ # calculate digest from file
+ $chaes_raw = chaes_file('filename.dat');
+ $chaes_hex = chaes_file_hex('filename.dat');
+ $chaes_b64 = chaes_file_b64('filename.dat');
+ $chaes_b64u = chaes_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $chaes_raw = chaes_file(*FILEHANDLE);
+ $chaes_hex = chaes_file_hex(*FILEHANDLE);
+ $chaes_b64 = chaes_file_b64(*FILEHANDLE);
+ $chaes_b64u = chaes_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::CHAES;
+
+ $d = Crypt::Digest::CHAES->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the CHAES digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::CHAES qw(chaes chaes_hex chaes_b64 chaes_b64u
+ chaes_file chaes_file_hex chaes_file_b64 chaes_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::CHAES ':all';
+
+=head1 FUNCTIONS
+
+=head2 chaes
+
+Logically joins all arguments into a single string, and returns its CHAES digest encoded as a binary string.
+
+ $chaes_raw = chaes('data string');
+ #or
+ $chaes_raw = chaes('any data', 'more data', 'even more data');
+
+=head2 chaes_hex
+
+Logically joins all arguments into a single string, and returns its CHAES digest encoded as a hexadecimal string.
+
+ $chaes_hex = chaes_hex('data string');
+ #or
+ $chaes_hex = chaes_hex('any data', 'more data', 'even more data');
+
+=head2 chaes_b64
+
+Logically joins all arguments into a single string, and returns its CHAES digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $chaes_b64 = chaes_b64('data string');
+ #or
+ $chaes_b64 = chaes_b64('any data', 'more data', 'even more data');
+
+=head2 chaes_b64u
+
+Logically joins all arguments into a single string, and returns its CHAES digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $chaes_b64url = chaes_b64u('data string');
+ #or
+ $chaes_b64url = chaes_b64u('any data', 'more data', 'even more data');
+
+=head2 chaes_file
+
+Reads file (defined by filename or filehandle) content, and returns its CHAES digest encoded as a binary string.
+
+ $chaes_raw = chaes_file('filename.dat');
+ #or
+ $chaes_raw = chaes_file(*FILEHANDLE);
+
+=head2 chaes_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its CHAES digest encoded as a hexadecimal string.
+
+ $chaes_hex = chaes_file_hex('filename.dat');
+ #or
+ $chaes_hex = chaes_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 chaes_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its CHAES digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $chaes_b64 = chaes_file_b64('filename.dat');
+ #or
+ $chaes_b64 = chaes_file_b64(*FILEHANDLE);
+
+=head2 chaes_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its CHAES digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $chaes_b64url = chaes_file_b64u('filename.dat');
+ #or
+ $chaes_b64url = chaes_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::CHAES->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::CHAES->hashsize();
+ #or
+ Crypt::Digest::CHAES::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/Cryptographic_hash_function#Hash_functions_based_on_block_ciphers|http://en.wikipedia.org/wiki/Cryptographic_hash_function#Hash_functions_based_on_block_ciphers>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/MD2.pm b/lib/Crypt/Digest/MD2.pm
new file mode 100644
index 00000000..12d3441d
--- /dev/null
+++ b/lib/Crypt/Digest/MD2.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::MD2;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( md2 md2_hex md2_b64 md2_b64u md2_file md2_file_hex md2_file_b64 md2_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub md2 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub md2_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub md2_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub md2_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub md2_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub md2_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub md2_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub md2_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::MD2 - Hash function MD2 [size: 128 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::MD2 qw( md2 md2_hex md2_b64 md2_b64u
+ md2_file md2_file_hex md2_file_b64 md2_file_b64u );
+
+ # calculate digest from string/buffer
+ $md2_raw = md2('data string');
+ $md2_hex = md2_hex('data string');
+ $md2_b64 = md2_b64('data string');
+ $md2_b64u = md2_b64u('data string');
+ # calculate digest from file
+ $md2_raw = md2_file('filename.dat');
+ $md2_hex = md2_file_hex('filename.dat');
+ $md2_b64 = md2_file_b64('filename.dat');
+ $md2_b64u = md2_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $md2_raw = md2_file(*FILEHANDLE);
+ $md2_hex = md2_file_hex(*FILEHANDLE);
+ $md2_b64 = md2_file_b64(*FILEHANDLE);
+ $md2_b64u = md2_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::MD2;
+
+ $d = Crypt::Digest::MD2->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the MD2 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::MD2 qw(md2 md2_hex md2_b64 md2_b64u
+ md2_file md2_file_hex md2_file_b64 md2_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::MD2 ':all';
+
+=head1 FUNCTIONS
+
+=head2 md2
+
+Logically joins all arguments into a single string, and returns its MD2 digest encoded as a binary string.
+
+ $md2_raw = md2('data string');
+ #or
+ $md2_raw = md2('any data', 'more data', 'even more data');
+
+=head2 md2_hex
+
+Logically joins all arguments into a single string, and returns its MD2 digest encoded as a hexadecimal string.
+
+ $md2_hex = md2_hex('data string');
+ #or
+ $md2_hex = md2_hex('any data', 'more data', 'even more data');
+
+=head2 md2_b64
+
+Logically joins all arguments into a single string, and returns its MD2 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $md2_b64 = md2_b64('data string');
+ #or
+ $md2_b64 = md2_b64('any data', 'more data', 'even more data');
+
+=head2 md2_b64u
+
+Logically joins all arguments into a single string, and returns its MD2 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $md2_b64url = md2_b64u('data string');
+ #or
+ $md2_b64url = md2_b64u('any data', 'more data', 'even more data');
+
+=head2 md2_file
+
+Reads file (defined by filename or filehandle) content, and returns its MD2 digest encoded as a binary string.
+
+ $md2_raw = md2_file('filename.dat');
+ #or
+ $md2_raw = md2_file(*FILEHANDLE);
+
+=head2 md2_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its MD2 digest encoded as a hexadecimal string.
+
+ $md2_hex = md2_file_hex('filename.dat');
+ #or
+ $md2_hex = md2_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 md2_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its MD2 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $md2_b64 = md2_file_b64('filename.dat');
+ #or
+ $md2_b64 = md2_file_b64(*FILEHANDLE);
+
+=head2 md2_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its MD2 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $md2_b64url = md2_file_b64u('filename.dat');
+ #or
+ $md2_b64url = md2_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::MD2->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::MD2->hashsize();
+ #or
+ Crypt::Digest::MD2::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/MD2_(cryptography)|http://en.wikipedia.org/wiki/MD2_(cryptography)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/MD4.pm b/lib/Crypt/Digest/MD4.pm
new file mode 100644
index 00000000..0725cdef
--- /dev/null
+++ b/lib/Crypt/Digest/MD4.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::MD4;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( md4 md4_hex md4_b64 md4_b64u md4_file md4_file_hex md4_file_b64 md4_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub md4 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub md4_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub md4_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub md4_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub md4_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub md4_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub md4_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub md4_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::MD4 - Hash function MD4 [size: 128 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::MD4 qw( md4 md4_hex md4_b64 md4_b64u
+ md4_file md4_file_hex md4_file_b64 md4_file_b64u );
+
+ # calculate digest from string/buffer
+ $md4_raw = md4('data string');
+ $md4_hex = md4_hex('data string');
+ $md4_b64 = md4_b64('data string');
+ $md4_b64u = md4_b64u('data string');
+ # calculate digest from file
+ $md4_raw = md4_file('filename.dat');
+ $md4_hex = md4_file_hex('filename.dat');
+ $md4_b64 = md4_file_b64('filename.dat');
+ $md4_b64u = md4_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $md4_raw = md4_file(*FILEHANDLE);
+ $md4_hex = md4_file_hex(*FILEHANDLE);
+ $md4_b64 = md4_file_b64(*FILEHANDLE);
+ $md4_b64u = md4_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::MD4;
+
+ $d = Crypt::Digest::MD4->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the MD4 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::MD4 qw(md4 md4_hex md4_b64 md4_b64u
+ md4_file md4_file_hex md4_file_b64 md4_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::MD4 ':all';
+
+=head1 FUNCTIONS
+
+=head2 md4
+
+Logically joins all arguments into a single string, and returns its MD4 digest encoded as a binary string.
+
+ $md4_raw = md4('data string');
+ #or
+ $md4_raw = md4('any data', 'more data', 'even more data');
+
+=head2 md4_hex
+
+Logically joins all arguments into a single string, and returns its MD4 digest encoded as a hexadecimal string.
+
+ $md4_hex = md4_hex('data string');
+ #or
+ $md4_hex = md4_hex('any data', 'more data', 'even more data');
+
+=head2 md4_b64
+
+Logically joins all arguments into a single string, and returns its MD4 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $md4_b64 = md4_b64('data string');
+ #or
+ $md4_b64 = md4_b64('any data', 'more data', 'even more data');
+
+=head2 md4_b64u
+
+Logically joins all arguments into a single string, and returns its MD4 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $md4_b64url = md4_b64u('data string');
+ #or
+ $md4_b64url = md4_b64u('any data', 'more data', 'even more data');
+
+=head2 md4_file
+
+Reads file (defined by filename or filehandle) content, and returns its MD4 digest encoded as a binary string.
+
+ $md4_raw = md4_file('filename.dat');
+ #or
+ $md4_raw = md4_file(*FILEHANDLE);
+
+=head2 md4_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its MD4 digest encoded as a hexadecimal string.
+
+ $md4_hex = md4_file_hex('filename.dat');
+ #or
+ $md4_hex = md4_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 md4_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its MD4 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $md4_b64 = md4_file_b64('filename.dat');
+ #or
+ $md4_b64 = md4_file_b64(*FILEHANDLE);
+
+=head2 md4_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its MD4 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $md4_b64url = md4_file_b64u('filename.dat');
+ #or
+ $md4_b64url = md4_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::MD4->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::MD4->hashsize();
+ #or
+ Crypt::Digest::MD4::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/MD4|http://en.wikipedia.org/wiki/MD4>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/MD5.pm b/lib/Crypt/Digest/MD5.pm
new file mode 100644
index 00000000..ef82f7af
--- /dev/null
+++ b/lib/Crypt/Digest/MD5.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::MD5;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( md5 md5_hex md5_b64 md5_b64u md5_file md5_file_hex md5_file_b64 md5_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub md5 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub md5_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub md5_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub md5_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub md5_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub md5_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub md5_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub md5_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::MD5 - Hash function MD5 [size: 128 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::MD5 qw( md5 md5_hex md5_b64 md5_b64u
+ md5_file md5_file_hex md5_file_b64 md5_file_b64u );
+
+ # calculate digest from string/buffer
+ $md5_raw = md5('data string');
+ $md5_hex = md5_hex('data string');
+ $md5_b64 = md5_b64('data string');
+ $md5_b64u = md5_b64u('data string');
+ # calculate digest from file
+ $md5_raw = md5_file('filename.dat');
+ $md5_hex = md5_file_hex('filename.dat');
+ $md5_b64 = md5_file_b64('filename.dat');
+ $md5_b64u = md5_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $md5_raw = md5_file(*FILEHANDLE);
+ $md5_hex = md5_file_hex(*FILEHANDLE);
+ $md5_b64 = md5_file_b64(*FILEHANDLE);
+ $md5_b64u = md5_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::MD5;
+
+ $d = Crypt::Digest::MD5->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the MD5 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::MD5 qw(md5 md5_hex md5_b64 md5_b64u
+ md5_file md5_file_hex md5_file_b64 md5_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::MD5 ':all';
+
+=head1 FUNCTIONS
+
+=head2 md5
+
+Logically joins all arguments into a single string, and returns its MD5 digest encoded as a binary string.
+
+ $md5_raw = md5('data string');
+ #or
+ $md5_raw = md5('any data', 'more data', 'even more data');
+
+=head2 md5_hex
+
+Logically joins all arguments into a single string, and returns its MD5 digest encoded as a hexadecimal string.
+
+ $md5_hex = md5_hex('data string');
+ #or
+ $md5_hex = md5_hex('any data', 'more data', 'even more data');
+
+=head2 md5_b64
+
+Logically joins all arguments into a single string, and returns its MD5 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $md5_b64 = md5_b64('data string');
+ #or
+ $md5_b64 = md5_b64('any data', 'more data', 'even more data');
+
+=head2 md5_b64u
+
+Logically joins all arguments into a single string, and returns its MD5 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $md5_b64url = md5_b64u('data string');
+ #or
+ $md5_b64url = md5_b64u('any data', 'more data', 'even more data');
+
+=head2 md5_file
+
+Reads file (defined by filename or filehandle) content, and returns its MD5 digest encoded as a binary string.
+
+ $md5_raw = md5_file('filename.dat');
+ #or
+ $md5_raw = md5_file(*FILEHANDLE);
+
+=head2 md5_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its MD5 digest encoded as a hexadecimal string.
+
+ $md5_hex = md5_file_hex('filename.dat');
+ #or
+ $md5_hex = md5_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 md5_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its MD5 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $md5_b64 = md5_file_b64('filename.dat');
+ #or
+ $md5_b64 = md5_file_b64(*FILEHANDLE);
+
+=head2 md5_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its MD5 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $md5_b64url = md5_file_b64u('filename.dat');
+ #or
+ $md5_b64url = md5_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::MD5->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::MD5->hashsize();
+ #or
+ Crypt::Digest::MD5::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/MD5|http://en.wikipedia.org/wiki/MD5>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/RIPEMD128.pm b/lib/Crypt/Digest/RIPEMD128.pm
new file mode 100644
index 00000000..ec91f8bd
--- /dev/null
+++ b/lib/Crypt/Digest/RIPEMD128.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::RIPEMD128;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( ripemd128 ripemd128_hex ripemd128_b64 ripemd128_b64u ripemd128_file ripemd128_file_hex ripemd128_file_b64 ripemd128_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub ripemd128 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub ripemd128_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub ripemd128_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub ripemd128_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub ripemd128_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub ripemd128_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub ripemd128_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub ripemd128_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::RIPEMD128 - Hash function RIPEMD-128 [size: 128 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::RIPEMD128 qw( ripemd128 ripemd128_hex ripemd128_b64 ripemd128_b64u
+ ripemd128_file ripemd128_file_hex ripemd128_file_b64 ripemd128_file_b64u );
+
+ # calculate digest from string/buffer
+ $ripemd128_raw = ripemd128('data string');
+ $ripemd128_hex = ripemd128_hex('data string');
+ $ripemd128_b64 = ripemd128_b64('data string');
+ $ripemd128_b64u = ripemd128_b64u('data string');
+ # calculate digest from file
+ $ripemd128_raw = ripemd128_file('filename.dat');
+ $ripemd128_hex = ripemd128_file_hex('filename.dat');
+ $ripemd128_b64 = ripemd128_file_b64('filename.dat');
+ $ripemd128_b64u = ripemd128_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $ripemd128_raw = ripemd128_file(*FILEHANDLE);
+ $ripemd128_hex = ripemd128_file_hex(*FILEHANDLE);
+ $ripemd128_b64 = ripemd128_file_b64(*FILEHANDLE);
+ $ripemd128_b64u = ripemd128_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::RIPEMD128;
+
+ $d = Crypt::Digest::RIPEMD128->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the RIPEMD128 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::RIPEMD128 qw(ripemd128 ripemd128_hex ripemd128_b64 ripemd128_b64u
+ ripemd128_file ripemd128_file_hex ripemd128_file_b64 ripemd128_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::RIPEMD128 ':all';
+
+=head1 FUNCTIONS
+
+=head2 ripemd128
+
+Logically joins all arguments into a single string, and returns its RIPEMD128 digest encoded as a binary string.
+
+ $ripemd128_raw = ripemd128('data string');
+ #or
+ $ripemd128_raw = ripemd128('any data', 'more data', 'even more data');
+
+=head2 ripemd128_hex
+
+Logically joins all arguments into a single string, and returns its RIPEMD128 digest encoded as a hexadecimal string.
+
+ $ripemd128_hex = ripemd128_hex('data string');
+ #or
+ $ripemd128_hex = ripemd128_hex('any data', 'more data', 'even more data');
+
+=head2 ripemd128_b64
+
+Logically joins all arguments into a single string, and returns its RIPEMD128 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd128_b64 = ripemd128_b64('data string');
+ #or
+ $ripemd128_b64 = ripemd128_b64('any data', 'more data', 'even more data');
+
+=head2 ripemd128_b64u
+
+Logically joins all arguments into a single string, and returns its RIPEMD128 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd128_b64url = ripemd128_b64u('data string');
+ #or
+ $ripemd128_b64url = ripemd128_b64u('any data', 'more data', 'even more data');
+
+=head2 ripemd128_file
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD128 digest encoded as a binary string.
+
+ $ripemd128_raw = ripemd128_file('filename.dat');
+ #or
+ $ripemd128_raw = ripemd128_file(*FILEHANDLE);
+
+=head2 ripemd128_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD128 digest encoded as a hexadecimal string.
+
+ $ripemd128_hex = ripemd128_file_hex('filename.dat');
+ #or
+ $ripemd128_hex = ripemd128_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 ripemd128_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD128 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd128_b64 = ripemd128_file_b64('filename.dat');
+ #or
+ $ripemd128_b64 = ripemd128_file_b64(*FILEHANDLE);
+
+=head2 ripemd128_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD128 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd128_b64url = ripemd128_file_b64u('filename.dat');
+ #or
+ $ripemd128_b64url = ripemd128_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::RIPEMD128->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::RIPEMD128->hashsize();
+ #or
+ Crypt::Digest::RIPEMD128::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/RIPEMD|http://en.wikipedia.org/wiki/RIPEMD>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/RIPEMD160.pm b/lib/Crypt/Digest/RIPEMD160.pm
new file mode 100644
index 00000000..b13d948b
--- /dev/null
+++ b/lib/Crypt/Digest/RIPEMD160.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::RIPEMD160;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( ripemd160 ripemd160_hex ripemd160_b64 ripemd160_b64u ripemd160_file ripemd160_file_hex ripemd160_file_b64 ripemd160_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub ripemd160 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub ripemd160_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub ripemd160_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub ripemd160_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub ripemd160_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub ripemd160_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub ripemd160_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub ripemd160_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::RIPEMD160 - Hash function RIPEMD-160 [size: 160 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::RIPEMD160 qw( ripemd160 ripemd160_hex ripemd160_b64 ripemd160_b64u
+ ripemd160_file ripemd160_file_hex ripemd160_file_b64 ripemd160_file_b64u );
+
+ # calculate digest from string/buffer
+ $ripemd160_raw = ripemd160('data string');
+ $ripemd160_hex = ripemd160_hex('data string');
+ $ripemd160_b64 = ripemd160_b64('data string');
+ $ripemd160_b64u = ripemd160_b64u('data string');
+ # calculate digest from file
+ $ripemd160_raw = ripemd160_file('filename.dat');
+ $ripemd160_hex = ripemd160_file_hex('filename.dat');
+ $ripemd160_b64 = ripemd160_file_b64('filename.dat');
+ $ripemd160_b64u = ripemd160_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $ripemd160_raw = ripemd160_file(*FILEHANDLE);
+ $ripemd160_hex = ripemd160_file_hex(*FILEHANDLE);
+ $ripemd160_b64 = ripemd160_file_b64(*FILEHANDLE);
+ $ripemd160_b64u = ripemd160_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::RIPEMD160;
+
+ $d = Crypt::Digest::RIPEMD160->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the RIPEMD160 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::RIPEMD160 qw(ripemd160 ripemd160_hex ripemd160_b64 ripemd160_b64u
+ ripemd160_file ripemd160_file_hex ripemd160_file_b64 ripemd160_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::RIPEMD160 ':all';
+
+=head1 FUNCTIONS
+
+=head2 ripemd160
+
+Logically joins all arguments into a single string, and returns its RIPEMD160 digest encoded as a binary string.
+
+ $ripemd160_raw = ripemd160('data string');
+ #or
+ $ripemd160_raw = ripemd160('any data', 'more data', 'even more data');
+
+=head2 ripemd160_hex
+
+Logically joins all arguments into a single string, and returns its RIPEMD160 digest encoded as a hexadecimal string.
+
+ $ripemd160_hex = ripemd160_hex('data string');
+ #or
+ $ripemd160_hex = ripemd160_hex('any data', 'more data', 'even more data');
+
+=head2 ripemd160_b64
+
+Logically joins all arguments into a single string, and returns its RIPEMD160 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd160_b64 = ripemd160_b64('data string');
+ #or
+ $ripemd160_b64 = ripemd160_b64('any data', 'more data', 'even more data');
+
+=head2 ripemd160_b64u
+
+Logically joins all arguments into a single string, and returns its RIPEMD160 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd160_b64url = ripemd160_b64u('data string');
+ #or
+ $ripemd160_b64url = ripemd160_b64u('any data', 'more data', 'even more data');
+
+=head2 ripemd160_file
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD160 digest encoded as a binary string.
+
+ $ripemd160_raw = ripemd160_file('filename.dat');
+ #or
+ $ripemd160_raw = ripemd160_file(*FILEHANDLE);
+
+=head2 ripemd160_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD160 digest encoded as a hexadecimal string.
+
+ $ripemd160_hex = ripemd160_file_hex('filename.dat');
+ #or
+ $ripemd160_hex = ripemd160_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 ripemd160_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD160 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd160_b64 = ripemd160_file_b64('filename.dat');
+ #or
+ $ripemd160_b64 = ripemd160_file_b64(*FILEHANDLE);
+
+=head2 ripemd160_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD160 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd160_b64url = ripemd160_file_b64u('filename.dat');
+ #or
+ $ripemd160_b64url = ripemd160_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::RIPEMD160->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::RIPEMD160->hashsize();
+ #or
+ Crypt::Digest::RIPEMD160::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/RIPEMD|http://en.wikipedia.org/wiki/RIPEMD>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/RIPEMD256.pm b/lib/Crypt/Digest/RIPEMD256.pm
new file mode 100644
index 00000000..d1ed3493
--- /dev/null
+++ b/lib/Crypt/Digest/RIPEMD256.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::RIPEMD256;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( ripemd256 ripemd256_hex ripemd256_b64 ripemd256_b64u ripemd256_file ripemd256_file_hex ripemd256_file_b64 ripemd256_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub ripemd256 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub ripemd256_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub ripemd256_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub ripemd256_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub ripemd256_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub ripemd256_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub ripemd256_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub ripemd256_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::RIPEMD256 - Hash function RIPEMD-256 [size: 256 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::RIPEMD256 qw( ripemd256 ripemd256_hex ripemd256_b64 ripemd256_b64u
+ ripemd256_file ripemd256_file_hex ripemd256_file_b64 ripemd256_file_b64u );
+
+ # calculate digest from string/buffer
+ $ripemd256_raw = ripemd256('data string');
+ $ripemd256_hex = ripemd256_hex('data string');
+ $ripemd256_b64 = ripemd256_b64('data string');
+ $ripemd256_b64u = ripemd256_b64u('data string');
+ # calculate digest from file
+ $ripemd256_raw = ripemd256_file('filename.dat');
+ $ripemd256_hex = ripemd256_file_hex('filename.dat');
+ $ripemd256_b64 = ripemd256_file_b64('filename.dat');
+ $ripemd256_b64u = ripemd256_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $ripemd256_raw = ripemd256_file(*FILEHANDLE);
+ $ripemd256_hex = ripemd256_file_hex(*FILEHANDLE);
+ $ripemd256_b64 = ripemd256_file_b64(*FILEHANDLE);
+ $ripemd256_b64u = ripemd256_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::RIPEMD256;
+
+ $d = Crypt::Digest::RIPEMD256->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the RIPEMD256 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::RIPEMD256 qw(ripemd256 ripemd256_hex ripemd256_b64 ripemd256_b64u
+ ripemd256_file ripemd256_file_hex ripemd256_file_b64 ripemd256_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::RIPEMD256 ':all';
+
+=head1 FUNCTIONS
+
+=head2 ripemd256
+
+Logically joins all arguments into a single string, and returns its RIPEMD256 digest encoded as a binary string.
+
+ $ripemd256_raw = ripemd256('data string');
+ #or
+ $ripemd256_raw = ripemd256('any data', 'more data', 'even more data');
+
+=head2 ripemd256_hex
+
+Logically joins all arguments into a single string, and returns its RIPEMD256 digest encoded as a hexadecimal string.
+
+ $ripemd256_hex = ripemd256_hex('data string');
+ #or
+ $ripemd256_hex = ripemd256_hex('any data', 'more data', 'even more data');
+
+=head2 ripemd256_b64
+
+Logically joins all arguments into a single string, and returns its RIPEMD256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd256_b64 = ripemd256_b64('data string');
+ #or
+ $ripemd256_b64 = ripemd256_b64('any data', 'more data', 'even more data');
+
+=head2 ripemd256_b64u
+
+Logically joins all arguments into a single string, and returns its RIPEMD256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd256_b64url = ripemd256_b64u('data string');
+ #or
+ $ripemd256_b64url = ripemd256_b64u('any data', 'more data', 'even more data');
+
+=head2 ripemd256_file
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD256 digest encoded as a binary string.
+
+ $ripemd256_raw = ripemd256_file('filename.dat');
+ #or
+ $ripemd256_raw = ripemd256_file(*FILEHANDLE);
+
+=head2 ripemd256_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD256 digest encoded as a hexadecimal string.
+
+ $ripemd256_hex = ripemd256_file_hex('filename.dat');
+ #or
+ $ripemd256_hex = ripemd256_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 ripemd256_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd256_b64 = ripemd256_file_b64('filename.dat');
+ #or
+ $ripemd256_b64 = ripemd256_file_b64(*FILEHANDLE);
+
+=head2 ripemd256_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd256_b64url = ripemd256_file_b64u('filename.dat');
+ #or
+ $ripemd256_b64url = ripemd256_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::RIPEMD256->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::RIPEMD256->hashsize();
+ #or
+ Crypt::Digest::RIPEMD256::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/RIPEMD|http://en.wikipedia.org/wiki/RIPEMD>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/RIPEMD320.pm b/lib/Crypt/Digest/RIPEMD320.pm
new file mode 100644
index 00000000..816ad936
--- /dev/null
+++ b/lib/Crypt/Digest/RIPEMD320.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::RIPEMD320;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( ripemd320 ripemd320_hex ripemd320_b64 ripemd320_b64u ripemd320_file ripemd320_file_hex ripemd320_file_b64 ripemd320_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub ripemd320 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub ripemd320_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub ripemd320_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub ripemd320_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub ripemd320_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub ripemd320_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub ripemd320_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub ripemd320_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::RIPEMD320 - Hash function RIPEMD-320 [size: 320 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::RIPEMD320 qw( ripemd320 ripemd320_hex ripemd320_b64 ripemd320_b64u
+ ripemd320_file ripemd320_file_hex ripemd320_file_b64 ripemd320_file_b64u );
+
+ # calculate digest from string/buffer
+ $ripemd320_raw = ripemd320('data string');
+ $ripemd320_hex = ripemd320_hex('data string');
+ $ripemd320_b64 = ripemd320_b64('data string');
+ $ripemd320_b64u = ripemd320_b64u('data string');
+ # calculate digest from file
+ $ripemd320_raw = ripemd320_file('filename.dat');
+ $ripemd320_hex = ripemd320_file_hex('filename.dat');
+ $ripemd320_b64 = ripemd320_file_b64('filename.dat');
+ $ripemd320_b64u = ripemd320_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $ripemd320_raw = ripemd320_file(*FILEHANDLE);
+ $ripemd320_hex = ripemd320_file_hex(*FILEHANDLE);
+ $ripemd320_b64 = ripemd320_file_b64(*FILEHANDLE);
+ $ripemd320_b64u = ripemd320_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::RIPEMD320;
+
+ $d = Crypt::Digest::RIPEMD320->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the RIPEMD320 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::RIPEMD320 qw(ripemd320 ripemd320_hex ripemd320_b64 ripemd320_b64u
+ ripemd320_file ripemd320_file_hex ripemd320_file_b64 ripemd320_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::RIPEMD320 ':all';
+
+=head1 FUNCTIONS
+
+=head2 ripemd320
+
+Logically joins all arguments into a single string, and returns its RIPEMD320 digest encoded as a binary string.
+
+ $ripemd320_raw = ripemd320('data string');
+ #or
+ $ripemd320_raw = ripemd320('any data', 'more data', 'even more data');
+
+=head2 ripemd320_hex
+
+Logically joins all arguments into a single string, and returns its RIPEMD320 digest encoded as a hexadecimal string.
+
+ $ripemd320_hex = ripemd320_hex('data string');
+ #or
+ $ripemd320_hex = ripemd320_hex('any data', 'more data', 'even more data');
+
+=head2 ripemd320_b64
+
+Logically joins all arguments into a single string, and returns its RIPEMD320 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd320_b64 = ripemd320_b64('data string');
+ #or
+ $ripemd320_b64 = ripemd320_b64('any data', 'more data', 'even more data');
+
+=head2 ripemd320_b64u
+
+Logically joins all arguments into a single string, and returns its RIPEMD320 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd320_b64url = ripemd320_b64u('data string');
+ #or
+ $ripemd320_b64url = ripemd320_b64u('any data', 'more data', 'even more data');
+
+=head2 ripemd320_file
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD320 digest encoded as a binary string.
+
+ $ripemd320_raw = ripemd320_file('filename.dat');
+ #or
+ $ripemd320_raw = ripemd320_file(*FILEHANDLE);
+
+=head2 ripemd320_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD320 digest encoded as a hexadecimal string.
+
+ $ripemd320_hex = ripemd320_file_hex('filename.dat');
+ #or
+ $ripemd320_hex = ripemd320_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 ripemd320_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD320 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $ripemd320_b64 = ripemd320_file_b64('filename.dat');
+ #or
+ $ripemd320_b64 = ripemd320_file_b64(*FILEHANDLE);
+
+=head2 ripemd320_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its RIPEMD320 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $ripemd320_b64url = ripemd320_file_b64u('filename.dat');
+ #or
+ $ripemd320_b64url = ripemd320_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::RIPEMD320->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::RIPEMD320->hashsize();
+ #or
+ Crypt::Digest::RIPEMD320::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/RIPEMD|http://en.wikipedia.org/wiki/RIPEMD>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA1.pm b/lib/Crypt/Digest/SHA1.pm
new file mode 100644
index 00000000..80e6afa1
--- /dev/null
+++ b/lib/Crypt/Digest/SHA1.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA1;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha1 sha1_hex sha1_b64 sha1_b64u sha1_file sha1_file_hex sha1_file_b64 sha1_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha1 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha1_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha1_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha1_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha1_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha1_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha1_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha1_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA1 - Hash function SHA-1 [size: 160 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA1 qw( sha1 sha1_hex sha1_b64 sha1_b64u
+ sha1_file sha1_file_hex sha1_file_b64 sha1_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha1_raw = sha1('data string');
+ $sha1_hex = sha1_hex('data string');
+ $sha1_b64 = sha1_b64('data string');
+ $sha1_b64u = sha1_b64u('data string');
+ # calculate digest from file
+ $sha1_raw = sha1_file('filename.dat');
+ $sha1_hex = sha1_file_hex('filename.dat');
+ $sha1_b64 = sha1_file_b64('filename.dat');
+ $sha1_b64u = sha1_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha1_raw = sha1_file(*FILEHANDLE);
+ $sha1_hex = sha1_file_hex(*FILEHANDLE);
+ $sha1_b64 = sha1_file_b64(*FILEHANDLE);
+ $sha1_b64u = sha1_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA1;
+
+ $d = Crypt::Digest::SHA1->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA1 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA1 qw(sha1 sha1_hex sha1_b64 sha1_b64u
+ sha1_file sha1_file_hex sha1_file_b64 sha1_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA1 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha1
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a binary string.
+
+ $sha1_raw = sha1('data string');
+ #or
+ $sha1_raw = sha1('any data', 'more data', 'even more data');
+
+=head2 sha1_hex
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a hexadecimal string.
+
+ $sha1_hex = sha1_hex('data string');
+ #or
+ $sha1_hex = sha1_hex('any data', 'more data', 'even more data');
+
+=head2 sha1_b64
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha1_b64 = sha1_b64('data string');
+ #or
+ $sha1_b64 = sha1_b64('any data', 'more data', 'even more data');
+
+=head2 sha1_b64u
+
+Logically joins all arguments into a single string, and returns its SHA1 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha1_b64url = sha1_b64u('data string');
+ #or
+ $sha1_b64url = sha1_b64u('any data', 'more data', 'even more data');
+
+=head2 sha1_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA1 digest encoded as a binary string.
+
+ $sha1_raw = sha1_file('filename.dat');
+ #or
+ $sha1_raw = sha1_file(*FILEHANDLE);
+
+=head2 sha1_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA1 digest encoded as a hexadecimal string.
+
+ $sha1_hex = sha1_file_hex('filename.dat');
+ #or
+ $sha1_hex = sha1_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha1_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA1 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha1_b64 = sha1_file_b64('filename.dat');
+ #or
+ $sha1_b64 = sha1_file_b64(*FILEHANDLE);
+
+=head2 sha1_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA1 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha1_b64url = sha1_file_b64u('filename.dat');
+ #or
+ $sha1_b64url = sha1_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA1->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA1->hashsize();
+ #or
+ Crypt::Digest::SHA1::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-1|http://en.wikipedia.org/wiki/SHA-1>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA224.pm b/lib/Crypt/Digest/SHA224.pm
new file mode 100644
index 00000000..9d342578
--- /dev/null
+++ b/lib/Crypt/Digest/SHA224.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA224;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha224 sha224_hex sha224_b64 sha224_b64u sha224_file sha224_file_hex sha224_file_b64 sha224_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha224 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha224_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha224_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha224_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha224_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha224_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha224_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha224_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA224 - Hash function SHA-224 [size: 224 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA224 qw( sha224 sha224_hex sha224_b64 sha224_b64u
+ sha224_file sha224_file_hex sha224_file_b64 sha224_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha224_raw = sha224('data string');
+ $sha224_hex = sha224_hex('data string');
+ $sha224_b64 = sha224_b64('data string');
+ $sha224_b64u = sha224_b64u('data string');
+ # calculate digest from file
+ $sha224_raw = sha224_file('filename.dat');
+ $sha224_hex = sha224_file_hex('filename.dat');
+ $sha224_b64 = sha224_file_b64('filename.dat');
+ $sha224_b64u = sha224_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha224_raw = sha224_file(*FILEHANDLE);
+ $sha224_hex = sha224_file_hex(*FILEHANDLE);
+ $sha224_b64 = sha224_file_b64(*FILEHANDLE);
+ $sha224_b64u = sha224_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA224;
+
+ $d = Crypt::Digest::SHA224->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA224 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA224 qw(sha224 sha224_hex sha224_b64 sha224_b64u
+ sha224_file sha224_file_hex sha224_file_b64 sha224_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA224 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha224
+
+Logically joins all arguments into a single string, and returns its SHA224 digest encoded as a binary string.
+
+ $sha224_raw = sha224('data string');
+ #or
+ $sha224_raw = sha224('any data', 'more data', 'even more data');
+
+=head2 sha224_hex
+
+Logically joins all arguments into a single string, and returns its SHA224 digest encoded as a hexadecimal string.
+
+ $sha224_hex = sha224_hex('data string');
+ #or
+ $sha224_hex = sha224_hex('any data', 'more data', 'even more data');
+
+=head2 sha224_b64
+
+Logically joins all arguments into a single string, and returns its SHA224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha224_b64 = sha224_b64('data string');
+ #or
+ $sha224_b64 = sha224_b64('any data', 'more data', 'even more data');
+
+=head2 sha224_b64u
+
+Logically joins all arguments into a single string, and returns its SHA224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha224_b64url = sha224_b64u('data string');
+ #or
+ $sha224_b64url = sha224_b64u('any data', 'more data', 'even more data');
+
+=head2 sha224_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA224 digest encoded as a binary string.
+
+ $sha224_raw = sha224_file('filename.dat');
+ #or
+ $sha224_raw = sha224_file(*FILEHANDLE);
+
+=head2 sha224_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA224 digest encoded as a hexadecimal string.
+
+ $sha224_hex = sha224_file_hex('filename.dat');
+ #or
+ $sha224_hex = sha224_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha224_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha224_b64 = sha224_file_b64('filename.dat');
+ #or
+ $sha224_b64 = sha224_file_b64(*FILEHANDLE);
+
+=head2 sha224_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha224_b64url = sha224_file_b64u('filename.dat');
+ #or
+ $sha224_b64url = sha224_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA224->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA224->hashsize();
+ #or
+ Crypt::Digest::SHA224::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-2|http://en.wikipedia.org/wiki/SHA-2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA256.pm b/lib/Crypt/Digest/SHA256.pm
new file mode 100644
index 00000000..4e68b95a
--- /dev/null
+++ b/lib/Crypt/Digest/SHA256.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA256;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha256 sha256_hex sha256_b64 sha256_b64u sha256_file sha256_file_hex sha256_file_b64 sha256_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha256 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha256_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha256_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha256_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha256_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha256_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha256_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha256_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA256 - Hash function SHA-256 [size: 256 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA256 qw( sha256 sha256_hex sha256_b64 sha256_b64u
+ sha256_file sha256_file_hex sha256_file_b64 sha256_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha256_raw = sha256('data string');
+ $sha256_hex = sha256_hex('data string');
+ $sha256_b64 = sha256_b64('data string');
+ $sha256_b64u = sha256_b64u('data string');
+ # calculate digest from file
+ $sha256_raw = sha256_file('filename.dat');
+ $sha256_hex = sha256_file_hex('filename.dat');
+ $sha256_b64 = sha256_file_b64('filename.dat');
+ $sha256_b64u = sha256_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha256_raw = sha256_file(*FILEHANDLE);
+ $sha256_hex = sha256_file_hex(*FILEHANDLE);
+ $sha256_b64 = sha256_file_b64(*FILEHANDLE);
+ $sha256_b64u = sha256_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA256;
+
+ $d = Crypt::Digest::SHA256->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA256 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA256 qw(sha256 sha256_hex sha256_b64 sha256_b64u
+ sha256_file sha256_file_hex sha256_file_b64 sha256_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA256 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha256
+
+Logically joins all arguments into a single string, and returns its SHA256 digest encoded as a binary string.
+
+ $sha256_raw = sha256('data string');
+ #or
+ $sha256_raw = sha256('any data', 'more data', 'even more data');
+
+=head2 sha256_hex
+
+Logically joins all arguments into a single string, and returns its SHA256 digest encoded as a hexadecimal string.
+
+ $sha256_hex = sha256_hex('data string');
+ #or
+ $sha256_hex = sha256_hex('any data', 'more data', 'even more data');
+
+=head2 sha256_b64
+
+Logically joins all arguments into a single string, and returns its SHA256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha256_b64 = sha256_b64('data string');
+ #or
+ $sha256_b64 = sha256_b64('any data', 'more data', 'even more data');
+
+=head2 sha256_b64u
+
+Logically joins all arguments into a single string, and returns its SHA256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha256_b64url = sha256_b64u('data string');
+ #or
+ $sha256_b64url = sha256_b64u('any data', 'more data', 'even more data');
+
+=head2 sha256_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA256 digest encoded as a binary string.
+
+ $sha256_raw = sha256_file('filename.dat');
+ #or
+ $sha256_raw = sha256_file(*FILEHANDLE);
+
+=head2 sha256_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA256 digest encoded as a hexadecimal string.
+
+ $sha256_hex = sha256_file_hex('filename.dat');
+ #or
+ $sha256_hex = sha256_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha256_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha256_b64 = sha256_file_b64('filename.dat');
+ #or
+ $sha256_b64 = sha256_file_b64(*FILEHANDLE);
+
+=head2 sha256_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha256_b64url = sha256_file_b64u('filename.dat');
+ #or
+ $sha256_b64url = sha256_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA256->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA256->hashsize();
+ #or
+ Crypt::Digest::SHA256::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-2|http://en.wikipedia.org/wiki/SHA-2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA384.pm b/lib/Crypt/Digest/SHA384.pm
new file mode 100644
index 00000000..f8ddfe26
--- /dev/null
+++ b/lib/Crypt/Digest/SHA384.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA384;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha384 sha384_hex sha384_b64 sha384_b64u sha384_file sha384_file_hex sha384_file_b64 sha384_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha384 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha384_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha384_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha384_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha384_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha384_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha384_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha384_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA384 - Hash function SHA-384 [size: 384 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA384 qw( sha384 sha384_hex sha384_b64 sha384_b64u
+ sha384_file sha384_file_hex sha384_file_b64 sha384_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha384_raw = sha384('data string');
+ $sha384_hex = sha384_hex('data string');
+ $sha384_b64 = sha384_b64('data string');
+ $sha384_b64u = sha384_b64u('data string');
+ # calculate digest from file
+ $sha384_raw = sha384_file('filename.dat');
+ $sha384_hex = sha384_file_hex('filename.dat');
+ $sha384_b64 = sha384_file_b64('filename.dat');
+ $sha384_b64u = sha384_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha384_raw = sha384_file(*FILEHANDLE);
+ $sha384_hex = sha384_file_hex(*FILEHANDLE);
+ $sha384_b64 = sha384_file_b64(*FILEHANDLE);
+ $sha384_b64u = sha384_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA384;
+
+ $d = Crypt::Digest::SHA384->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA384 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA384 qw(sha384 sha384_hex sha384_b64 sha384_b64u
+ sha384_file sha384_file_hex sha384_file_b64 sha384_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA384 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha384
+
+Logically joins all arguments into a single string, and returns its SHA384 digest encoded as a binary string.
+
+ $sha384_raw = sha384('data string');
+ #or
+ $sha384_raw = sha384('any data', 'more data', 'even more data');
+
+=head2 sha384_hex
+
+Logically joins all arguments into a single string, and returns its SHA384 digest encoded as a hexadecimal string.
+
+ $sha384_hex = sha384_hex('data string');
+ #or
+ $sha384_hex = sha384_hex('any data', 'more data', 'even more data');
+
+=head2 sha384_b64
+
+Logically joins all arguments into a single string, and returns its SHA384 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha384_b64 = sha384_b64('data string');
+ #or
+ $sha384_b64 = sha384_b64('any data', 'more data', 'even more data');
+
+=head2 sha384_b64u
+
+Logically joins all arguments into a single string, and returns its SHA384 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha384_b64url = sha384_b64u('data string');
+ #or
+ $sha384_b64url = sha384_b64u('any data', 'more data', 'even more data');
+
+=head2 sha384_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA384 digest encoded as a binary string.
+
+ $sha384_raw = sha384_file('filename.dat');
+ #or
+ $sha384_raw = sha384_file(*FILEHANDLE);
+
+=head2 sha384_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA384 digest encoded as a hexadecimal string.
+
+ $sha384_hex = sha384_file_hex('filename.dat');
+ #or
+ $sha384_hex = sha384_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha384_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA384 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha384_b64 = sha384_file_b64('filename.dat');
+ #or
+ $sha384_b64 = sha384_file_b64(*FILEHANDLE);
+
+=head2 sha384_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA384 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha384_b64url = sha384_file_b64u('filename.dat');
+ #or
+ $sha384_b64url = sha384_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA384->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA384->hashsize();
+ #or
+ Crypt::Digest::SHA384::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-2|http://en.wikipedia.org/wiki/SHA-2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA3_224.pm b/lib/Crypt/Digest/SHA3_224.pm
new file mode 100644
index 00000000..192c044e
--- /dev/null
+++ b/lib/Crypt/Digest/SHA3_224.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA3_224;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha3_224 sha3_224_hex sha3_224_b64 sha3_224_b64u sha3_224_file sha3_224_file_hex sha3_224_file_b64 sha3_224_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha3_224 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha3_224_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha3_224_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha3_224_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha3_224_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha3_224_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha3_224_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha3_224_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA3_224 - Hash function SHA3-224 [size: 224 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA3_224 qw( sha3_224 sha3_224_hex sha3_224_b64 sha3_224_b64u
+ sha3_224_file sha3_224_file_hex sha3_224_file_b64 sha3_224_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha3_224_raw = sha3_224('data string');
+ $sha3_224_hex = sha3_224_hex('data string');
+ $sha3_224_b64 = sha3_224_b64('data string');
+ $sha3_224_b64u = sha3_224_b64u('data string');
+ # calculate digest from file
+ $sha3_224_raw = sha3_224_file('filename.dat');
+ $sha3_224_hex = sha3_224_file_hex('filename.dat');
+ $sha3_224_b64 = sha3_224_file_b64('filename.dat');
+ $sha3_224_b64u = sha3_224_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha3_224_raw = sha3_224_file(*FILEHANDLE);
+ $sha3_224_hex = sha3_224_file_hex(*FILEHANDLE);
+ $sha3_224_b64 = sha3_224_file_b64(*FILEHANDLE);
+ $sha3_224_b64u = sha3_224_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA3_224;
+
+ $d = Crypt::Digest::SHA3_224->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA3_224 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA3_224 qw(sha3_224 sha3_224_hex sha3_224_b64 sha3_224_b64u
+ sha3_224_file sha3_224_file_hex sha3_224_file_b64 sha3_224_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA3_224 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha3_224
+
+Logically joins all arguments into a single string, and returns its SHA3_224 digest encoded as a binary string.
+
+ $sha3_224_raw = sha3_224('data string');
+ #or
+ $sha3_224_raw = sha3_224('any data', 'more data', 'even more data');
+
+=head2 sha3_224_hex
+
+Logically joins all arguments into a single string, and returns its SHA3_224 digest encoded as a hexadecimal string.
+
+ $sha3_224_hex = sha3_224_hex('data string');
+ #or
+ $sha3_224_hex = sha3_224_hex('any data', 'more data', 'even more data');
+
+=head2 sha3_224_b64
+
+Logically joins all arguments into a single string, and returns its SHA3_224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_224_b64 = sha3_224_b64('data string');
+ #or
+ $sha3_224_b64 = sha3_224_b64('any data', 'more data', 'even more data');
+
+=head2 sha3_224_b64u
+
+Logically joins all arguments into a single string, and returns its SHA3_224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_224_b64url = sha3_224_b64u('data string');
+ #or
+ $sha3_224_b64url = sha3_224_b64u('any data', 'more data', 'even more data');
+
+=head2 sha3_224_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_224 digest encoded as a binary string.
+
+ $sha3_224_raw = sha3_224_file('filename.dat');
+ #or
+ $sha3_224_raw = sha3_224_file(*FILEHANDLE);
+
+=head2 sha3_224_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_224 digest encoded as a hexadecimal string.
+
+ $sha3_224_hex = sha3_224_file_hex('filename.dat');
+ #or
+ $sha3_224_hex = sha3_224_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha3_224_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_224_b64 = sha3_224_file_b64('filename.dat');
+ #or
+ $sha3_224_b64 = sha3_224_file_b64(*FILEHANDLE);
+
+=head2 sha3_224_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_224_b64url = sha3_224_file_b64u('filename.dat');
+ #or
+ $sha3_224_b64url = sha3_224_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA3_224->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA3_224->hashsize();
+ #or
+ Crypt::Digest::SHA3_224::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-3|http://en.wikipedia.org/wiki/SHA-3>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA3_256.pm b/lib/Crypt/Digest/SHA3_256.pm
new file mode 100644
index 00000000..9a13a52e
--- /dev/null
+++ b/lib/Crypt/Digest/SHA3_256.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA3_256;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha3_256 sha3_256_hex sha3_256_b64 sha3_256_b64u sha3_256_file sha3_256_file_hex sha3_256_file_b64 sha3_256_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha3_256 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha3_256_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha3_256_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha3_256_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha3_256_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha3_256_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha3_256_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha3_256_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA3_256 - Hash function SHA3-256 [size: 256 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA3_256 qw( sha3_256 sha3_256_hex sha3_256_b64 sha3_256_b64u
+ sha3_256_file sha3_256_file_hex sha3_256_file_b64 sha3_256_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha3_256_raw = sha3_256('data string');
+ $sha3_256_hex = sha3_256_hex('data string');
+ $sha3_256_b64 = sha3_256_b64('data string');
+ $sha3_256_b64u = sha3_256_b64u('data string');
+ # calculate digest from file
+ $sha3_256_raw = sha3_256_file('filename.dat');
+ $sha3_256_hex = sha3_256_file_hex('filename.dat');
+ $sha3_256_b64 = sha3_256_file_b64('filename.dat');
+ $sha3_256_b64u = sha3_256_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha3_256_raw = sha3_256_file(*FILEHANDLE);
+ $sha3_256_hex = sha3_256_file_hex(*FILEHANDLE);
+ $sha3_256_b64 = sha3_256_file_b64(*FILEHANDLE);
+ $sha3_256_b64u = sha3_256_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA3_256;
+
+ $d = Crypt::Digest::SHA3_256->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA3_256 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA3_256 qw(sha3_256 sha3_256_hex sha3_256_b64 sha3_256_b64u
+ sha3_256_file sha3_256_file_hex sha3_256_file_b64 sha3_256_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA3_256 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha3_256
+
+Logically joins all arguments into a single string, and returns its SHA3_256 digest encoded as a binary string.
+
+ $sha3_256_raw = sha3_256('data string');
+ #or
+ $sha3_256_raw = sha3_256('any data', 'more data', 'even more data');
+
+=head2 sha3_256_hex
+
+Logically joins all arguments into a single string, and returns its SHA3_256 digest encoded as a hexadecimal string.
+
+ $sha3_256_hex = sha3_256_hex('data string');
+ #or
+ $sha3_256_hex = sha3_256_hex('any data', 'more data', 'even more data');
+
+=head2 sha3_256_b64
+
+Logically joins all arguments into a single string, and returns its SHA3_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_256_b64 = sha3_256_b64('data string');
+ #or
+ $sha3_256_b64 = sha3_256_b64('any data', 'more data', 'even more data');
+
+=head2 sha3_256_b64u
+
+Logically joins all arguments into a single string, and returns its SHA3_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_256_b64url = sha3_256_b64u('data string');
+ #or
+ $sha3_256_b64url = sha3_256_b64u('any data', 'more data', 'even more data');
+
+=head2 sha3_256_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_256 digest encoded as a binary string.
+
+ $sha3_256_raw = sha3_256_file('filename.dat');
+ #or
+ $sha3_256_raw = sha3_256_file(*FILEHANDLE);
+
+=head2 sha3_256_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_256 digest encoded as a hexadecimal string.
+
+ $sha3_256_hex = sha3_256_file_hex('filename.dat');
+ #or
+ $sha3_256_hex = sha3_256_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha3_256_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_256_b64 = sha3_256_file_b64('filename.dat');
+ #or
+ $sha3_256_b64 = sha3_256_file_b64(*FILEHANDLE);
+
+=head2 sha3_256_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_256_b64url = sha3_256_file_b64u('filename.dat');
+ #or
+ $sha3_256_b64url = sha3_256_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA3_256->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA3_256->hashsize();
+ #or
+ Crypt::Digest::SHA3_256::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-3|http://en.wikipedia.org/wiki/SHA-3>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA3_384.pm b/lib/Crypt/Digest/SHA3_384.pm
new file mode 100644
index 00000000..cf98a9e2
--- /dev/null
+++ b/lib/Crypt/Digest/SHA3_384.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA3_384;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha3_384 sha3_384_hex sha3_384_b64 sha3_384_b64u sha3_384_file sha3_384_file_hex sha3_384_file_b64 sha3_384_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha3_384 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha3_384_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha3_384_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha3_384_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha3_384_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha3_384_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha3_384_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha3_384_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA3_384 - Hash function SHA3-384 [size: 384 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA3_384 qw( sha3_384 sha3_384_hex sha3_384_b64 sha3_384_b64u
+ sha3_384_file sha3_384_file_hex sha3_384_file_b64 sha3_384_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha3_384_raw = sha3_384('data string');
+ $sha3_384_hex = sha3_384_hex('data string');
+ $sha3_384_b64 = sha3_384_b64('data string');
+ $sha3_384_b64u = sha3_384_b64u('data string');
+ # calculate digest from file
+ $sha3_384_raw = sha3_384_file('filename.dat');
+ $sha3_384_hex = sha3_384_file_hex('filename.dat');
+ $sha3_384_b64 = sha3_384_file_b64('filename.dat');
+ $sha3_384_b64u = sha3_384_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha3_384_raw = sha3_384_file(*FILEHANDLE);
+ $sha3_384_hex = sha3_384_file_hex(*FILEHANDLE);
+ $sha3_384_b64 = sha3_384_file_b64(*FILEHANDLE);
+ $sha3_384_b64u = sha3_384_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA3_384;
+
+ $d = Crypt::Digest::SHA3_384->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA3_384 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA3_384 qw(sha3_384 sha3_384_hex sha3_384_b64 sha3_384_b64u
+ sha3_384_file sha3_384_file_hex sha3_384_file_b64 sha3_384_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA3_384 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha3_384
+
+Logically joins all arguments into a single string, and returns its SHA3_384 digest encoded as a binary string.
+
+ $sha3_384_raw = sha3_384('data string');
+ #or
+ $sha3_384_raw = sha3_384('any data', 'more data', 'even more data');
+
+=head2 sha3_384_hex
+
+Logically joins all arguments into a single string, and returns its SHA3_384 digest encoded as a hexadecimal string.
+
+ $sha3_384_hex = sha3_384_hex('data string');
+ #or
+ $sha3_384_hex = sha3_384_hex('any data', 'more data', 'even more data');
+
+=head2 sha3_384_b64
+
+Logically joins all arguments into a single string, and returns its SHA3_384 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_384_b64 = sha3_384_b64('data string');
+ #or
+ $sha3_384_b64 = sha3_384_b64('any data', 'more data', 'even more data');
+
+=head2 sha3_384_b64u
+
+Logically joins all arguments into a single string, and returns its SHA3_384 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_384_b64url = sha3_384_b64u('data string');
+ #or
+ $sha3_384_b64url = sha3_384_b64u('any data', 'more data', 'even more data');
+
+=head2 sha3_384_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_384 digest encoded as a binary string.
+
+ $sha3_384_raw = sha3_384_file('filename.dat');
+ #or
+ $sha3_384_raw = sha3_384_file(*FILEHANDLE);
+
+=head2 sha3_384_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_384 digest encoded as a hexadecimal string.
+
+ $sha3_384_hex = sha3_384_file_hex('filename.dat');
+ #or
+ $sha3_384_hex = sha3_384_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha3_384_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_384 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_384_b64 = sha3_384_file_b64('filename.dat');
+ #or
+ $sha3_384_b64 = sha3_384_file_b64(*FILEHANDLE);
+
+=head2 sha3_384_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_384 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_384_b64url = sha3_384_file_b64u('filename.dat');
+ #or
+ $sha3_384_b64url = sha3_384_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA3_384->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA3_384->hashsize();
+ #or
+ Crypt::Digest::SHA3_384::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-3|http://en.wikipedia.org/wiki/SHA-3>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA3_512.pm b/lib/Crypt/Digest/SHA3_512.pm
new file mode 100644
index 00000000..3762a6bc
--- /dev/null
+++ b/lib/Crypt/Digest/SHA3_512.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA3_512;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha3_512 sha3_512_hex sha3_512_b64 sha3_512_b64u sha3_512_file sha3_512_file_hex sha3_512_file_b64 sha3_512_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha3_512 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha3_512_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha3_512_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha3_512_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha3_512_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha3_512_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha3_512_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha3_512_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA3_512 - Hash function SHA3-512 [size: 512 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA3_512 qw( sha3_512 sha3_512_hex sha3_512_b64 sha3_512_b64u
+ sha3_512_file sha3_512_file_hex sha3_512_file_b64 sha3_512_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha3_512_raw = sha3_512('data string');
+ $sha3_512_hex = sha3_512_hex('data string');
+ $sha3_512_b64 = sha3_512_b64('data string');
+ $sha3_512_b64u = sha3_512_b64u('data string');
+ # calculate digest from file
+ $sha3_512_raw = sha3_512_file('filename.dat');
+ $sha3_512_hex = sha3_512_file_hex('filename.dat');
+ $sha3_512_b64 = sha3_512_file_b64('filename.dat');
+ $sha3_512_b64u = sha3_512_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha3_512_raw = sha3_512_file(*FILEHANDLE);
+ $sha3_512_hex = sha3_512_file_hex(*FILEHANDLE);
+ $sha3_512_b64 = sha3_512_file_b64(*FILEHANDLE);
+ $sha3_512_b64u = sha3_512_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA3_512;
+
+ $d = Crypt::Digest::SHA3_512->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA3_512 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA3_512 qw(sha3_512 sha3_512_hex sha3_512_b64 sha3_512_b64u
+ sha3_512_file sha3_512_file_hex sha3_512_file_b64 sha3_512_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA3_512 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha3_512
+
+Logically joins all arguments into a single string, and returns its SHA3_512 digest encoded as a binary string.
+
+ $sha3_512_raw = sha3_512('data string');
+ #or
+ $sha3_512_raw = sha3_512('any data', 'more data', 'even more data');
+
+=head2 sha3_512_hex
+
+Logically joins all arguments into a single string, and returns its SHA3_512 digest encoded as a hexadecimal string.
+
+ $sha3_512_hex = sha3_512_hex('data string');
+ #or
+ $sha3_512_hex = sha3_512_hex('any data', 'more data', 'even more data');
+
+=head2 sha3_512_b64
+
+Logically joins all arguments into a single string, and returns its SHA3_512 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_512_b64 = sha3_512_b64('data string');
+ #or
+ $sha3_512_b64 = sha3_512_b64('any data', 'more data', 'even more data');
+
+=head2 sha3_512_b64u
+
+Logically joins all arguments into a single string, and returns its SHA3_512 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_512_b64url = sha3_512_b64u('data string');
+ #or
+ $sha3_512_b64url = sha3_512_b64u('any data', 'more data', 'even more data');
+
+=head2 sha3_512_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_512 digest encoded as a binary string.
+
+ $sha3_512_raw = sha3_512_file('filename.dat');
+ #or
+ $sha3_512_raw = sha3_512_file(*FILEHANDLE);
+
+=head2 sha3_512_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_512 digest encoded as a hexadecimal string.
+
+ $sha3_512_hex = sha3_512_file_hex('filename.dat');
+ #or
+ $sha3_512_hex = sha3_512_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha3_512_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_512 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha3_512_b64 = sha3_512_file_b64('filename.dat');
+ #or
+ $sha3_512_b64 = sha3_512_file_b64(*FILEHANDLE);
+
+=head2 sha3_512_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA3_512 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha3_512_b64url = sha3_512_file_b64u('filename.dat');
+ #or
+ $sha3_512_b64url = sha3_512_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA3_512->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA3_512->hashsize();
+ #or
+ Crypt::Digest::SHA3_512::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-3|http://en.wikipedia.org/wiki/SHA-3>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA512.pm b/lib/Crypt/Digest/SHA512.pm
new file mode 100644
index 00000000..6b109faf
--- /dev/null
+++ b/lib/Crypt/Digest/SHA512.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA512;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha512 sha512_hex sha512_b64 sha512_b64u sha512_file sha512_file_hex sha512_file_b64 sha512_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha512 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha512_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha512_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha512_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha512_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha512_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha512_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha512_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA512 - Hash function SHA-512 [size: 512 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA512 qw( sha512 sha512_hex sha512_b64 sha512_b64u
+ sha512_file sha512_file_hex sha512_file_b64 sha512_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha512_raw = sha512('data string');
+ $sha512_hex = sha512_hex('data string');
+ $sha512_b64 = sha512_b64('data string');
+ $sha512_b64u = sha512_b64u('data string');
+ # calculate digest from file
+ $sha512_raw = sha512_file('filename.dat');
+ $sha512_hex = sha512_file_hex('filename.dat');
+ $sha512_b64 = sha512_file_b64('filename.dat');
+ $sha512_b64u = sha512_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha512_raw = sha512_file(*FILEHANDLE);
+ $sha512_hex = sha512_file_hex(*FILEHANDLE);
+ $sha512_b64 = sha512_file_b64(*FILEHANDLE);
+ $sha512_b64u = sha512_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA512;
+
+ $d = Crypt::Digest::SHA512->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA512 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA512 qw(sha512 sha512_hex sha512_b64 sha512_b64u
+ sha512_file sha512_file_hex sha512_file_b64 sha512_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA512 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha512
+
+Logically joins all arguments into a single string, and returns its SHA512 digest encoded as a binary string.
+
+ $sha512_raw = sha512('data string');
+ #or
+ $sha512_raw = sha512('any data', 'more data', 'even more data');
+
+=head2 sha512_hex
+
+Logically joins all arguments into a single string, and returns its SHA512 digest encoded as a hexadecimal string.
+
+ $sha512_hex = sha512_hex('data string');
+ #or
+ $sha512_hex = sha512_hex('any data', 'more data', 'even more data');
+
+=head2 sha512_b64
+
+Logically joins all arguments into a single string, and returns its SHA512 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha512_b64 = sha512_b64('data string');
+ #or
+ $sha512_b64 = sha512_b64('any data', 'more data', 'even more data');
+
+=head2 sha512_b64u
+
+Logically joins all arguments into a single string, and returns its SHA512 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha512_b64url = sha512_b64u('data string');
+ #or
+ $sha512_b64url = sha512_b64u('any data', 'more data', 'even more data');
+
+=head2 sha512_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512 digest encoded as a binary string.
+
+ $sha512_raw = sha512_file('filename.dat');
+ #or
+ $sha512_raw = sha512_file(*FILEHANDLE);
+
+=head2 sha512_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512 digest encoded as a hexadecimal string.
+
+ $sha512_hex = sha512_file_hex('filename.dat');
+ #or
+ $sha512_hex = sha512_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha512_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha512_b64 = sha512_file_b64('filename.dat');
+ #or
+ $sha512_b64 = sha512_file_b64(*FILEHANDLE);
+
+=head2 sha512_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha512_b64url = sha512_file_b64u('filename.dat');
+ #or
+ $sha512_b64url = sha512_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA512->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA512->hashsize();
+ #or
+ Crypt::Digest::SHA512::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-2|http://en.wikipedia.org/wiki/SHA-2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA512_224.pm b/lib/Crypt/Digest/SHA512_224.pm
new file mode 100644
index 00000000..eec53e81
--- /dev/null
+++ b/lib/Crypt/Digest/SHA512_224.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA512_224;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha512_224 sha512_224_hex sha512_224_b64 sha512_224_b64u sha512_224_file sha512_224_file_hex sha512_224_file_b64 sha512_224_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha512_224 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha512_224_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha512_224_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha512_224_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha512_224_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha512_224_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha512_224_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha512_224_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA512_224 - Hash function SHA-512/224 [size: 224 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA512_224 qw( sha512_224 sha512_224_hex sha512_224_b64 sha512_224_b64u
+ sha512_224_file sha512_224_file_hex sha512_224_file_b64 sha512_224_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha512_224_raw = sha512_224('data string');
+ $sha512_224_hex = sha512_224_hex('data string');
+ $sha512_224_b64 = sha512_224_b64('data string');
+ $sha512_224_b64u = sha512_224_b64u('data string');
+ # calculate digest from file
+ $sha512_224_raw = sha512_224_file('filename.dat');
+ $sha512_224_hex = sha512_224_file_hex('filename.dat');
+ $sha512_224_b64 = sha512_224_file_b64('filename.dat');
+ $sha512_224_b64u = sha512_224_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha512_224_raw = sha512_224_file(*FILEHANDLE);
+ $sha512_224_hex = sha512_224_file_hex(*FILEHANDLE);
+ $sha512_224_b64 = sha512_224_file_b64(*FILEHANDLE);
+ $sha512_224_b64u = sha512_224_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA512_224;
+
+ $d = Crypt::Digest::SHA512_224->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA512_224 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA512_224 qw(sha512_224 sha512_224_hex sha512_224_b64 sha512_224_b64u
+ sha512_224_file sha512_224_file_hex sha512_224_file_b64 sha512_224_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA512_224 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha512_224
+
+Logically joins all arguments into a single string, and returns its SHA512_224 digest encoded as a binary string.
+
+ $sha512_224_raw = sha512_224('data string');
+ #or
+ $sha512_224_raw = sha512_224('any data', 'more data', 'even more data');
+
+=head2 sha512_224_hex
+
+Logically joins all arguments into a single string, and returns its SHA512_224 digest encoded as a hexadecimal string.
+
+ $sha512_224_hex = sha512_224_hex('data string');
+ #or
+ $sha512_224_hex = sha512_224_hex('any data', 'more data', 'even more data');
+
+=head2 sha512_224_b64
+
+Logically joins all arguments into a single string, and returns its SHA512_224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha512_224_b64 = sha512_224_b64('data string');
+ #or
+ $sha512_224_b64 = sha512_224_b64('any data', 'more data', 'even more data');
+
+=head2 sha512_224_b64u
+
+Logically joins all arguments into a single string, and returns its SHA512_224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha512_224_b64url = sha512_224_b64u('data string');
+ #or
+ $sha512_224_b64url = sha512_224_b64u('any data', 'more data', 'even more data');
+
+=head2 sha512_224_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_224 digest encoded as a binary string.
+
+ $sha512_224_raw = sha512_224_file('filename.dat');
+ #or
+ $sha512_224_raw = sha512_224_file(*FILEHANDLE);
+
+=head2 sha512_224_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_224 digest encoded as a hexadecimal string.
+
+ $sha512_224_hex = sha512_224_file_hex('filename.dat');
+ #or
+ $sha512_224_hex = sha512_224_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha512_224_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_224 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha512_224_b64 = sha512_224_file_b64('filename.dat');
+ #or
+ $sha512_224_b64 = sha512_224_file_b64(*FILEHANDLE);
+
+=head2 sha512_224_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_224 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha512_224_b64url = sha512_224_file_b64u('filename.dat');
+ #or
+ $sha512_224_b64url = sha512_224_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA512_224->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA512_224->hashsize();
+ #or
+ Crypt::Digest::SHA512_224::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-2|http://en.wikipedia.org/wiki/SHA-2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHA512_256.pm b/lib/Crypt/Digest/SHA512_256.pm
new file mode 100644
index 00000000..d02044b7
--- /dev/null
+++ b/lib/Crypt/Digest/SHA512_256.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::SHA512_256;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( sha512_256 sha512_256_hex sha512_256_b64 sha512_256_b64u sha512_256_file sha512_256_file_hex sha512_256_file_b64 sha512_256_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub sha512_256 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub sha512_256_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub sha512_256_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub sha512_256_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub sha512_256_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub sha512_256_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub sha512_256_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub sha512_256_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHA512_256 - Hash function SHA-512/256 [size: 256 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::SHA512_256 qw( sha512_256 sha512_256_hex sha512_256_b64 sha512_256_b64u
+ sha512_256_file sha512_256_file_hex sha512_256_file_b64 sha512_256_file_b64u );
+
+ # calculate digest from string/buffer
+ $sha512_256_raw = sha512_256('data string');
+ $sha512_256_hex = sha512_256_hex('data string');
+ $sha512_256_b64 = sha512_256_b64('data string');
+ $sha512_256_b64u = sha512_256_b64u('data string');
+ # calculate digest from file
+ $sha512_256_raw = sha512_256_file('filename.dat');
+ $sha512_256_hex = sha512_256_file_hex('filename.dat');
+ $sha512_256_b64 = sha512_256_file_b64('filename.dat');
+ $sha512_256_b64u = sha512_256_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $sha512_256_raw = sha512_256_file(*FILEHANDLE);
+ $sha512_256_hex = sha512_256_file_hex(*FILEHANDLE);
+ $sha512_256_b64 = sha512_256_file_b64(*FILEHANDLE);
+ $sha512_256_b64u = sha512_256_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::SHA512_256;
+
+ $d = Crypt::Digest::SHA512_256->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA512_256 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::SHA512_256 qw(sha512_256 sha512_256_hex sha512_256_b64 sha512_256_b64u
+ sha512_256_file sha512_256_file_hex sha512_256_file_b64 sha512_256_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::SHA512_256 ':all';
+
+=head1 FUNCTIONS
+
+=head2 sha512_256
+
+Logically joins all arguments into a single string, and returns its SHA512_256 digest encoded as a binary string.
+
+ $sha512_256_raw = sha512_256('data string');
+ #or
+ $sha512_256_raw = sha512_256('any data', 'more data', 'even more data');
+
+=head2 sha512_256_hex
+
+Logically joins all arguments into a single string, and returns its SHA512_256 digest encoded as a hexadecimal string.
+
+ $sha512_256_hex = sha512_256_hex('data string');
+ #or
+ $sha512_256_hex = sha512_256_hex('any data', 'more data', 'even more data');
+
+=head2 sha512_256_b64
+
+Logically joins all arguments into a single string, and returns its SHA512_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha512_256_b64 = sha512_256_b64('data string');
+ #or
+ $sha512_256_b64 = sha512_256_b64('any data', 'more data', 'even more data');
+
+=head2 sha512_256_b64u
+
+Logically joins all arguments into a single string, and returns its SHA512_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha512_256_b64url = sha512_256_b64u('data string');
+ #or
+ $sha512_256_b64url = sha512_256_b64u('any data', 'more data', 'even more data');
+
+=head2 sha512_256_file
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_256 digest encoded as a binary string.
+
+ $sha512_256_raw = sha512_256_file('filename.dat');
+ #or
+ $sha512_256_raw = sha512_256_file(*FILEHANDLE);
+
+=head2 sha512_256_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_256 digest encoded as a hexadecimal string.
+
+ $sha512_256_hex = sha512_256_file_hex('filename.dat');
+ #or
+ $sha512_256_hex = sha512_256_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 sha512_256_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_256 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $sha512_256_b64 = sha512_256_file_b64('filename.dat');
+ #or
+ $sha512_256_b64 = sha512_256_file_b64(*FILEHANDLE);
+
+=head2 sha512_256_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its SHA512_256 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $sha512_256_b64url = sha512_256_file_b64u('filename.dat');
+ #or
+ $sha512_256_b64url = sha512_256_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::SHA512_256->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::SHA512_256->hashsize();
+ #or
+ Crypt::Digest::SHA512_256::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-2|http://en.wikipedia.org/wiki/SHA-2>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/SHAKE.pm b/lib/Crypt/Digest/SHAKE.pm
new file mode 100644
index 00000000..efdb0776
--- /dev/null
+++ b/lib/Crypt/Digest/SHAKE.pm
@@ -0,0 +1,106 @@
+package Crypt::Digest::SHAKE;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub new { my $class = shift; _new(@_) }
+
+sub addfile {
+ my ($self, $file) = @_;
+
+ my $handle;
+ if (ref(\$file) eq 'SCALAR') { #filename
+ open($handle, "<", $file) || croak "FATAL: cannot open '$file': $!";
+ binmode($handle);
+ }
+ else { #handle
+ $handle = $file
+ }
+ croak "FATAL: invalid handle" unless defined $handle;
+
+ my $n;
+ my $buf = "";
+ while (($n = read($handle, $buf, 32*1024))) {
+ $self->add($buf)
+ }
+ croak "FATAL: read failed: $!" unless defined $n;
+
+ return $self;
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::SHAKE - Hash functions SHAKE128, SHAKE256 from SHA3 family
+
+=head1 SYNOPSIS
+
+ use Crypt::Digest::SHAKE
+
+ $d = Crypt::Digest::SHAKE->new(128);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $part1 = $d->done(100); # 100 raw bytes
+ $part2 = $d->done(100); # another 100 raw bytes
+ #...
+
+=head1 DESCRIPTION
+
+Provides an interface to the SHA3's sponge function SHAKE.
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Digest::SHA3-SHAKE->new($num);
+ # $num ... 128 or 256
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 done
+
+ $result_raw = $d->done($len);
+ # can be called multiple times
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/SHA-3|http://en.wikipedia.org/wiki/SHA-3>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/Tiger192.pm b/lib/Crypt/Digest/Tiger192.pm
new file mode 100644
index 00000000..1e66a4cb
--- /dev/null
+++ b/lib/Crypt/Digest/Tiger192.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::Tiger192;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( tiger192 tiger192_hex tiger192_b64 tiger192_b64u tiger192_file tiger192_file_hex tiger192_file_b64 tiger192_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub tiger192 { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub tiger192_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub tiger192_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub tiger192_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub tiger192_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub tiger192_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub tiger192_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub tiger192_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::Tiger192 - Hash function Tiger-192 [size: 192 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::Tiger192 qw( tiger192 tiger192_hex tiger192_b64 tiger192_b64u
+ tiger192_file tiger192_file_hex tiger192_file_b64 tiger192_file_b64u );
+
+ # calculate digest from string/buffer
+ $tiger192_raw = tiger192('data string');
+ $tiger192_hex = tiger192_hex('data string');
+ $tiger192_b64 = tiger192_b64('data string');
+ $tiger192_b64u = tiger192_b64u('data string');
+ # calculate digest from file
+ $tiger192_raw = tiger192_file('filename.dat');
+ $tiger192_hex = tiger192_file_hex('filename.dat');
+ $tiger192_b64 = tiger192_file_b64('filename.dat');
+ $tiger192_b64u = tiger192_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $tiger192_raw = tiger192_file(*FILEHANDLE);
+ $tiger192_hex = tiger192_file_hex(*FILEHANDLE);
+ $tiger192_b64 = tiger192_file_b64(*FILEHANDLE);
+ $tiger192_b64u = tiger192_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::Tiger192;
+
+ $d = Crypt::Digest::Tiger192->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the Tiger192 digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::Tiger192 qw(tiger192 tiger192_hex tiger192_b64 tiger192_b64u
+ tiger192_file tiger192_file_hex tiger192_file_b64 tiger192_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::Tiger192 ':all';
+
+=head1 FUNCTIONS
+
+=head2 tiger192
+
+Logically joins all arguments into a single string, and returns its Tiger192 digest encoded as a binary string.
+
+ $tiger192_raw = tiger192('data string');
+ #or
+ $tiger192_raw = tiger192('any data', 'more data', 'even more data');
+
+=head2 tiger192_hex
+
+Logically joins all arguments into a single string, and returns its Tiger192 digest encoded as a hexadecimal string.
+
+ $tiger192_hex = tiger192_hex('data string');
+ #or
+ $tiger192_hex = tiger192_hex('any data', 'more data', 'even more data');
+
+=head2 tiger192_b64
+
+Logically joins all arguments into a single string, and returns its Tiger192 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $tiger192_b64 = tiger192_b64('data string');
+ #or
+ $tiger192_b64 = tiger192_b64('any data', 'more data', 'even more data');
+
+=head2 tiger192_b64u
+
+Logically joins all arguments into a single string, and returns its Tiger192 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $tiger192_b64url = tiger192_b64u('data string');
+ #or
+ $tiger192_b64url = tiger192_b64u('any data', 'more data', 'even more data');
+
+=head2 tiger192_file
+
+Reads file (defined by filename or filehandle) content, and returns its Tiger192 digest encoded as a binary string.
+
+ $tiger192_raw = tiger192_file('filename.dat');
+ #or
+ $tiger192_raw = tiger192_file(*FILEHANDLE);
+
+=head2 tiger192_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its Tiger192 digest encoded as a hexadecimal string.
+
+ $tiger192_hex = tiger192_file_hex('filename.dat');
+ #or
+ $tiger192_hex = tiger192_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 tiger192_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its Tiger192 digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $tiger192_b64 = tiger192_file_b64('filename.dat');
+ #or
+ $tiger192_b64 = tiger192_file_b64(*FILEHANDLE);
+
+=head2 tiger192_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its Tiger192 digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $tiger192_b64url = tiger192_file_b64u('filename.dat');
+ #or
+ $tiger192_b64url = tiger192_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::Tiger192->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::Tiger192->hashsize();
+ #or
+ Crypt::Digest::Tiger192::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/Tiger_(cryptography)|http://en.wikipedia.org/wiki/Tiger_(cryptography)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Digest/Whirlpool.pm b/lib/Crypt/Digest/Whirlpool.pm
new file mode 100644
index 00000000..553dc6d0
--- /dev/null
+++ b/lib/Crypt/Digest/Whirlpool.pm
@@ -0,0 +1,227 @@
+package Crypt::Digest::Whirlpool;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Digest Exporter);
+our %EXPORT_TAGS = ( all => [qw( whirlpool whirlpool_hex whirlpool_b64 whirlpool_b64u whirlpool_file whirlpool_file_hex whirlpool_file_b64 whirlpool_file_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+$Carp::Internal{(__PACKAGE__)}++;
+use CryptX;
+
+sub hashsize { Crypt::Digest::hashsize(__PACKAGE__) }
+
+sub whirlpool { Crypt::Digest::digest_data(__PACKAGE__, @_) }
+sub whirlpool_hex { Crypt::Digest::digest_data_hex(__PACKAGE__, @_) }
+sub whirlpool_b64 { Crypt::Digest::digest_data_b64(__PACKAGE__, @_) }
+sub whirlpool_b64u { Crypt::Digest::digest_data_b64u(__PACKAGE__, @_) }
+
+sub whirlpool_file { Crypt::Digest::digest_file(__PACKAGE__, @_) }
+sub whirlpool_file_hex { Crypt::Digest::digest_file_hex(__PACKAGE__, @_) }
+sub whirlpool_file_b64 { Crypt::Digest::digest_file_b64(__PACKAGE__, @_) }
+sub whirlpool_file_b64u { Crypt::Digest::digest_file_b64u(__PACKAGE__, @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Digest::Whirlpool - Hash function Whirlpool [size: 512 bits]
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Digest::Whirlpool qw( whirlpool whirlpool_hex whirlpool_b64 whirlpool_b64u
+ whirlpool_file whirlpool_file_hex whirlpool_file_b64 whirlpool_file_b64u );
+
+ # calculate digest from string/buffer
+ $whirlpool_raw = whirlpool('data string');
+ $whirlpool_hex = whirlpool_hex('data string');
+ $whirlpool_b64 = whirlpool_b64('data string');
+ $whirlpool_b64u = whirlpool_b64u('data string');
+ # calculate digest from file
+ $whirlpool_raw = whirlpool_file('filename.dat');
+ $whirlpool_hex = whirlpool_file_hex('filename.dat');
+ $whirlpool_b64 = whirlpool_file_b64('filename.dat');
+ $whirlpool_b64u = whirlpool_file_b64u('filename.dat');
+ # calculate digest from filehandle
+ $whirlpool_raw = whirlpool_file(*FILEHANDLE);
+ $whirlpool_hex = whirlpool_file_hex(*FILEHANDLE);
+ $whirlpool_b64 = whirlpool_file_b64(*FILEHANDLE);
+ $whirlpool_b64u = whirlpool_file_b64u(*FILEHANDLE);
+
+ ### OO interface:
+ use Crypt::Digest::Whirlpool;
+
+ $d = Crypt::Digest::Whirlpool->new;
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->digest; # raw bytes
+ $result_hex = $d->hexdigest; # hexadecimal form
+ $result_b64 = $d->b64digest; # Base64 form
+ $result_b64u = $d->b64udigest; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the Whirlpool digest algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Digest::Whirlpool qw(whirlpool whirlpool_hex whirlpool_b64 whirlpool_b64u
+ whirlpool_file whirlpool_file_hex whirlpool_file_b64 whirlpool_file_b64u);
+
+Or all of them at once:
+
+ use Crypt::Digest::Whirlpool ':all';
+
+=head1 FUNCTIONS
+
+=head2 whirlpool
+
+Logically joins all arguments into a single string, and returns its Whirlpool digest encoded as a binary string.
+
+ $whirlpool_raw = whirlpool('data string');
+ #or
+ $whirlpool_raw = whirlpool('any data', 'more data', 'even more data');
+
+=head2 whirlpool_hex
+
+Logically joins all arguments into a single string, and returns its Whirlpool digest encoded as a hexadecimal string.
+
+ $whirlpool_hex = whirlpool_hex('data string');
+ #or
+ $whirlpool_hex = whirlpool_hex('any data', 'more data', 'even more data');
+
+=head2 whirlpool_b64
+
+Logically joins all arguments into a single string, and returns its Whirlpool digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $whirlpool_b64 = whirlpool_b64('data string');
+ #or
+ $whirlpool_b64 = whirlpool_b64('any data', 'more data', 'even more data');
+
+=head2 whirlpool_b64u
+
+Logically joins all arguments into a single string, and returns its Whirlpool digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $whirlpool_b64url = whirlpool_b64u('data string');
+ #or
+ $whirlpool_b64url = whirlpool_b64u('any data', 'more data', 'even more data');
+
+=head2 whirlpool_file
+
+Reads file (defined by filename or filehandle) content, and returns its Whirlpool digest encoded as a binary string.
+
+ $whirlpool_raw = whirlpool_file('filename.dat');
+ #or
+ $whirlpool_raw = whirlpool_file(*FILEHANDLE);
+
+=head2 whirlpool_file_hex
+
+Reads file (defined by filename or filehandle) content, and returns its Whirlpool digest encoded as a hexadecimal string.
+
+ $whirlpool_hex = whirlpool_file_hex('filename.dat');
+ #or
+ $whirlpool_hex = whirlpool_file_hex(*FILEHANDLE);
+
+B<BEWARE:> You have to make sure that the filehandle is in binary mode before you pass it as argument to the addfile() method.
+
+=head2 whirlpool_file_b64
+
+Reads file (defined by filename or filehandle) content, and returns its Whirlpool digest encoded as a Base64 string, B<with> trailing '=' padding.
+
+ $whirlpool_b64 = whirlpool_file_b64('filename.dat');
+ #or
+ $whirlpool_b64 = whirlpool_file_b64(*FILEHANDLE);
+
+=head2 whirlpool_file_b64u
+
+Reads file (defined by filename or filehandle) content, and returns its Whirlpool digest encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $whirlpool_b64url = whirlpool_file_b64u('filename.dat');
+ #or
+ $whirlpool_b64url = whirlpool_file_b64u(*FILEHANDLE);
+
+=head1 METHODS
+
+The OO interface provides the same set of functions as L<Crypt::Digest>.
+
+=head2 new
+
+ $d = Crypt::Digest::Whirlpool->new();
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 add_bits
+
+ $d->add_bits($bit_string); # e.g. $d->add_bits("111100001010");
+ #or
+ $d->add_bits($data, $nbits); # e.g. $d->add_bits("\xF0\xA0", 16);
+
+=head2 hashsize
+
+ $d->hashsize;
+ #or
+ Crypt::Digest::Whirlpool->hashsize();
+ #or
+ Crypt::Digest::Whirlpool::hashsize();
+
+=head2 digest
+
+ $result_raw = $d->digest();
+
+=head2 hexdigest
+
+ $result_hex = $d->hexdigest();
+
+=head2 b64digest
+
+ $result_b64 = $d->b64digest();
+
+=head2 b64udigest
+
+ $result_b64url = $d->b64udigest();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Digest|Crypt::Digest>
+
+=item * L<http://en.wikipedia.org/wiki/Whirlpool_(cryptography)|http://en.wikipedia.org/wiki/Whirlpool_(cryptography)>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/KeyDerivation.pm b/lib/Crypt/KeyDerivation.pm
new file mode 100644
index 00000000..921f4844
--- /dev/null
+++ b/lib/Crypt/KeyDerivation.pm
@@ -0,0 +1,167 @@
+package Crypt::KeyDerivation;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw(pbkdf1 pbkdf2 hkdf hkdf_expand hkdf_extract)] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Digest;
+
+sub pbkdf1 {
+ my ($password, $salt, $iteration_count, $hash_name, $len) = @_;
+ $iteration_count ||= 5000;
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA256');
+ $len ||= 32;
+ return _pkcs_5_alg1($password, $salt, $iteration_count, $hash_name, $len);
+}
+
+sub pbkdf2 {
+ my ($password, $salt, $iteration_count, $hash_name, $len) = @_;
+ $iteration_count ||= 5000;
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA256');
+ $len ||= 32;
+ return _pkcs_5_alg2($password, $salt, $iteration_count, $hash_name, $len);
+}
+
+sub hkdf_extract {
+ # RFC: HKDF-Extract(salt, IKM, [Hash]) -> PRK
+ #my ($hash_name, $salt, $keying_material) = @_;
+ my ($keying_material, $salt, $hash_name) = @_;
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA256');
+ $salt = pack("H*", "00" x Crypt::Digest->hashsize($hash_name)) unless defined $salt; # according to rfc5869 defaults to HashLen zero octets
+ return _hkdf_extract($hash_name, $salt, $keying_material);
+}
+
+sub hkdf_expand {
+ # RFC: HKDF-Expand(PRK, info, L, [Hash]) -> OKM
+ #my ($hash_name, $info, $keying_material, $len) = @_;
+ my ($keying_material, $hash_name, $len, $info) = @_;
+ $len ||= 32;
+ $info ||= '';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA256');
+ return _hkdf_expand($hash_name, $info, $keying_material, $len);
+}
+
+sub hkdf {
+ #my ($hash_name, $salt, $info, $keying_material, $len) = @_;
+ my ($keying_material, $salt, $hash_name, $len, $info) = @_;
+ $len ||= 32;
+ $info ||= '';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA256');
+ $salt = pack("H*", "00" x Crypt::Digest->hashsize($hash_name)) unless defined $salt; # according to rfc5869 defaults to HashLen zero octets
+ return _hkdf($hash_name, $salt, $info, $keying_material, $len);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::KeyDerivation - PBKDF1, PBKFD2 and HKDF key derivation functions
+
+=head1 SYNOPSIS
+
+ ### PBKDF1/2
+ $derived_key1 = pbkdf1($password, $salt, $iteration_count, $hash_name, $len);
+ $derived_key2 = pbkdf2($password, $salt, $iteration_count, $hash_name, $len);
+
+ ### HKDF & co.
+ $derived_key3 = hkdf($keying_material, $salt, $hash_name, $len, $info);
+ $prk = hkdf_extract($keying_material, $salt, $hash_name);
+ $okm1 = hkdf_expand($prk, $hash_name, $len, $info);
+
+=head1 DESCRIPTION
+
+Provides an interface to Key derivation functions:
+
+=over
+
+=item * PBKFD1 and PBKDF according to PKCS#5 v2.0 L<https://tools.ietf.org/html/rfc2898|https://tools.ietf.org/html/rfc2898>
+
+=item * HKDF (+ related) according to L<https://tools.ietf.org/html/rfc5869|https://tools.ietf.org/html/rfc5869>
+
+=back
+
+=head1 FUNCTIONS
+
+=head2 pbkdf1
+
+B<BEWARE:> if you are not sure, do not use C<pbkdf1> but rather choose C<pbkdf2>.
+
+ $derived_key = pbkdf1($password, $salt, $iteration_count, $hash_name, $len);
+ #or
+ $derived_key = pbkdf1($password, $salt, $iteration_count, $hash_name);
+ #or
+ $derived_key = pbkdf1($password, $salt, $iteration_count);
+ #or
+ $derived_key = pbkdf1($password, $salt);
+
+ # $password ......... input keying material (password)
+ # $salt ............. salt/nonce (expected length: 8)
+ # $iteration_count .. optional, DEFAULT: 5000
+ # $hash_name ........ optional, DEFAULT: 'SHA256'
+ # $len .............. optional, derived key len, DEFAULT: 32
+
+=head2 pbkdf2
+
+ $derived_key = pbkdf2($password, $salt, $iteration_count, $hash_name, $len);
+ #or
+ $derived_key = pbkdf2($password, $salt, $iteration_count, $hash_name);
+ #or
+ $derived_key = pbkdf2($password, $salt, $iteration_count);
+ #or
+ $derived_key = pbkdf2($password, $salt);
+
+ # $password ......... input keying material (password)
+ # $salt ............. salt/nonce
+ # $iteration_count .. optional, DEFAULT: 5000
+ # $hash_name ........ optional, DEFAULT: 'SHA256'
+ # $len .............. optional, derived key len, DEFAULT: 32
+
+=head2 hkdf
+
+ $okm2 = hkdf($password, $salt, $hash_name, $len, $info);
+ #or
+ $okm2 = hkdf($password, $salt, $hash_name, $len);
+ #or
+ $okm2 = hkdf($password, $salt, $hash_name);
+ #or
+ $okm2 = hkdf($password, $salt);
+
+ # $password ... input keying material (password)
+ # $salt ....... salt/nonce, if undef defaults to HashLen zero octets
+ # $hash_name .. optional, DEFAULT: 'SHA256'
+ # $len ........ optional, derived key len, DEFAULT: 32
+ # $info ....... optional context and application specific information, DEFAULT: ''
+
+=head2 hkdf_extract
+
+ $prk = hkdf_extract($password, $salt, $hash_name);
+ #or
+ $prk = hkdf_extract($password, $salt, $hash_name);
+
+ # $password ... input keying material (password)
+ # $salt ....... salt/nonce, if undef defaults to HashLen zero octets
+ # $hash_name .. optional, DEFAULT: 'SHA256'
+
+
+=head2 hkdf_expand
+
+ $okm = hkdf_expand($pseudokey, $hash_name, $len, $info);
+ #or
+ $okm = hkdf_expand($pseudokey, $hash_name, $len);
+ #or
+ $okm = hkdf_expand($pseudokey, $hash_name);
+ #or
+ $okm = hkdf_expand($pseudokey);
+
+ # $pseudokey .. input keying material
+ # $hash_name .. optional, DEFAULT: 'SHA256'
+ # $len ........ optional, derived key len, DEFAULT: 32
+ # $info ....... optional context and application specific information, DEFAULT: ''
diff --git a/lib/Crypt/Mac.pm b/lib/Crypt/Mac.pm
new file mode 100644
index 00000000..a52ae8a0
--- /dev/null
+++ b/lib/Crypt/Mac.pm
@@ -0,0 +1,53 @@
+package Crypt::Mac;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw( mac mac_hex )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+sub add {
+ my $self = shift;
+ $self->_add_single($_) for (@_);
+ return $self;
+}
+
+sub addfile {
+ my ($self, $file) = @_;
+
+ my $handle;
+ if (ref(\$file) eq 'SCALAR') {
+ #filename
+ open($handle, "<", $file) || die "FATAL: cannot open '$file': $!";
+ binmode($handle);
+ }
+ else {
+ #handle
+ $handle = $file
+ }
+ die "FATAL: invalid handle" unless defined $handle;
+
+ my $n;
+ my $buf = "";
+ while (($n = read($handle, $buf, 32*1024))) {
+ $self->_add_single($buf)
+ }
+ die "FATAL: read failed: $!" unless defined $n;
+
+ return $self;
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+__END__
+
+=head1 NAME
+
+Crypt::mode - [internal only]
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/Mac/BLAKE2b.pm b/lib/Crypt/Mac/BLAKE2b.pm
new file mode 100644
index 00000000..657a94c3
--- /dev/null
+++ b/lib/Crypt/Mac/BLAKE2b.pm
@@ -0,0 +1,156 @@
+package Crypt::Mac::BLAKE2b;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2b blake2b_hex blake2b_b64 blake2b_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+sub new { my $class = shift; _new(@_) }
+sub blake2b { Crypt::Mac::BLAKE2b->new(shift, shift)->add(@_)->mac }
+sub blake2b_hex { Crypt::Mac::BLAKE2b->new(shift, shift)->add(@_)->hexmac }
+sub blake2b_b64 { Crypt::Mac::BLAKE2b->new(shift, shift)->add(@_)->b64mac }
+sub blake2b_b64u { Crypt::Mac::BLAKE2b->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::BLAKE2b - Message authentication code BLAKE2b MAC (RFC 7693)
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::BLAKE2b qw( blake2b blake2b_hex );
+
+ # calculate MAC from string/buffer
+ $blake2b_raw = blake2b($size, $key, 'data buffer');
+ $blake2b_hex = blake2b_hex($size, $key, 'data buffer');
+ $blake2b_b64 = blake2b_b64($size, $key, 'data buffer');
+ $blake2b_b64u = blake2b_b64u($size, $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::BLAKE2b;
+
+ $d = Crypt::Mac::BLAKE2b->new($size, $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2b message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::BLAKE2b qw(blake2b blake2b_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::BLAKE2b ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2b
+
+Logically joins all arguments into a single string, and returns its BLAKE2b message authentication code encoded as a binary string.
+
+ $blake2b_raw = blake2b($size, $key, 'data buffer');
+ #or
+ $blake2b_raw = blake2b($size, $key, 'any data', 'more data', 'even more data');
+
+=head2 blake2b_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2b message authentication code encoded as a hexadecimal string.
+
+ $blake2b_hex = blake2b_hex($size, $key, 'data buffer');
+ #or
+ $blake2b_hex = blake2b_hex($size, $key, 'any data', 'more data', 'even more data');
+
+=head2 blake2b_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2b message authentication code encoded as a Base64 string.
+
+ $blake2b_b64 = blake2b_b64($size, $key, 'data buffer');
+ #or
+ $blake2b_b64 = blake2b_b64($size, $key, 'any data', 'more data', 'even more data');
+
+=head2 blake2b_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2b message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2b_b64url = blake2b_b64u($size, $key, 'data buffer');
+ #or
+ $blake2b_b64url = blake2b_b64u($size, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::BLAKE2b->new($size, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/BLAKE2s.pm b/lib/Crypt/Mac/BLAKE2s.pm
new file mode 100644
index 00000000..c696abc3
--- /dev/null
+++ b/lib/Crypt/Mac/BLAKE2s.pm
@@ -0,0 +1,156 @@
+package Crypt::Mac::BLAKE2s;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( blake2s blake2s_hex blake2s_b64 blake2s_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+sub new { my $class = shift; _new(@_) }
+sub blake2s { Crypt::Mac::BLAKE2s->new(shift, shift)->add(@_)->mac }
+sub blake2s_hex { Crypt::Mac::BLAKE2s->new(shift, shift)->add(@_)->hexmac }
+sub blake2s_b64 { Crypt::Mac::BLAKE2s->new(shift, shift)->add(@_)->b64mac }
+sub blake2s_b64u { Crypt::Mac::BLAKE2s->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::BLAKE2s - Message authentication code BLAKE2s MAC (RFC 7693)
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::BLAKE2s qw( blake2s blake2s_hex );
+
+ # calculate MAC from string/buffer
+ $blake2s_raw = blake2s($size, $key, 'data buffer');
+ $blake2s_hex = blake2s_hex($size, $key, 'data buffer');
+ $blake2s_b64 = blake2s_b64($size, $key, 'data buffer');
+ $blake2s_b64u = blake2s_b64u($size, $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::BLAKE2s;
+
+ $d = Crypt::Mac::BLAKE2s->new($size, $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the BLAKE2s message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::BLAKE2s qw(blake2s blake2s_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::BLAKE2s ':all';
+
+=head1 FUNCTIONS
+
+=head2 blake2s
+
+Logically joins all arguments into a single string, and returns its BLAKE2s message authentication code encoded as a binary string.
+
+ $blake2s_raw = blake2s($size, $key, 'data buffer');
+ #or
+ $blake2s_raw = blake2s($size, $key, 'any data', 'more data', 'even more data');
+
+=head2 blake2s_hex
+
+Logically joins all arguments into a single string, and returns its BLAKE2s message authentication code encoded as a hexadecimal string.
+
+ $blake2s_hex = blake2s_hex($size, $key, 'data buffer');
+ #or
+ $blake2s_hex = blake2s_hex($size, $key, 'any data', 'more data', 'even more data');
+
+=head2 blake2s_b64
+
+Logically joins all arguments into a single string, and returns its BLAKE2s message authentication code encoded as a Base64 string.
+
+ $blake2s_b64 = blake2s_b64($size, $key, 'data buffer');
+ #or
+ $blake2s_b64 = blake2s_b64($size, $key, 'any data', 'more data', 'even more data');
+
+=head2 blake2s_b64u
+
+Logically joins all arguments into a single string, and returns its BLAKE2s message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $blake2s_b64url = blake2s_b64u($size, $key, 'data buffer');
+ #or
+ $blake2s_b64url = blake2s_b64u($size, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::BLAKE2s->new($size, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://tools.ietf.org/html/rfc7693|https://tools.ietf.org/html/rfc7693>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/F9.pm b/lib/Crypt/Mac/F9.pm
new file mode 100644
index 00000000..3caf72c6
--- /dev/null
+++ b/lib/Crypt/Mac/F9.pm
@@ -0,0 +1,156 @@
+package Crypt::Mac::F9;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( f9 f9_hex f9_b64 f9_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+sub f9 { Crypt::Mac::F9->new(shift, shift)->add(@_)->mac }
+sub f9_hex { Crypt::Mac::F9->new(shift, shift)->add(@_)->hexmac }
+sub f9_b64 { Crypt::Mac::F9->new(shift, shift)->add(@_)->b64mac }
+sub f9_b64u { Crypt::Mac::F9->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::F9 - Message authentication code F9
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::F9 qw( f9 f9_hex );
+
+ # calculate MAC from string/buffer
+ $f9_raw = f9($cipher_name, $key, 'data buffer');
+ $f9_hex = f9_hex($cipher_name, $key, 'data buffer');
+ $f9_b64 = f9_b64($cipher_name, $key, 'data buffer');
+ $f9_b64u = f9_b64u($cipher_name, $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::F9;
+
+ $d = Crypt::Mac::F9->new($cipher_name, $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the F9 message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::F9 qw(f9 f9_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::F9 ':all';
+
+=head1 FUNCTIONS
+
+=head2 f9
+
+Logically joins all arguments into a single string, and returns its F9 message authentication code encoded as a binary string.
+
+ $f9_raw = f9($cipher_name, $key, 'data buffer');
+ #or
+ $f9_raw = f9($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 f9_hex
+
+Logically joins all arguments into a single string, and returns its F9 message authentication code encoded as a hexadecimal string.
+
+ $f9_hex = f9_hex($cipher_name, $key, 'data buffer');
+ #or
+ $f9_hex = f9_hex($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 f9_b64
+
+Logically joins all arguments into a single string, and returns its F9 message authentication code encoded as a Base64 string.
+
+ $f9_b64 = f9_b64($cipher_name, $key, 'data buffer');
+ #or
+ $f9_b64 = f9_b64($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 f9_b64u
+
+Logically joins all arguments into a single string, and returns its F9 message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $f9_b64url = f9_b64u($cipher_name, $key, 'data buffer');
+ #or
+ $f9_b64url = f9_b64u($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::F9->new($cipher_name, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/HMAC.pm b/lib/Crypt/Mac/HMAC.pm
new file mode 100644
index 00000000..8a2567f3
--- /dev/null
+++ b/lib/Crypt/Mac/HMAC.pm
@@ -0,0 +1,160 @@
+package Crypt::Mac::HMAC;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( hmac hmac_hex hmac_b64 hmac_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Digest;
+
+sub new { my $class = shift; _new(Crypt::Digest::_trans_digest_name(shift), @_) }
+sub hmac { Crypt::Mac::HMAC->new(shift, shift)->add(@_)->mac }
+sub hmac_hex { Crypt::Mac::HMAC->new(shift, shift)->add(@_)->hexmac }
+sub hmac_b64 { Crypt::Mac::HMAC->new(shift, shift)->add(@_)->b64mac }
+sub hmac_b64u { Crypt::Mac::HMAC->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::HMAC - Message authentication code HMAC
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::HMAC qw( hmac hmac_hex );
+
+ # calculate MAC from string/buffer
+ $hmac_raw = hmac('SHA256', $key, 'data buffer');
+ $hmac_hex = hmac_hex('SHA256', $key, 'data buffer');
+ $hmac_b64 = hmac_b64('SHA256', $key, 'data buffer');
+ $hmac_b64u = hmac_b64u('SHA256', $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::HMAC;
+
+ $d = Crypt::Mac::HMAC->new('SHA256', $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the HMAC message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::HMAC qw(hmac hmac_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::HMAC ':all';
+
+=head1 FUNCTIONS
+
+=head2 hmac
+
+Logically joins all arguments into a single string, and returns its HMAC message authentication code encoded as a binary string.
+
+ $hmac_raw = hmac($hash_name, $key, 'data buffer');
+ #or
+ $hmac_raw = hmac($hash_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 hmac_hex
+
+Logically joins all arguments into a single string, and returns its HMAC message authentication code encoded as a hexadecimal string.
+
+ $hmac_hex = hmac_hex($hash_name, $key, 'data buffer');
+ #or
+ $hmac_hex = hmac_hex($hash_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 hmac_b64
+
+Logically joins all arguments into a single string, and returns its HMAC message authentication code encoded as a Base64 string.
+
+ $hmac_b64 = hmac_b64($hash_name, $key, 'data buffer');
+ #or
+ $hmac_b64 = hmac_b64($hash_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 hmac_b64u
+
+Logically joins all arguments into a single string, and returns its HMAC message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $hmac_b64url = hmac_b64u($hash_name, $key, 'data buffer');
+ #or
+ $hmac_b64url = hmac_b64u($hash_name, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::HMAC->new($hash_name, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://en.wikipedia.org/wiki/Hmac|https://en.wikipedia.org/wiki/Hmac>
+
+=item * L<https://tools.ietf.org/html/rfc2104|https://tools.ietf.org/html/rfc2104>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/OMAC.pm b/lib/Crypt/Mac/OMAC.pm
new file mode 100644
index 00000000..3d752f07
--- /dev/null
+++ b/lib/Crypt/Mac/OMAC.pm
@@ -0,0 +1,158 @@
+package Crypt::Mac::OMAC;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( omac omac_hex omac_b64 omac_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+sub omac { Crypt::Mac::OMAC->new(shift, shift)->add(@_)->mac }
+sub omac_hex { Crypt::Mac::OMAC->new(shift, shift)->add(@_)->hexmac }
+sub omac_b64 { Crypt::Mac::OMAC->new(shift, shift)->add(@_)->b64mac }
+sub omac_b64u { Crypt::Mac::OMAC->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::OMAC - Message authentication code OMAC
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::OMAC qw( omac omac_hex );
+
+ # calculate MAC from string/buffer
+ $omac_raw = omac($cipher_name, $key, 'data buffer');
+ $omac_hex = omac_hex($cipher_name, $key, 'data buffer');
+ $omac_b64 = omac_b64($cipher_name, $key, 'data buffer');
+ $omac_b64u = omac_b64u($cipher_name, $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::OMAC;
+
+ $d = Crypt::Mac::OMAC->new($cipher_name, $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the OMAC message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::OMAC qw(omac omac_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::OMAC ':all';
+
+=head1 FUNCTIONS
+
+=head2 omac
+
+Logically joins all arguments into a single string, and returns its OMAC message authentication code encoded as a binary string.
+
+ $omac_raw = omac($cipher_name, $key, 'data buffer');
+ #or
+ $omac_raw = omac($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 omac_hex
+
+Logically joins all arguments into a single string, and returns its OMAC message authentication code encoded as a hexadecimal string.
+
+ $omac_hex = omac_hex($cipher_name, $key, 'data buffer');
+ #or
+ $omac_hex = omac_hex($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 omac_b64
+
+Logically joins all arguments into a single string, and returns its OMAC message authentication code encoded as a Base64 string.
+
+ $omac_b64 = omac_b64($cipher_name, $key, 'data buffer');
+ #or
+ $omac_b64 = omac_b64($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 omac_b64u
+
+Logically joins all arguments into a single string, and returns its OMAC message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $omac_b64url = omac_b64u($cipher_name, $key, 'data buffer');
+ #or
+ $omac_b64url = omac_b64u($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::OMAC->new($cipher_name, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://en.wikipedia.org/wiki/OMAC_%28cryptography%29|https://en.wikipedia.org/wiki/OMAC_%28cryptography%29>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/PMAC.pm b/lib/Crypt/Mac/PMAC.pm
new file mode 100644
index 00000000..04917ded
--- /dev/null
+++ b/lib/Crypt/Mac/PMAC.pm
@@ -0,0 +1,158 @@
+package Crypt::Mac::PMAC;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( pmac pmac_hex pmac_b64 pmac_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+sub pmac { Crypt::Mac::PMAC->new(shift, shift)->add(@_)->mac }
+sub pmac_hex { Crypt::Mac::PMAC->new(shift, shift)->add(@_)->hexmac }
+sub pmac_b64 { Crypt::Mac::PMAC->new(shift, shift)->add(@_)->b64mac }
+sub pmac_b64u { Crypt::Mac::PMAC->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::PMAC - Message authentication code PMAC
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::PMAC qw( pmac pmac_hex );
+
+ # calculate MAC from string/buffer
+ $pmac_raw = pmac($cipher_name, $key, 'data buffer');
+ $pmac_hex = pmac_hex($cipher_name, $key, 'data buffer');
+ $pmac_b64 = pmac_b64($cipher_name, $key, 'data buffer');
+ $pmac_b64u = pmac_b64u($cipher_name, $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::PMAC;
+
+ $d = Crypt::Mac::PMAC->new($cipher_name, $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the PMAC message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::PMAC qw(pmac pmac_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::PMAC ':all';
+
+=head1 FUNCTIONS
+
+=head2 pmac
+
+Logically joins all arguments into a single string, and returns its PMAC message authentication code encoded as a binary string.
+
+ $pmac_raw = pmac($cipher_name, $key, 'data buffer');
+ #or
+ $pmac_raw = pmac($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 pmac_hex
+
+Logically joins all arguments into a single string, and returns its PMAC message authentication code encoded as a hexadecimal string.
+
+ $pmac_hex = pmac_hex($cipher_name, $key, 'data buffer');
+ #or
+ $pmac_hex = pmac_hex($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 pmac_b64
+
+Logically joins all arguments into a single string, and returns its PMAC message authentication code encoded as a Base64 string.
+
+ $pmac_b64 = pmac_b64($cipher_name, $key, 'data buffer');
+ #or
+ $pmac_b64 = pmac_b64($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 pmac_b64u
+
+Logically joins all arguments into a single string, and returns its PMAC message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $pmac_b64url = pmac_b64u($cipher_name, $key, 'data buffer');
+ #or
+ $pmac_b64url = pmac_b64u($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::PMAC->new($cipher_name, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://en.wikipedia.org/wiki/PMAC_%28cryptography%29|https://en.wikipedia.org/wiki/PMAC_%28cryptography%29>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/Pelican.pm b/lib/Crypt/Mac/Pelican.pm
new file mode 100644
index 00000000..374b5c99
--- /dev/null
+++ b/lib/Crypt/Mac/Pelican.pm
@@ -0,0 +1,156 @@
+package Crypt::Mac::Pelican;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( pelican pelican_hex pelican_b64 pelican_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+sub new { my $class = shift; _new(@_) }
+sub pelican { Crypt::Mac::Pelican->new(shift)->add(@_)->mac }
+sub pelican_hex { Crypt::Mac::Pelican->new(shift)->add(@_)->hexmac }
+sub pelican_b64 { Crypt::Mac::Pelican->new(shift)->add(@_)->b64mac }
+sub pelican_b64u { Crypt::Mac::Pelican->new(shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::Pelican - Message authentication code Pelican (AES based MAC)
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::Pelican qw( pelican pelican_hex );
+
+ # calculate MAC from string/buffer
+ $pelican_raw = pelican($key, 'data buffer');
+ $pelican_hex = pelican_hex($key, 'data buffer');
+ $pelican_b64 = pelican_b64($key, 'data buffer');
+ $pelican_b64u = pelican_b64u($key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::Pelican;
+
+ $d = Crypt::Mac::Pelican->new($key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the Pelican message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::Pelican qw(pelican pelican_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::Pelican ':all';
+
+=head1 FUNCTIONS
+
+=head2 pelican
+
+Logically joins all arguments into a single string, and returns its Pelican message authentication code encoded as a binary string.
+
+ $pelican_raw = pelican($key, 'data buffer');
+ #or
+ $pelican_raw = pelican($key, 'any data', 'more data', 'even more data');
+
+=head2 pelican_hex
+
+Logically joins all arguments into a single string, and returns its Pelican message authentication code encoded as a hexadecimal string.
+
+ $pelican_hex = pelican_hex($key, 'data buffer');
+ #or
+ $pelican_hex = pelican_hex($key, 'any data', 'more data', 'even more data');
+
+=head2 pelican_b64
+
+Logically joins all arguments into a single string, and returns its Pelican message authentication code encoded as a Base64 string.
+
+ $pelican_b64 = pelican_b64($key, 'data buffer');
+ #or
+ $pelican_b64 = pelican_b64($key, 'any data', 'more data', 'even more data');
+
+=head2 pelican_b64u
+
+Logically joins all arguments into a single string, and returns its Pelican message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $pelican_b64url = pelican_b64u($key, 'data buffer');
+ #or
+ $pelican_b64url = pelican_b64u($key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::Pelican->new($key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<http://eprint.iacr.org/2005/088.pdf|http://eprint.iacr.org/2005/088.pdf>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/Poly1305.pm b/lib/Crypt/Mac/Poly1305.pm
new file mode 100644
index 00000000..1d9bf08f
--- /dev/null
+++ b/lib/Crypt/Mac/Poly1305.pm
@@ -0,0 +1,156 @@
+package Crypt::Mac::Poly1305;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( poly1305 poly1305_hex poly1305_b64 poly1305_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+sub new { my $class = shift; _new(@_) }
+sub poly1305 { Crypt::Mac::Poly1305->new(shift)->add(@_)->mac }
+sub poly1305_hex { Crypt::Mac::Poly1305->new(shift)->add(@_)->hexmac }
+sub poly1305_b64 { Crypt::Mac::Poly1305->new(shift)->add(@_)->b64mac }
+sub poly1305_b64u { Crypt::Mac::Poly1305->new(shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::Poly1305 - Message authentication code Poly1305 (RFC 7539)
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::Poly1305 qw( poly1305 poly1305_hex );
+
+ # calculate MAC from string/buffer
+ $poly1305_raw = poly1305($key, 'data buffer');
+ $poly1305_hex = poly1305_hex($key, 'data buffer');
+ $poly1305_b64 = poly1305_b64($key, 'data buffer');
+ $poly1305_b64u = poly1305_b64u($key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::Poly1305;
+
+ $d = Crypt::Mac::Poly1305->new($key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the Poly1305 message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::Poly1305 qw(poly1305 poly1305_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::Poly1305 ':all';
+
+=head1 FUNCTIONS
+
+=head2 poly1305
+
+Logically joins all arguments into a single string, and returns its Poly1305 message authentication code encoded as a binary string.
+
+ $poly1305_raw = poly1305($key, 'data buffer');
+ #or
+ $poly1305_raw = poly1305($key, 'any data', 'more data', 'even more data');
+
+=head2 poly1305_hex
+
+Logically joins all arguments into a single string, and returns its Poly1305 message authentication code encoded as a hexadecimal string.
+
+ $poly1305_hex = poly1305_hex($key, 'data buffer');
+ #or
+ $poly1305_hex = poly1305_hex($key, 'any data', 'more data', 'even more data');
+
+=head2 poly1305_b64
+
+Logically joins all arguments into a single string, and returns its Poly1305 message authentication code encoded as a Base64 string.
+
+ $poly1305_b64 = poly1305_b64($key, 'data buffer');
+ #or
+ $poly1305_b64 = poly1305_b64($key, 'any data', 'more data', 'even more data');
+
+=head2 poly1305_b64u
+
+Logically joins all arguments into a single string, and returns its Poly1305 message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $poly1305_b64url = poly1305_b64u($key, 'data buffer');
+ #or
+ $poly1305_b64url = poly1305_b64u($key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::Poly1305->new($key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://www.ietf.org/rfc/rfc7539.txt|https://www.ietf.org/rfc/rfc7539.txt>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Mac/XCBC.pm b/lib/Crypt/Mac/XCBC.pm
new file mode 100644
index 00000000..61da2248
--- /dev/null
+++ b/lib/Crypt/Mac/XCBC.pm
@@ -0,0 +1,158 @@
+package Crypt::Mac::XCBC;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::Mac Exporter);
+our %EXPORT_TAGS = ( all => [qw( xcbc xcbc_hex xcbc_b64 xcbc_b64u )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use CryptX;
+use Crypt::Cipher;
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+sub xcbc { Crypt::Mac::XCBC->new(shift, shift)->add(@_)->mac }
+sub xcbc_hex { Crypt::Mac::XCBC->new(shift, shift)->add(@_)->hexmac }
+sub xcbc_b64 { Crypt::Mac::XCBC->new(shift, shift)->add(@_)->b64mac }
+sub xcbc_b64u { Crypt::Mac::XCBC->new(shift, shift)->add(@_)->b64umac }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mac::XCBC - Message authentication code XCBC (RFC 3566)
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::Mac::XCBC qw( xcbc xcbc_hex );
+
+ # calculate MAC from string/buffer
+ $xcbc_raw = xcbc($cipher_name, $key, 'data buffer');
+ $xcbc_hex = xcbc_hex($cipher_name, $key, 'data buffer');
+ $xcbc_b64 = xcbc_b64($cipher_name, $key, 'data buffer');
+ $xcbc_b64u = xcbc_b64u($cipher_name, $key, 'data buffer');
+
+ ### OO interface:
+ use Crypt::Mac::XCBC;
+
+ $d = Crypt::Mac::XCBC->new($cipher_name, $key);
+ $d->add('any data');
+ $d->addfile('filename.dat');
+ $d->addfile(*FILEHANDLE);
+ $result_raw = $d->mac; # raw bytes
+ $result_hex = $d->hexmac; # hexadecimal form
+ $result_b64 = $d->b64mac; # Base64 form
+ $result_b64u = $d->b64umac; # Base64 URL Safe form
+
+=head1 DESCRIPTION
+
+Provides an interface to the XCBC message authentication code (MAC) algorithm.
+
+=head1 EXPORT
+
+Nothing is exported by default.
+
+You can export selected functions:
+
+ use Crypt::Mac::XCBC qw(xcbc xcbc_hex );
+
+Or all of them at once:
+
+ use Crypt::Mac::XCBC ':all';
+
+=head1 FUNCTIONS
+
+=head2 xcbc
+
+Logically joins all arguments into a single string, and returns its XCBC message authentication code encoded as a binary string.
+
+ $xcbc_raw = xcbc($cipher_name, $key, 'data buffer');
+ #or
+ $xcbc_raw = xcbc($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 xcbc_hex
+
+Logically joins all arguments into a single string, and returns its XCBC message authentication code encoded as a hexadecimal string.
+
+ $xcbc_hex = xcbc_hex($cipher_name, $key, 'data buffer');
+ #or
+ $xcbc_hex = xcbc_hex($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 xcbc_b64
+
+Logically joins all arguments into a single string, and returns its XCBC message authentication code encoded as a Base64 string.
+
+ $xcbc_b64 = xcbc_b64($cipher_name, $key, 'data buffer');
+ #or
+ $xcbc_b64 = xcbc_b64($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head2 xcbc_b64u
+
+Logically joins all arguments into a single string, and returns its XCBC message authentication code encoded as a Base64 URL Safe string (see RFC 4648 section 5).
+
+ $xcbc_b64url = xcbc_b64u($cipher_name, $key, 'data buffer');
+ #or
+ $xcbc_b64url = xcbc_b64u($cipher_name, $key, 'any data', 'more data', 'even more data');
+
+=head1 METHODS
+
+=head2 new
+
+ $d = Crypt::Mac::XCBC->new($cipher_name, $key);
+
+=head2 clone
+
+ $d->clone();
+
+=head2 reset
+
+ $d->reset();
+
+=head2 add
+
+ $d->add('any data');
+ #or
+ $d->add('any data', 'more data', 'even more data');
+
+=head2 addfile
+
+ $d->addfile('filename.dat');
+ #or
+ $d->addfile(*FILEHANDLE);
+
+=head2 mac
+
+ $result_raw = $d->mac();
+
+=head2 hexmac
+
+ $result_hex = $d->hexmac();
+
+=head2 b64mac
+
+ $result_b64 = $d->b64mac();
+
+=head2 b64umac
+
+ $result_b64url = $d->b64umac();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=item * L<https://www.ietf.org/rfc/rfc3566.txt|https://www.ietf.org/rfc/rfc3566.txt>
+
+=back
+
+=cut
+
+__END__ \ No newline at end of file
diff --git a/lib/Crypt/Misc.pm b/lib/Crypt/Misc.pm
new file mode 100644
index 00000000..74cea07e
--- /dev/null
+++ b/lib/Crypt/Misc.pm
@@ -0,0 +1,363 @@
+package Crypt::Misc;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 5.57 'import';
+use Carp 'croak';
+our %EXPORT_TAGS = ( all => [qw(encode_b64 decode_b64 encode_b64u decode_b64u
+ pem_to_der der_to_pem
+ read_rawfile write_rawfile
+ slow_eq is_v4uuid random_v4uuid
+ increment_octets_be increment_octets_le
+ )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp 'carp';
+use CryptX;
+use Crypt::Digest 'digest_data';
+use Crypt::Mode::CBC;
+use Crypt::Mode::CFB;
+use Crypt::Mode::ECB;
+use Crypt::Mode::OFB;
+use Crypt::Cipher;
+use Crypt::PRNG 'random_bytes';
+
+sub encode_b64 {
+ CryptX::_encode_base64(@_);
+}
+
+sub decode_b64 {
+ CryptX::_decode_base64(@_);
+}
+
+sub encode_b64u {
+ CryptX::_encode_base64url(@_);
+}
+
+sub decode_b64u {
+ CryptX::_decode_base64url(@_);
+}
+
+sub increment_octets_be {
+ CryptX::_increment_octets_be(@_);
+ #$_[0] = CryptX::_increment_octets_be($_[0]);
+}
+
+sub increment_octets_le {
+ CryptX::_increment_octets_le(@_);
+ #$_[0] = CryptX::_increment_octets_le($_[0]);
+}
+
+sub pem_to_der {
+ my ($data, $password) = @_;
+
+ my ($begin, $obj1, $content, $end, $obj2) = $data =~ m/(----[- ]BEGIN ([^\r\n\-]+KEY)[ -]----)(.*?)(----[- ]END ([^\r\n\-]+)[ -]----)/s;
+ return undef unless $content;
+
+ $content =~ s/^\s+//sg;
+ $content =~ s/\s+$//sg;
+ $content =~ s/\r\n/\n/sg; # CR-LF >> LF
+ $content =~ s/\r/\n/sg; # CR >> LF
+ $content =~ s/\\\n//sg; # \ + LF
+
+ my ($headers, undef, $b64) = $content =~ /^(([^:]+:.*?\n)*)(.*)$/s;
+ return undef unless $b64;
+
+ my $binary = decode_b64($b64);
+ return undef unless $binary;
+
+ my ($ptype, $cipher_name, $iv_hex);
+ for my $h (split /\n/, ($headers||'')) {
+ my ($k, $v) = split /:\s*/, $h, 2;
+ $ptype = $v if $k eq 'Proc-Type';
+ ($cipher_name, $iv_hex) = $v =~ /^\s*(.*?)\s*,\s*([0-9a-fA-F]+)\s*$/ if $k eq 'DEK-Info';
+ }
+ if ($cipher_name && $iv_hex && $ptype && $ptype eq '4,ENCRYPTED') {
+ croak "FATAL: encrypted PEM but no password provided" unless defined $password;
+ my $iv = pack("H*", $iv_hex);
+ my ($mode, $klen) = _name2mode($cipher_name);
+ my $key = _password2key($password, $klen, $iv, 'MD5');
+ return $mode->decrypt($binary, $key, $iv);
+ }
+ return $binary;
+}
+
+sub der_to_pem {
+ my ($data, $header_name, $password, $cipher_name) = @_;
+ my $content = $data;
+ my @headers;
+
+ if ($password) {
+ $cipher_name ||= 'AES-256-CBC';
+ my ($mode, $klen, $ilen) = _name2mode($cipher_name);
+ my $iv = random_bytes($ilen);
+ my $key = _password2key($password, $klen, $iv, 'MD5');
+ $content = $mode->encrypt($data, $key, $iv);
+ push @headers, 'Proc-Type: 4,ENCRYPTED', "DEK-Info: ".uc($cipher_name).",".unpack("H*", $iv);
+ }
+
+ my $pem = "-----BEGIN $header_name-----\n";
+ if (@headers) {
+ $pem .= "$_\n" for @headers;
+ $pem .= "\n";
+ }
+ my @l = encode_b64($content) =~ /.{1,64}/g;
+ $pem .= join("\n", @l) . "\n";
+ $pem .= "-----END $header_name-----\n";
+ return $pem;
+}
+
+sub read_rawfile {
+ my $f = shift;
+ croak "FATAL: read_rawfile() non-existing file '$f'" unless -f $f;
+ open my $fh, "<", $f or croak "FATAL: read_rawfile() cannot open file '$f': $!";
+ binmode $fh;
+ return do { local $/; <$fh> };
+}
+
+sub write_rawfile {
+ # write_rawfile($filename, $data);
+ croak "FATAL: write_rawfile() no data" unless defined $_[1];
+ open my $fh, ">", $_[0] or croak "FATAL: write_rawfile() cannot open file '$_[0]': $!";
+ binmode $fh;
+ print $fh $_[1] or croak "FATAL: write_rawfile() cannot write to '$_[0]': $!";
+ close $fh or croak "FATAL: write_rawfile() cannot close '$_[0]': $!";
+ return;
+}
+
+sub slow_eq {
+ my ($a, $b) = @_;
+ return unless defined $a && defined $b;
+ my $diff = length $a ^ length $b;
+ for(my $i = 0; $i < length $a && $i < length $b; $i++) {
+ $diff |= ord(substr $a, $i) ^ ord(substr $b, $i);
+ }
+ return $diff == 0;
+}
+
+sub random_v4uuid() {
+ # Version 4 - random - UUID: xxxxxxxx-xxxx-4xxx-Yxxx-xxxxxxxxxxxx
+ # where x is any hexadecimal digit and Y is one of 8, 9, A, B (1000, 1001, 1010, 1011)
+ # e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479
+ my $raw = random_bytes(16);
+ # xxxxxxxxxxxx4xxxYxxxxxxxxxxxxxxx
+ $raw &= pack("H*", "FFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFF");
+ $raw |= pack("H*", "00000000000040000000000000000000");
+ $raw &= pack("H*", "FFFFFFFFFFFFFFFF3FFFFFFFFFFFFFFF"); # 0x3 == 0011b
+ $raw |= pack("H*", "00000000000000008000000000000000"); # 0x8 == 1000b
+ my $hex = unpack("H*", $raw);
+ $hex =~ s/^(.{8})(.{4})(.{4})(.{4})(.{12}).*$/$1-$2-$3-$4-$5/;
+ return $hex;
+}
+
+sub is_v4uuid($) {
+ my $uuid = shift;
+ return 0 if !$uuid;
+ return 1 if $uuid =~ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+ return 0;
+}
+
+### private functions
+
+sub _name2mode {
+ my $cipher_name = uc(shift);
+ my %trans = ( 'DES-EDE3' => 'DES_EDE' );
+
+ my ($cipher, undef, $klen, $mode) = $cipher_name =~ /^(AES|CAMELLIA|DES|DES-EDE3|SEED)(-(\d+))?-(CBC|CFB|ECB|OFB)$/i;
+ croak "FATAL: unsupported cipher '$cipher_name'" unless $cipher && $mode;
+ $cipher = $trans{$cipher} || $cipher;
+ $klen = $klen ? int($klen/8) : Crypt::Cipher::min_keysize($cipher);
+ my $ilen = Crypt::Cipher::blocksize($cipher);
+ croak "FATAL: unsupported cipher '$cipher_name'" unless $klen && $ilen;
+
+ return (Crypt::Mode::CBC->new($cipher), $klen, $ilen) if $mode eq 'CBC';
+ return (Crypt::Mode::CFB->new($cipher), $klen, $ilen) if $mode eq 'CFB';
+ return (Crypt::Mode::ECB->new($cipher), $klen, $ilen) if $mode eq 'ECB';
+ return (Crypt::Mode::OFB->new($cipher), $klen, $ilen) if $mode eq 'OFB';
+}
+
+sub _password2key {
+ my ($password, $klen, $iv, $hash) = @_;
+ my $salt = substr($iv, 0, 8);
+ my $key = '';
+ while (length($key) < $klen) {
+ $key .= digest_data($hash, $key . $password . $salt);
+ }
+ return substr($key, 0, $klen);
+}
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Misc - miscellaneous functions related to (or used by) CryptX
+
+=head1 SYNOPSIS
+
+This module contains a collection of mostly unsorted functions loosely-related to CryptX distribution but not implementing cryptography.
+
+Most of them are also available in other perl modules but once you utilize CryptX you might avoid dependencies on other modules by using
+functions from Crypt::Misc.
+
+=head1 DESCRIPTION
+
+ use Crypt::Misc ':all';
+
+ # Base64 and Base64/URL-safe functions
+ $base64 = encode_b64($rawbytes);
+ $rawbytes = decode_b64($base64);
+ $base64url = encode_b64u($encode_b64u);
+ $rawbytes = decode_b64u($base64url);
+
+ # read/write file
+ $rawdata = read_rawfile($filename);
+ write_rawfile($filename, $rawdata);
+
+ # convert PEM/DER
+ $der_data = pem_to_der($pem_data);
+ $pem_data = der_to_pem($der_data);
+
+ # others
+ die "mismatch" unless slow_eq($str1, $str2);
+
+=head1 FUNCTIONS
+
+By default, Crypt::Misc doesn't import any function. You can import individual functions like this:
+
+ use Crypt::Misc qw(read_rawfile);
+
+Or import all available functions:
+
+ use Crypt::Misc ':all';
+
+=head2 encode_b64
+
+I<Since: CryptX-0.029>
+
+ $base64string = encode_b64($rawdata);
+
+Encode $rawbytes into Base64 string, no line-endings in the output string.
+
+=head2 decode_b64
+
+I<Since: CryptX-0.029>
+
+ $rawdata = encode_b64($base64string);
+
+Decode a Base64 string.
+
+=head2 encode_b64u
+
+I<Since: CryptX-0.029>
+
+ $base64url_string = encode_b64($rawdata);
+
+Encode $rawbytes into Base64/URL-Safe string, no line-endings in the output string.
+
+=head2 decode_b64u
+
+I<Since: CryptX-0.029>
+
+ $rawdata = encode_b64($base64url_string);
+
+Decode a Base64/URL-Safe string.
+
+=head2 read_rawfile
+
+I<Since: CryptX-0.029>
+
+ $rawdata = read_rawfile($filename);
+
+Read file C<$filename> into a scalar as a binary data (without decoding/transformation).
+
+=head2 write_rawfile
+
+I<Since: CryptX-0.029>
+
+ write_rawfile($filename, $rawdata);
+
+Write C<$rawdata> to file <$filename> as binary data.
+
+=head2 slow_eq
+
+I<Since: CryptX-0.029>
+
+ if (slow_eq($data1, $data2)) { ... }
+
+Constant time compare (to avoid timing side-channel).
+
+=head2 pem_to_der
+
+I<Since: CryptX-0.029>
+
+ $der_data = pem_to_der($pem_data);
+ #or
+ $der_data = pem_to_der($pem_data, $password);
+
+Convert PEM to DER representation. Supports also password protected PEM data.
+
+=head2 der_to_pem
+
+I<Since: CryptX-0.029>
+
+ $pem_data = der_to_pem($pem_data, $header_name);
+ #or
+ $pem_data = der_to_pem($pem_data, $header_name, $password);
+ #or
+ $pem_data = der_to_pem($pem_data, $header_name, $passord, $cipher_name);
+
+ # $header_name e.g. "PUBLIC KEY", "RSA PRIVATE KEY" ...
+ # $cipher_name e.g. "DES-EDE3-CBC", "AES-256-CBC" (DEFAULT) ...
+
+Convert DER to PEM representation. Supports also password protected PEM data.
+
+=head2 random_v4uuid
+
+I<Since: CryptX-0.031>
+
+ my $uuid = random_v4uuid();
+
+Returns cryptographically strong Version 4 random UUID: C<xxxxxxxx-xxxx-4xxx-Yxxx-xxxxxxxxxxxx>
+where C<x> is any hexadecimal digit and C<Y> is one of 8, 9, A, B (1000, 1001, 1010, 1011)
+e.g. C<f47ac10b-58cc-4372-a567-0e02b2c3d479>.
+
+=head2 is_v4uuid
+
+I<Since: CryptX-0.031>
+
+ if (is_v4uuid($uuid)) {
+ ...
+ }
+
+Checks the given C<$uuid> string whether it matches V4 UUID format and returns C<0> (mismatch) or C<1> (match).
+
+=head2 increment_octets_le
+
+I<Since: CryptX-0.048>
+
+ $octects = increment_octets_le($octets);
+
+Take input C<$octets> as a little-endian big number and return an increment.
+
+=head2 increment_octets_be
+
+I<Since: CryptX-0.048>
+
+ $octects = increment_octets_be($octets);
+
+Take input C<$octets> as a big-endian big number and return an increment.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>
+
+=back
+
+=cut
diff --git a/lib/Crypt/Mode.pm b/lib/Crypt/Mode.pm
new file mode 100644
index 00000000..0db6b5b9
--- /dev/null
+++ b/lib/Crypt/Mode.pm
@@ -0,0 +1,72 @@
+package Crypt::Mode;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+### METHODS
+
+sub new { die } # overriden in subclass
+
+sub encrypt {
+ my ($self, $pt) = (shift, shift);
+ $self->_start(1, @_);
+ return $self->add($pt) . $self->finish;
+}
+
+sub decrypt {
+ my ($self, $ct) = (shift, shift);
+ $self->_start(-1, @_);
+ return $self->add($ct) . $self->finish;
+}
+
+sub start_encrypt {
+ my $self = shift;
+ $self->_start(1, @_);
+ return $self;
+}
+
+sub start_decrypt {
+ my $self = shift;
+ $self->_start(-1, @_);
+ return $self;
+}
+
+sub finish {
+ shift->_finish(@_);
+}
+
+sub add {
+ my $self = shift;
+ my $rv = '';
+ $rv .= $self->_crypt($_) for (@_);
+ return $rv;
+}
+
+sub _crypt {
+ my $self = shift;
+ my $dir = $self->_get_dir;
+ return $self->_encrypt(@_) if $dir == 1;
+ return $self->_decrypt(@_) if $dir == -1;
+ return;
+}
+
+sub _finish {
+ my $self = shift;
+ my $dir = $self->_get_dir;
+ return $self->_finish_enc(@_) if $dir == 1;
+ return $self->_finish_dec(@_) if $dir == -1;
+ return;
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+__END__
+
+=head1 NAME
+
+Crypt::Mode - [internal only]
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/Mode/CBC.pm b/lib/Crypt/Mode/CBC.pm
new file mode 100644
index 00000000..be15194a
--- /dev/null
+++ b/lib/Crypt/Mode/CBC.pm
@@ -0,0 +1,108 @@
+package Crypt::Mode::CBC;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Crypt::Cipher;
+use base 'Crypt::Mode';
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mode::CBC - Block cipher mode CBC [Cipher-block chaining]
+
+=head1 SYNOPSIS
+
+ use Crypt::Mode::CBC;
+ my $m = Crypt::Mode::CBC->new('AES');
+
+ #(en|de)crypt at once
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = $m->add('some data');
+ $ciphertext .= $m->add('more data');
+ $ciphertext .= $m->finish;
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+ $plaintext .= $m->finish;
+
+=head1 DESCRIPTION
+
+This module implements CBC cipher mode. B<NOTE:> it works only with ciphers from L<CryptX> (Crypt::Cipher::NNNN).
+
+=head1 METHODS
+
+=head2 new
+
+ my $m = Crypt::Mode::CBC->new('AES');
+ #or
+ my $m = Crypt::Mode::CBC->new('AES', $padding);
+ #or
+ my $m = Crypt::Mode::CBC->new('AES', $padding, $cipher_rounds);
+
+ # $padding .... 0 no padding (plaintext size has to be myltiple of block length)
+ # 1 PKCS5 padding, Crypt::CBC's "standard" - DEFAULT
+ # 2 Crypt::CBC's "oneandzeroes"
+ # $cipher_rounds ... optional num of rounds for given cipher
+
+=head2 encrypt
+
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+
+=head2 decrypt
+
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+=head2 start_encrypt
+
+See example below L</finish>.
+
+=head2 start_decrypt
+
+See example below L</finish>.
+
+=head2 add
+
+See example below L</finish>.
+
+=head2 finish
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = '';
+ $ciphertext .= $m->add('some data');
+ $ciphertext .= $m->add('more data');
+ $ciphertext .= $m->finish;
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = '';
+ $plaintext .= $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+ $plaintext .= $m->finish;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<Crypt::Cipher::AES|Crypt::Cipher::AES>, L<Crypt::Cipher::Blowfish|Crypt::Cipher::Blowfish>, ...
+
+=item * L<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29|https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29>
+
+=back
diff --git a/lib/Crypt/Mode/CFB.pm b/lib/Crypt/Mode/CFB.pm
new file mode 100644
index 00000000..6dc55f47
--- /dev/null
+++ b/lib/Crypt/Mode/CFB.pm
@@ -0,0 +1,99 @@
+package Crypt::Mode::CFB;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Crypt::Cipher;
+use base 'Crypt::Mode';
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mode::CFB - Block cipher mode CFB [Cipher feedback]
+
+=head1 SYNOPSIS
+
+ use Crypt::Mode::CFB;
+ my $m = Crypt::Mode::CFB->new('AES');
+
+ #(en|de)crypt at once
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = $m->add('some data');
+ $ciphertext .= $m->add('more data');
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+
+=head1 DESCRIPTION
+
+This module implements CFB cipher mode. B<NOTE:> it works only with ciphers from L<CryptX> (Crypt::Cipher::NNNN).
+
+=head1 METHODS
+
+=head2 new
+
+ my $m = Crypt::Mode::CFB->new('AES');
+ #or
+ my $m = Crypt::Mode::CFB->new('AES', $cipher_rounds);
+
+ # $cipher_rounds ... optional num of rounds for given cipher
+
+=head2 encrypt
+
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+
+=head2 decrypt
+
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+=head2 start_encrypt
+
+See example below L</finish>.
+
+=head2 start_decrypt
+
+See example below L</finish>.
+
+=head2 add
+
+See example below L</finish>.
+
+=head2 finish
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = '';
+ $ciphertext .= $m->add('some data');
+ $ciphertext .= $m->add('more data');
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = '';
+ $plaintext .= $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<Crypt::Cipher::AES|Crypt::Cipher::AES>, L<Crypt::Cipher::Blowfish|Crypt::Cipher::Blowfish>, ...
+
+=item * L<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29|https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29>
+
+=back
diff --git a/lib/Crypt/Mode/CTR.pm b/lib/Crypt/Mode/CTR.pm
new file mode 100644
index 00000000..060e8140
--- /dev/null
+++ b/lib/Crypt/Mode/CTR.pm
@@ -0,0 +1,106 @@
+package Crypt::Mode::CTR;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Crypt::Cipher;
+use base 'Crypt::Mode';
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mode::CTR - Block cipher mode CTR [Counter mode]
+
+=head1 SYNOPSIS
+
+ use Crypt::Mode::CTR;
+ my $m = Crypt::Mode::CTR->new('AES');
+
+ #(en|de)crypt at once
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = $m->add('some data');
+ $ciphertext .= $m->add('more data');
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+
+=head1 DESCRIPTION
+
+This module implements CTR cipher mode. B<NOTE:> it works only with ciphers from L<CryptX> (Crypt::Cipher::NNNN).
+
+=head1 METHODS
+
+=head2 new
+
+ my $m = Crypt::Mode::CTR->new($cipher_name);
+ #or
+ my $m = Crypt::Mode::CTR->new($cipher_name, $ctr_mode, $ctr_width);
+ #or
+ my $m = Crypt::Mode::CTR->new($cipher_name, $ctr_mode, $ctr_width, $cipher_rounds);
+
+ # $ctr_mode .... 0 little-endian counter (DEFAULT)
+ # 1 big-endian counter
+ # 2 little-endian + RFC3686 incrementing
+ # 3 big-endian + RFC3686 incrementing
+ # $ctr_width ... counter width in bytes (DEFAULT = full block width)
+ # $cipher_rounds ... optional num of rounds for given cipher
+
+=head2 encrypt
+
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+
+=head2 decrypt
+
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+=head2 start_encrypt
+
+See example below L</finish>.
+
+=head2 start_decrypt
+
+See example below L</finish>.
+
+=head2 add
+
+See example below L</finish>.
+
+=head2 finish
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = '';
+ $ciphertext .= $m->add('some data');
+ $ciphertext .= $m->add('more data');
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = '';
+ $plaintext .= $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<Crypt::Cipher::AES|Crypt::Cipher::AES>, L<Crypt::Cipher::Blowfish|Crypt::Cipher::Blowfish>, ...
+
+=item * L<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29|https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29>
+
+=back
diff --git a/lib/Crypt/Mode/ECB.pm b/lib/Crypt/Mode/ECB.pm
new file mode 100644
index 00000000..2fa877e7
--- /dev/null
+++ b/lib/Crypt/Mode/ECB.pm
@@ -0,0 +1,109 @@
+package Crypt::Mode::ECB;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Crypt::Cipher;
+use base 'Crypt::Mode';
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mode::ECB - Block cipher mode ECB [Electronic codebook]
+
+=head1 SYNOPSIS
+
+ use Crypt::Mode::ECB;
+ my $m = Crypt::Mode::ECB->new('AES');
+
+ #(en|de)crypt at once
+ my $ciphertext = $m->encrypt($plaintext, $key);
+ my $plaintext = $m->decrypt($ciphertext, $key);
+
+ #encrypt more chunks
+ $m->start_encrypt($key);
+ my $ciphertext = $m->add('some data');
+ $ciphertext .= $m->add('more data');
+ $ciphertext .= $m->finish;
+
+ #decrypt more chunks
+ $m->start_decrypt($key);
+ my $plaintext = $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+ $plaintext .= $m->finish;
+
+=head1 DESCRIPTION
+
+This module implements ECB cipher mode. B<NOTE:> it works only with ciphers from L<CryptX> (Crypt::Cipher::NNNN).
+B<BEWARE: ECB is inherently insecure>, if you are not sure go for L<Crypt::Mode::CBC>!
+
+=head1 METHODS
+
+=head2 new
+
+ my $m = Crypt::Mode::ECB->new('AES');
+ #or
+ my $m = Crypt::Mode::ECB->new('AES', $padding);
+ #or
+ my $m = Crypt::Mode::ECB->new('AES', $padding, $cipher_rounds);
+
+ # $padding .... 0 no padding (plaintext size has to be myltiple of block length)
+ # 1 PKCS5 padding, Crypt::CBC's "standard" - DEFAULT
+ # 2 Crypt::CBC's "oneandzeroes"
+ # $cipher_rounds ... optional num of rounds for given cipher
+
+=head2 encrypt
+
+ my $ciphertext = $m->encrypt($plaintext, $key);
+
+=head2 decrypt
+
+ my $plaintext = $m->decrypt($ciphertext, $key);
+
+=head2 start_encrypt
+
+See example below L</finish>.
+
+=head2 start_decrypt
+
+See example below L</finish>.
+
+=head2 add
+
+See example below L</finish>.
+
+=head2 finish
+
+ #encrypt more chunks
+ $m->start_encrypt($key);
+ my $ciphertext = '';
+ $ciphertext .= $m->add('some data');
+ $ciphertext .= $m->add('more data');
+ $ciphertext .= $m->finish;
+
+ #decrypt more chunks
+ $m->start_decrypt($key);
+ my $plaintext = '';
+ $plaintext .= $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+ $plaintext .= $m->finish;
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<Crypt::Cipher::AES|Crypt::Cipher::AES>, L<Crypt::Cipher::Blowfish|Crypt::Cipher::Blowfish>, ...
+
+=item * L<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29|https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29>
+
+=back
diff --git a/lib/Crypt/Mode/OFB.pm b/lib/Crypt/Mode/OFB.pm
new file mode 100644
index 00000000..efd888ed
--- /dev/null
+++ b/lib/Crypt/Mode/OFB.pm
@@ -0,0 +1,99 @@
+package Crypt::Mode::OFB;
+
+### BEWARE - GENERATED FILE, DO NOT EDIT MANUALLY!
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Crypt::Cipher;
+use base 'Crypt::Mode';
+
+sub new { my $class = shift; _new(Crypt::Cipher::_trans_cipher_name(shift), @_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Mode::OFB - Block cipher mode OFB [Output feedback]
+
+=head1 SYNOPSIS
+
+ use Crypt::Mode::OFB;
+ my $m = Crypt::Mode::OFB->new('AES');
+
+ #(en|de)crypt at once
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = $m->add('some data');
+ $ciphertext .= $m->add('more data');
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+
+=head1 DESCRIPTION
+
+This module implements OFB cipher mode. B<NOTE:> it works only with ciphers from L<CryptX> (Crypt::Cipher::NNNN).
+
+=head1 METHODS
+
+=head2 new
+
+ my $m = Crypt::Mode::OFB->new('AES');
+ #or
+ my $m = Crypt::Mode::OFB->new('AES', $cipher_rounds);
+
+ # $cipher_rounds ... optional num of rounds for given cipher
+
+=head2 encrypt
+
+ my $ciphertext = $m->encrypt($plaintext, $key, $iv);
+
+=head2 decrypt
+
+ my $plaintext = $m->decrypt($ciphertext, $key, $iv);
+
+=head2 start_encrypt
+
+See example below L</finish>.
+
+=head2 start_decrypt
+
+See example below L</finish>.
+
+=head2 add
+
+See example below L</finish>.
+
+=head2 finish
+
+ #encrypt more chunks
+ $m->start_encrypt($key, $iv);
+ my $ciphertext = '';
+ $ciphertext .= $m->add('some data');
+ $ciphertext .= $m->add('more data');
+
+ #decrypt more chunks
+ $m->start_decrypt($key, $iv);
+ my $plaintext = '';
+ $plaintext .= $m->add($some_ciphertext);
+ $plaintext .= $m->add($more_ciphertext);
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<CryptX|CryptX>, L<Crypt::Cipher|Crypt::Cipher>
+
+=item * L<Crypt::Cipher::AES|Crypt::Cipher::AES>, L<Crypt::Cipher::Blowfish|Crypt::Cipher::Blowfish>, ...
+
+=item * L<https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29|https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29>
+
+=back
diff --git a/lib/Crypt/PK.pm b/lib/Crypt/PK.pm
new file mode 100644
index 00000000..c240ab4c
--- /dev/null
+++ b/lib/Crypt/PK.pm
@@ -0,0 +1,33 @@
+package Crypt::PK;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use Carp;
+
+sub _ssh_parse {
+ my $raw = shift;
+ return unless defined $raw;
+ my $len = length($raw);
+ my @parts = ();
+ my $i = 0;
+ while (1) {
+ last unless $i + 4 <= $len;
+ my $part_len = unpack("N4", substr($raw, $i, 4));
+ last unless $i + 4 + $part_len <= $len;
+ push @parts, substr($raw, $i + 4, $part_len);
+ $i += $part_len + 4;
+ }
+ return @parts;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Crypt::PK - [internal only]
+
+=cut \ No newline at end of file
diff --git a/lib/Crypt/PK/DH.pm b/lib/Crypt/PK/DH.pm
new file mode 100644
index 00000000..445aea48
--- /dev/null
+++ b/lib/Crypt/PK/DH.pm
@@ -0,0 +1,643 @@
+package Crypt::PK::DH;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw( dh_encrypt dh_decrypt dh_sign_message dh_verify_message dh_sign_hash dh_verify_hash dh_shared_secret )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+use CryptX;
+use Crypt::Digest 'digest_data';
+use Crypt::Misc qw(read_rawfile);
+
+my %DH_PARAMS = (
+ ike768 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF'
+ },
+ ike1024 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381'.
+ 'FFFFFFFFFFFFFFFF'
+ },
+ ike1536 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D'.
+ 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F'.
+ '83655D23DCA3AD961C62F356208552BB9ED529077096966D'.
+ '670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF'
+ },
+ ike2048 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D'.
+ 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F'.
+ '83655D23DCA3AD961C62F356208552BB9ED529077096966D'.
+ '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'.
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9'.
+ 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510'.
+ '15728E5A8AACAA68FFFFFFFFFFFFFFFF'
+ },
+ ike3072 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D'.
+ 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F'.
+ '83655D23DCA3AD961C62F356208552BB9ED529077096966D'.
+ '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'.
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9'.
+ 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510'.
+ '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64'.
+ 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'.
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B'.
+ 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C'.
+ 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31'.
+ '43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF'
+ },
+ ike4096 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D'.
+ 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F'.
+ '83655D23DCA3AD961C62F356208552BB9ED529077096966D'.
+ '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'.
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9'.
+ 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510'.
+ '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64'.
+ 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'.
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B'.
+ 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C'.
+ 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31'.
+ '43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7'.
+ '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA'.
+ '2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6'.
+ '287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED'.
+ '1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9'.
+ '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199'.
+ 'FFFFFFFFFFFFFFFF'
+ },
+ ike6144 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D'.
+ 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F'.
+ '83655D23DCA3AD961C62F356208552BB9ED529077096966D'.
+ '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'.
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9'.
+ 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510'.
+ '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64'.
+ 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'.
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B'.
+ 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C'.
+ 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31'.
+ '43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7'.
+ '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA'.
+ '2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6'.
+ '287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED'.
+ '1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9'.
+ '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492'.
+ '36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD'.
+ 'F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831'.
+ '179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B'.
+ 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF'.
+ '5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6'.
+ 'D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3'.
+ '23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA'.
+ 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328'.
+ '06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C'.
+ 'DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE'.
+ '12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF'
+ },
+ ike8192 => { g => 2, p => 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1'.
+ '29024E088A67CC74020BBEA63B139B22514A08798E3404DD'.
+ 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245'.
+ 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED'.
+ 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D'.
+ 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F'.
+ '83655D23DCA3AD961C62F356208552BB9ED529077096966D'.
+ '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B'.
+ 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9'.
+ 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510'.
+ '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64'.
+ 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7'.
+ 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B'.
+ 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C'.
+ 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31'.
+ '43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7'.
+ '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA'.
+ '2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6'.
+ '287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED'.
+ '1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9'.
+ '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492'.
+ '36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD'.
+ 'F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831'.
+ '179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B'.
+ 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF'.
+ '5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6'.
+ 'D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3'.
+ '23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA'.
+ 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328'.
+ '06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C'.
+ 'DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE'.
+ '12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4'.
+ '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300'.
+ '741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568'.
+ '3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9'.
+ '22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B'.
+ '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A'.
+ '062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36'.
+ '4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1'.
+ 'B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92'.
+ '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47'.
+ '9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71'.
+ '60C980DD98EDD3DFFFFFFFFFFFFFFFFF'
+ }
+);
+
+sub new {
+ my ($class, $f) = @_;
+ my $self = _new();
+ $self->import_key($f) if $f;
+ return $self;
+}
+
+sub import_key {
+ my ($self, $key) = @_;
+ croak "FATAL: undefined key" unless $key;
+ my $data;
+ if (ref($key) eq 'SCALAR') {
+ $data = $$key;
+ }
+ elsif (-f $key) {
+ $data = read_rawfile($key);
+ }
+ else {
+ croak "FATAL: non-existing file '$key'";
+ }
+ croak "FATAL: invalid key format" unless $data;
+ return $self->_import($data);
+}
+
+sub import_key_raw {
+ my ($self, $raw_bytes, $type, $param) = @_;
+ my ($g, $p, $x, $y);
+
+ if (ref $param eq 'HASH') {
+ $g = $param->{g} or croak "FATAL: 'g' param not specified";
+ $p = $param->{p} or croak "FATAL: 'p' param not specified";
+ $g =~ s/^0x//;
+ $p =~ s/^0x//;
+ } elsif (my $dhparam = $DH_PARAMS{$param}) {
+ $g = $dhparam->{g};
+ $p = $dhparam->{p};
+ } else {
+ croak "FATAL: invalid parameter";
+ }
+
+ if ($type eq 'private') {
+ $type = 1;
+ } elsif ($type eq 'public') {
+ $type = 0;
+ } else {
+ croak "FATAL: invalid key type '$type'";
+ }
+ my $rv = $self->_import_raw($raw_bytes, $type, $g, $p);
+ croak "FATAL: invalid public key" unless $self->_is_pubkey_valid;
+ return $rv;
+}
+
+sub encrypt {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+ return $self->_encrypt($data, $hash_name);
+}
+
+sub decrypt {
+ my ($self, $data) = @_;
+ return $self->_decrypt($data);
+}
+
+sub sign_message {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_sign($data_hash);
+}
+
+sub verify_message {
+ my ($self, $sig, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_verify($sig, $data_hash);
+}
+
+sub sign_hash {
+ my ($self, $data_hash) = @_;
+ return $self->_sign($data_hash);
+}
+
+sub verify_hash {
+ my ($self, $sig, $data_hash) = @_;
+ return $self->_verify($sig, $data_hash);
+}
+
+sub generate_key {
+ my ($key,$param) = @_;
+
+ if (!ref $param) {
+ if (my $dhparam = $DH_PARAMS{$param}) {
+ $param = $dhparam;
+ } else {
+ croak "FATAL: invalid key length" unless ($param >= 96 || $param <= 512);
+ return $key->_generate_key($param);
+ }
+ }
+ my $g = $param->{g} or croak "FATAL: 'g' param not specified";
+ my $p = $param->{p} or croak "FATAL: 'p' param not specified";
+ $g =~ s/^0x//;
+ $p =~ s/^0x//;
+ return $key->_generate_key_ex($g, $p);
+}
+
+### FUNCTIONS
+
+sub dh_encrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->encrypt(@_);
+}
+
+sub dh_decrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->decrypt(@_);
+}
+
+sub dh_sign_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_message(@_);
+}
+
+sub dh_verify_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_message(@_);
+}
+
+sub dh_sign_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_hash(@_);
+}
+
+sub dh_verify_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_hash(@_);
+}
+
+sub dh_shared_secret {
+ my ($privkey, $pubkey) = @_;
+ $privkey = __PACKAGE__->new($privkey) unless ref $privkey;
+ $pubkey = __PACKAGE__->new($pubkey) unless ref $pubkey;
+ carp "FATAL: invalid 'privkey' param" unless ref($privkey) eq __PACKAGE__ && $privkey->is_private;
+ carp "FATAL: invalid 'pubkey' param" unless ref($pubkey) eq __PACKAGE__;
+ return $privkey->shared_secret($pubkey);
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::PK::DH - Public key cryptography based on Diffie-Hellman
+
+=head1 SYNOPSIS
+
+ ### OO interface
+
+ #Encryption: Alice
+ my $pub = Crypt::PK::DH->new('Bob_pub_dh1.key');
+ my $ct = $pub->encrypt("secret message");
+ #
+ #Encryption: Bob (received ciphertext $ct)
+ my $priv = Crypt::PK::DH->new('Bob_priv_dh1.key');
+ my $pt = $priv->decrypt($ct);
+
+ #Signature: Alice
+ my $priv = Crypt::PK::DH->new('Alice_priv_dh1.key');
+ my $sig = $priv->sign_message($message);
+ #
+ #Signature: Bob (received $message + $sig)
+ my $pub = Crypt::PK::DH->new('Alice_pub_dh1.key');
+ $pub->verify_message($sig, $message) or die "ERROR";
+
+ #Shared secret
+ my $priv = Crypt::PK::DH->new('Alice_priv_dh1.key');
+ my $pub = Crypt::PK::DH->new('Bob_pub_dh1.key');
+ my $shared_secret = $priv->shared_secret($pub);
+
+ #Key generation
+ my $pk = Crypt::PK::DH->new();
+ $pk->generate_key(128);
+ my $private = $pk->export_key('private');
+ my $public = $pk->export_key('public');
+
+ or
+
+ my $pk = Crypt::PK::DH->new();
+ $pk->generate_key('ike2048');
+ my $private = $pk->export_key('private');
+ my $public = $pk->export_key('public');
+
+ or
+
+ my $pk = Crypt::PK::DH->new();
+ $pk->generate_key({ p => $p, g => $g });
+ my $private = $pk->export_key('private');
+ my $public = $pk->export_key('public');
+
+ ### Functional interface
+
+ #Encryption: Alice
+ my $ct = dh_encrypt('Bob_pub_dh1.key', "secret message");
+ #Encryption: Bob (received ciphertext $ct)
+ my $pt = dh_decrypt('Bob_priv_dh1.key', $ct);
+
+ #Signature: Alice
+ my $sig = dh_sign_message('Alice_priv_dh1.key', $message);
+ #Signature: Bob (received $message + $sig)
+ dh_verify_message('Alice_pub_dh1.key', $sig, $message) or die "ERROR";
+
+ #Shared secret
+ my $shared_secret = dh_shared_secret('Alice_priv_dh1.key', 'Bob_pub_dh1.key');
+
+=head1 METHODS
+
+=head2 new
+
+ my $pk = Crypt::PK::DH->new();
+ #or
+ my $pk = Crypt::PK::DH->new($priv_or_pub_key_filename);
+ #or
+ my $pk = Crypt::PK::DH->new(\$buffer_containing_priv_or_pub_key);
+
+=head2 generate_key
+
+Uses Yarrow-based cryptographically strong random number generator seeded with
+random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32).
+
+ $pk->generate_key($keysize);
+ ### $keysize (in bytes) corresponds to DH params (p, g) predefined by libtomcrypt
+ # 96 => DH-768
+ # 128 => DH-1024
+ # 160 => DH-1280
+ # 192 => DH-1536
+ # 224 => DH-1792
+ # 256 => DH-2048
+ # 320 => DH-2560
+ # 384 => DH-3072
+ # 512 => DH-4096
+
+The following variants are available since CryptX-0.032
+
+ $pk->generate_key($name)
+ ### $name corresponds to values defined in RFC7296 and RFC3526
+ # ike768 => 768-bit MODP (Group 1)
+ # ike1024 => 1024-bit MODP (Group 2)
+ # ike1536 => 1536-bit MODP (Group 5)
+ # ike2048 => 2048-bit MODP (Group 14)
+ # ike3072 => 3072-bit MODP (Group 15)
+ # ike4096 => 4096-bit MODP (Group 16)
+ # ike6144 => 6144-bit MODP (Group 17)
+ # ike8192 => 8192-bit MODP (Group 18)
+
+ $pk->generate_key($param_hash)
+ ## $param_hash is { g => $g, p => $p }
+ ## where $g is the generator (base) in a hex string and $p is the prime in a hex string
+
+=head2 import_key
+
+Loads private or public key (exported by L</export_key>).
+
+ $pk->import_key($filename);
+ #or
+ $pk->import_key(\$buffer_containing_key);
+
+=head2 import_key_raw
+
+I<Since: CryptX-0.032>
+
+ $pk->import_key_raw($raw_bytes, $type, $params)
+ ### $raw_bytes is a binary string containing the key
+ ### $type is either 'private' or 'public'
+ ### $param is either a name ('ike2038') or hash containing the p,g values { g=>$g, p=>$p }
+ ### in hex strings
+
+=head2 export_key
+
+ my $private = $pk->export_key('private');
+ #or
+ my $public = $pk->export_key('public');
+
+=head2 export_key_raw
+
+I<Since: CryptX-0.032>
+
+ $raw_bytes = $dh->export_key_raw('public')
+ #or
+ $raw_bytes = $dh->export_key_raw('private')
+
+=head2 encrypt
+
+ my $pk = Crypt::PK::DH->new($pub_key_filename);
+ my $ct = $pk->encrypt($message);
+ #or
+ my $ct = $pk->encrypt($message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 decrypt
+
+ my $pk = Crypt::PK::DH->new($priv_key_filename);
+ my $pt = $pk->decrypt($ciphertext);
+
+=head2 sign_message
+
+ my $pk = Crypt::PK::DH->new($priv_key_filename);
+ my $signature = $priv->sign_message($message);
+ #or
+ my $signature = $priv->sign_message($message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 verify_message
+
+ my $pk = Crypt::PK::DH->new($pub_key_filename);
+ my $valid = $pub->verify_message($signature, $message)
+ #or
+ my $valid = $pub->verify_message($signature, $message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 sign_hash
+
+ my $pk = Crypt::PK::DH->new($priv_key_filename);
+ my $signature = $priv->sign_hash($message_hash);
+
+=head2 verify_hash
+
+ my $pk = Crypt::PK::DH->new($pub_key_filename);
+ my $valid = $pub->verify_hash($signature, $message_hash);
+
+=head2 shared_secret
+
+ # Alice having her priv key $pk and Bob's public key $pkb
+ my $pk = Crypt::PK::DH->new($priv_key_filename);
+ my $pkb = Crypt::PK::DH->new($pub_key_filename);
+ my $shared_secret = $pk->shared_secret($pkb);
+
+ # Bob having his priv key $pk and Alice's public key $pka
+ my $pk = Crypt::PK::DH->new($priv_key_filename);
+ my $pka = Crypt::PK::DH->new($pub_key_filename);
+ my $shared_secret = $pk->shared_secret($pka); # same value as computed by Alice
+
+=head2 is_private
+
+ my $rv = $pk->is_private;
+ # 1 .. private key loaded
+ # 0 .. public key loaded
+ # undef .. no key loaded
+
+=head2 size
+
+ my $size = $pk->size;
+ # returns key size in bytes or undef if no key loaded
+
+=head2 key2hash
+
+ my $hash = $pk->key2hash;
+
+ # returns hash like this (or undef if no key loaded):
+ {
+ type => 0, # integer: 1 .. private, 0 .. public
+ size => 256, # integer: key size in bytes
+ x => "FBC1062F73B9A17BB8473A2F5A074911FA7F20D28FB...", #private key
+ y => "AB9AAA40774D3CD476B52F82E7EE2D8A8D40CD88BF4...", #public key
+ g => "2", # generator/base
+ p => "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80D...", # prime
+}
+
+=head2 params2hash
+
+I<Since: CryptX-0.032>
+
+ my $params = $pk->params2hash;
+
+ # returns hash like this (or undef if no key loaded):
+ {
+ g => "2", # generator/base
+ p => "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80D...", # prime
+}
+
+=head1 FUNCTIONS
+
+=head2 dh_encrypt
+
+DH based encryption as implemented by libtomcrypt. See method L</encrypt> below.
+
+ my $ct = dh_encrypt($pub_key_filename, $message);
+ #or
+ my $ct = dh_encrypt(\$buffer_containing_pub_key, $message);
+ #or
+ my $ct = dh_encrypt($pub_key_filename, $message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+Encryption works similar to the L<Crypt::PK::ECC> encryption whereas shared DH key is computed, and
+the hash of the shared key XOR'ed against the plaintext forms the ciphertext.
+
+=head2 dh_decrypt
+
+DH based decryption as implemented by libtomcrypt. See method L</decrypt> below.
+
+ my $pt = dh_decrypt($priv_key_filename, $ciphertext);
+ #or
+ my $pt = dh_decrypt(\$buffer_containing_priv_key, $ciphertext);
+
+=head2 dh_sign_message
+
+Generate DH signature as implemented by libtomcrypt. See method L</sign_message> below.
+
+ my $sig = dh_sign_message($priv_key_filename, $message);
+ #or
+ my $sig = dh_sign_message(\$buffer_containing_priv_key, $message);
+ #or
+ my $sig = dh_sign_message($priv_key, $message, $hash_name);
+
+=head2 dh_verify_message
+
+Verify DH signature as implemented by libtomcrypt. See method L</verify_message> below.
+
+ dh_verify_message($pub_key_filename, $signature, $message) or die "ERROR";
+ #or
+ dh_verify_message(\$buffer_containing_pub_key, $signature, $message) or die "ERROR";
+ #or
+ dh_verify_message($pub_key, $signature, $message, $hash_name) or die "ERROR";
+
+=head2 dh_sign_hash
+
+Generate DH signature as implemented by libtomcrypt. See method L</sign_hash> below.
+
+ my $sig = dh_sign_hash($priv_key_filename, $message_hash);
+ #or
+ my $sig = dh_sign_hash(\$buffer_containing_priv_key, $message_hash);
+
+=head2 dh_verify_hash
+
+Verify DH signature as implemented by libtomcrypt. See method L</verify_hash> below.
+
+ dh_verify_hash($pub_key_filename, $signature, $message_hash) or die "ERROR";
+ #or
+ dh_verify_hash(\$buffer_containing_pub_key, $signature, $message_hash) or die "ERROR";
+
+=head2 dh_shared_secret
+
+DH based shared secret generation. See method L</shared_secret> below.
+
+ #on Alice side
+ my $shared_secret = dh_shared_secret('Alice_priv_dh1.key', 'Bob_pub_dh1.key');
+
+ #on Bob side
+ my $shared_secret = dh_shared_secret('Bob_priv_dh1.key', 'Alice_pub_dh1.key');
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange|https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange>
+
+=back
diff --git a/lib/Crypt/PK/DSA.pm b/lib/Crypt/PK/DSA.pm
new file mode 100644
index 00000000..79cbcdf4
--- /dev/null
+++ b/lib/Crypt/PK/DSA.pm
@@ -0,0 +1,617 @@
+package Crypt::PK::DSA;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw( dsa_encrypt dsa_decrypt dsa_sign_message dsa_verify_message dsa_sign_hash dsa_verify_hash )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+use CryptX qw(_encode_json _decode_json);
+use Crypt::Digest 'digest_data';
+use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem);
+use Crypt::PK;
+
+sub new {
+ my ($class, $f, $p) = @_;
+ my $self = _new();
+ $self->import_key($f, $p) if $f;
+ return $self;
+}
+
+sub export_key_pem {
+ my ($self, $type, $password, $cipher) = @_;
+ my $key = $self->export_key_der($type||'');
+ return unless $key;
+ return der_to_pem($key, "DSA PRIVATE KEY", $password, $cipher) if $type eq 'private';
+ return der_to_pem($key, "DSA PUBLIC KEY") if $type eq 'public';
+ return der_to_pem($key, "PUBLIC KEY") if $type eq 'public_x509';
+}
+
+sub import_key {
+ my ($self, $key, $password) = @_;
+ croak "FATAL: undefined key" unless $key;
+
+ # special case
+ if (ref($key) eq 'HASH') {
+ if ($key->{p} && $key->{q} && $key->{g} && $key->{y}) {
+ # hash exported via key2hash
+ return $self->_import_hex($key->{p}, $key->{q}, $key->{g}, $key->{x}, $key->{y});
+ }
+ }
+
+ my $data;
+ if (ref($key) eq 'SCALAR') {
+ $data = $$key;
+ }
+ elsif (-f $key) {
+ $data = read_rawfile($key);
+ }
+ else {
+ croak "FATAL: non-existing file '$key'";
+ }
+ croak "FATAL: invalid key data" unless $data;
+
+ if ($data =~ /-----BEGIN (DSA PRIVATE|DSA PUBLIC|PRIVATE|PUBLIC) KEY-----(.*?)-----END/sg) {
+ $data = pem_to_der($data, $password);
+ return $self->_import($data);
+ }
+ elsif ($data =~ /---- BEGIN SSH2 PUBLIC KEY ----(.*?)---- END SSH2 PUBLIC KEY ----/sg) {
+ $data = pem_to_der($data);
+ my ($typ, $p, $q, $g, $y) = Crypt::PK::_ssh_parse($data);
+ return $self->_import_hex(unpack('H*',$p), unpack('H*',$q), unpack('H*',$g), undef, unpack('H*',$y)) if $typ && $p && $q && $g && $y && $typ eq 'ssh-dss';
+ }
+ elsif ($data =~ /ssh-dss\s+(\S+)/) {
+ $data = decode_b64("$1");
+ my ($typ, $p, $q, $g, $y) = Crypt::PK::_ssh_parse($data);
+ return $self->_import_hex(unpack('H*',$p), unpack('H*',$q), unpack('H*',$g), undef, unpack('H*',$y)) if $typ && $p && $q && $g && $y && $typ eq 'ssh-dss';
+ }
+ else {
+ return $self->_import($data);
+ }
+ croak "FATAL: invalid or unsupported DSA key format";
+}
+
+sub encrypt {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+ return $self->_encrypt($data, $hash_name);
+}
+
+sub decrypt {
+ my ($self, $data) = @_;
+ return $self->_decrypt($data);
+}
+
+sub _truncate {
+ my ($self, $hash) = @_;
+ ### section 4.6 of FIPS 186-4
+ # let N be the bit length of q
+ # z = the leftmost min(N, outlen) bits of Hash(M).
+ my $q = $self->size_q; # = size in bytes
+ return $hash if $q >= length($hash);
+ return substr($hash, 0, $q);
+}
+
+sub sign_message {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_sign($self->_truncate($data_hash));
+}
+
+sub verify_message {
+ my ($self, $sig, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_verify($sig, $self->_truncate($data_hash));
+}
+
+sub sign_hash {
+ my ($self, $data_hash) = @_;
+ return $self->_sign($self->_truncate($data_hash));
+}
+
+sub verify_hash {
+ my ($self, $sig, $data_hash) = @_;
+ return $self->_verify($sig, $self->_truncate($data_hash));
+}
+
+### FUNCTIONS
+
+sub dsa_encrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->encrypt(@_);
+}
+
+sub dsa_decrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->decrypt(@_);
+}
+
+sub dsa_sign_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_message(@_);
+}
+
+sub dsa_verify_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_message(@_);
+}
+
+sub dsa_sign_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_hash(@_);
+}
+
+sub dsa_verify_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_hash(@_);
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::PK::DSA - Public key cryptography based on DSA
+
+=head1 SYNOPSIS
+
+ ### OO interface
+
+ #Encryption: Alice
+ my $pub = Crypt::PK::DSA->new('Bob_pub_dsa1.der');
+ my $ct = $pub->encrypt("secret message");
+ #
+ #Encryption: Bob (received ciphertext $ct)
+ my $priv = Crypt::PK::DSA->new('Bob_priv_dsa1.der');
+ my $pt = $priv->decrypt($ct);
+
+ #Signature: Alice
+ my $priv = Crypt::PK::DSA->new('Alice_priv_dsa1.der');
+ my $sig = $priv->sign_message($message);
+ #
+ #Signature: Bob (received $message + $sig)
+ my $pub = Crypt::PK::DSA->new('Alice_pub_dsa1.der');
+ $pub->verify_message($sig, $message) or die "ERROR";
+
+ #Key generation
+ my $pk = Crypt::PK::DSA->new();
+ $pk->generate_key(30, 256);
+ my $private_der = $pk->export_key_der('private');
+ my $public_der = $pk->export_key_der('public');
+ my $private_pem = $pk->export_key_pem('private');
+ my $public_pem = $pk->export_key_pem('public');
+
+ ### Functional interface
+
+ #Encryption: Alice
+ my $ct = dsa_encrypt('Bob_pub_dsa1.der', "secret message");
+ #Encryption: Bob (received ciphertext $ct)
+ my $pt = dsa_decrypt('Bob_priv_dsa1.der', $ct);
+
+ #Signature: Alice
+ my $sig = dsa_sign_message('Alice_priv_dsa1.der', $message);
+ #Signature: Bob (received $message + $sig)
+ dsa_verify_message('Alice_pub_dsa1.der', $sig, $message) or die "ERROR";
+
+=head1 METHODS
+
+=head2 new
+
+ my $pk = Crypt::PK::DSA->new();
+ #or
+ my $pk = Crypt::PK::DSA->new($priv_or_pub_key_filename);
+ #or
+ my $pk = Crypt::PK::DSA->new(\$buffer_containing_priv_or_pub_key);
+
+Support for password protected PEM keys
+
+ my $pk = Crypt::PK::DSA->new($priv_pem_key_filename, $password);
+ #or
+ my $pk = Crypt::PK::DSA->new(\$buffer_containing_priv_pem_key, $password);
+
+=head2 generate_key
+
+Uses Yarrow-based cryptographically strong random number generator seeded with
+random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32).
+
+ $pk->generate_key($group_size, $modulus_size);
+ # $group_size ... in bytes .. 15 < $group_size < 1024
+ # $modulus_size .. in bytes .. ($modulus_size - $group_size) < 512
+
+ ### Bits of Security according to libtomcrypt documentation
+ # 80 bits => generate_key(20, 128)
+ # 120 bits => generate_key(30, 256)
+ # 140 bits => generate_key(35, 384)
+ # 160 bits => generate_key(40, 512)
+
+ ### Sizes according section 4.2 of FIPS 186-4
+ # (L and N are the bit lengths of p and q respectively)
+ # L = 1024, N = 160 => generate_key(20, 128)
+ # L = 2048, N = 224 => generate_key(28, 256)
+ # L = 2048, N = 256 => generate_key(32, 256)
+ # L = 3072, N = 256 => generate_key(32, 384)
+
+=head2 import_key
+
+Loads private or public key in DER or PEM format.
+
+ $pk->import_key($filename);
+ #or
+ $pk->import_key(\$buffer_containing_key);
+
+Support for password protected PEM keys
+
+ $pk->import_key($pem_filename, $password);
+ #or
+ $pk->import_key(\$buffer_containing_pem_key, $password);
+
+Loading private or public keys form perl hash:
+
+ $pk->import_key($hashref);
+
+ # where $hashref is a key exported via key2hash
+ $pk->import_key({
+ p => "AAF839A764E04D80824B79FA1F0496C093...", #prime modulus
+ q => "D05C4CB45F29D353442F1FEC43A6BE2BE8...", #prime divisor
+ g => "847E8896D12C9BF18FE283AE7AD58ED7F3...", #generator of a subgroup of order q in GF(p)
+ x => "6C801901AC74E2DC714D75A9F6969483CF...", #private key, random 0 < x < q
+ y => "8F7604D77FA62C7539562458A63C7611B7...", #public key, where y = g^x mod p
+ });
+
+Supported key formats:
+
+=over
+
+=item * DSA public keys
+
+ -----BEGIN PUBLIC KEY-----
+ MIIBtjCCASsGByqGSM44BAEwggEeAoGBAJKyu+puNMGLpGIhbD1IatnwlI79ePr4
+ YHe2KBhRkheKxWUZRpN1Vd/+usS2IHSJ9op5cSWETiP05d7PMtJaitklw7jhudq3
+ GxNvV/GRdCQm3H6d76FHP88dms4vcDYc6ry6wKERGfNEtZ+4BAKrMZK+gDYsF4Aw
+ U6WVR969kYZhAhUA6w25FgSRmJ8W4XkvC60n8Wv3DpMCgYA4ZFE+3tLOM24PZj9Z
+ rxuqUzZZdR+kIzrsIYpWN9ustbmdKLKwsqIaUIxc5zxHEhbAjAIf8toPD+VEQIpY
+ 7vgJgDhXuPq45BgN19iLTzOJwIhAFXPZvnAdIo9D/AnMw688gT6g6U8QCZwX2XYg
+ ICiVcriYVNcjVKHSFY/X0Oi7CgOBhAACgYB4ZTn4OYT/pjUd6tNhGPtOS3CE1oaj
+ 5ScbetXg4ZDpceEyQi8VG+/ZTbs8var8X77JdEdeQA686cAxpOaVgW8V4odvcmfA
+ BfueiGnPXjqGfppiHAyL1Ngyd+EsXKmKVXZYAVFVI0WuJKiZBSVURU7+ByxOfpGa
+ fZhibr0SggWixQ==
+ -----END PUBLIC KEY-----
+
+=item * DSA private keys
+
+ -----BEGIN DSA PRIVATE KEY-----
+ MIIBuwIBAAKBgQCSsrvqbjTBi6RiIWw9SGrZ8JSO/Xj6+GB3tigYUZIXisVlGUaT
+ dVXf/rrEtiB0ifaKeXElhE4j9OXezzLSWorZJcO44bnatxsTb1fxkXQkJtx+ne+h
+ Rz/PHZrOL3A2HOq8usChERnzRLWfuAQCqzGSvoA2LBeAMFOllUfevZGGYQIVAOsN
+ uRYEkZifFuF5LwutJ/Fr9w6TAoGAOGRRPt7SzjNuD2Y/Wa8bqlM2WXUfpCM67CGK
+ VjfbrLW5nSiysLKiGlCMXOc8RxIWwIwCH/LaDw/lRECKWO74CYA4V7j6uOQYDdfY
+ i08zicCIQBVz2b5wHSKPQ/wJzMOvPIE+oOlPEAmcF9l2ICAolXK4mFTXI1Sh0hWP
+ 19DouwoCgYB4ZTn4OYT/pjUd6tNhGPtOS3CE1oaj5ScbetXg4ZDpceEyQi8VG+/Z
+ Tbs8var8X77JdEdeQA686cAxpOaVgW8V4odvcmfABfueiGnPXjqGfppiHAyL1Ngy
+ d+EsXKmKVXZYAVFVI0WuJKiZBSVURU7+ByxOfpGafZhibr0SggWixQIVAL7Sia03
+ 8bvANjjL9Sitk8slrM6P
+ -----END DSA PRIVATE KEY-----
+
+=item * DSA private keys in password protected PEM format:
+
+ -----BEGIN DSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: DES-CBC,227ADC3AA0299491
+
+ UISxBYAxPQMl2eK9LMAeHsssF6IxO+4G2ta2Jn8VE+boJrrH3iSTKeMXGjGaXl0z
+ DwcLGV+KMR70y+cxtTb34rFy+uSpBy10dOQJhxALDbe1XfCDQIUfaXRfMNA3um2I
+ JdZixUD/zcxBOUzao+MCr0V9XlJDgqBhJ5EEr53XHH07Eo5fhiBfbbR9NzdUPFrQ
+ p2ASyZtFh7RXoIBUCQgg21oeLddcNWV7gd/Y46kghO9s0JbJ8C+IsuWEPRSq502h
+ tSoDN6B0sxbVvOUICLLbQaxt7yduTAhRxVIJZ1PWATTVD7CZBVz9uIDZ7LOv+er2
+ 1q3vkwb8E9spPsA240+BnfD571XEop4jrawxC0VKQZ+3cPVLc6jhIsxvzzFQUt67
+ g66v8GUgt7KF3KhVV7qEtntybQWDWb+K/uTIH9Ra8nP820d3Rnl61pPXDPlluteT
+ WSLOvEMN2zRmkaxQNv/tLdT0SYpQtdjw74G3A6T7+KnvinKrjtp1a/AXkCF9hNEx
+ DGbxOYo1UOmk8qdxWCrab34nO+Q8oQc9wjXHG+ZtRYIMoGMKREK8DeL4H1RPNkMf
+ rwXWk8scd8QFmJAb8De1VQ==
+ -----END DSA PRIVATE KEY-----
+
+=item * SSH public DSA keys
+
+ ssh-dss AAAAB3NzaC1kc3MAAACBAKU8/avmk...4XOwuEssAVhmwA==
+
+=item * SSH public DSA keys (RFC-4716 format)
+
+ ---- BEGIN SSH2 PUBLIC KEY ----
+ Comment: "1024-bit DSA, converted from OpenSSH"
+ AAAAB3NzaC1kc3MAAACBAKU8/avmkFeGnSqwYG7dZnQlG+01QNaxu3F5v0NcL/SRUW7Idp
+ Uq8t14siK0mA6yjphLhOf5t8gugTEVBllP86ANSbFigH7WN3v6ydJWqm60pNhNHN//50cn
+ NtIsXbxeq3VtsI64pkH1OJqeZDHLmu73k4T0EKOzsylSfF/wtVBJAAAAFQChpubLHViwPB
+ +jSvUb8e4THS7PBQAAAIAJD1PMCiTCQa1xyD/NCWOajCufTOIzKAhm6l+nlBVPiKI+262X
+ pYt127Ke4mPL8XJBizoTjSQN08uHMg/8L6W/cdO2aZ+mhkBnS1xAm83DAwqLrDraR1w/4Q
+ RFxr5Vbyy8qnejrPjTJobBN1BGsv84wHkjmoCn6pFIfkGYeATlJgAAAIAHYPU1zMVBTDWr
+ u7SNC4G2UyWGWYYLjLytBVHfQmBa51CmqrSs2kCfGLGA1ynfYENsxcJq9nsXrb4i17H5BH
+ JFkH0g7BUDpeBeLr8gsK3WgfqWwtZsDkltObw9chUD/siK6q/dk/fSIB2Ho0inev7k68Z5
+ ZkNI4XOwuEssAVhmwA==
+ ---- END SSH2 PUBLIC KEY ----
+
+=back
+
+=head2 export_key_der
+
+ my $private_der = $pk->export_key_der('private');
+ #or
+ my $public_der = $pk->export_key_der('public');
+
+=head2 export_key_pem
+
+ my $private_pem = $pk->export_key_pem('private');
+ #or
+ my $public_pem = $pk->export_key_pem('public');
+ #or
+ my $public_pem = $pk->export_key_pem('public_x509');
+
+With parameter C<'public'> uses header and footer lines:
+
+ -----BEGIN DSA PUBLIC KEY------
+ -----END DSA PUBLIC KEY------
+
+With parameter C<'public_x509'> uses header and footer lines:
+
+ -----BEGIN PUBLIC KEY------
+ -----END PUBLIC KEY------
+
+Support for password protected PEM keys
+
+ my $private_pem = $pk->export_key_pem('private', $password);
+ #or
+ my $private_pem = $pk->export_key_pem('private', $password, $cipher);
+
+ # supported ciphers: 'DES-CBC'
+ # 'DES-EDE3-CBC'
+ # 'SEED-CBC'
+ # 'CAMELLIA-128-CBC'
+ # 'CAMELLIA-192-CBC'
+ # 'CAMELLIA-256-CBC'
+ # 'AES-128-CBC'
+ # 'AES-192-CBC'
+ # 'AES-256-CBC' (DEFAULT)
+
+=head2 encrypt
+
+ my $pk = Crypt::PK::DSA->new($pub_key_filename);
+ my $ct = $pk->encrypt($message);
+ #or
+ my $ct = $pk->encrypt($message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 decrypt
+
+ my $pk = Crypt::PK::DSA->new($priv_key_filename);
+ my $pt = $pk->decrypt($ciphertext);
+
+=head2 sign_message
+
+ my $pk = Crypt::PK::DSA->new($priv_key_filename);
+ my $signature = $priv->sign_message($message);
+ #or
+ my $signature = $priv->sign_message($message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 verify_message
+
+ my $pk = Crypt::PK::DSA->new($pub_key_filename);
+ my $valid = $pub->verify_message($signature, $message)
+ #or
+ my $valid = $pub->verify_message($signature, $message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 sign_hash
+
+ my $pk = Crypt::PK::DSA->new($priv_key_filename);
+ my $signature = $priv->sign_hash($message_hash);
+
+=head2 verify_hash
+
+ my $pk = Crypt::PK::DSA->new($pub_key_filename);
+ my $valid = $pub->verify_hash($signature, $message_hash);
+
+=head2 is_private
+
+ my $rv = $pk->is_private;
+ # 1 .. private key loaded
+ # 0 .. public key loaded
+ # undef .. no key loaded
+
+=head2 size
+
+ my $size = $pk->size;
+ # returns key size in bytes or undef if no key loaded
+
+=head2 key2hash
+
+ my $hash = $pk->key2hash;
+
+ # returns hash like this (or undef if no key loaded):
+ {
+ type => 1, # integer: 1 .. private, 0 .. public
+ size => 256, # integer: key size in bytes
+ # all the rest are hex strings
+ p => "AAF839A764E04D80824B79FA1F0496C093...", #prime modulus
+ q => "D05C4CB45F29D353442F1FEC43A6BE2BE8...", #prime divisor
+ g => "847E8896D12C9BF18FE283AE7AD58ED7F3...", #generator of a subgroup of order q in GF(p)
+ x => "6C801901AC74E2DC714D75A9F6969483CF...", #private key, random 0 < x < q
+ y => "8F7604D77FA62C7539562458A63C7611B7...", #public key, where y = g^x mod p
+ }
+
+=head1 FUNCTIONS
+
+=head2 dsa_encrypt
+
+DSA based encryption as implemented by libtomcrypt. See method L</encrypt> below.
+
+ my $ct = dsa_encrypt($pub_key_filename, $message);
+ #or
+ my $ct = dsa_encrypt(\$buffer_containing_pub_key, $message);
+ #or
+ my $ct = dsa_encrypt($pub_key_filename, $message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+Encryption works similar to the L<Crypt::PK::ECC> encryption whereas shared DSA key is computed, and
+the hash of the shared key XOR'ed against the plaintext forms the ciphertext.
+
+=head2 dsa_decrypt
+
+DSA based decryption as implemented by libtomcrypt. See method L</decrypt> below.
+
+ my $pt = dsa_decrypt($priv_key_filename, $ciphertext);
+ #or
+ my $pt = dsa_decrypt(\$buffer_containing_priv_key, $ciphertext);
+
+=head2 dsa_sign_message
+
+Generate DSA signature. See method L</sign_message> below.
+
+ my $sig = dsa_sign_message($priv_key_filename, $message);
+ #or
+ my $sig = dsa_sign_message(\$buffer_containing_priv_key, $message);
+ #or
+ my $sig = dsa_sign_message($priv_key, $message, $hash_name);
+
+=head2 dsa_verify_message
+
+Verify DSA signature. See method L</verify_message> below.
+
+ dsa_verify_message($pub_key_filename, $signature, $message) or die "ERROR";
+ #or
+ dsa_verify_message(\$buffer_containing_pub_key, $signature, $message) or die "ERROR";
+ #or
+ dsa_verify_message($pub_key, $signature, $message, $hash_name) or die "ERROR";
+
+=head2 dsa_sign_hash
+
+Generate DSA signature. See method L</sign_hash> below.
+
+ my $sig = dsa_sign_hash($priv_key_filename, $message_hash);
+ #or
+ my $sig = dsa_sign_hash(\$buffer_containing_priv_key, $message_hash);
+
+=head2 dsa_verify_hash
+
+Verify DSA signature. See method L</verify_hash> below.
+
+ dsa_verify_hash($pub_key_filename, $signature, $message_hash) or die "ERROR";
+ #or
+ dsa_verify_hash(\$buffer_containing_pub_key, $signature, $message_hash) or die "ERROR";
+
+=head1 OpenSSL interoperability
+
+ ### let's have:
+ # DSA private key in PEM format - dsakey.priv.pem
+ # DSA public key in PEM format - dsakey.pub.pem
+ # data file to be signed - input.data
+
+=head2 Sign by OpenSSL, verify by Crypt::PK::DSA
+
+Create signature (from commandline):
+
+ openssl dgst -sha1 -sign dsakey.priv.pem -out input.sha1-dsa.sig input.data
+
+Verify signature (Perl code):
+
+ use Crypt::PK::DSA;
+ use Crypt::Digest 'digest_file';
+ use File::Slurp 'read_file';
+
+ my $pkdsa = Crypt::PK::DSA->new("dsakey.pub.pem");
+ my $signature = read_file("input.sha1-dsa.sig", binmode=>':raw');
+ my $valid = $pkdsa->verify_hash($signature, digest_file("SHA1", "input.data"), "SHA1", "v1.5");
+ print $valid ? "SUCCESS" : "FAILURE";
+
+=head2 Sign by Crypt::PK::DSA, verify by OpenSSL
+
+Create signature (Perl code):
+
+ use Crypt::PK::DSA;
+ use Crypt::Digest 'digest_file';
+ use File::Slurp 'write_file';
+
+ my $pkdsa = Crypt::PK::DSA->new("dsakey.priv.pem");
+ my $signature = $pkdsa->sign_hash(digest_file("SHA1", "input.data"), "SHA1", "v1.5");
+ write_file("input.sha1-dsa.sig", {binmode=>':raw'}, $signature);
+
+Verify signature (from commandline):
+
+ openssl dgst -sha1 -verify dsakey.pub.pem -signature input.sha1-dsa.sig input.data
+
+=head2 Keys generated by Crypt::PK::DSA
+
+Generate keys (Perl code):
+
+ use Crypt::PK::DSA;
+ use File::Slurp 'write_file';
+
+ my $pkdsa = Crypt::PK::DSA->new;
+ $pkdsa->generate_key(20, 128);
+ write_file("dsakey.pub.der", {binmode=>':raw'}, $pkdsa->export_key_der('public'));
+ write_file("dsakey.priv.der", {binmode=>':raw'}, $pkdsa->export_key_der('private'));
+ write_file("dsakey.pub.pem", $pkdsa->export_key_pem('public_x509'));
+ write_file("dsakey.priv.pem", $pkdsa->export_key_pem('private'));
+ write_file("dsakey-passwd.priv.pem", $pkdsa->export_key_pem('private', 'secret'));
+
+Use keys by OpenSSL:
+
+ openssl dsa -in dsakey.priv.der -text -inform der
+ openssl dsa -in dsakey.priv.pem -text
+ openssl dsa -in dsakey-passwd.priv.pem -text -inform pem -passin pass:secret
+ openssl dsa -in dsakey.pub.der -pubin -text -inform der
+ openssl dsa -in dsakey.pub.pem -pubin -text
+
+=head2 Keys generated by OpenSSL
+
+Generate keys:
+
+ openssl dsaparam -genkey -out dsakey.priv.pem 1024
+ openssl dsa -in dsakey.priv.pem -out dsakey.priv.der -outform der
+ openssl dsa -in dsakey.priv.pem -out dsakey.pub.pem -pubout
+ openssl dsa -in dsakey.priv.pem -out dsakey.pub.der -outform der -pubout
+ openssl dsa -in dsakey.priv.pem -passout pass:secret -des3 -out dsakey-passwd.priv.pem
+
+Load keys (Perl code):
+
+ use Crypt::PK::DSA;
+ use File::Slurp 'write_file';
+
+ my $pkdsa = Crypt::PK::DSA->new;
+ $pkdsa->import_key("dsakey.pub.der");
+ $pkdsa->import_key("dsakey.priv.der");
+ $pkdsa->import_key("dsakey.pub.pem");
+ $pkdsa->import_key("dsakey.priv.pem");
+ $pkdsa->import_key("dsakey-passwd.priv.pem", "secret");
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<https://en.wikipedia.org/wiki/Digital_Signature_Algorithm|https://en.wikipedia.org/wiki/Digital_Signature_Algorithm>
+
+=back
diff --git a/lib/Crypt/PK/ECC.pm b/lib/Crypt/PK/ECC.pm
new file mode 100644
index 00000000..58f1d7a7
--- /dev/null
+++ b/lib/Crypt/PK/ECC.pm
@@ -0,0 +1,1398 @@
+package Crypt::PK::ECC;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw( ecc_encrypt ecc_decrypt ecc_sign_message ecc_verify_message ecc_sign_hash ecc_verify_hash ecc_shared_secret )] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+use CryptX qw(_encode_json _decode_json);
+use Crypt::Digest qw(digest_data digest_data_b64u);
+use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem);
+use Crypt::PK;
+
+our %curve = (
+ ### http://www.ecc-brainpool.org/download/Domain-parameters.pdf (v1.0 19.10.2005)
+ brainpoolp160r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.1',
+ prime => "E95E4A5F737059DC60DFC7AD95B3D8139515620F",
+ A => "340E7BE2A280EB74E2BE61BADA745D97E8F7C300",
+ B => "1E589A8595423412134FAA2DBDEC95C8D8675E58",
+ Gx => "BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3",
+ Gy => "1667CB477A1A8EC338F94741669C976316DA6321",
+ order => "E95E4A5F737059DC60DF5991D45029409E60FC09",
+ cofactor => 1,
+ },
+ brainpoolp192r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.3',
+ prime => "C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297",
+ A => "6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF",
+ B => "469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9",
+ Gx => "C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6",
+ Gy => "14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F",
+ order => "C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1",
+ cofactor => 1,
+ },
+ brainpoolp224r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.5',
+ prime => "D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF",
+ A => "68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43",
+ B => "2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B",
+ Gx => "0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D",
+ Gy => "58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD",
+ order => "D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F",
+ cofactor => 1,
+ },
+ brainpoolp256r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.7',
+ prime => "A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377",
+ A => "7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9",
+ B => "26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6",
+ Gx => "8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262",
+ Gy => "547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997",
+ order => "A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7",
+ cofactor => 1,
+ },
+ brainpoolp320r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.9',
+ prime => "D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27",
+ A => "3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4",
+ B => "520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6",
+ Gx => "43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611",
+ Gy => "14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1",
+ order => "D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311",
+ cofactor => 1,
+ },
+ brainpoolp384r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.11',
+ prime => "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
+ A => "7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
+ B => "04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
+ Gx => "1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
+ Gy => "8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
+ order => "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
+ cofactor => 1,
+ },
+ brainpoolp512r1 => {
+ oid => '1.3.36.3.3.2.8.1.1.13',
+ prime => "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
+ A => "7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
+ B => "3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
+ Gx => "81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
+ Gy => "7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
+ order => "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
+ cofactor => 1,
+ },
+ ### http://www.secg.org/collateral/sec2_final.pdf (September 20, 2000 - Version 1.0)
+ secp112r1 => {
+ oid => '1.3.132.0.6',
+ prime => "DB7C2ABF62E35E668076BEAD208B",
+ A => "DB7C2ABF62E35E668076BEAD2088",
+ B => "659EF8BA043916EEDE8911702B22",
+ Gx => "09487239995A5EE76B55F9C2F098",
+ Gy => "A89CE5AF8724C0A23E0E0FF77500",
+ order => "DB7C2ABF62E35E7628DFAC6561C5",
+ cofactor => 1,
+ },
+ secp112r2 => {
+ oid => '1.3.132.0.7',
+ prime => "DB7C2ABF62E35E668076BEAD208B",
+ A => "6127C24C05F38A0AAAF65C0EF02C",
+ B => "51DEF1815DB5ED74FCC34C85D709",
+ Gx => "4BA30AB5E892B4E1649DD0928643",
+ Gy => "ADCD46F5882E3747DEF36E956E97",
+ order => "36DF0AAFD8B8D7597CA10520D04B",
+ cofactor => 4,
+ },
+ secp128r1 => {
+ oid => '1.3.132.0.28',
+ prime => "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF",
+ A => "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC",
+ B => "E87579C11079F43DD824993C2CEE5ED3",
+ Gx => "161FF7528B899B2D0C28607CA52C5B86",
+ Gy => "CF5AC8395BAFEB13C02DA292DDED7A83",
+ order => "FFFFFFFE0000000075A30D1B9038A115",
+ cofactor => 1,
+ },
+ secp128r2 => {
+ oid => '1.3.132.0.29',
+ prime => "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF",
+ A => "D6031998D1B3BBFEBF59CC9BBFF9AEE1",
+ B => "5EEEFCA380D02919DC2C6558BB6D8A5D",
+ Gx => "7B6AA5D85E572983E6FB32A7CDEBC140",
+ Gy => "27B6916A894D3AEE7106FE805FC34B44",
+ order => "3FFFFFFF7FFFFFFFBE0024720613B5A3",
+ cofactor => 4,
+ },
+ secp160k1 => {
+ oid => '1.3.132.0.9',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73",
+ A => "0000000000000000000000000000000000000000",
+ B => "0000000000000000000000000000000000000007",
+ Gx => "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB",
+ Gy => "938CF935318FDCED6BC28286531733C3F03C4FEE",
+ order => "0100000000000000000001B8FA16DFAB9ACA16B6B3",
+ cofactor => 1,
+ },
+ secp160r1 => {
+ oid => '1.3.132.0.8',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+ A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC",
+ B => "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+ Gx => "4A96B5688EF573284664698968C38BB913CBFC82",
+ Gy => "23A628553168947D59DCC912042351377AC5FB32",
+ order => "0100000000000000000001F4C8F927AED3CA752257",
+ cofactor => 1,
+ },
+ secp160r2 => {
+ oid => '1.3.132.0.30',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73",
+ A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70",
+ B => "B4E134D3FB59EB8BAB57274904664D5AF50388BA",
+ Gx => "52DCB034293A117E1F4FF11B30F7199D3144CE6D",
+ Gy => "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E",
+ order => "0100000000000000000000351EE786A818F3A1A16B",
+ cofactor => 1,
+ },
+ secp192k1 => {
+ oid => '1.3.132.0.31',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37",
+ A => "000000000000000000000000000000000000000000000000",
+ B => "000000000000000000000000000000000000000000000003",
+ Gx => "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D",
+ Gy => "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D",
+ order => "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D",
+ cofactor => 1,
+ },
+ secp192r1 => { # == NIST P-192, X9.62 prime192v1
+ oid => '1.2.840.10045.3.1.1',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF",
+ A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC",
+ B => "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1",
+ Gx => "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012",
+ Gy => "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811",
+ order => "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831",
+ cofactor => 1,
+ },
+ secp224k1 => {
+ oid => '1.3.132.0.32',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D",
+ A => "00000000000000000000000000000000000000000000000000000000",
+ B => "00000000000000000000000000000000000000000000000000000005",
+ Gx => "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C",
+ Gy => "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5",
+ order => "010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7",
+ cofactor => 1,
+ },
+ secp224r1 => { # == NIST P-224
+ oid => '1.3.132.0.33',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001",
+ A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE",
+ B => "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4",
+ Gx => "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21",
+ Gy => "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",
+ order => "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D",
+ cofactor => 1,
+ },
+ secp256k1 => {
+ oid => '1.3.132.0.10',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
+ A => "0000000000000000000000000000000000000000000000000000000000000000",
+ B => "0000000000000000000000000000000000000000000000000000000000000007",
+ Gx => "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
+ Gy => "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",
+ order => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
+ cofactor => 1,
+ },
+ secp256r1 => { # == NIST P-256, X9.62 prime256v1
+ oid => '1.2.840.10045.3.1.7',
+ prime => "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
+ A => "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
+ B => "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
+ Gx => "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
+ Gy => "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
+ order => "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
+ cofactor => 1,
+ },
+ secp384r1 => { # == NIST P-384
+ oid => '1.3.132.0.34',
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
+ A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
+ B => "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
+ Gx => "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
+ Gy => "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
+ order => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
+ cofactor => 1,
+ },
+ secp521r1 => { # == NIST P-521
+ oid => '1.3.132.0.35',
+ prime => "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
+ A => "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
+ B => "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
+ Gx => "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
+ Gy => "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
+ order => "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
+ cofactor => 1
+ },
+ ### http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf (July 2013)
+ nistp192 => { # == secp192r1, X9.62 prime192v1
+ oid => '1.2.840.10045.3.1.1',
+ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
+ B => '64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1',
+ Gx => '188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012',
+ Gy => '07192B95FFC8DA78631011ED6B24CDD573F977A11E794811',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831',
+ cofactor => 1,
+ },
+ nistp224 => { # == secp224r1
+ oid => '1.3.132.0.33',
+ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE',
+ B => 'B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4',
+ Gx => 'B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21',
+ Gy => 'BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D',
+ cofactor => 1,
+ },
+ nistp256 => { # == secp256r1, X9.62 prime256v1
+ oid => '1.2.840.10045.3.1.7',
+ prime => 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC',
+ B => '5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B',
+ Gx => '6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296',
+ Gy => '4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5',
+ order => 'FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551',
+ cofactor => 1,
+ },
+ nistp384 => { # == secp384r1
+ oid => '1.3.132.0.34',
+ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC',
+ B => 'B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF',
+ Gx => 'AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7',
+ Gy => '3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973',
+ cofactor => 1,
+ },
+ nistp521 => { # == secp521r1
+ oid => '1.3.132.0.35',
+ prime => '1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
+ A => '1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC',
+ B => '051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00',
+ Gx => '0C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66',
+ Gy => '11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650',
+ order => '1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409',
+ cofactor => 1,
+ },
+ ### ANS X9.62 elliptic curves - http://www.flexiprovider.de/CurvesGfpX962.html
+ prime192v1 => { # == secp192r1, NIST P-192
+ oid => '1.2.840.10045.3.1.1',
+ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
+ B => '64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1',
+ Gx => '188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012',
+ Gy => '07192B95FFC8DA78631011ED6B24CDD573F977A11E794811',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831',
+ cofactor => 1,
+ },
+ prime192v2 => {
+ oid => '1.2.840.10045.3.1.2',
+ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
+ B => 'CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953',
+ Gx => 'EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A',
+ Gy => '6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31',
+ cofactor => 1
+ },
+ prime192v3 => {
+ oid => '1.2.840.10045.3.1.3',
+ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
+ B => '22123DC2395A05CAA7423DAECCC94760A7D462256BD56916',
+ Gx => '7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896',
+ Gy => '38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13',
+ cofactor => 1,
+ },
+ prime239v1 => {
+ oid => '1.2.840.10045.3.1.4',
+ prime => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF',
+ A => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC',
+ B => '6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A',
+ Gx => '0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF',
+ Gy => '7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE',
+ order => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B',
+ cofactor => 1,
+ },
+ prime239v2 => {
+ oid => '1.2.840.10045.3.1.5',
+ prime => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF',
+ A => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC',
+ B => '617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C',
+ Gx => '38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7',
+ Gy => '5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA',
+ order => '7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063',
+ cofactor => 1,
+ },
+ prime239v3 => {
+ oid => '1.2.840.10045.3.1.6',
+ prime => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF',
+ A => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC',
+ B => '255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E',
+ Gx => '6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A',
+ Gy => '1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3',
+ order => '7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551',
+ cofactor => 1,
+ },
+ prime256v1 => { # == secp256r1, NIST P-256
+ oid => '1.2.840.10045.3.1.7',
+ prime => 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC',
+ B => '5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B',
+ Gx => '6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296',
+ Gy => '4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5',
+ order => 'FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551',
+ cofactor => 1,
+ },
+);
+
+my %jwkcrv = (
+ 'P-192' => 'secp192r1',
+ 'P-224' => 'secp224r1',
+ 'P-256' => 'secp256r1',
+ 'P-384' => 'secp384r1',
+ 'P-521' => 'secp521r1',
+);
+
+sub _import_hex {
+ my ($self, $x, $y, $k, $crv) = @_;
+ my $p = $curve{$crv}{prime};
+ croak "FATAL: invalid or unknown curve" if !$p;
+ $p =~ s/^0+//;
+ my $hex_size = length($p) % 2 ? length($p) + 1 : length($p);
+ if ($k) {
+ $k =~ /^0+/;
+ croak "FATAL: too long private key (k)" if length($k) > $hex_size;
+ my $priv_hex = "0" x ($hex_size - length($k)) . $k;
+ return $self->import_key_raw(pack("H*", $priv_hex), $crv);
+ }
+ elsif ($x && $y) {
+ $x =~ /^0+/;
+ $y =~ /^0+/;
+ croak "FATAL: too long public key (x)" if length($x) > $hex_size;
+ croak "FATAL: too long public key (y)" if length($y) > $hex_size;
+ my $pub_hex = "04" . ("0" x ($hex_size - length($x))) . $x . ("0" x ($hex_size - length($y))) . $y;
+ return $self->import_key_raw(pack("H*", $pub_hex), $crv);
+ }
+}
+
+sub _curve_name_lookup {
+ my ($self, $key) = @_;
+
+ return $key->{curve_name} if $key->{curve_name} && exists $curve{$key->{curve_name}};
+
+ defined(my $A = $key->{curve_A}) or return;
+ defined(my $B = $key->{curve_B}) or return;
+ defined(my $Gx = $key->{curve_Gx}) or return;
+ defined(my $Gy = $key->{curve_Gy}) or return;
+ defined(my $order = $key->{curve_order}) or return;
+ defined(my $prime = $key->{curve_prime}) or return;
+ defined(my $cofactor = $key->{curve_cofactor}) or return;
+ $A =~ s/^0+//;
+ $B =~ s/^0+//;
+ $Gx =~ s/^0+//;
+ $Gy =~ s/^0+//;
+ $order =~ s/^0+//;
+ $prime =~ s/^0+//;
+
+ for my $k (sort keys %curve) {
+ (my $c_A = $curve{$k}{A} ) =~ s/^0+//;
+ (my $c_B = $curve{$k}{B} ) =~ s/^0+//;
+ (my $c_Gx = $curve{$k}{Gx} ) =~ s/^0+//;
+ (my $c_Gy = $curve{$k}{Gy} ) =~ s/^0+//;
+ (my $c_order = $curve{$k}{order} ) =~ s/^0+//;
+ (my $c_prime = $curve{$k}{prime} ) =~ s/^0+//;
+ my $c_cofactor = $curve{$k}{cofactor};
+ return $k if $A eq $c_A && $B eq $c_B && $Gx eq $c_Gx && $Gy eq $c_Gy &&
+ $order eq $c_order && $prime eq $c_prime && $cofactor == $c_cofactor;
+ }
+}
+
+sub new {
+ my ($class, $f, $p) = @_;
+ my $self = _new();
+ $self->import_key($f, $p) if $f;
+ return $self;
+}
+
+sub export_key_pem {
+ my ($self, $type, $password, $cipher) = @_;
+ my $key = $self->export_key_der($type||'');
+ return unless $key;
+ return der_to_pem($key, "EC PRIVATE KEY", $password, $cipher) if substr($type, 0, 7) eq 'private';
+ return der_to_pem($key, "PUBLIC KEY") if substr($type,0, 6) eq 'public';
+}
+
+sub export_key_jwk {
+ my ($self, $type, $wanthash) = @_;
+ my $kh = $self->key2hash;
+ my $curve = $self->_curve_name_lookup($kh);
+ $curve = 'P-192' if $curve =~ /(secp192r1|nistp192|prime192v1)/;
+ $curve = 'P-224' if $curve =~ /(secp224r1|nistp224)/;
+ $curve = 'P-256' if $curve =~ /(secp256r1|nistp256|prime256v1)/;
+ $curve = 'P-384' if $curve =~ /(secp384r1|nistp384)/;
+ $curve = 'P-521' if $curve =~ /(secp521r1|nistp521)/;
+ if ($type && $type eq 'private') {
+ return unless $kh->{pub_x} && $kh->{pub_y} && $kh->{k};
+ for (qw/pub_x pub_y k/) {
+ $kh->{$_} = "0$kh->{$_}" if length($kh->{$_}) % 2;
+ }
+ # NOTE: x + y are not necessary in privkey
+ # but they are used in https://tools.ietf.org/html/rfc7517#appendix-A.2
+ my $hash = {
+ kty => "EC", crv=>$curve,
+ x => encode_b64u(pack("H*", $kh->{pub_x})),
+ y => encode_b64u(pack("H*", $kh->{pub_y})),
+ d => encode_b64u(pack("H*", $kh->{k})),
+ };
+ return $wanthash ? $hash : _encode_json($hash);
+ }
+ elsif ($type && $type eq 'public') {
+ return unless $kh->{pub_x} && $kh->{pub_y};
+ for (qw/pub_x pub_y/) {
+ $kh->{$_} = "0$kh->{$_}" if length($kh->{$_}) % 2;
+ }
+ my $hash = {
+ kty => "EC", crv=>$curve,
+ x => encode_b64u(pack("H*", $kh->{pub_x})),
+ y => encode_b64u(pack("H*", $kh->{pub_y})),
+ };
+ return $wanthash ? $hash : _encode_json($hash);
+ }
+}
+
+sub export_key_jwk_thumbprint {
+ my ($self, $hash_name) = @_;
+ $hash_name ||= 'SHA256';
+ my $h = $self->export_key_jwk('public', 1);
+ my $json = _encode_json({crv=>$h->{crv}, kty=>$h->{kty}, x=>$h->{x}, y=>$h->{y}});
+ return digest_data_b64u($hash_name, $json);
+}
+
+sub import_key {
+ my ($self, $key, $password) = @_;
+ croak "FATAL: undefined key" unless $key;
+
+ # special case
+ if (ref($key) eq 'HASH') {
+ if (($key->{pub_x} && $key->{pub_y}) || $key->{k}) {
+ # hash exported via key2hash
+ my $curve = $self->_curve_name_lookup($key);
+ croak "FATAL: invalid or unknown curve" if !$curve;
+ return $self->_import_hex($key->{pub_x}, $key->{pub_y}, $key->{k}, $curve);
+ }
+ if ($key->{crv} && $key->{kty} && $key->{kty} eq "EC" && ($key->{d} || ($key->{x} && $key->{y}))) {
+ # hash with items corresponding to JSON Web Key (JWK)
+ $key = {%$key}; # make a copy as we will modify it
+ for (qw/x y d/) {
+ $key->{$_} = eval { unpack("H*", decode_b64u($key->{$_})) } if exists $key->{$_};
+ }
+ if (my $curve = $jwkcrv{$key->{crv}}) {
+ return $self->_import_hex($key->{x}, $key->{y}, $key->{d}, $curve);
+ }
+ # curve is not JWK compliant e.g. P-192 P-224 P-256 P-384 P-521 (we'll try to import anyway)
+ return $self->_import_hex($key->{x}, $key->{y}, $key->{d}, lc($key->{crv}));
+ }
+ croak "FATAL: unexpected ECC key hash";
+ }
+
+ my $data;
+ if (ref($key) eq 'SCALAR') {
+ $data = $$key;
+ }
+ elsif (-f $key) {
+ $data = read_rawfile($key);
+ }
+ else {
+ croak "FATAL: non-existing file '$key'";
+ }
+ croak "FATAL: invalid key data" unless $data;
+
+ if ($data =~ /-----BEGIN (EC PRIVATE|EC PUBLIC|PUBLIC) KEY-----(.*?)-----END/sg) {
+ $data = pem_to_der($data, $password);
+ return $self->_import($data);
+ }
+ elsif ($data =~ /-----BEGIN PRIVATE KEY-----(.*?)-----END/sg) {
+ $data = pem_to_der($data, $password);
+ return $self->_import_pkcs8($data);
+ }
+ elsif ($data =~ /-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END/sg) {
+ # XXX-TODO: pkcs#8 encrypted private key
+ croak "FATAL: encrypted pkcs8 EC private keys are not supported";
+ }
+ elsif ($data =~ /^\s*(\{.*?\})\s*$/s) {
+ # JSON Web Key (JWK) - http://tools.ietf.org/html/draft-ietf-jose-json-web-key
+ my $json = "$1";
+ my $h = _decode_json($json);
+ if ($h && $h->{kty} eq "EC") {
+ for (qw/x y d/) {
+ $h->{$_} = eval { unpack("H*", decode_b64u($h->{$_})) } if exists $h->{$_};
+ }
+ if (my $curve = $jwkcrv{$h->{crv}}) {
+ return $self->_import_hex($h->{x}, $h->{y}, $h->{d}, $curve);
+ }
+ # curve is not JWK compliant e.g. P-192 P-224 P-256 P-384 P-521 (we'll try to import anyway)
+ return $self->_import_hex($h->{x}, $h->{y}, $h->{d}, lc($h->{crv}));
+ }
+ }
+ elsif ($data =~ /---- BEGIN SSH2 PUBLIC KEY ----(.*?)---- END SSH2 PUBLIC KEY ----/sg) {
+ $data = pem_to_der($data);
+ my ($typ, $skip, $pubkey) = Crypt::PK::_ssh_parse($data);
+ return $self->import_key_raw($pubkey, "$2") if $pubkey && $typ =~ /^ecdsa-(.+?)-(.*)$/;
+ }
+ elsif ($data =~ /(ecdsa-\S+)\s+(\S+)/) {
+ $data = decode_b64("$2");
+ my ($typ, $skip, $pubkey) = Crypt::PK::_ssh_parse($data);
+ return $self->import_key_raw($pubkey, "$2") if $pubkey && $typ =~ /^ecdsa-(.+?)-(.*)$/;
+ }
+ else {
+ my $rv = eval { $self->_import($data) } || eval { $self->_import_pkcs8($data) };
+ return $rv if $rv;
+ }
+ croak "FATAL: invalid or unsupported EC key format";
+}
+
+sub encrypt {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+ return $self->_encrypt($data, $hash_name);
+}
+
+sub decrypt {
+ my ($self, $data) = @_;
+ return $self->_decrypt($data);
+}
+
+sub sign_message {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_sign($data_hash);
+}
+
+sub sign_message_rfc7518 {
+ my ($self, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_sign_rfc7518($data_hash);
+}
+
+sub verify_message {
+ my ($self, $sig, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_verify($sig, $data_hash);
+}
+
+sub verify_message_rfc7518 {
+ my ($self, $sig, $data, $hash_name) = @_;
+ $hash_name ||= 'SHA1';
+ my $data_hash = digest_data($hash_name, $data);
+ return $self->_verify_rfc7518($sig, $data_hash);
+}
+
+sub sign_hash {
+ my ($self, $data_hash) = @_;
+ return $self->_sign($data_hash);
+}
+
+sub verify_hash {
+ my ($self, $sig, $data_hash) = @_;
+ return $self->_verify($sig, $data_hash);
+}
+
+sub curve2hash {
+ my $self = shift;
+ my $kh = $self->key2hash;
+ return {
+ prime => $kh->{curve_prime},
+ A => $kh->{curve_A},
+ B => $kh->{curve_B},
+ Gx => $kh->{curve_Gx},
+ Gy => $kh->{curve_Gy},
+ cofactor => $kh->{curve_cofactor},
+ order => $kh->{curve_order}
+ };
+}
+
+### FUNCTIONS
+
+sub ecc_encrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->encrypt(@_);
+}
+
+sub ecc_decrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->decrypt(@_);
+}
+
+sub ecc_sign_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_message(@_);
+}
+
+sub ecc_verify_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_message(@_);
+}
+
+sub ecc_sign_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_hash(@_);
+}
+
+sub ecc_verify_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_hash(@_);
+}
+
+sub ecc_shared_secret {
+ my ($privkey, $pubkey) = @_;
+ $privkey = __PACKAGE__->new($privkey) unless ref $privkey;
+ $pubkey = __PACKAGE__->new($pubkey) unless ref $pubkey;
+ carp "FATAL: invalid 'privkey' param" unless ref($privkey) eq __PACKAGE__ && $privkey->is_private;
+ carp "FATAL: invalid 'pubkey' param" unless ref($pubkey) eq __PACKAGE__;
+ return $privkey->shared_secret($pubkey);
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::PK::ECC - Public key cryptography based on EC
+
+=head1 SYNOPSIS
+
+ ### OO interface
+
+ #Encryption: Alice
+ my $pub = Crypt::PK::ECC->new('Bob_pub_ecc1.der');
+ my $ct = $pub->encrypt("secret message");
+ #
+ #Encryption: Bob (received ciphertext $ct)
+ my $priv = Crypt::PK::ECC->new('Bob_priv_ecc1.der');
+ my $pt = $priv->decrypt($ct);
+
+ #Signature: Alice
+ my $priv = Crypt::PK::ECC->new('Alice_priv_ecc1.der');
+ my $sig = $priv->sign_message($message);
+ #
+ #Signature: Bob (received $message + $sig)
+ my $pub = Crypt::PK::ECC->new('Alice_pub_ecc1.der');
+ $pub->verify_message($sig, $message) or die "ERROR";
+
+ #Shared secret
+ my $priv = Crypt::PK::ECC->new('Alice_priv_ecc1.der');
+ my $pub = Crypt::PK::ECC->new('Bob_pub_ecc1.der');
+ my $shared_secret = $priv->shared_secret($pub);
+
+ #Key generation
+ my $pk = Crypt::PK::ECC->new();
+ $pk->generate_key('secp160r1');
+ my $private_der = $pk->export_key_der('private');
+ my $public_der = $pk->export_key_der('public');
+ my $private_pem = $pk->export_key_pem('private');
+ my $public_pem = $pk->export_key_pem('public');
+ my $public_raw = $pk->export_key_raw('public');
+
+ ### Functional interface
+
+ #Encryption: Alice
+ my $ct = ecc_encrypt('Bob_pub_ecc1.der', "secret message");
+ #Encryption: Bob (received ciphertext $ct)
+ my $pt = ecc_decrypt('Bob_priv_ecc1.der', $ct);
+
+ #Signature: Alice
+ my $sig = ecc_sign_message('Alice_priv_ecc1.der', $message);
+ #Signature: Bob (received $message + $sig)
+ ecc_verify_message('Alice_pub_ecc1.der', $sig, $message) or die "ERROR";
+
+ #Shared secret
+ my $shared_secret = ecc_shared_secret('Alice_priv_ecc1.der', 'Bob_pub_ecc1.der');
+
+=head1 DESCRIPTION
+
+The module provides a set of core ECC functions as well as implementation of ECDSA and ECDH.
+
+Supports elliptic curves C<y^2 = x^3 + a*x + b> over prime fields C<Fp = Z/pZ> (binary fields not supported).
+
+=head1 METHODS
+
+=head2 new
+
+ my $pk = Crypt::PK::ECC->new();
+ #or
+ my $pk = Crypt::PK::ECC->new($priv_or_pub_key_filename);
+ #or
+ my $pk = Crypt::PK::ECC->new(\$buffer_containing_priv_or_pub_key);
+
+Support for password protected PEM keys
+
+ my $pk = Crypt::PK::ECC->new($priv_pem_key_filename, $password);
+ #or
+ my $pk = Crypt::PK::ECC->new(\$buffer_containing_priv_pem_key, $password);
+
+=head2 generate_key
+
+Uses Yarrow-based cryptographically strong random number generator seeded with
+random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32).
+
+ $pk->generate_key($curve_name);
+ #or
+ $pk->generate_key($hashref_with_curve_params);
+
+The following pre-defined C<$curve_name> values are supported:
+
+ # curves from http://www.ecc-brainpool.org/download/Domain-parameters.pdf
+ 'brainpoolp160r1'
+ 'brainpoolp192r1'
+ 'brainpoolp224r1'
+ 'brainpoolp256r1'
+ 'brainpoolp320r1'
+ 'brainpoolp384r1'
+ 'brainpoolp512r1'
+ # curves from http://www.secg.org/collateral/sec2_final.pdf
+ 'secp112r1'
+ 'secp112r2'
+ 'secp128r1'
+ 'secp128r2'
+ 'secp160k1'
+ 'secp160r1'
+ 'secp160r2'
+ 'secp192k1'
+ 'secp192r1' ... same as nistp192, prime192v1
+ 'secp224k1'
+ 'secp224r1' ... same as nistp224
+ 'secp256k1' ... used by Bitcoin
+ 'secp256r1' ... same as nistp256, prime256v1
+ 'secp384r1' ... same as nistp384
+ 'secp521r1' ... same as nistp521
+ #curves from http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
+ 'nistp192' ... same as secp192r1, prime192v1
+ 'nistp224' ... same as secp224r1
+ 'nistp256' ... same as secp256r1, prime256v1
+ 'nistp384' ... same as secp384r1
+ 'nistp521' ... same as secp521r1
+ # curves from ANS X9.62
+ 'prime192v1' ... same as nistp192, secp192r1
+ 'prime192v2'
+ 'prime192v3'
+ 'prime239v1'
+ 'prime239v2'
+ 'prime239v3'
+ 'prime256v1' ... same as nistp256, secp256r1
+
+Using custom curve parameters:
+
+ $pk->generate_key({ prime => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF',
+ A => 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC',
+ B => '22123DC2395A05CAA7423DAECCC94760A7D462256BD56916',
+ Gx => '7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896',
+ Gy => '38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0',
+ order => 'FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13',
+ cofactor => 1 });
+
+See L<http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf>, L<http://www.secg.org/collateral/sec2_final.pdf>, L<http://www.ecc-brainpool.org/download/Domain-parameters.pdf>
+
+=head2 import_key
+
+Loads private or public key in DER or PEM format.
+
+ $pk->import_key($filename);
+ #or
+ $pk->import_key(\$buffer_containing_key);
+
+Support for password protected PEM keys:
+
+ $pk->import_key($filename, $password);
+ #or
+ $pk->import_key(\$buffer_containing_key, $password);
+
+Loading private or public keys form perl hash:
+
+ $pk->import_key($hashref);
+
+ # the $hashref is either a key exported via key2hash
+ $pk->import_key({
+ curve_A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC",
+ curve_B => "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+ curve_bits => 160,
+ curve_bytes => 20,
+ curve_cofactor => 1,
+ curve_Gx => "4A96B5688EF573284664698968C38BB913CBFC82",
+ curve_Gy => "23A628553168947D59DCC912042351377AC5FB32",
+ curve_order => "0100000000000000000001F4C8F927AED3CA752257",
+ curve_prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+ k => "B0EE84A749FE95DF997E33B8F333E12101E824C3",
+ pub_x => "5AE1ACE3ED0AEA9707CE5C0BCE014F6A2F15023A",
+ pub_y => "895D57E992D0A15F88D6680B27B701F615FCDC0F",
+ });
+
+ # or with the curve defined just by name
+ $pk->import_key({
+ curve_name => "secp160r1",
+ k => "B0EE84A749FE95DF997E33B8F333E12101E824C3",
+ pub_x => "5AE1ACE3ED0AEA9707CE5C0BCE014F6A2F15023A",
+ pub_y => "895D57E992D0A15F88D6680B27B701F615FCDC0F",
+ });
+
+ # or a hash with items corresponding to JWK (JSON Web Key)
+ $pk->import_key({
+ kty => "EC",
+ crv => "P-256",
+ x => "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
+ y => "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
+ d => "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
+ });
+
+Supported key formats:
+
+ # all formats can be loaded from a file
+ my $pk = Crypt::PK::ECC->new($filename);
+
+ # or from a buffer containing the key
+ my $pk = Crypt::PK::ECC->new(\$buffer_with_key);
+
+=over
+
+=item * EC private keys with with all curve parameters
+
+ -----BEGIN EC PRIVATE KEY-----
+ MIIB+gIBAQQwCKEAcA6cIt6CGfyLKm57LyXWv2PgTjydrHSbvhDJTOl+7bzUW8DS
+ rgSdtSPONPq1oIIBWzCCAVcCAQEwPAYHKoZIzj0BAQIxAP//////////////////
+ ///////////////////////+/////wAAAAAAAAAA/////zB7BDD/////////////
+ /////////////////////////////v////8AAAAAAAAAAP////wEMLMxL6fiPufk
+ mI4Fa+P4LRkYHZxu/oFBEgMUCI9QE4daxlY5jYou0Z0qhcjt0+wq7wMVAKM1kmqj
+ GaJ6HQCJamdzpIJ6zaxzBGEEqofKIr6LBTeOscce8yCtdG4dO2KLp5uYWfdB4IJU
+ KjhVAvJdv1UpbDpUXjhydgq3NhfeSpYmLG9dnpi/kpLcKfj0Hb0omhR86doxE7Xw
+ uMAKYLHOHX6BnXpDHXyQ6g5fAjEA////////////////////////////////x2NN
+ gfQ3Ld9YGg2ySLCneuzsGWrMxSlzAgEBoWQDYgAEeGyHPLmHcszPQ9MIIYnznpzi
+ QbvuJtYSjCqtIGxDfzgcLcc3nCc5tBxo+qX6OJEzcWdDAC0bwplY+9Z9jHR3ylNy
+ ovlHoK4ItdWkVO8NH89SLSRyVuOF8N5t3CHIo93B
+ -----END EC PRIVATE KEY-----
+
+=item * EC private keys with curve defined by OID (short form)
+
+ -----BEGIN EC PRIVATE KEY-----
+ MHcCAQEEIBG1c3z52T8XwMsahGVdOZWgKCQJfv+l7djuJjgetdbDoAoGCCqGSM49
+ AwEHoUQDQgAEoBUyo8CQAFPeYPvv78ylh5MwFZjTCLQeb042TjiMJxG+9DLFmRSM
+ lBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ==
+ -----END EC PRIVATE KEY-----
+
+=item * EC private keys in password protected PEM format
+
+ -----BEGIN EC PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: AES-128-CBC,98245C830C9282F7937E13D1D5BA11EC
+
+ 0Y85oZ2+BKXYwrkBjsZdj6gnhOAfS5yDVmEsxFCDug+R3+Kw3QvyIfO4MVo9iWoA
+ D7wtoRfbt2OlBaLVl553+6QrUoa2DyKf8kLHQs1x1/J7tJOMM4SCXjlrOaToQ0dT
+ o7fOnjQjHne16pjgBVqGilY/I79Ab85AnE4uw7vgEucBEiU0d3nrhwuS2Opnhzyx
+ 009q9VLDPwY2+q7tXjTqnk9mCmQgsiaDJqY09wlauSukYPgVuOJFmi1VdkRSDKYZ
+ rUUsQvz6Q6Q+QirSlfHna+NhUgQ2eyhGszwcP6NU8iqIxI+NCwfFVuAzw539yYwS
+ 8SICczoC/YRlaclayXuomQ==
+ -----END EC PRIVATE KEY-----
+
+=item * EC public keys with all curve parameters
+
+ -----BEGIN PUBLIC KEY-----
+ MIH1MIGuBgcqhkjOPQIBMIGiAgEBMCwGByqGSM49AQECIQD/////////////////
+ ///////////////////+///8LzAGBAEABAEHBEEEeb5mfvncu6xVoGKVzocLBwKb
+ /NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIh
+ AP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABITjF/nKK3jg
+ pjmBRXKWAv7ekR1Ko/Nb5FFPHXjH0sDrpS7qRxFALwJHv7ylGnekgfKU3vzcewNs
+ lvjpBYt0Yg4=
+ -----END PUBLIC KEY-----
+
+=item * EC public keys with curve defined by OID (short form)
+
+ -----BEGIN PUBLIC KEY-----
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoBUyo8CQAFPeYPvv78ylh5MwFZjT
+ CLQeb042TjiMJxG+9DLFmRSMlBQ9T/RsLLc+PmpB1+7yPAR+oR5gZn3kJQ==
+ -----END PUBLIC KEY-----
+
+=item * PKCS#8 private keys with all curve parameters
+
+ -----BEGIN PRIVATE KEY-----
+ MIIBMAIBADCB0wYHKoZIzj0CATCBxwIBATAkBgcqhkjOPQEBAhkA////////////
+ /////////v//////////MEsEGP////////////////////7//////////AQYIhI9
+ wjlaBcqnQj2uzMlHYKfUYiVr1WkWAxUAxGloRDXes3jEtlypWR4qV2MFmi4EMQR9
+ KXeBAMZaHaF4NxZYjc4ri0rujiKPGJY4qQ8iY3M3M0tJ3LZqbcj5l4rKdkipQ7AC
+ GQD///////////////96YtAxyD9ClPZA7BMCAQEEVTBTAgEBBBiKolTGIsTgOCtl
+ 6dpdos0LvuaExCDFyT6hNAMyAAREwaCX0VY1LZxLW3G75tmft4p9uhc0J7/+NGaP
+ DN3Tr7SXkT9+co2a+8KPJhQy10k=
+ -----END PRIVATE KEY-----
+
+=item * PKCS#8 private keys with curve defined by OID (short form)
+
+ -----BEGIN PRIVATE KEY-----
+ MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQMEVTBTAgEBBBjFP/caeQV4WO3fnWWS
+ f917PGzwtypd/t+hNAMyAATSg6pBT7RO6l/p+aKcrFsGuthUdfwJWS5V3NGcVt1b
+ lEHQYjWya2YnHaPq/iMFa7A=
+ -----END PRIVATE KEY-----
+
+=item * PKCS#8 encrypted private keys ARE NOT SUPPORTED YET!
+
+ -----BEGIN ENCRYPTED PRIVATE KEY-----
+ MIGYMBwGCiqGSIb3DQEMAQMwDgQINApjTa6oFl0CAggABHi+59l4d4e6KtG9yci2
+ BSC65LEsQSnrnFAExfKptNU1zMFsDLCRvDeDQDbxc6HlfoxyqFL4SmH1g3RvC/Vv
+ NfckdL5O2L8MRnM+ljkFtV2Te4fszWcJFdd7KiNOkPpn+7sWLfzQdvhHChLKUzmz
+ 4INKZyMv/G7VpZ0=
+ -----END ENCRYPTED PRIVATE KEY-----
+
+=item * SSH public EC keys
+
+ ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNT...T3xYfJIs=
+
+=item * SSH public EC keys (RFC-4716 format)
+
+ ---- BEGIN SSH2 PUBLIC KEY ----
+ Comment: "521-bit ECDSA, converted from OpenSSH"
+ AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFk35srteP9twCwYK
+ vU9ovMBi77Dd6lEBPrFaMEb0CZdZ5MC3nSqflGHRWkSbUpjdPdO7cYQNpK9YXHbNSO5hbU
+ 1gFZgyiGFxwJYYz8NAjedBXMgyH4JWplK5FQm5P5cvaglItC9qkKioUXhCc67YMYBtivXl
+ Ue0PgIq6kbHTqbX6+5Nw==
+ ---- END SSH2 PUBLIC KEY ----
+
+=item * EC private keys in JSON Web Key (JWK) format
+
+See L<http://tools.ietf.org/html/draft-ietf-jose-json-web-key>
+
+ {
+ "kty":"EC",
+ "crv":"P-256",
+ "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
+ "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
+ "d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
+ }
+
+B<BEWARE:> For JWK support you need to have L<JSON::PP>, L<JSON::XS> or L<Cpanel::JSON::XS> module.
+
+=item * EC public keys in JSON Web Key (JWK) format
+
+ {
+ "kty":"EC",
+ "crv":"P-256",
+ "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
+ "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
+ }
+
+B<BEWARE:> For JWK support you need to have L<JSON::PP>, L<JSON::XS> or L<Cpanel::JSON::XS> module.
+
+=back
+
+=head2 import_key_raw
+
+Import raw public/private key - can load data exported by L</export_key_raw>.
+
+ $pk->import_key_raw($key, $curve);
+ # $key .... data exported by export_key_raw()
+ # $curve .. curve name or hashref with curve parameters - same as by generate_key()
+
+=head2 export_key_der
+
+ my $private_der = $pk->export_key_der('private');
+ #or
+ my $public_der = $pk->export_key_der('public');
+
+Since CryptX-0.36 C<export_key_der> can also export keys in a format
+that does not explicitely contain curve parameters but only curve OID.
+
+ my $private_der = $pk->export_key_der('private_short');
+ #or
+ my $public_der = $pk->export_key_der('public_short');
+
+=head2 export_key_pem
+
+ my $private_pem = $pk->export_key_pem('private');
+ #or
+ my $public_pem = $pk->export_key_pem('public');
+
+Since CryptX-0.36 C<export_key_pem> can also export keys in a format
+that does not explicitely contain curve parameters but only curve OID.
+
+ my $private_pem = $pk->export_key_pem('private_short');
+ #or
+ my $public_pem = $pk->export_key_pem('public_short');
+
+Support for password protected PEM keys
+
+ my $private_pem = $pk->export_key_pem('private', $password);
+ #or
+ my $private_pem = $pk->export_key_pem('private', $password, $cipher);
+
+ # supported ciphers: 'DES-CBC'
+ # 'DES-EDE3-CBC'
+ # 'SEED-CBC'
+ # 'CAMELLIA-128-CBC'
+ # 'CAMELLIA-192-CBC'
+ # 'CAMELLIA-256-CBC'
+ # 'AES-128-CBC'
+ # 'AES-192-CBC'
+ # 'AES-256-CBC' (DEFAULT)
+
+=head2 export_key_jwk
+
+I<Since: CryptX-0.022>
+
+Exports public/private keys as a JSON Web Key (JWK).
+
+ my $private_json_text = $pk->export_key_jwk('private');
+ #or
+ my $public_json_text = $pk->export_key_jwk('public');
+
+Also exports public/private keys as a perl HASH with JWK structure.
+
+ my $jwk_hash = $pk->export_key_jwk('private', 1);
+ #or
+ my $jwk_hash = $pk->export_key_jwk('public', 1);
+
+B<BEWARE:> For JWK support you need to have L<JSON::PP>, L<JSON::XS> or L<Cpanel::JSON::XS> module.
+
+=head2 export_key_jwk_thumbprint
+
+I<Since: CryptX-0.031>
+
+Exports the key's JSON Web Key Thumbprint as a string.
+
+If you don't know what this is, see RFC 7638 (C<https://tools.ietf.org/html/rfc7638>).
+
+ my $thumbprint = $pk->export_key_jwk_thumbprint('SHA256');
+
+=head2 export_key_raw
+
+Export raw public/private key. Public key is exported in ANS X9.63 format (compressed or uncompressed),
+private key is exported as raw bytes (padded with leading zeros to have the same size as the ECC curve).
+
+ my $pubkey_octets = $pk->export_key_raw('public');
+ #or
+ my $pubckey_octets = $pk->export_key_raw('public_compressed');
+ #or
+ my $privkey_octets = $pk->export_key_raw('private');
+
+=head2 encrypt
+
+ my $pk = Crypt::PK::ECC->new($pub_key_filename);
+ my $ct = $pk->encrypt($message);
+ #or
+ my $ct = $pk->encrypt($message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 decrypt
+
+ my $pk = Crypt::PK::ECC->new($priv_key_filename);
+ my $pt = $pk->decrypt($ciphertext);
+
+=head2 sign_message
+
+ my $pk = Crypt::PK::ECC->new($priv_key_filename);
+ my $signature = $priv->sign_message($message);
+ #or
+ my $signature = $priv->sign_message($message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 sign_message_rfc7518
+
+I<Since: CryptX-0.024>
+
+Same as L<sign_message|/sign_message> only the signature format is as defined by L<https://tools.ietf.org/html/rfc7518>
+(JWA - JSON Web Algorithms).
+
+=head2 verify_message
+
+ my $pk = Crypt::PK::ECC->new($pub_key_filename);
+ my $valid = $pub->verify_message($signature, $message)
+ #or
+ my $valid = $pub->verify_message($signature, $message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+=head2 verify_message_rfc7518
+
+I<Since: CryptX-0.024>
+
+Same as L<verify_message|/verify_message> only the signature format is as defined by L<https://tools.ietf.org/html/rfc7518>
+(JWA - JSON Web Algorithms).
+
+=head2 sign_hash
+
+ my $pk = Crypt::PK::ECC->new($priv_key_filename);
+ my $signature = $priv->sign_hash($message_hash);
+
+=head2 verify_hash
+
+ my $pk = Crypt::PK::ECC->new($pub_key_filename);
+ my $valid = $pub->verify_hash($signature, $message_hash);
+
+=head2 shared_secret
+
+ # Alice having her priv key $pk and Bob's public key $pkb
+ my $pk = Crypt::PK::ECC->new($priv_key_filename);
+ my $pkb = Crypt::PK::ECC->new($pub_key_filename);
+ my $shared_secret = $pk->shared_secret($pkb);
+
+ # Bob having his priv key $pk and Alice's public key $pka
+ my $pk = Crypt::PK::ECC->new($priv_key_filename);
+ my $pka = Crypt::PK::ECC->new($pub_key_filename);
+ my $shared_secret = $pk->shared_secret($pka); # same value as computed by Alice
+
+=head2 is_private
+
+ my $rv = $pk->is_private;
+ # 1 .. private key loaded
+ # 0 .. public key loaded
+ # undef .. no key loaded
+
+=head2 size
+
+ my $size = $pk->size;
+ # returns key size in bytes or undef if no key loaded
+
+=head2 key2hash
+
+ my $hash = $pk->key2hash;
+
+ # returns hash like this (or undef if no key loaded):
+ {
+ size => 20, # integer: key (curve) size in bytes
+ type => 1, # integer: 1 .. private, 0 .. public
+ #curve parameters
+ curve_A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC",
+ curve_B => "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+ curve_bits => 160,
+ curve_bytes => 20,
+ curve_cofactor => 1,
+ curve_Gx => "4A96B5688EF573284664698968C38BB913CBFC82",
+ curve_Gy => "23A628553168947D59DCC912042351377AC5FB32",
+ curve_name => "secp160r1",
+ curve_order => "0100000000000000000001F4C8F927AED3CA752257",
+ curve_prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+ #private key
+ k => "B0EE84A749FE95DF997E33B8F333E12101E824C3",
+ #public key point coordinates
+ pub_x => "5AE1ACE3ED0AEA9707CE5C0BCE014F6A2F15023A",
+ pub_y => "895D57E992D0A15F88D6680B27B701F615FCDC0F",
+ }
+
+=head2 curve2hash
+
+I<Since: CryptX-0.024>
+
+ my $crv = $pk->curve2hash;
+
+ # returns a hash that can be passed to: $pk->generate_key($crv)
+ {
+ A => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC",
+ B => "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45",
+ cofactor => 1,
+ Gx => "4A96B5688EF573284664698968C38BB913CBFC82",
+ Gy => "23A628553168947D59DCC912042351377AC5FB32",
+ order => "0100000000000000000001F4C8F927AED3CA752257",
+ prime => "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF",
+ }
+
+=head1 FUNCTIONS
+
+=head2 ecc_encrypt
+
+Elliptic Curve Diffie-Hellman (ECDH) encryption as implemented by libtomcrypt. See method L</encrypt> below.
+
+ my $ct = ecc_encrypt($pub_key_filename, $message);
+ #or
+ my $ct = ecc_encrypt(\$buffer_containing_pub_key, $message);
+ #or
+ my $ct = ecc_encrypt($pub_key_filename, $message, $hash_name);
+
+ #NOTE: $hash_name can be 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+
+ECCDH Encryption is performed by producing a random key, hashing it, and XOR'ing the digest against the plaintext.
+
+=head2 ecc_decrypt
+
+Elliptic Curve Diffie-Hellman (ECDH) decryption as implemented by libtomcrypt. See method L</decrypt> below.
+
+ my $pt = ecc_decrypt($priv_key_filename, $ciphertext);
+ #or
+ my $pt = ecc_decrypt(\$buffer_containing_priv_key, $ciphertext);
+
+=head2 ecc_sign_message
+
+Elliptic Curve Digital Signature Algorithm (ECDSA) - signature generation. See method L</sign_message> below.
+
+ my $sig = ecc_sign_message($priv_key_filename, $message);
+ #or
+ my $sig = ecc_sign_message(\$buffer_containing_priv_key, $message);
+ #or
+ my $sig = ecc_sign_message($priv_key, $message, $hash_name);
+
+=head2 ecc_verify_message
+
+Elliptic Curve Digital Signature Algorithm (ECDSA) - signature verification. See method L</verify_message> below.
+
+ ecc_verify_message($pub_key_filename, $signature, $message) or die "ERROR";
+ #or
+ ecc_verify_message(\$buffer_containing_pub_key, $signature, $message) or die "ERROR";
+ #or
+ ecc_verify_message($pub_key, $signature, $message, $hash_name) or die "ERROR";
+
+=head2 ecc_sign_hash
+
+Elliptic Curve Digital Signature Algorithm (ECDSA) - signature generation. See method L</sign_hash> below.
+
+ my $sig = ecc_sign_hash($priv_key_filename, $message_hash);
+ #or
+ my $sig = ecc_sign_hash(\$buffer_containing_priv_key, $message_hash);
+
+=head2 ecc_verify_hash
+
+Elliptic Curve Digital Signature Algorithm (ECDSA) - signature verification. See method L</verify_hash> below.
+
+ ecc_verify_hash($pub_key_filename, $signature, $message_hash) or die "ERROR";
+ #or
+ ecc_verify_hash(\$buffer_containing_pub_key, $signature, $message_hash) or die "ERROR";
+
+=head2 ecc_shared_secret
+
+Elliptic curve Diffie-Hellman (ECDH) - construct a Diffie-Hellman shared secret with a private and public ECC key. See method L</shared_secret> below.
+
+ #on Alice side
+ my $shared_secret = ecc_shared_secret('Alice_priv_ecc1.der', 'Bob_pub_ecc1.der');
+
+ #on Bob side
+ my $shared_secret = ecc_shared_secret('Bob_priv_ecc1.der', 'Alice_pub_ecc1.der');
+
+=head1 OpenSSL interoperability
+
+ ### let's have:
+ # ECC private key in PEM format - eckey.priv.pem
+ # ECC public key in PEM format - eckey.pub.pem
+ # data file to be signed - input.data
+
+=head2 Sign by OpenSSL, verify by Crypt::PK::ECC
+
+Create signature (from commandline):
+
+ openssl dgst -sha1 -sign eckey.priv.pem -out input.sha1-ec.sig input.data
+
+Verify signature (Perl code):
+
+ use Crypt::PK::ECC;
+ use Crypt::Digest 'digest_file';
+ use File::Slurp 'read_file';
+
+ my $pkec = Crypt::PK::ECC->new("eckey.pub.pem");
+ my $signature = read_file("input.sha1-ec.sig", binmode=>':raw');
+ my $valid = $pkec->verify_hash($signature, digest_file("SHA1", "input.data"), "SHA1", "v1.5");
+ print $valid ? "SUCCESS" : "FAILURE";
+
+=head2 Sign by Crypt::PK::ECC, verify by OpenSSL
+
+Create signature (Perl code):
+
+ use Crypt::PK::ECC;
+ use Crypt::Digest 'digest_file';
+ use File::Slurp 'write_file';
+
+ my $pkec = Crypt::PK::ECC->new("eckey.priv.pem");
+ my $signature = $pkec->sign_hash(digest_file("SHA1", "input.data"), "SHA1", "v1.5");
+ write_file("input.sha1-ec.sig", {binmode=>':raw'}, $signature);
+
+Verify signature (from commandline):
+
+ openssl dgst -sha1 -verify eckey.pub.pem -signature input.sha1-ec.sig input.data
+
+=head2 Keys generated by Crypt::PK::ECC
+
+Generate keys (Perl code):
+
+ use Crypt::PK::ECC;
+ use File::Slurp 'write_file';
+
+ my $pkec = Crypt::PK::ECC->new;
+ $pkec->generate_key('secp160k1');
+ write_file("eckey.pub.der", {binmode=>':raw'}, $pkec->export_key_der('public'));
+ write_file("eckey.priv.der", {binmode=>':raw'}, $pkec->export_key_der('private'));
+ write_file("eckey.pub.pem", $pkec->export_key_pem('public'));
+ write_file("eckey.priv.pem", $pkec->export_key_pem('private'));
+ write_file("eckey-passwd.priv.pem", $pkec->export_key_pem('private', 'secret'));
+
+Use keys by OpenSSL:
+
+ openssl ec -in eckey.priv.der -text -inform der
+ openssl ec -in eckey.priv.pem -text
+ openssl ec -in eckey-passwd.priv.pem -text -inform pem -passin pass:secret
+ openssl ec -in eckey.pub.der -pubin -text -inform der
+ openssl ec -in eckey.pub.pem -pubin -text
+
+=head2 Keys generated by OpenSSL
+
+Generate keys:
+
+ openssl ecparam -param_enc explicit -name prime192v3 -genkey -out eckey.priv.pem
+ openssl ec -param_enc explicit -in eckey.priv.pem -out eckey.pub.pem -pubout
+ openssl ec -param_enc explicit -in eckey.priv.pem -out eckey.priv.der -outform der
+ openssl ec -param_enc explicit -in eckey.priv.pem -out eckey.pub.der -outform der -pubout
+ openssl ec -param_enc explicit -in eckey.priv.pem -out eckey.privc.der -outform der -conv_form compressed
+ openssl ec -param_enc explicit -in eckey.priv.pem -out eckey.pubc.der -outform der -pubout -conv_form compressed
+ openssl ec -param_enc explicit -in eckey.priv.pem -passout pass:secret -des3 -out eckey-passwd.priv.pem
+
+Load keys (Perl code):
+
+ use Crypt::PK::ECC;
+ use File::Slurp 'write_file';
+
+ my $pkec = Crypt::PK::ECC->new;
+ $pkec->import_key("eckey.pub.der");
+ $pkec->import_key("eckey.pubc.der");
+ $pkec->import_key("eckey.priv.der");
+ $pkec->import_key("eckey.privc.der");
+ $pkec->import_key("eckey.pub.pem");
+ $pkec->import_key("eckey.priv.pem");
+ $pkec->import_key("eckey-passwd.priv.pem", "secret");
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<https://en.wikipedia.org/wiki/Elliptic_curve_cryptography|https://en.wikipedia.org/wiki/Elliptic_curve_cryptography>
+
+=item * L<https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman|https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman>
+
+=item * L<https://en.wikipedia.org/wiki/ECDSA|https://en.wikipedia.org/wiki/ECDSA>
+
+=back
diff --git a/lib/Crypt/PK/RSA.pm b/lib/Crypt/PK/RSA.pm
new file mode 100644
index 00000000..2fe541ac
--- /dev/null
+++ b/lib/Crypt/PK/RSA.pm
@@ -0,0 +1,939 @@
+package Crypt::PK::RSA;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+require Exporter; our @ISA = qw(Exporter); ### use Exporter 'import';
+our %EXPORT_TAGS = ( all => [qw(rsa_encrypt rsa_decrypt rsa_sign_message rsa_verify_message rsa_sign_hash rsa_verify_hash)] );
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+our @EXPORT = qw();
+
+use Carp;
+use CryptX qw(_encode_json _decode_json);
+use Crypt::Digest qw(digest_data digest_data_b64u);
+use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem);
+use Crypt::PK;
+
+sub new {
+ my ($class, $f, $p) = @_;
+ my $self = _new();
+ $self->import_key($f, $p) if $f;
+ return $self;
+}
+
+sub export_key_pem {
+ my ($self, $type, $password, $cipher) = @_;
+ my $key = $self->export_key_der($type||'');
+ return unless $key;
+
+ # PKCS#1 RSAPrivateKey** (PEM header: BEGIN RSA PRIVATE KEY)
+ # PKCS#8 PrivateKeyInfo* (PEM header: BEGIN PRIVATE KEY)
+ # PKCS#8 EncryptedPrivateKeyInfo** (PEM header: BEGIN ENCRYPTED PRIVATE KEY)
+ return der_to_pem($key, "RSA PRIVATE KEY", $password, $cipher) if $type eq 'private';
+
+ # PKCS#1 RSAPublicKey* (PEM header: BEGIN RSA PUBLIC KEY)
+ return der_to_pem($key, "RSA PUBLIC KEY") if $type eq 'public';
+ # X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)
+ return der_to_pem($key, "PUBLIC KEY") if $type eq 'public_x509';
+}
+
+sub export_key_jwk {
+ my ($self, $type, $wanthash) = @_;
+ my $kh = $self->key2hash;
+ if ($type eq 'private') {
+ return unless $kh->{N} && $kh->{e} && $kh->{d} && $kh->{p} && $kh->{q} && $kh->{dP} && $kh->{dQ} && $kh->{qP};
+ for (qw/N e d p q dP dQ qP/) {
+ $kh->{$_} = "0$kh->{$_}" if length($kh->{$_}) % 2;
+ }
+ my $hash = {
+ kty => "RSA",
+ n => encode_b64u(pack("H*", $kh->{N})),
+ e => encode_b64u(pack("H*", $kh->{e})),
+ d => encode_b64u(pack("H*", $kh->{d})),
+ p => encode_b64u(pack("H*", $kh->{p})),
+ q => encode_b64u(pack("H*", $kh->{q})),
+ dp => encode_b64u(pack("H*", $kh->{dP})),
+ dq => encode_b64u(pack("H*", $kh->{dQ})),
+ qi => encode_b64u(pack("H*", $kh->{qP})),
+ };
+ return $wanthash ? $hash : _encode_json($hash);
+ }
+ elsif ($type eq 'public') {
+ return unless $kh->{N} && $kh->{e};
+ for (qw/N e/) {
+ $kh->{$_} = "0$kh->{$_}" if length($kh->{$_}) % 2;
+ }
+ my $hash = {
+ kty => "RSA",
+ n => encode_b64u(pack("H*", $kh->{N})),
+ e => encode_b64u(pack("H*", $kh->{e})),
+ };
+ return $wanthash ? $hash : _encode_json($hash);
+ }
+}
+
+sub export_key_jwk_thumbprint {
+ my ($self, $hash_name) = @_;
+ $hash_name ||= 'SHA256';
+ my $h = $self->export_key_jwk('public', 1);
+ my $json = _encode_json({kty=>$h->{kty}, n=>$h->{n}, e=>$h->{e}});
+ return digest_data_b64u($hash_name, $json);
+}
+
+sub import_key {
+ my ($self, $key, $password) = @_;
+ croak "FATAL: undefined key" unless $key;
+
+ # special case
+ if (ref($key) eq 'HASH') {
+ if ($key->{N} && $key->{e}) {
+ # hash exported via key2hash
+ return $self->_import_hex($key->{N}, $key->{e}, $key->{d}, $key->{p}, $key->{q}, $key->{dP}, $key->{dQ}, $key->{qP});
+ }
+ if ($key->{n} && $key->{e} && $key->{kty} && $key->{kty} eq "RSA") {
+ $key = {%$key}; #make a copy so that the modifications below stay local
+
+ # hash with items corresponding to JSON Web Key (JWK)
+ for (qw/n e d p q dp dq qi/) {
+ $key->{$_} = eval { unpack("H*", decode_b64u($key->{$_})) } if exists $key->{$_};
+ }
+ return $self->_import_hex($key->{n}, $key->{e}, $key->{d}, $key->{p}, $key->{q}, $key->{dp}, $key->{dq}, $key->{qi});
+ }
+ croak "FATAL: unexpected RSA key hash";
+ }
+
+ my $data;
+ if (ref($key) eq 'SCALAR') {
+ $data = $$key;
+ }
+ elsif (-f $key) {
+ $data = read_rawfile($key);
+ }
+ else {
+ croak "FATAL: non-existing file '$key'";
+ }
+ croak "FATAL: invalid key data" unless $data;
+
+ if ($data =~ /-----BEGIN (RSA PRIVATE|RSA PUBLIC|PUBLIC) KEY-----(.*?)-----END/sg) {
+ # PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY)
+ # PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY)
+ # X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY)
+ $data = pem_to_der($data, $password);
+ return $self->_import($data) if $data;
+ }
+ elsif ($data =~ /-----BEGIN PRIVATE KEY-----(.*?)-----END/sg) {
+ # PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY)
+ $data = pem_to_der($data, $password);
+ return $self->_import_pkcs8($data) if $data;
+ }
+ elsif ($data =~ /-----BEGIN ENCRYPTED PRIVATE KEY-----(.*?)-----END/sg) {
+ # XXX-TODO: PKCS#8 EncryptedPrivateKeyInfo (PEM header: BEGIN ENCRYPTED PRIVATE KEY)
+ croak "FATAL: encrypted pkcs8 RSA private keys are not supported";
+ }
+ elsif ($data =~ /^\s*(\{.*?\})\s*$/s) {
+ # JSON Web Key (JWK) - http://tools.ietf.org/html/draft-ietf-jose-json-web-key
+ my $json = "$1";
+ my $h = _decode_json($json);
+ if ($h && $h->{kty} eq "RSA") {
+ for (qw/n e d p q dp dq qi/) {
+ $h->{$_} = eval { unpack("H*", decode_b64u($h->{$_})) } if exists $h->{$_};
+ }
+ return $self->_import_hex($h->{n}, $h->{e}, $h->{d}, $h->{p}, $h->{q}, $h->{dp}, $h->{dq}, $h->{qi}) if $h->{n} && $h->{e};
+ }
+ }
+ elsif ($data =~ /---- BEGIN SSH2 PUBLIC KEY ----(.*?)---- END SSH2 PUBLIC KEY ----/sg) {
+ $data = pem_to_der($data);
+ my ($typ, $N, $e) = Crypt::PK::_ssh_parse($data);
+ return $self->_import_hex(unpack("H*", $e), unpack("H*", $N)) if $typ && $e && $N && $typ eq 'ssh-rsa';
+ }
+ elsif ($data =~ /ssh-rsa\s+(\S+)/) {
+ $data = decode_b64("$1");
+ my ($typ, $N, $e) = Crypt::PK::_ssh_parse($data);
+ return $self->_import_hex(unpack("H*", $e), unpack("H*", $N)) if $typ && $e && $N && $typ eq 'ssh-rsa';
+ }
+ else {
+ # DER format
+ my $rv = eval { $self->_import($data) } || eval { $self->_import_pkcs8($data) };
+ return $rv if $rv;
+ }
+
+ croak "FATAL: invalid or unsupported RSA key format";
+}
+
+sub encrypt {
+ my ($self, $data, $padding, $hash_name, $lparam) = @_;
+ $lparam ||= '';
+ $padding ||= 'oaep';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+
+ return $self->_encrypt($data, $padding, $hash_name, $lparam);
+}
+
+sub decrypt {
+ my ($self, $data, $padding, $hash_name, $lparam) = @_;
+ $lparam ||= '';
+ $padding ||= 'oaep';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+
+ return $self->_decrypt($data, $padding, $hash_name, $lparam);
+}
+
+sub sign_hash {
+ my ($self, $data, $hash_name, $padding, $saltlen) = @_;
+ $saltlen ||= 12;
+ $padding ||= 'pss';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+
+ return $self->_sign($data, $padding, $hash_name, $saltlen);
+}
+
+sub sign_message {
+ my ($self, $data, $hash_name, $padding, $saltlen) = @_;
+ $saltlen ||= 12;
+ $padding ||= 'pss';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+
+ return $self->_sign(digest_data($hash_name, $data), $padding, $hash_name, $saltlen);
+}
+
+sub verify_hash {
+ my ($self, $sig, $data, $hash_name, $padding, $saltlen) = @_;
+ $saltlen ||= 12;
+ $padding ||= 'pss';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+
+ return $self->_verify($sig, $data, $padding, $hash_name, $saltlen);
+}
+
+sub verify_message {
+ my ($self, $sig, $data, $hash_name, $padding, $saltlen) = @_;
+ $saltlen ||= 12;
+ $padding ||= 'pss';
+ $hash_name = Crypt::Digest::_trans_digest_name($hash_name||'SHA1');
+
+ return $self->_verify($sig, digest_data($hash_name, $data), $padding, $hash_name, $saltlen);
+}
+
+### FUNCTIONS
+
+sub rsa_encrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->encrypt(@_);
+}
+
+sub rsa_decrypt {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->decrypt(@_);
+}
+
+sub rsa_sign_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_hash(@_);
+}
+
+sub rsa_verify_hash {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_hash(@_);
+}
+
+sub rsa_sign_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->sign_message(@_);
+}
+
+sub rsa_verify_message {
+ my $key = shift;
+ $key = __PACKAGE__->new($key) unless ref $key;
+ carp "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
+ return $key->verify_message(@_);
+}
+
+sub CLONE_SKIP { 1 } # prevent cloning
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::PK::RSA - Public key cryptography based on RSA
+
+=head1 SYNOPSIS
+
+ ### OO interface
+
+ #Encryption: Alice
+ my $pub = Crypt::PK::RSA->new('Bob_pub_rsa1.der');
+ my $ct = $pub->encrypt("secret message");
+ #
+ #Encryption: Bob (received ciphertext $ct)
+ my $priv = Crypt::PK::RSA->new('Bob_priv_rsa1.der');
+ my $pt = $priv->decrypt($ct);
+
+ #Signature: Alice
+ my $priv = Crypt::PK::RSA->new('Alice_priv_rsa1.der');
+ my $sig = $priv->sign_message($message);
+ #
+ #Signature: Bob (received $message + $sig)
+ my $pub = Crypt::PK::RSA->new('Alice_pub_rsa1.der');
+ $pub->verify_message($sig, $message) or die "ERROR";
+
+ #Key generation
+ my $pk = Crypt::PK::RSA->new();
+ $pk->generate_key(256, 65537);
+ my $private_der = $pk->export_key_der('private');
+ my $public_der = $pk->export_key_der('public');
+ my $private_pem = $pk->export_key_pem('private');
+ my $public_pem = $pk->export_key_pem('public');
+
+ ### Functional interface
+
+ #Encryption: Alice
+ my $ct = rsa_encrypt('Bob_pub_rsa1.der', "secret message");
+ #Encryption: Bob (received ciphertext $ct)
+ my $pt = rsa_decrypt('Bob_priv_rsa1.der', $ct);
+
+ #Signature: Alice
+ my $sig = rsa_sign_message('Alice_priv_rsa1.der', $message);
+ #Signature: Bob (received $message + $sig)
+ rsa_verify_message('Alice_pub_rsa1.der', $sig, $message) or die "ERROR";
+
+=head1 DESCRIPTION
+
+The module provides a full featured RSA implementation.
+
+=head1 METHODS
+
+=head2 new
+
+ my $pk = Crypt::PK::RSA->new();
+ #or
+ my $pk = Crypt::PK::RSA->new($priv_or_pub_key_filename);
+ #or
+ my $pk = Crypt::PK::RSA->new(\$buffer_containing_priv_or_pub_key);
+
+Support for password protected PEM keys
+
+ my $pk = Crypt::PK::RSA->new($priv_pem_key_filename, $password);
+ #or
+ my $pk = Crypt::PK::RSA->new(\$buffer_containing_priv_pem_key, $password);
+
+=head2 generate_key
+
+Uses Yarrow-based cryptographically strong random number generator seeded with
+random data taken from C</dev/random> (UNIX) or C<CryptGenRandom> (Win32).
+
+ $pk->generate_key($size, $e);
+ # $size .. key size: 128-512 bytes (DEFAULT is 256)
+ # $e ..... exponent: 3, 17, 257 or 65537 (DEFAULT is 65537)
+
+=head2 import_key
+
+Loads private or public key in DER or PEM format.
+
+ $pk->import_key($priv_or_pub_key_filename);
+ #or
+ $pk->import_key(\$buffer_containing_priv_or_pub_key);
+
+Support for password protected PEM keys
+
+ $pk->import_key($pem_filename, $password);
+ #or
+ $pk->import_key(\$buffer_containing_pem_key, $password);
+
+Loading private or public keys form perl hash:
+
+ $pk->import_key($hashref);
+
+ # the $hashref is either a key exported via key2hash
+ $pk->import_key({
+ e => "10001", #public exponent
+ d => "9ED5C3D3F866E06957CA0E9478A273C39BBDA4EEAC5B...", #private exponent
+ N => "D0A5CCCAE03DF9C2F5C4C8C0CE840D62CDE279990DC6...", #modulus
+ p => "D3EF0028FFAB508E2773C659E428A80FB0E9211346B4...", #p factor of N
+ q => "FC07E46B163CAB6A83B8E467D169534B2077DCDEECAE...", #q factor of N
+ qP => "88C6D406F833DF73C8B734548E0385261AD51F4187CF...", #1/q mod p CRT param
+ dP => "486F142FEF0A1F53269AC43D2EE4D263E2841B60DA36...", #d mod (p - 1) CRT param
+ dQ => "4597284B2968B72C4212DB7E8F24360B987B80514DA9...", #d mod (q - 1) CRT param
+ });
+
+ # or a hash with items corresponding to JWK (JSON Web Key)
+ $pk->import_key({
+ {
+ kty => "RSA",
+ n => "0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
+ e => "AQAB",
+ d => "X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
+ p => "83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
+ q => "3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
+ dp => "G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
+ dq => "s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
+ qi => "GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
+ });
+
+Supported key formats:
+
+ # all formats can be loaded from a file
+ my $pk = Crypt::PK::RSA->new($filename);
+
+ # or from a buffer containing the key
+ my $pk = Crypt::PK::RSA->new(\$buffer_with_key);
+
+=over
+
+=item * RSA public keys
+
+ -----BEGIN PUBLIC KEY-----
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHlYKg9DeHB3/dY1D9WCyJTnl5
+ vEzAXpUOL9tDtdPUl96brIbbdMLooO1hKjsq98kLs1q4vOn/pxvzk0BRwhiu7Vvb
+ VUjAn/2HHDDL0U1utqqlMJhaffeLI3HEq5o/lSMFY7sSkZU/E4YX1yqAN0SE7xfK
+ B2uzcNq60sMIfp6siQIDAQAB
+ -----END PUBLIC KEY-----
+
+=item * RSA private keys
+
+ -----BEGIN RSA PRIVATE KEY-----
+ MIICXQIBAAKBgQDHlYKg9DeHB3/dY1D9WCyJTnl5vEzAXpUOL9tDtdPUl96brIbb
+ dMLooO1hKjsq98kLs1q4vOn/pxvzk0BRwhiu7VvbVUjAn/2HHDDL0U1utqqlMJha
+ ffeLI3HEq5o/lSMFY7sSkZU/E4YX1yqAN0SE7xfKB2uzcNq60sMIfp6siQIDAQAB
+ AoGBAI5+GgNcGQDYw9uF+t7FwxZM5sGZRJrbbEPyuvL+sDxKKW6voKCyHi4EJzaF
+ 9jRZMDqgVJcsmUwjPPuMGBHHJ+MI5Zb3L0jbZkyx8u+U5gf88oy9eZmfGOjmHcMB
+ oCgzyoLmJETuyADg2onLanuY3jggFb3tq/jimKjO8xM2R6zhAkEA7uXWWyJI9cCN
+ zrVt5R5v6oosjZ4r5VILGMqBRLrzfTvH+WDMK6Rl/2MHE+YDeLajzunaM8qY2456
+ GTYEXQsIdQJBANXfMEtXocSdPtoVj3ME8Do/0r+ApgTdcDPCwXOzkmkEJW/UFMSn
+ b8CYF5G6sZQN9L5z3s2nvi55PaFV8Q0LMUUCQBh9GvIQm6YFbQPpeTBpZFOIgnSp
+ 6BoDxPtvlryy5U7LF/6qO4OlwIbjYdBaXbS8FCKbujBg7jZjboSzEtNu1BkCQDGT
+ w0Yz0jQZn3A+fzpScr2N/fSWheWqz0+wXdfMUKw3YdZCe236wlUK7KvDc1a2xX1A
+ ru1NbTCoujikC3TSm2ECQQDKQshchJlZJmFv9vCFQlGCA/EX+4406xvOOiixbPYC
+ pIB4Ee2cmvEdAqSaOjrvgs5zvaCCFBO0MecPStCAxUX6
+ -----END RSA PRIVATE KEY-----
+
+=item * RSA private keys in password protected PEM format
+
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: DES-EDE3-CBC,4D697440FF5AEF18
+
+ C09H49Gn99o8b8O2r4+Hqao4r3udvC+QSSfsk20sXatyuZSEmbhyqKAB+13NRj+3
+ KIsRTqnL9VkeibIGgLHuekOFKAqeSVZ0PmR4bGWEFxUPAYUvg9N9pIa6hGtNZG+y
+ TEpOAfFITb1pbHQhp3j8y7qmKc5kY5LrZSFE8WwA24NTG773E07wJgRxKDkXNGOl
+ kki6oYArNEps0DdtHFxzgdRg0+yaotXuFJRuC5V4YzKGG/oSRcgYyXKTwCndb3xt
+ aHgI2WprQAPg+qOpLABzoi7bEjCqbHWrwkvnAngylbim2Uyvw1e1xKnzlgIHU7pv
+ e/J+s00pTItfqW1IpY2mh4C9nkfkfVKBKaAv7jO0s6aPySATqsdlrzv2kpF6Ub4J
+ kgaZDOfZ4K3qkyAYVLWcQeDqg4glv9Ah2J05bTm4qrIMmthYnThyQlGvcjUfCMXs
+ 0t+mEQbsRY7xKt0o6HzzvQlJ+JsFlLORoslAubJX9iLqpEdnlrj1lD9bo6uIClZ5
+ 5+aoLcAyz1D4OsauuP5i8VFu+Is+QG4SN/vHVuArjkqi3VpLwSAjNDY+KWbq042l
+ CqlM2mwm6FIGUZQFxiLHJD7WDmk1xmae++m+XG9CEDTfrUQ5v+l0O6BTrl80XUfU
+ w3gzAWbSjz3UK0FpKeABVFPE9fjNP9fTcS6qL5YJWBPflwxCAbVgsBOW4bOMpDGK
+ BJDQTeShWn4BlYCe/vgThI9ERdgZhRz4NcFeDgVA/CqQzVqptvz4PSqH46fqUN2n
+ 4PtJgKE5cASYUBuAjlD71FecSVVM/OTzL1uxYzXBilzvVn2vSHgo9g==
+ -----END RSA PRIVATE KEY-----
+
+=item * PKCS#8 encoded private keys
+
+ -----BEGIN PRIVATE KEY-----
+ MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANPN17xW4EkH5PXG
+ 1i/i3rE1EXFcCHyxmz95VRBDs1p3MuYf9mxntbfYAmuzS3KrRWh3IyX/Eh80N/v9
+ OXPlwZbVqSTX+L3pCEJtRtsWn0zmswGThjMZiwle0oWuap63L35F1QN8EDaSPSBC
+ yGELNRr6rwVYq0w5b+LOcaCZ+/H1AgMBAAECgYEApfu3aGpww+rC3HUhX0+ckyTy
+ cXLdV9LbxidwqRlVEb0+DyfXNucjelp2sy5EHy3na9GJovo8mmWSxhCRGKliRkQ6
+ XgrEMZdCSaWI2AazuHAGlUJRFEVkvdla3AuBAn6y0YdDp/3kbg0yahmKyD8Gq74z
+ nUYbDL3R5JtR2Ad/KlUCQQDvSEICTHbO/BF7hVmlKRYZSNHKEPrv8X/OlppS14Kv
+ QRwc+CZ5+l6T1Y+l5cHJQUXrXZoWS1K741TXdUhjjUd7AkEA4pod804Ex8sttdWi
+ pHMfeyj+IbPAk5XnBc91jT7AYIeL8ccjtfl99xhMsGFaxrh3wA/4SGEvwzWkbxcq
+ H8G5TwJAKNG+0P2SVwURRm0dOdukdXPCtiHnbP9Zujhe4zr4hEUrMpXymmRntfh8
+ pORpBpgoAVraams3Fe5WDttnGfSD+QJAOOC6V9HjfUrQhG3FT0XeRwm5EDiQQ/tC
+ a8DxHqz7mL8tL1ju68ReC+G7jiJBqNOwqzLW/UP3uyYByiikWChGHQJAHUau7jIM
+ 45ErO096n94Vh95p76ANxOroWszOt39TyvJOykIfoPwFagLrBWV9Jjos2/D54KE+
+ fyoy4t3yHT+/nw==
+ -----END PRIVATE KEY-----
+
+=item * PKCS#8 encrypted private keys ARE NOT SUPPORTED YET!
+
+ -----BEGIN ENCRYPTED PRIVATE KEY-----
+ MIICojAcBgoqhkiG9w0BDAEDMA4ECCQk+Rr1yzzcAgIIAASCAoD/mgpUFjxxM/Ty
+ Yt+NeT0Fo4echgoGksqs6+rYhO16oshG664emZfkuNoFGGzJ38X6GVuqIXhlPnYQ
+ biKvL37dN/KnoGytFHq9Wnk8dDwjGHPtwajhW5WuIV3NuhW/AO1PF/cRZKFjWrPt
+ NWY5CrpfH6t6zojoe+5uyXpH29lQy4OqvSRdPIt/12UcB+tzV7XzSWEuXh8HAi8a
+ sYUu6tuCFnq4GrD2ffM4KWFmL5GqBAwN6m0KkyrNni9XT+RaA6zEhv/lVcwg2esa
+ 4/EzRs0ixzzZDKaml8oCMl9RHtFAbQmdlfV7Ip4rGK9BwY6UFiDMIVru6HynOVQK
+ vvZ+j//bgO+3ubrv7psX+vC9Fy/MoH2Tc7MIwDN/QVTciPZlzjWBnBNxMfeFKtEn
+ d7NFiapgfLuRQIiDTMrW/clcqvO54NphxhrcgUEoxos4twKZARntqPZHtf8nEM2x
+ 2sEF5kI65aEF/5Yy16qvP0vZAA2B1kcIdXZ8XLZCp4c3olhkIrmgUpo1gyFXdCoC
+ 7dT5Cz7/YLkq5hkcFrtp4V9BZMR24fSttc4p24N5xuZ+JneGnGkLX6B+nJAtm9vw
+ bZA6P+23GI0qeMzL3HJXwCOTSsWfm/H9W5+2Zmw851aAmE+pZLni/pk3e3iNSWgs
+ 946x/doA5O0uCFsU7oxme+WAIp2SjhxGoe808Lf1CCFMPboFi1O/E0NsX8SIEX+i
+ U+UHi4kxZqVkr3Q5SB/9kiSv8K1bE787yueQOT/dsTYYaMsjAbkEZo0o/47F32T6
+ A2ioXHOV/pr5zNHqE5tL+qKEcLYbAUF1O+WvmdqYz+vHQjRQBatAqTmncvLDYr/j
+ 1HPwZX2d
+ -----END ENCRYPTED PRIVATE KEY-----
+
+=item * SSH public RSA keys
+
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...6mdYs5iJNGu/ltUdc=
+
+=item * SSH public RSA keys (RFC-4716 format)
+
+ ---- BEGIN SSH2 PUBLIC KEY ----
+ Comment: "768-bit RSA, converted from OpenSSH"
+ AAAAB3NzaC1yc2EAAAADAQABAAAAYQDYebeGQFCnlQiNRE7r9UEbjr+DQMTdw1ZHGB2w6x
+ D/DzKem8761GdCpqsLrGaw2D7aSIoP1B5Sz870YoVWHn6Ao7Hvm17V3Kxfn4B01GNQTM5+
+ L26mdYs5iJNGu/ltUdc=
+ ---- END SSH2 PUBLIC KEY ----
+
+=item * RSA private keys in JSON Web Key (JWK) format
+
+See L<http://tools.ietf.org/html/draft-ietf-jose-json-web-key>
+
+ {
+ "kty":"RSA",
+ "n":"0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
+ "e":"AQAB",
+ "d":"X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
+ "p":"83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
+ "q":"3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
+ "dp":"G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
+ "dq":"s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
+ "qi":"GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
+ }
+
+B<BEWARE:> For JWK support you need to have L<JSON::PP>, L<JSON::XS> or L<Cpanel::JSON::XS> module.
+
+=item * RSA public keys in JSON Web Key (JWK) format
+
+ {
+ "kty":"RSA",
+ "n": "0vx7agoebGcQSuuPiLJXZp...tN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECP",
+ "e":"AQAB",
+ }
+
+B<BEWARE:> For JWK support you need to have L<JSON::PP>, L<JSON::XS> or L<Cpanel::JSON::XS> module.
+
+=back
+
+=head2 export_key_der
+
+ my $private_der = $pk->export_key_der('private');
+ #or
+ my $public_der = $pk->export_key_der('public');
+
+=head2 export_key_pem
+
+ my $private_pem = $pk->export_key_pem('private');
+ #or
+ my $public_pem = $pk->export_key_pem('public');
+ #or
+ my $public_pem = $pk->export_key_pem('public_x509');
+
+With parameter C<'public'> uses header and footer lines:
+
+ -----BEGIN RSA PUBLIC KEY------
+ -----END RSA PUBLIC KEY------
+
+With parameter C<'public_x509'> uses header and footer lines:
+
+ -----BEGIN PUBLIC KEY------
+ -----END PUBLIC KEY------
+
+Support for password protected PEM keys
+
+ my $private_pem = $pk->export_key_pem('private', $password);
+ #or
+ my $private_pem = $pk->export_key_pem('private', $password, $cipher);
+
+ # supported ciphers: 'DES-CBC'
+ # 'DES-EDE3-CBC'
+ # 'SEED-CBC'
+ # 'CAMELLIA-128-CBC'
+ # 'CAMELLIA-192-CBC'
+ # 'CAMELLIA-256-CBC'
+ # 'AES-128-CBC'
+ # 'AES-192-CBC'
+ # 'AES-256-CBC' (DEFAULT)
+
+=head2 export_key_jwk
+
+I<Since: CryptX-0.022>
+
+Exports public/private keys as a JSON Web Key (JWK).
+
+ my $private_json_text = $pk->export_key_jwk('private');
+ #or
+ my $public_json_text = $pk->export_key_jwk('public');
+
+Also exports public/private keys as a perl HASH with JWK structure.
+
+ my $jwk_hash = $pk->export_key_jwk('private', 1);
+ #or
+ my $jwk_hash = $pk->export_key_jwk('public', 1);
+
+B<BEWARE:> For JWK support you need to have L<JSON::PP>, L<JSON::XS> or L<Cpanel::JSON::XS> module.
+
+=head2 export_key_jwk_thumbprint
+
+I<Since: CryptX-0.031>
+
+Exports the key's JSON Web Key Thumbprint as a string.
+
+If you don't know what this is, see RFC 7638 (C<https://tools.ietf.org/html/rfc7638>).
+
+ my $thumbprint = $pk->export_key_jwk_thumbprint('SHA256');
+
+=head2 encrypt
+
+ my $pk = Crypt::PK::RSA->new($pub_key_filename);
+ my $ct = $pk->encrypt($message);
+ #or
+ my $ct = $pk->encrypt($message, $padding);
+ #or
+ my $ct = $pk->encrypt($message, 'oaep', $hash_name, $lparam);
+
+ # $padding .................... 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
+ # $hash_name (only for oaep) .. 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $lparam (only for oaep) ..... DEFAULT is empty string
+
+=head2 decrypt
+
+ my $pk = Crypt::PK::RSA->new($priv_key_filename);
+ my $pt = $pk->decrypt($ciphertext);
+ #or
+ my $pt = $pk->decrypt($ciphertext, $padding);
+ #or
+ my $pt = $pk->decrypt($ciphertext, 'oaep', $hash_name, $lparam);
+
+ # $padding .................... 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
+ # $hash_name (only for oaep) .. 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $lparam (only for oaep) ..... DEFAULT is empty string
+
+=head2 sign_message
+
+ my $pk = Crypt::PK::RSA->new($priv_key_filename);
+ my $signature = $priv->sign_message($message);
+ #or
+ my $signature = $priv->sign_message($message, $hash_name);
+ #or
+ my $signature = $priv->sign_message($message, $hash_name, $padding);
+ #or
+ my $signature = $priv->sign_message($message, $hash_name, 'pss', $saltlen);
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 verify_message
+
+ my $pk = Crypt::PK::RSA->new($pub_key_filename);
+ my $valid = $pub->verify_message($signature, $message);
+ #or
+ my $valid = $pub->verify_message($signature, $message, $hash_name);
+ #or
+ my $valid = $pub->verify_message($signature, $message, $hash_name, $padding);
+ #or
+ my $valid = $pub->verify_message($signature, $message, $hash_name, 'pss', $saltlen);
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 sign_hash
+
+ my $pk = Crypt::PK::RSA->new($priv_key_filename);
+ my $signature = $priv->sign_hash($message_hash);
+ #or
+ my $signature = $priv->sign_hash($message_hash, $hash_name);
+ #or
+ my $signature = $priv->sign_hash($message_hash, $hash_name, $padding);
+ #or
+ my $signature = $priv->sign_hash($message_hash, $hash_name, 'pss', $saltlen);
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 verify_hash
+
+ my $pk = Crypt::PK::RSA->new($pub_key_filename);
+ my $valid = $pub->verify_hash($signature, $message_hash);
+ #or
+ my $valid = $pub->verify_hash($signature, $message_hash, $hash_name);
+ #or
+ my $valid = $pub->verify_hash($signature, $message_hash, $hash_name, $padding);
+ #or
+ my $valid = $pub->verify_hash($signature, $message_hash, $hash_name, 'pss', $saltlen);
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 is_private
+
+ my $rv = $pk->is_private;
+ # 1 .. private key loaded
+ # 0 .. public key loaded
+ # undef .. no key loaded
+
+=head2 size
+
+ my $size = $pk->size;
+ # returns key size in bytes or undef if no key loaded
+
+=head2 key2hash
+
+ my $hash = $pk->key2hash;
+
+ # returns hash like this (or undef if no key loaded):
+ {
+ type => 1, # integer: 1 .. private, 0 .. public
+ size => 256, # integer: key size in bytes
+ # all the rest are hex strings
+ e => "10001", #public exponent
+ d => "9ED5C3D3F866E06957CA0E9478A273C39BBDA4EEAC5B...", #private exponent
+ N => "D0A5CCCAE03DF9C2F5C4C8C0CE840D62CDE279990DC6...", #modulus
+ p => "D3EF0028FFAB508E2773C659E428A80FB0E9211346B4...", #p factor of N
+ q => "FC07E46B163CAB6A83B8E467D169534B2077DCDEECAE...", #q factor of N
+ qP => "88C6D406F833DF73C8B734548E0385261AD51F4187CF...", #1/q mod p CRT param
+ dP => "486F142FEF0A1F53269AC43D2EE4D263E2841B60DA36...", #d mod (p - 1) CRT param
+ dQ => "4597284B2968B72C4212DB7E8F24360B987B80514DA9...", #d mod (q - 1) CRT param
+ }
+
+=head1 FUNCTIONS
+
+=head2 rsa_encrypt
+
+RSA based encryption. See method L</encrypt> below.
+
+ my $ct = rsa_encrypt($pub_key_filename, $message);
+ #or
+ my $ct = rsa_encrypt(\$buffer_containing_pub_key, $message);
+ #or
+ my $ct = rsa_encrypt($pub_key, $message, $padding);
+ #or
+ my $ct = rsa_encrypt($pub_key, $message, 'oaep', $hash_name, $lparam);
+
+ # $padding .................... 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
+ # $hash_name (only for oaep) .. 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $lparam (only for oaep) ..... DEFAULT is empty string
+
+=head2 rsa_decrypt
+
+RSA based decryption. See method L</decrypt> below.
+
+ my $pt = rsa_decrypt($priv_key_filename, $ciphertext);
+ #or
+ my $pt = rsa_decrypt(\$buffer_containing_priv_key, $ciphertext);
+ #or
+ my $pt = rsa_decrypt($priv_key, $ciphertext, $padding);
+ #or
+ my $pt = rsa_decrypt($priv_key, $ciphertext, 'oaep', $hash_name, $lparam);
+
+ # $padding .................... 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
+ # $hash_name (only for oaep) .. 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $lparam (only for oaep) ..... DEFAULT is empty string
+
+=head2 rsa_sign_message
+
+Generate RSA signature. See method L</sign_message> below.
+
+ my $sig = rsa_sign_message($priv_key_filename, $message);
+ #or
+ my $sig = rsa_sign_message(\$buffer_containing_priv_key, $message);
+ #or
+ my $sig = rsa_sign_message($priv_key, $message, $hash_name);
+ #or
+ my $sig = rsa_sign_message($priv_key, $message, $hash_name, $padding);
+ #or
+ my $sig = rsa_sign_message($priv_key, $message, $hash_name, 'pss', $saltlen);
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 rsa_verify_message
+
+Verify RSA signature. See method L</verify_message> below.
+
+ rsa_verify_message($pub_key_filename, $signature, $message) or die "ERROR";
+ #or
+ rsa_verify_message(\$buffer_containing_pub_key, $signature, $message) or die "ERROR";
+ #or
+ rsa_verify_message($pub_key, $signature, $message, $hash_name) or die "ERROR";
+ #or
+ rsa_verify_message($pub_key, $signature, $message, $hash_name, $padding) or die "ERROR";
+ #or
+ rsa_verify_message($pub_key, $signature, $message, $hash_name, 'pss', $saltlen) or die "ERROR";
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 rsa_sign_hash
+
+Generate RSA signature. See method L</sign_hash> below.
+
+ my $sig = rsa_sign_hash($priv_key_filename, $message_hash);
+ #or
+ my $sig = rsa_sign_hash(\$buffer_containing_priv_key, $message_hash);
+ #or
+ my $sig = rsa_sign_hash($priv_key, $message_hash, $hash_name);
+ #or
+ my $sig = rsa_sign_hash($priv_key, $message_hash, $hash_name, $padding);
+ #or
+ my $sig = rsa_sign_hash($priv_key, $message_hash, $hash_name, 'pss', $saltlen);
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head2 rsa_verify_hash
+
+Verify RSA signature. See method L</verify_hash> below.
+
+ rsa_verify_hash($pub_key_filename, $signature, $message_hash) or die "ERROR";
+ #or
+ rsa_verify_hash(\$buffer_containing_pub_key, $signature, $message_hash) or die "ERROR";
+ #or
+ rsa_verify_hash($pub_key, $signature, $message_hash, $hash_name) or die "ERROR";
+ #or
+ rsa_verify_hash($pub_key, $signature, $message_hash, $hash_name, $padding) or die "ERROR";
+ #or
+ rsa_verify_hash($pub_key, $signature, $message_hash, $hash_name, 'pss', $saltlen) or die "ERROR";
+
+ # $hash_name ............... 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
+ # $padding ................. 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
+ # $saltlen (only for pss) .. DEFAULT is 12
+
+=head1 OpenSSL interoperability
+
+ ### let's have:
+ # RSA private key in PEM format - rsakey.priv.pem
+ # RSA public key in PEM format - rsakey.pub.pem
+ # data file to be signed or encrypted - input.data
+
+=head2 Encrypt by OpenSSL, decrypt by Crypt::PK::RSA
+
+Create encrypted file (from commandline):
+
+ openssl rsautl -encrypt -inkey rsakey.pub.pem -pubin -out input.encrypted.rsa -in input.data
+
+Decrypt file (Perl code):
+
+ use Crypt::PK::RSA;
+ use File::Slurp 'read_file';
+
+ my $pkrsa = Crypt::PK::RSA->new("rsakey.priv.pem");
+ my $encfile = read_file("input.encrypted.rsa", binmode=>':raw');
+ my $plaintext = $pkrsa->decrypt($encfile, 'v1.5');
+ print $plaintext;
+
+=head2 Encrypt by Crypt::PK::RSA, decrypt by OpenSSL
+
+Create encrypted file (Perl code):
+
+ use Crypt::PK::RSA;
+ use File::Slurp 'write_file';
+
+ my $plaintext = 'secret message';
+ my $pkrsa = Crypt::PK::RSA->new("rsakey.pub.pem");
+ my $encrypted = $pkrsa->encrypt($plaintext, 'v1.5');
+ write_file("input.encrypted.rsa", {binmode=>':raw'}, $encrypted);
+
+Decrypt file (from commandline):
+
+ openssl rsautl -decrypt -inkey rsakey.priv.pem -in input.encrypted.rsa
+
+=head2 Sign by OpenSSL, verify by Crypt::PK::RSA
+
+Create signature (from commandline):
+
+ openssl dgst -sha1 -sign rsakey.priv.pem -out input.sha1-rsa.sig input.data
+
+Verify signature (Perl code):
+
+ use Crypt::PK::RSA;
+ use Crypt::Digest 'digest_file';
+ use File::Slurp 'read_file';
+
+ my $pkrsa = Crypt::PK::RSA->new("rsakey.pub.pem");
+ my $signature = read_file("input.sha1-rsa.sig", binmode=>':raw');
+ my $valid = $pkrsa->verify_hash($signature, digest_file("SHA1", "input.data"), "SHA1", "v1.5");
+ print $valid ? "SUCCESS" : "FAILURE";
+
+=head2 Sign by Crypt::PK::RSA, verify by OpenSSL
+
+Create signature (Perl code):
+
+ use Crypt::PK::RSA;
+ use Crypt::Digest 'digest_file';
+ use File::Slurp 'write_file';
+
+ my $pkrsa = Crypt::PK::RSA->new("rsakey.priv.pem");
+ my $signature = $pkrsa->sign_hash(digest_file("SHA1", "input.data"), "SHA1", "v1.5");
+ write_file("input.sha1-rsa.sig", {binmode=>':raw'}, $signature);
+
+Verify signature (from commandline):
+
+ openssl dgst -sha1 -verify rsakey.pub.pem -signature input.sha1-rsa.sig input.data
+
+=head2 Keys generated by Crypt::PK::RSA
+
+Generate keys (Perl code):
+
+ use Crypt::PK::RSA;
+ use File::Slurp 'write_file';
+
+ my $pkrsa = Crypt::PK::RSA->new;
+ $pkrsa->generate_key(256, 65537);
+ write_file("rsakey.pub.der", {binmode=>':raw'}, $pkrsa->export_key_der('public'));
+ write_file("rsakey.priv.der", {binmode=>':raw'}, $pkrsa->export_key_der('private'));
+ write_file("rsakey.pub.pem", $pkrsa->export_key_pem('public_x509'));
+ write_file("rsakey.priv.pem", $pkrsa->export_key_pem('private'));
+ write_file("rsakey-passwd.priv.pem", $pkrsa->export_key_pem('private', 'secret'));
+
+Use keys by OpenSSL:
+
+ openssl rsa -in rsakey.priv.der -text -inform der
+ openssl rsa -in rsakey.priv.pem -text
+ openssl rsa -in rsakey-passwd.priv.pem -text -inform pem -passin pass:secret
+ openssl rsa -in rsakey.pub.der -pubin -text -inform der
+ openssl rsa -in rsakey.pub.pem -pubin -text
+
+=head2 Keys generated by OpenSSL
+
+Generate keys:
+
+ openssl genrsa -out rsakey.priv.pem 1024
+ openssl rsa -in rsakey.priv.pem -out rsakey.priv.der -outform der
+ openssl rsa -in rsakey.priv.pem -out rsakey.pub.pem -pubout
+ openssl rsa -in rsakey.priv.pem -out rsakey.pub.der -outform der -pubout
+ openssl rsa -in rsakey.priv.pem -passout pass:secret -des3 -out rsakey-passwd.priv.pem
+
+Load keys (Perl code):
+
+ use Crypt::PK::RSA;
+ use File::Slurp 'write_file';
+
+ my $pkrsa = Crypt::PK::RSA->new;
+ $pkrsa->import_key("rsakey.pub.der");
+ $pkrsa->import_key("rsakey.priv.der");
+ $pkrsa->import_key("rsakey.pub.pem");
+ $pkrsa->import_key("rsakey.priv.pem");
+ $pkrsa->import_key("rsakey-passwd.priv.pem", "secret");
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<https://en.wikipedia.org/wiki/RSA_%28algorithm%29|https://en.wikipedia.org/wiki/RSA_%28algorithm%29>
+
+=back
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
diff --git a/lib/Crypt/PRNG/ChaCha20.pm b/lib/Crypt/PRNG/ChaCha20.pm
new file mode 100644
index 00000000..b9ef7779
--- /dev/null
+++ b/lib/Crypt/PRNG/ChaCha20.pm
@@ -0,0 +1,159 @@
+package Crypt::PRNG::ChaCha20;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::PRNG 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();
+
+use CryptX;
+use base 'Crypt::PRNG';
+
+{
+ ### stolen from Bytes::Random::Secure
+ my $RNG_object = undef;
+ my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once.
+ $RNG_object = Crypt::PRNG::ChaCha20->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::ChaCha20 - Cryptographically secure PRNG based on ChaCha20 (stream cipher) algorithm
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::PRNG::ChaCha20 qw(random_bytes random_bytes_hex random_bytes_b64 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::ChaCha20;
+
+ $prng = Crypt::PRNG::ChaCha20->new;
+ #or
+ $prng = Crypt::PRNG::ChaCha20->new("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 = rand;
+ $floating_point_number_0_to_88 = rand(88);
+ $unsigned_32bit_int = irand;
+
+=head1 DESCRIPTION
+
+Provides an interface to the ChaCha20 based pseudo random number generator
+
+All methods and functions are the same as for L<Crypt::PRNG>.
+
+=head1 FUNCTIONS
+
+=head2 random_bytes
+
+See L<Crypt::PRNG/random_bytes>.
+
+=head2 random_bytes_hex
+
+See L<Crypt::PRNG/random_bytes_hex>.
+
+=head2 random_bytes_b64
+
+See L<Crypt::PRNG/random_bytes_b64>.
+
+=head2 random_bytes_b64u
+
+See L<Crypt::PRNG/random_bytes_b64u>.
+
+=head2 random_string
+
+See L<Crypt::PRNG/random_string>.
+
+=head2 random_string_from
+
+See L<Crypt::PRNG/random_string_from>.
+
+=head2 rand
+
+See L<Crypt::PRNG/rand>.
+
+=head2 irand
+
+See L<Crypt::PRNG/irand>.
+
+=head1 METHODS
+
+=head2 new
+
+See L<Crypt::PRNG/new>.
+
+=head2 bytes
+
+See L<Crypt::PRNG/bytes>.
+
+=head2 bytes_hex
+
+See L<Crypt::PRNG/bytes_hex>.
+
+=head2 bytes_b64
+
+See L<Crypt::PRNG/bytes_b64>.
+
+=head2 bytes_b64u
+
+See L<Crypt::PRNG/bytes_b64u>.
+
+=head2 string
+
+See L<Crypt::PRNG/string>.
+
+=head2 string_from
+
+See L<Crypt::PRNG/string_from>.
+
+=head2 double
+
+See L<Crypt::PRNG/double>.
+
+=head2 int32
+
+See L<Crypt::PRNG/int32>.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::PRNG>
+
+=item * L<https://tools.ietf.org/html/rfc7539>
+
+=back
diff --git a/lib/Crypt/PRNG/Fortuna.pm b/lib/Crypt/PRNG/Fortuna.pm
new file mode 100644
index 00000000..5ef50297
--- /dev/null
+++ b/lib/Crypt/PRNG/Fortuna.pm
@@ -0,0 +1,160 @@
+package Crypt::PRNG::Fortuna;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::PRNG 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();
+
+use CryptX;
+use base 'Crypt::PRNG';
+
+{
+ ### stolen from Bytes::Random::Secure
+ my $RNG_object = undef;
+ my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once.
+ $RNG_object = Crypt::PRNG::Fortuna->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::Fortuna - Cryptographically secure PRNG based on Fortuna algorithm
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::PRNG::Fortuna 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::Fortuna;
+
+ $prng = Crypt::PRNG::Fortuna->new;
+ #or
+ $prng = Crypt::PRNG::Fortuna->new("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 = rand;
+ $floating_point_number_0_to_88 = rand(88);
+ $unsigned_32bit_int = irand;
+
+=head1 DESCRIPTION
+
+Provides an interface to the Fortuna based pseudo random number generator
+
+All methods and functions are the same as for L<Crypt::PRNG>.
+
+=head1 FUNCTIONS
+
+=head2 random_bytes
+
+See L<Crypt::PRNG/random_bytes>.
+
+=head2 random_bytes_hex
+
+See L<Crypt::PRNG/random_bytes_hex>.
+
+=head2 random_bytes_b64
+
+See L<Crypt::PRNG/random_bytes_b64>.
+
+=head2 random_bytes_b64u
+
+See L<Crypt::PRNG/random_bytes_b64u>.
+
+=head2 random_string
+
+See L<Crypt::PRNG/random_string>.
+
+=head2 random_string_from
+
+See L<Crypt::PRNG/random_string_from>.
+
+=head2 rand
+
+See L<Crypt::PRNG/rand>.
+
+=head2 irand
+
+See L<Crypt::PRNG/irand>.
+
+=head1 METHODS
+
+=head2 new
+
+See L<Crypt::PRNG/new>.
+
+=head2 bytes
+
+See L<Crypt::PRNG/bytes>.
+
+=head2 bytes_hex
+
+See L<Crypt::PRNG/bytes_hex>.
+
+=head2 bytes_b64
+
+See L<Crypt::PRNG/bytes_b64>.
+
+=head2 bytes_b64u
+
+See L<Crypt::PRNG/bytes_b64u>.
+
+=head2 string
+
+See L<Crypt::PRNG/string>.
+
+=head2 string_from
+
+See L<Crypt::PRNG/string_from>.
+
+=head2 double
+
+See L<Crypt::PRNG/double>.
+
+=head2 int32
+
+See L<Crypt::PRNG/int32>.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::PRNG>
+
+=item * L<https://en.wikipedia.org/wiki/Fortuna_%28PRNG%29|https://en.wikipedia.org/wiki/Fortuna_%28PRNG%29>
+
+=back
diff --git a/lib/Crypt/PRNG/RC4.pm b/lib/Crypt/PRNG/RC4.pm
new file mode 100644
index 00000000..fa5d6223
--- /dev/null
+++ b/lib/Crypt/PRNG/RC4.pm
@@ -0,0 +1,159 @@
+package Crypt::PRNG::RC4;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::PRNG 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();
+
+use CryptX;
+use base 'Crypt::PRNG';
+
+{
+ ### stolen from Bytes::Random::Secure
+ my $RNG_object = undef;
+ my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once.
+ $RNG_object = Crypt::PRNG::RC4->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::RC4 - Cryptographically secure PRNG based on RC4 (stream cipher) algorithm
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::PRNG::RC4 qw(random_bytes random_bytes_hex random_bytes_b64 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::RC4;
+
+ $prng = Crypt::PRNG::RC4->new;
+ #or
+ $prng = Crypt::PRNG::RC4->new("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 = rand;
+ $floating_point_number_0_to_88 = rand(88);
+ $unsigned_32bit_int = irand;
+
+=head1 DESCRIPTION
+
+Provides an interface to the RC4 based pseudo random number generator
+
+All methods and functions are the same as for L<Crypt::PRNG>.
+
+=head1 FUNCTIONS
+
+=head2 random_bytes
+
+See L<Crypt::PRNG/random_bytes>.
+
+=head2 random_bytes_hex
+
+See L<Crypt::PRNG/random_bytes_hex>.
+
+=head2 random_bytes_b64
+
+See L<Crypt::PRNG/random_bytes_b64>.
+
+=head2 random_bytes_b64u
+
+See L<Crypt::PRNG/random_bytes_b64u>.
+
+=head2 random_string
+
+See L<Crypt::PRNG/random_string>.
+
+=head2 random_string_from
+
+See L<Crypt::PRNG/random_string_from>.
+
+=head2 rand
+
+See L<Crypt::PRNG/rand>.
+
+=head2 irand
+
+See L<Crypt::PRNG/irand>.
+
+=head1 METHODS
+
+=head2 new
+
+See L<Crypt::PRNG/new>.
+
+=head2 bytes
+
+See L<Crypt::PRNG/bytes>.
+
+=head2 bytes_hex
+
+See L<Crypt::PRNG/bytes_hex>.
+
+=head2 bytes_b64
+
+See L<Crypt::PRNG/bytes_b64>.
+
+=head2 bytes_b64u
+
+See L<Crypt::PRNG/bytes_b64u>.
+
+=head2 string
+
+See L<Crypt::PRNG/string>.
+
+=head2 string_from
+
+See L<Crypt::PRNG/string_from>.
+
+=head2 double
+
+See L<Crypt::PRNG/double>.
+
+=head2 int32
+
+See L<Crypt::PRNG/int32>.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::PRNG>
+
+=item * L<https://en.wikipedia.org/wiki/RC4_cipher|https://en.wikipedia.org/wiki/RC4_cipher>
+
+=back
diff --git a/lib/Crypt/PRNG/Sober128.pm b/lib/Crypt/PRNG/Sober128.pm
new file mode 100644
index 00000000..b27230f7
--- /dev/null
+++ b/lib/Crypt/PRNG/Sober128.pm
@@ -0,0 +1,159 @@
+package Crypt::PRNG::Sober128;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::PRNG 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();
+
+use CryptX;
+use base 'Crypt::PRNG';
+
+{
+ ### stolen from Bytes::Random::Secure
+ my $RNG_object = undef;
+ my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once.
+ $RNG_object = Crypt::PRNG::Sober128->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::Sober128 - Cryptographically secure PRNG based on Sober128 (stream cipher) algorithm
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::PRNG::Sober128 qw(random_bytes random_bytes_hex random_bytes_b64 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::Sober128;
+
+ $prng = Crypt::PRNG::Sober128->new;
+ #or
+ $prng = Crypt::PRNG::Sober128->new("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 = rand;
+ $floating_point_number_0_to_88 = rand(88);
+ $unsigned_32bit_int = irand;
+
+=head1 DESCRIPTION
+
+Provides an interface to the Sober128 based pseudo random number generator
+
+All methods and functions are the same as for L<Crypt::PRNG>.
+
+=head1 FUNCTIONS
+
+=head2 random_bytes
+
+See L<Crypt::PRNG/random_bytes>.
+
+=head2 random_bytes_hex
+
+See L<Crypt::PRNG/random_bytes_hex>.
+
+=head2 random_bytes_b64
+
+See L<Crypt::PRNG/random_bytes_b64>.
+
+=head2 random_bytes_b64u
+
+See L<Crypt::PRNG/random_bytes_b64u>.
+
+=head2 random_string
+
+See L<Crypt::PRNG/random_string>.
+
+=head2 random_string_from
+
+See L<Crypt::PRNG/random_string_from>.
+
+=head2 rand
+
+See L<Crypt::PRNG/rand>.
+
+=head2 irand
+
+See L<Crypt::PRNG/irand>.
+
+=head1 METHODS
+
+=head2 new
+
+See L<Crypt::PRNG/new>.
+
+=head2 bytes
+
+See L<Crypt::PRNG/bytes>.
+
+=head2 bytes_hex
+
+See L<Crypt::PRNG/bytes_hex>.
+
+=head2 bytes_b64
+
+See L<Crypt::PRNG/bytes_b64>.
+
+=head2 bytes_b64u
+
+See L<Crypt::PRNG/bytes_b64u>.
+
+=head2 string
+
+See L<Crypt::PRNG/string>.
+
+=head2 string_from
+
+See L<Crypt::PRNG/string_from>.
+
+=head2 double
+
+See L<Crypt::PRNG/double>.
+
+=head2 int32
+
+See L<Crypt::PRNG/int32>.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::PRNG>
+
+=item * L<https://en.wikipedia.org/wiki/SOBER-128|https://en.wikipedia.org/wiki/SOBER-128>
+
+=back
diff --git a/lib/Crypt/PRNG/Yarrow.pm b/lib/Crypt/PRNG/Yarrow.pm
new file mode 100644
index 00000000..3a04befc
--- /dev/null
+++ b/lib/Crypt/PRNG/Yarrow.pm
@@ -0,0 +1,158 @@
+package Crypt::PRNG::Yarrow;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use base qw(Crypt::PRNG 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();
+
+use CryptX;
+
+{
+ ### stolen from Bytes::Random::Secure
+ my $RNG_object = undef;
+ my $fetch_RNG = sub { # Lazily, instantiate the RNG object, but only once.
+ $RNG_object = Crypt::PRNG::Yarrow->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::Yarrow - Cryptographically secure PRNG based on Yarrow algorithm
+
+=head1 SYNOPSIS
+
+ ### Functional interface:
+ use Crypt::PRNG::Yarrow qw(random_bytes random_bytes_hex random_bytes_b64 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::Yarrow;
+
+ $prng = Crypt::PRNG::Yarrow->new;
+ #or
+ $prng = Crypt::PRNG::Yarrow->new("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 = rand;
+ $floating_point_number_0_to_88 = rand(88);
+ $unsigned_32bit_int = irand;
+
+=head1 DESCRIPTION
+
+Provides an interface to the Yarrow based pseudo random number generator
+
+All methods and functions are the same as for L<Crypt::PRNG>.
+
+=head1 FUNCTIONS
+
+=head2 random_bytes
+
+See L<Crypt::PRNG/random_bytes>.
+
+=head2 random_bytes_hex
+
+See L<Crypt::PRNG/random_bytes_hex>.
+
+=head2 random_bytes_b64
+
+See L<Crypt::PRNG/random_bytes_b64>.
+
+=head2 random_bytes_b64u
+
+See L<Crypt::PRNG/random_bytes_b64u>.
+
+=head2 random_string
+
+See L<Crypt::PRNG/random_string>.
+
+=head2 random_string_from
+
+See L<Crypt::PRNG/random_string_from>.
+
+=head2 rand
+
+See L<Crypt::PRNG/rand>.
+
+=head2 irand
+
+See L<Crypt::PRNG/irand>.
+
+=head1 METHODS
+
+=head2 new
+
+See L<Crypt::PRNG/new>.
+
+=head2 bytes
+
+See L<Crypt::PRNG/bytes>.
+
+=head2 bytes_hex
+
+See L<Crypt::PRNG/bytes_hex>.
+
+=head2 bytes_b64
+
+See L<Crypt::PRNG/bytes_b64>.
+
+=head2 bytes_b64u
+
+See L<Crypt::PRNG/bytes_b64u>.
+
+=head2 string
+
+See L<Crypt::PRNG/string>.
+
+=head2 string_from
+
+See L<Crypt::PRNG/string_from>.
+
+=head2 double
+
+See L<Crypt::PRNG/double>.
+
+=head2 int32
+
+See L<Crypt::PRNG/int32>.
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::PRNG>
+
+=item * L<https://en.wikipedia.org/wiki/Yarrow_algorithm|https://en.wikipedia.org/wiki/Yarrow_algorithm>
+
+=back
diff --git a/lib/Crypt/Stream/ChaCha.pm b/lib/Crypt/Stream/ChaCha.pm
new file mode 100644
index 00000000..bbdc7dd9
--- /dev/null
+++ b/lib/Crypt/Stream/ChaCha.pm
@@ -0,0 +1,76 @@
+package Crypt::Stream::ChaCha;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+
+sub new { my $class = shift; _new(@_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Stream::ChaCha - Stream cipher ChaCha
+
+=head1 SYNOPSIS
+
+ use Crypt::Stream::ChaCha;
+
+ # encrypt
+ $key = "1234567890123456";
+ $iv = "123456789012";
+ $stream = Crypt::Stream::ChaCha->new($key, $iv);
+ $ct = $stream->crypt("plain message");
+
+ # decrypt
+ $key = "1234567890123456";
+ $iv = "123456789012";
+ $stream = Crypt::Stream::ChaCha->new($key, $iv);
+ $pt = $stream->crypt($ct);
+
+=head1 DESCRIPTION
+
+Provides an interface to the ChaCha stream cipher.
+
+=head1 METHODS
+
+=head2 new
+
+ $stream = Crypt::Stream::ChaCha->new($key, $iv);
+ #or
+ $stream = Crypt::Stream::ChaCha->new($key, $iv, $counter, $rounds);
+
+ # $key .. 32 or 16 bytes
+ # $iv .. 8 or 12 bytes
+ # $counter .. initial counter value (DEFAULT: 0)
+ # $rounds .. rounds (DEFAULT: 20)
+
+=head2 crypt
+
+ $ciphertext = $stream->crypt($plaintext);
+ #or
+ $plaintext = $stream->crypt($ciphertext);
+
+=head2 keystream
+
+ $random_key = $stream->keystream($length);
+
+=head2 clone
+
+ $stream->clone();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::Stream::RC4>, L<Crypt::Stream::Sober128>
+
+=item * L<https://tools.ietf.org/html/rfc7539>
+
+=back
+
+=cut
diff --git a/lib/Crypt/Stream/RC4.pm b/lib/Crypt/Stream/RC4.pm
new file mode 100644
index 00000000..c9156866
--- /dev/null
+++ b/lib/Crypt/Stream/RC4.pm
@@ -0,0 +1,68 @@
+package Crypt::Stream::RC4;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+
+sub new { my $class = shift; _new(@_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Stream::RC4 - Stream cipher RC4
+
+=head1 SYNOPSIS
+
+ use Crypt::Stream::RC4;
+
+ # encrypt
+ $key = "1234567890123456";
+ $stream = Crypt::Stream::RC4->new($key);
+ $ct = $stream->crypt("plain message");
+
+ # decrypt
+ $key = "1234567890123456";
+ $stream = Crypt::Stream::RC4->new($key);
+ $pt = $stream->crypt($ct);
+
+=head1 DESCRIPTION
+
+Provides an interface to the RC4 stream cipher.
+
+=head1 METHODS
+
+=head2 new
+
+ $stream = Crypt::Stream::RC4->new($key);
+ # $key .. length 5-256 bytes (40 - 2048 bits)
+
+=head2 crypt
+
+ $ciphertext = $stream->crypt($plaintext);
+ #or
+ $plaintext = $stream->crypt($ciphertext);
+
+=head2 keystream
+
+ $random_key = $stream->keystream($length);
+
+=head2 clone
+
+ $stream->clone();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::Stream::ChaCha>, L<Crypt::Stream::Sober128>
+
+=item * L<https://en.wikipedia.org/wiki/RC4_cipher|https://en.wikipedia.org/wiki/RC4_cipher>
+
+=back
+
+=cut
diff --git a/lib/Crypt/Stream/Sober128.pm b/lib/Crypt/Stream/Sober128.pm
new file mode 100644
index 00000000..55366af4
--- /dev/null
+++ b/lib/Crypt/Stream/Sober128.pm
@@ -0,0 +1,71 @@
+package Crypt::Stream::Sober128;
+
+use strict;
+use warnings;
+our $VERSION = '0.048';
+
+use CryptX;
+
+sub new { my $class = shift; _new(@_) }
+
+1;
+
+=pod
+
+=head1 NAME
+
+Crypt::Stream::Sober128 - Stream cipher Sober128
+
+=head1 SYNOPSIS
+
+ use Crypt::Stream::Sober128;
+
+ # encrypt
+ $key = "1234567890123456";
+ $iv = "123456789012";
+ $stream = Crypt::Stream::Sober128->new($key, $iv);
+ $ct = $stream->crypt("plain message");
+
+ # decrypt
+ $key = "1234567890123456";
+ $iv = "123456789012";
+ $stream = Crypt::Stream::Sober128->new($key, $iv);
+ $pt = $stream->crypt($ct);
+
+=head1 DESCRIPTION
+
+Provides an interface to the Sober128 stream cipher.
+
+=head1 METHODS
+
+=head2 new
+
+ $stream = Crypt::Stream::Sober128->new($key, $iv);
+ # $key .. keylen must be multiple of 4 bytes
+ # $iv .. ivlen must be multiple of 4 bytes
+
+=head2 crypt
+
+ $ciphertext = $stream->crypt($plaintext);
+ #or
+ $plaintext = $stream->crypt($ciphertext);
+
+=head2 keystream
+
+ $random_key = $stream->keystream($length);
+
+=head2 clone
+
+ $stream->clone();
+
+=head1 SEE ALSO
+
+=over
+
+=item * L<Crypt::Stream::RC4>, L<Crypt::Stream::ChaCha>
+
+=item * L<https://en.wikipedia.org/wiki/SOBER-128|https://en.wikipedia.org/wiki/SOBER-128>
+
+=back
+
+=cut