summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes2
-rw-r--r--CryptX.xs6
-rw-r--r--inc/CryptX_Digest_SHAKE.xs.inc74
-rw-r--r--lib/Crypt/Digest/SHAKE.pm106
-rw-r--r--t/003_all_pm_pod.t2
-rw-r--r--t/digest_shake.t36
-rw-r--r--typemap1
7 files changed, 225 insertions, 2 deletions
diff --git a/Changes b/Changes
index 0f84fbe2..00753a61 100644
--- a/Changes
+++ b/Changes
@@ -9,13 +9,13 @@ TODO:
- maybe: add CCM interface for new-add-add-done mode
- maybe: switch yarrow > fortuna for Crypt::PK::*
- maybe: add encode_b32/decode_b32
- - maybe: x509_rsa_pubkey + x509_rsa_pubkey_alg
0.047_001 2017/04/07
- NEW: Crypt::Digest::SHA3_224
- NEW: Crypt::Digest::SHA3_256
- NEW: Crypt::Digest::SHA3_384
- NEW: Crypt::Digest::SHA3_512
+ - NEW: Crypt::Digest::SHAKE
- NEW: Crypt::AuthEnc::ChaCha20Poly1305
- NEW: Crypt::Mac::Poly1305
- NEW: Crypt::PRNG::ChaCha20
diff --git a/CryptX.xs b/CryptX.xs
index 8b07bedb..aa23a95b 100644
--- a/CryptX.xs
+++ b/CryptX.xs
@@ -26,6 +26,11 @@ typedef struct digest_struct { /* used by Crypt::Digest */
struct ltc_hash_descriptor *desc;
} *Crypt__Digest;
+typedef struct digest_shake_struct { /* used by Crypt::Digest::SHAKE */
+ hash_state state;
+ int num;
+} *Crypt__Digest__SHAKE;
+
typedef struct ccm_struct { /* used by Crypt::AuthEnc::CCM */
ccm_state state;
int id;
@@ -456,6 +461,7 @@ CryptX__decode_base64(SV * in)
###############################################################################
INCLUDE: inc/CryptX_Digest.xs.inc
+INCLUDE: inc/CryptX_Digest_SHAKE.xs.inc
INCLUDE: inc/CryptX_Cipher.xs.inc
INCLUDE: inc/CryptX_Checksum_Adler32.xs.inc
diff --git a/inc/CryptX_Digest_SHAKE.xs.inc b/inc/CryptX_Digest_SHAKE.xs.inc
new file mode 100644
index 00000000..aa5335fa
--- /dev/null
+++ b/inc/CryptX_Digest_SHAKE.xs.inc
@@ -0,0 +1,74 @@
+MODULE = CryptX PACKAGE = Crypt::Digest::SHAKE
+
+Crypt::Digest::SHAKE
+_new(int num)
+ CODE:
+ {
+ int rv;
+
+ Newz(0, RETVAL, 1, struct digest_shake_struct);
+ if (!RETVAL) croak("FATAL: Newz failed");
+
+ RETVAL->num = num;
+ rv = sha3_shake_init(&RETVAL->state, RETVAL->num);
+ if (rv != CRYPT_OK) croak("FATAL: sha3_shake_init failed: %s", error_to_string(rv));
+ }
+ OUTPUT:
+ RETVAL
+
+void
+DESTROY(Crypt::Digest::SHAKE self)
+ CODE:
+ Safefree(self);
+
+void
+reset(Crypt::Digest::SHAKE self)
+ CODE:
+ {
+ int rv;
+ rv = sha3_shake_init(&self->state, self->num);
+ if (rv != CRYPT_OK) croak("FATAL: sha3_shake_init failed: %s", error_to_string(rv));
+ }
+
+Crypt::Digest::SHAKE
+clone(Crypt::Digest::SHAKE self)
+ CODE:
+ Newz(0, RETVAL, 1, struct digest_shake_struct);
+ Copy(&self->state, &RETVAL->state, 1, struct digest_shake_struct);
+ OUTPUT:
+ RETVAL
+
+void
+add(Crypt::Digest::SHAKE self, ...)
+ PPCODE:
+ {
+ STRLEN inlen;
+ int rv, i;
+ unsigned char *in;
+
+ for(i=1; i<items; i++) {
+ in = (unsigned char *)SvPVbyte(ST(i), inlen);
+ if (inlen>0) {
+ rv = sha3_shake_process(&self->state, in, (unsigned long)inlen);
+ if (rv != CRYPT_OK) croak("FATAL: sha3_shake_process failed: %s", error_to_string(rv));
+ }
+ }
+ XPUSHs(ST(0)); /* return self */
+ }
+
+SV *
+done(Crypt::Digest::SHAKE self, STRLEN out_len)
+ CODE:
+ {
+ int rv;
+ unsigned char *out_data;
+
+ RETVAL = NEWSV(0, out_len);
+ SvPOK_only(RETVAL);
+ SvCUR_set(RETVAL, out_len);
+ out_data = (unsigned char *)SvPV_nolen(RETVAL);
+ rv = sha3_shake_done(&self->state, out_data, out_len);
+ if (rv != CRYPT_OK) croak("FATAL: sha3_shake_done failed: %s", error_to_string(rv));
+ }
+ OUTPUT:
+ RETVAL
diff --git a/lib/Crypt/Digest/SHAKE.pm b/lib/Crypt/Digest/SHAKE.pm
new file mode 100644
index 00000000..709859fc
--- /dev/null
+++ b/lib/Crypt/Digest/SHAKE.pm
@@ -0,0 +1,106 @@
+package Crypt::Digest::SHAKE;
+
+use strict;
+use warnings;
+our $VERSION = '0.047_001';
+
+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/t/003_all_pm_pod.t b/t/003_all_pm_pod.t
index 16734ce4..d2228db6 100644
--- a/t/003_all_pm_pod.t
+++ b/t/003_all_pm_pod.t
@@ -5,7 +5,7 @@ use Test::More;
plan skip_all => "File::Find not installed" unless eval { require File::Find };
plan skip_all => "Test::Pod not installed" unless eval { require Test::Pod };
-plan tests => 87;
+plan tests => 88;
my @files;
File::Find::find({ wanted=>sub { push @files, $_ if /\.pm$/ }, no_chdir=>1 }, 'lib');
diff --git a/t/digest_shake.t b/t/digest_shake.t
new file mode 100644
index 00000000..c79b5dd5
--- /dev/null
+++ b/t/digest_shake.t
@@ -0,0 +1,36 @@
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+
+use Crypt::Digest::SHAKE;
+
+
+my $sh128 = Crypt::Digest::SHAKE->new(128);
+ok($sh128, "Crypt::Digest::SHAKE->new(128)");
+
+my $sh256 = Crypt::Digest::SHAKE->new(256);
+ok($sh256, "Crypt::Digest::SHAKE->new(256)");
+
+is(unpack("H*", $sh128->add("")->done(32)), "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26");
+is(unpack("H*", $sh256->add("")->done(64)), "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be");
+
+is(unpack("H*", Crypt::Digest::SHAKE->new(128)->add("The quick brown fox jumps over the lazy dog")->done(32)),
+ "f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66e");
+is(unpack("H*", Crypt::Digest::SHAKE->new(128)->add("The quick brown fox jumps over the lazy dof")->done(32)),
+ "853f4538be0db9621a6cea659a06c1107b1f83f02b13d18297bd39d7411cf10c");
+
+{
+ my $sh128 = Crypt::Digest::SHAKE->new(128);
+ $sh128->add("The qui");
+ $sh128->add("ck bro");
+ $sh128->add("wn fox j");
+ $sh128->add("umps o");
+ $sh128->add("ver the l");
+ $sh128->add("azy dof");
+ my $res = $sh128->done(5);
+ $res .= $sh128->done(7);
+ $res .= $sh128->done(8);
+ $res .= $sh128->done(12);
+ is(unpack("H*", $res), "853f4538be0db9621a6cea659a06c1107b1f83f02b13d18297bd39d7411cf10c");
+}
diff --git a/typemap b/typemap
index e9d61761..d0b9f6d4 100644
--- a/typemap
+++ b/typemap
@@ -5,6 +5,7 @@ TYPEMAP
Crypt::Cipher T_PTROBJ
Crypt::Digest T_PTROBJ
+Crypt::Digest::SHAKE T_PTROBJ
Crypt::Checksum::Adler32 T_PTROBJ
Crypt::Checksum::CRC32 T_PTROBJ