Repository: debris/tiny-keccak (language: rust)

Issue:
KangarooTwelve
We should consider implementing KangarooTwelve  https://keccak.team/2017/is_sha3_slow.html  which should be speed competitive with Blake2b.  At some point, someone should figure out if it retains SHA3's claims of resistance to length extension attacks.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 9b38af7..405e5de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ target
 Cargo.lock
 *.swp
 .idea
+.DS_Store
diff --git a/.travis.yml b/.travis.yml
index 835feb1..dec5ee8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,7 @@ install:
   - source ~/.cargo/env || true
 
 script:
-  - cross test --verbose --all --release --target $TARGET
+  - cross test --all-features --verbose --all --release --target $TARGET
 
 cache: cargo
 
diff --git a/Cargo.toml b/Cargo.toml
index 90eee13..606df89 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,9 +4,9 @@ description = "An implementation of the FIPS-202-defined SHA-3 and SHAKE functio
 homepage = "https://github.com/debris/tiny-keccak"
 license = "CC0-1.0"
 name = "tiny-keccak"
-version = "1.4.4"
+version = "1.5.0"
 authors = ["debris <marek.kotewicz@gmail.com>"]
-keywords = ["sha3", "sha-3", "keccak", "crypto", "tiny"]
+keywords = ["sha3", "sha-3", "keccak", "crypto", "kangarootwelve"]
 categories = ["cryptography", "no-std"]
 
 [dependencies]
@@ -25,3 +25,24 @@ members = ["comparison"]
 
 [badges]
 travis-ci = { repository = "debris/tiny-keccak", branch = "master"}
+
+[features]
+default = ["keccak"]
+keccak = []
+k12 = []
+
+[[test]]
+name = "keccak"
+required-features = ["keccak"]
+
+[[test]]
+name = "kangaroo"
+required-features = ["k12"]
+
+[[bench]]
+name = "keccak"
+required-features = ["keccak"]
+
+[[bench]]
+name = "kangaroo"
+required-features = ["k12"]
diff --git a/README.md b/README.md
index 7a3da18..ede63b1 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # tiny-keccak
 
-An implementation of the FIPS-202-defined SHA-3 and SHAKE functions.
+An implementation of sha3, shake, keccak and KangarooTwelve functions.
 
 [![Build Status][travis-image]][travis-url]
 
@@ -24,7 +24,7 @@ Add this to your `Cargo.toml`:
 
 ```toml
 [dependencies]
-tiny-keccak = "1.4.2"
+tiny-keccak = "1.5"
 ```
 
 ## Example
@@ -34,12 +34,10 @@ use tiny_keccak::Keccak;
 
 fn main() {
     let mut sha3 = Keccak::new_sha3_256();
-    let data: Vec<u8> = From::from("hello");
-    let data2: Vec<u8> = From::from("world");
 
-    sha3.update(&data);
+    sha3.update("hello".as_ref());
     sha3.update(&[b' ']);
-    sha3.update(&data2);
+    sha3.update("world".as_ref());
 
     let mut res: [u8; 32] = [0; 32];
     sha3.finalize(&mut res);
diff --git a/benches/kangaroo.rs b/benches/kangaroo.rs
new file mode 100644
index 0000000..820ca9d
--- /dev/null
+++ b/benches/kangaroo.rs
@@ -0,0 +1,18 @@
+#![feature(test)]
+
+extern crate test;
+
+use tiny_keccak::*;
+use test::Bencher;
+
+#[bench]
+fn bench_k12(b: &mut Bencher) {
+    const BYTES: usize = 32;
+    b.bytes = BYTES as u64;
+
+    b.iter(|| {
+        let data = [0u8; BYTES];
+        let mut result = [0u8; BYTES];
+        k12(&[], &data, &mut result);
+    });
+}
diff --git a/benches/sha3.rs b/benches/keccak.rs
similarity index 61%
rename from benches/sha3.rs
rename to benches/keccak.rs
index 3095f41..fab66ea 100644
--- a/benches/sha3.rs
+++ b/benches/keccak.rs
@@ -1,14 +1,8 @@
-//! should be started with:
-//! ```bash
-//! multirust run nightly cargo bench
-//! ```
-
 #![feature(test)]
 
 extern crate test;
 
 use tiny_keccak::*;
-
 use test::Bencher;
 
 #[bench]
@@ -26,11 +20,23 @@ fn bench_sha3_256_input_4096_bytes(b: &mut Bencher) {
 
 #[bench]
 fn keccakf_u64(b: &mut Bencher) {
-    const BYTES: usize = 25;
-    b.bytes = BYTES as u64;
+    const WORDS: usize = 25;
+    b.bytes = (WORDS * 8) as u64;
 
     b.iter(|| {
-        let mut data = [0u64; BYTES];
+        let mut data = [0u64; WORDS];
         keccakf(&mut data);
     });
 }
+
+#[bench]
+fn bench_keccak256(b: &mut Bencher) {
+    const BYTES: usize = 32;
+    b.bytes = BYTES as u64;
+
+    b.iter(|| {
+        let data = [0u8; BYTES];
+        let mut result = [0u8; BYTES];
+        Keccak::keccak256(&data, &mut result);
+    });
+}
diff --git a/src/kangaroo.rs b/src/kangaroo.rs
new file mode 100644
index 0000000..18026da
--- /dev/null
+++ b/src/kangaroo.rs
@@ -0,0 +1,142 @@
+use crunchy::unroll;
+use super::{KeccakFamily, Permutation, Buffer};
+
+const ROUNDS: usize = 12;
+const K12_RATE: usize = 168;
+
+const RC: [u64; ROUNDS] = [
+	0x000000008000808b,
+	0x800000000000008b,
+	0x8000000000008089,
+	0x8000000000008003,
+	0x8000000000008002,
+	0x8000000000000080,
+	0x000000000000800a,
+	0x800000008000000a,
+	0x8000000080008081,
+	0x8000000000008080,
+	0x0000000080000001,
+	0x8000000080008008,
+];
+
+/// keccak-f[1600, 12]
+keccak_function!(keccakf, ROUNDS, RC);
+
+struct Reduced;
+
+impl Permutation for Reduced {
+    #[inline]
+    fn execute(buffer: &mut Buffer) {
+        keccakf(buffer.words());
+    }
+}
+
+/// KangarooTwelve's length encoding.
+struct EncodedLen {
+    offset: usize,
+    buffer: [u8; 9],
+}
+
+impl EncodedLen {
+    fn new(len: usize) -> Self {
+        let len_view = (len as u64).to_be_bytes();
+        let offset = len_view.iter().position(|i| *i != 0).unwrap_or(8);
+        let mut buffer = [0u8; 9];
+        buffer[..8].copy_from_slice(&len_view);
+        buffer[8] = 8 - offset as u8;
+
+        EncodedLen {
+            offset,
+            buffer,
+        }
+    }
+
+    fn value(&self) -> &[u8] {
+        &self.buffer[self.offset..]
+    }
+}
+
+/// Hashes the data with `KangarooTwelve` hash function using custom string.
+pub fn k12(custom_string: &[u8], data: &[u8], result: &mut [u8]) {
+    let mut k12 = KangarooTwelve::new(custom_string);
+    k12.update(data);
+    k12.finalize(result);
+}
+
+/// KangarooTwelve implementation.
+#[derive(Clone)]
+pub struct KangarooTwelve<T> {
+    state: KeccakFamily<Reduced>,
+    current_chunk: KeccakFamily<Reduced>,
+    custom_string: Option<T>,
+    written: usize,
+    chunks: usize,
+}
+
+impl<T: AsRef<[u8]>> KangarooTwelve<T> {
+    const MAX_CHUNK_SIZE: usize = 8192;
+
+    pub fn new(custom_string: T) -> Self {
+        KangarooTwelve {
+            state: KeccakFamily::new(K12_RATE, 0),
+            current_chunk: KeccakFamily::new(K12_RATE, 0x0b),
+            custom_string: Some(custom_string),
+            written: 0,
+            chunks: 0,
+        }
+    }
+
+    pub fn update(&mut self, input: &[u8]) {
+        let mut to_absorb = input;
+        if self.chunks == 0 {
+            let todo = core::cmp::min(Self::MAX_CHUNK_SIZE - self.written, to_absorb.len());
+            self.state.update(&to_absorb[..todo]);
+            self.written += todo;
+            to_absorb = &to_absorb[todo..];
+
+            if to_absorb.len() > 0 && self.written == Self::MAX_CHUNK_SIZE {
+                self.state.update(&[0x03, 0, 0, 0, 0, 0, 0, 0]);
+                self.written = 0;
+                self.chunks += 1;
+            }
+        }
+
+        while to_absorb.len() > 0 {
+            if self.written == Self::MAX_CHUNK_SIZE {
+                let mut chunk_hash = [0u8; 32];
+                let current_chunk = core::mem::replace(&mut self.current_chunk, KeccakFamily::new(K12_RATE, 0x0b));
+                current_chunk.finalize(&mut chunk_hash);
+                self.state.update(&chunk_hash);
+                self.written = 0;
+                self.chunks += 1;
+            }
+
+            let todo = core::cmp::min(Self::MAX_CHUNK_SIZE - self.written, to_absorb.len());
+            self.current_chunk.update(&to_absorb[..todo]);
+            self.written += todo;
+            to_absorb = &to_absorb[todo..];
+        }
+    }
+
+    pub fn finalize(mut self, output: &mut [u8]) {
+        let custom_string = self.custom_string.take()
+            .expect("KangarooTwelve cannot be initialized without custom_string; qed");
+        let encoded_len = EncodedLen::new(custom_string.as_ref().len());
+        self.update(custom_string.as_ref());
+        self.update(encoded_len.value());
+
+        if self.chunks == 0 {
+            self.state.delim = 0x07;
+        } else {
+            let encoded_chunks = EncodedLen::new(self.chunks);
+            let mut tmp_chunk = [0u8; 32];
+            self.current_chunk.finalize(&mut tmp_chunk);
+            self.state.update(&tmp_chunk);
+            self.state.update(encoded_chunks.value());
+            self.state.update(&[0xff, 0xff]);
+            self.state.delim = 0x06;
+        }
+
+        self.state.finalize(output);
+    }
+}
diff --git a/src/keccak.rs b/src/keccak.rs
new file mode 100644
index 0000000..cafcbf8
--- /dev/null
+++ b/src/keccak.rs
@@ -0,0 +1,200 @@
+use crunchy::unroll;
+use super::{KeccakFamily, Permutation, Buffer};
+
+const ROUNDS: usize = 24;
+
+const RC: [u64; ROUNDS] = [
+    1u64,
+    0x8082u64,
+    0x800000000000808au64,
+    0x8000000080008000u64,
+    0x808bu64,
+    0x80000001u64,
+    0x8000000080008081u64,
+    0x8000000000008009u64,
+    0x8au64,
+    0x88u64,
+    0x80008009u64,
+    0x8000000au64,
+    0x8000808bu64,
+    0x800000000000008bu64,
+    0x8000000000008089u64,
+    0x8000000000008003u64,
+    0x8000000000008002u64,
+    0x8000000000000080u64,
+    0x800au64,
+    0x800000008000000au64,
+    0x8000000080008081u64,
+    0x8000000000008080u64,
+    0x80000001u64,
+    0x8000000080008008u64,
+];
+
+/// keccak-f[1600, 24]
+keccak_function!(keccakf, ROUNDS, RC);
+
+macro_rules! impl_constructor {
+    ($name: ident, $alias: ident, $bits: expr, $delim: expr) => {
+        pub fn $name() -> Keccak {
+            Keccak::new(200 - $bits / 4, $delim)
+        }
+
+        pub fn $alias(data: &[u8], result: &mut [u8]) {
+            let mut keccak = Keccak::$name();
+            keccak.update(data);
+            keccak.finalize(result);
+        }
+    };
+}
+
+macro_rules! impl_global_alias {
+    ($alias: ident, $size: expr) => {
+        pub fn $alias(data: &[u8]) -> [u8; $size / 8] {
+            let mut result = [0u8; $size / 8];
+            Keccak::$alias(data, &mut result);
+            result
+        }
+    };
+}
+
+impl_global_alias!(shake128, 128);
+impl_global_alias!(shake256, 256);
+impl_global_alias!(keccak224, 224);
+impl_global_alias!(keccak256, 256);
+impl_global_alias!(keccak384, 384);
+impl_global_alias!(keccak512, 512);
+impl_global_alias!(sha3_224, 224);
+impl_global_alias!(sha3_256, 256);
+impl_global_alias!(sha3_384, 384);
+impl_global_alias!(sha3_512, 512);
+
+pub(crate) struct Normal;
+
+impl Permutation for Normal {
+    #[inline]
+    fn execute(buffer: &mut Buffer) {
+        keccakf(buffer.words());
+    }
+}
+
+/// shake, keccak and sha3 implementation.
+///
+/// ```rust
+/// use tiny_keccak::Keccak;
+///
+/// fn main() {
+///     let mut sha3 = Keccak::new_sha3_256();
+///
+///     sha3.update("hello".as_ref());
+///     sha3.update(&[b' ']);
+///     sha3.update("world".as_ref());
+///
+///     let mut res: [u8; 32] = [0; 32];
+///     sha3.finalize(&mut res);
+///
+///     let expected = vec![
+///         0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04,
+///         0x09, 0x99, 0xaa, 0xc8, 0x9e, 0x76, 0x22, 0xf3,
+///         0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94,
+///         0xa3, 0x1c, 0x3b, 0xfb, 0xf2, 0x4e, 0x39, 0x38
+///     ];
+///
+///     let ref_ex: &[u8] = &expected;
+///     assert_eq!(&res, ref_ex);
+/// }
+/// ```
+#[derive(Clone)]
+pub struct Keccak {
+    state: KeccakFamily<Normal>
+}
+
+impl Keccak {
+    pub fn new(rate: usize, delim: u8) -> Keccak {
+        Keccak {
+            state: KeccakFamily::new(rate, delim),
+        }
+    }
+
+    impl_constructor!(new_shake128, shake128, 128, 0x1f);
+    impl_constructor!(new_shake256, shake256, 256, 0x1f);
+    impl_constructor!(new_keccak224, keccak224, 224, 0x01);
+    impl_constructor!(new_keccak256, keccak256, 256, 0x01);
+    impl_constructor!(new_keccak384, keccak384, 384, 0x01);
+    impl_constructor!(new_keccak512, keccak512, 512, 0x01);
+    impl_constructor!(new_sha3_224, sha3_224, 224, 0x06);
+    impl_constructor!(new_sha3_256, sha3_256, 256, 0x06);
+    impl_constructor!(new_sha3_384, sha3_384, 384, 0x06);
+    impl_constructor!(new_sha3_512, sha3_512, 512, 0x06);
+
+    pub fn update(&mut self, input: &[u8]) {
+        self.state.update(input);
+    }
+
+    #[deprecated(
+        since = "1.5.0",
+        note = "Please use the update function instead"
+    )]
+    pub fn absorb(&mut self, input: &[u8]) {
+        self.state.update(input);
+    }
+
+    pub fn keccakf(&mut self) {
+        self.state.keccakf()
+    }
+
+    pub fn finalize(self, output: &mut [u8]) {
+        self.state.finalize(output);
+    }
+
+    pub fn pad(&mut self) {
+        self.state.pad();
+    }
+
+    pub fn fill_block(&mut self) {
+        self.state.keccakf();
+        self.state.offset = 0;
+    }
+
+    pub fn squeeze(&mut self, output: &mut [u8]) {
+        self.state.squeeze(output);
+    }
+
+    #[inline]
+    pub fn xof(mut self) -> XofReader {
+        self.pad();
+
+        self.keccakf();
+
+        XofReader {
+            keccak: self.state,
+            offset: 0,
+        }
+    }
+}
+
+pub struct XofReader {
+    keccak: KeccakFamily<Normal>,
+    offset: usize,
+}
+
+impl XofReader {
+    pub fn squeeze(&mut self, output: &mut [u8]) {
+        // second foldp
+        let mut op = 0;
+        let mut l = output.len();
+        let mut rate = self.keccak.rate - self.offset;
+        let mut offset = self.offset;
+        while l >= rate {
+            self.keccak.buffer.setout(&mut output[op..], offset, rate);
+            self.keccak.keccakf();
+            op += rate;
+            l -= rate;
+            rate = self.keccak.rate;
+            offset = 0;
+        }
+
+        self.keccak.buffer.setout(&mut output[op..], offset, l);
+        self.offset = offset + l;
+    }
+}
+
diff --git a/src/lib.rs b/src/lib.rs
index 7f6f822..78662c4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,13 +1,4 @@
-//! An implementation of the FIPS-202-defined SHA-3 and SHAKE functions.
-//!
-//! The `Keccak-f[1600]` permutation is fully unrolled; it's nearly as fast
-//! as the Keccak team's optimized permutation.
-//!
-//! ## Building
-//!
-//! ```bash
-//! cargo build
-//! ```
+//! An implementation of sha3, shake, keccak and KangarooTwelve functions.
 //!
 //! ## Usage
 //!
@@ -15,24 +6,22 @@
 //!
 //! ```toml
 //! [dependencies]
-//! tiny-keccak = "1.0"
+//! tiny-keccak = "1.5"
 //! ```
 //!
-//! Original implementation in C:
-//! https://github.com/coruus/keccak-tiny
-//!
-//! Implementor: David Leon Gil
+//! ## Features
+//! - keccak (enabled by default)
+//! - k12 (**not** enabled by default, implements KangarooTwelve)
 //!
-//! Port to rust:
-//! Marek Kotewicz (marek.kotewicz@gmail.com)
+//! Inspired by implementations:
+//! - [keccak-tiny](https://github.com/coruus/keccak-tiny)
+//! - [GoKangarooTwelve](https://github.com/mimoo/GoKangarooTwelve)
 //!
 //! License: CC0, attribution kindly requested. Blame taken too,
 //! but not liability.
 
 #![no_std]
 
-use crunchy::unroll;
-
 const RHO: [u32; 24] = [
     1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
 ];
@@ -41,111 +30,105 @@ const PI: [usize; 24] = [
     10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
 ];
 
-const RC: [u64; 24] = [
-    1u64,
-    0x8082u64,
-    0x800000000000808au64,
-    0x8000000080008000u64,
-    0x808bu64,
-    0x80000001u64,
-    0x8000000080008081u64,
-    0x8000000000008009u64,
-    0x8au64,
-    0x88u64,
-    0x80008009u64,
-    0x8000000au64,
-    0x8000808bu64,
-    0x800000000000008bu64,
-    0x8000000000008089u64,
-    0x8000000000008003u64,
-    0x8000000000008002u64,
-    0x8000000000000080u64,
-    0x800au64,
-    0x800000008000000au64,
-    0x8000000080008081u64,
-    0x8000000000008080u64,
-    0x80000001u64,
-    0x8000000080008008u64,
-];
+const WORDS: usize = 25;
+
+macro_rules! keccak_function {
+    ($name: ident, $rounds: expr, $rc: expr) => {
 
-#[allow(unused_assignments)]
-/// keccak-f[1600]
-pub fn keccakf(a: &mut [u64; PLEN]) {
-    for i in 0..24 {
-        let mut array: [u64; 5] = [0; 5];
+        #[allow(unused_assignments)]
+        #[allow(non_upper_case_globals)]
+        pub fn $name(a: &mut [u64; $crate::WORDS]) {
+            for i in 0..$rounds {
+                let mut array: [u64; 5] = [0; 5];
 
-        // Theta
-        unroll! {
-            for x in 0..5 {
+                // Theta
                 unroll! {
-                    for y_count in 0..5 {
-                        let y = y_count * 5;
-                        array[x] ^= a[x + y];
+                    for x in 0..5 {
+                        unroll! {
+                            for y_count in 0..5 {
+                                let y = y_count * 5;
+                                array[x] ^= a[x + y];
+                            }
+                        }
                     }
                 }
-            }
-        }
 
-        unroll! {
-            for x in 0..5 {
                 unroll! {
-                    for y_count in 0..5 {
-                        let y = y_count * 5;
-                        a[y + x] ^= array[(x + 4) % 5] ^ array[(x + 1) % 5].rotate_left(1);
+                    for x in 0..5 {
+                        unroll! {
+                            for y_count in 0..5 {
+                                let y = y_count * 5;
+                                a[y + x] ^= array[(x + 4) % 5] ^ array[(x + 1) % 5].rotate_left(1);
+                            }
+                        }
                     }
                 }
-            }
-        }
-
-        // Rho and pi
-        let mut last = a[1];
-        unroll! {
-            for x in 0..24 {
-                array[0] = a[PI[x]];
-                a[PI[x]] = last.rotate_left(RHO[x]);
-                last = array[0];
-            }
-        }
-
-        // Chi
-        unroll! {
-            for y_step in 0..5 {
-                let y = y_step * 5;
 
+                // Rho and pi
+                let mut last = a[1];
                 unroll! {
-                    for x in 0..5 {
-                        array[x] = a[y + x];
+                    for x in 0..24 {
+                        array[0] = a[$crate::PI[x]];
+                        a[$crate::PI[x]] = last.rotate_left($crate::RHO[x]);
+                        last = array[0];
                     }
                 }
 
+                // Chi
                 unroll! {
-                    for x in 0..5 {
-                        a[y + x] = array[x] ^ ((!array[(x + 1) % 5]) & (array[(x + 2) % 5]));
+                    for y_step in 0..5 {
+                        let y = y_step * 5;
+
+                        unroll! {
+                            for x in 0..5 {
+                                array[x] = a[y + x];
+                            }
+                        }
+
+                        unroll! {
+                            for x in 0..5 {
+                                a[y + x] = array[x] ^ ((!array[(x + 1) % 5]) & (array[(x + 2) % 5]));
+                            }
+                        }
                     }
-                }
+                };
+
+                // Iota
+                a[0] ^= $rc[i];
             }
-        };
+        }
 
-        // Iota
-        a[0] ^= RC[i];
     }
 }
 
-/// Total number of lanes.
-const PLEN: usize = 25;
+#[cfg(feature = "k12")]
+mod kangaroo;
+
+#[cfg(feature = "keccak")]
+mod keccak;
+
+#[cfg(feature = "k12")]
+pub use kangaroo::{k12, KangarooTwelve, keccakf as keccakf12};
+
+#[cfg(feature = "keccak")]
+pub use keccak::*;
+
+trait Permutation {
+    fn execute(a: &mut Buffer);
+}
 
 #[derive(Default, Clone)]
-struct Buffer([u64; PLEN]);
+struct Buffer([u64; WORDS]);
 
 impl Buffer {
-    fn inner(&mut self) -> &mut [u64; PLEN] {
+    fn words(&mut self) -> &mut [u64; WORDS] {
         &mut self.0
     }
 
     #[cfg(target_endian = "little")]
     #[inline]
     fn execute<F: FnOnce(&mut [u8])>(&mut self, offset: usize, len: usize, f: F) {
-        let buffer: &mut [u8; PLEN * 8] = unsafe { ::core::mem::transmute(&mut self.0) };
+        let buffer: &mut [u8; WORDS * 8] = unsafe { core::mem::transmute(&mut self.0) };
         f(&mut buffer[offset..][..len]);
     }
 
@@ -161,7 +144,7 @@ impl Buffer {
         let start = offset / 8;
         let end = (offset + len + 7) / 8;
         swap_endianess(&mut self.0[start..end]);
-        let buffer: &mut [u8; PLEN * 8] = unsafe { ::core::mem::transmute(&mut self.0) };
+        let buffer: &mut [u8; WORDS * 8] = unsafe { core::mem::transmute(&mut self.0) };
         f(&mut buffer[offset..][..len]);
         swap_endianess(&mut self.0[start..end]);
     }
@@ -192,128 +175,43 @@ impl Buffer {
     }
 }
 
-/// This structure should be used to create keccak/sha3 hash.
-///
-/// ```rust
-/// use tiny_keccak::Keccak;
-///
-/// fn main() {
-///     let mut sha3 = Keccak::new_sha3_256();
-///     let data: Vec<u8> = From::from("hello");
-///     let data2: Vec<u8> = From::from("world");
-///
-///     sha3.update(&data);
-///     sha3.update(&[b' ']);
-///     sha3.update(&data2);
-///
-///     let mut res: [u8; 32] = [0; 32];
-///     sha3.finalize(&mut res);
-///
-///     let expected = vec![
-///         0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04,
-///         0x09, 0x99, 0xaa, 0xc8, 0x9e, 0x76, 0x22, 0xf3,
-///         0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94,
-///         0xa3, 0x1c, 0x3b, 0xfb, 0xf2, 0x4e, 0x39, 0x38
-///     ];
-///
-///     let ref_ex: &[u8] = &expected;
-///     assert_eq!(&res, ref_ex);
-/// }
-/// ```
-pub struct Keccak {
+struct KeccakFamily<P> {
     buffer: Buffer,
     offset: usize,
     rate: usize,
     delim: u8,
+    permutation: core::marker::PhantomData<P>,
 }
 
-impl Clone for Keccak {
+impl <P> Clone for KeccakFamily<P> {
     fn clone(&self) -> Self {
-        let mut res = Keccak::new(self.rate, self.delim);
-        res.buffer = self.buffer.clone();
-        res.offset = self.offset;
-        res
-    }
-}
-
-macro_rules! impl_constructor {
-    ($name: ident, $alias: ident, $bits: expr, $delim: expr) => {
-        pub fn $name() -> Keccak {
-            Keccak::new(200 - $bits / 4, $delim)
-        }
-
-        pub fn $alias(data: &[u8], result: &mut [u8]) {
-            let mut keccak = Keccak::$name();
-            keccak.update(data);
-            keccak.finalize(result);
+        KeccakFamily {
+            buffer: self.buffer.clone(),
+            offset: self.offset,
+            rate: self.rate,
+            delim: self.delim,
+            permutation: core::marker::PhantomData,
         }
-    };
-}
-
-macro_rules! impl_global_alias {
-    ($alias: ident, $size: expr) => {
-        pub fn $alias(data: &[u8]) -> [u8; $size / 8] {
-            let mut result = [0u8; $size / 8];
-            Keccak::$alias(data, &mut result);
-            result
-        }
-    };
+    }
 }
 
-impl_global_alias!(shake128, 128);
-impl_global_alias!(shake256, 256);
-impl_global_alias!(keccak224, 224);
-impl_global_alias!(keccak256, 256);
-impl_global_alias!(keccak384, 384);
-impl_global_alias!(keccak512, 512);
-impl_global_alias!(sha3_224, 224);
-impl_global_alias!(sha3_256, 256);
-impl_global_alias!(sha3_384, 384);
-impl_global_alias!(sha3_512, 512);
-
-impl Keccak {
-    pub fn new(rate: usize, delim: u8) -> Keccak {
+impl <P: Permutation> KeccakFamily<P> {
+    fn new(rate: usize, delim: u8) -> Self {
         assert!(rate != 0, "rate cannot be equal 0");
-        Keccak {
+        KeccakFamily {
             buffer: Buffer::default(),
             offset: 0,
             rate,
             delim,
+            permutation: core::marker::PhantomData,
         }
     }
 
-    impl_constructor!(new_shake128, shake128, 128, 0x1f);
-    impl_constructor!(new_shake256, shake256, 256, 0x1f);
-    impl_constructor!(new_keccak224, keccak224, 224, 0x01);
-    impl_constructor!(new_keccak256, keccak256, 256, 0x01);
-    impl_constructor!(new_keccak384, keccak384, 384, 0x01);
-    impl_constructor!(new_keccak512, keccak512, 512, 0x01);
-    impl_constructor!(new_sha3_224, sha3_224, 224, 0x06);
-    impl_constructor!(new_sha3_256, sha3_256, 256, 0x06);
-    impl_constructor!(new_sha3_384, sha3_384, 384, 0x06);
-    impl_constructor!(new_sha3_512, sha3_512, 512, 0x06);
-
-    pub fn update(&mut self, input: &[u8]) {
-        self.absorb(input);
+    fn keccakf(&mut self) {
+        P::execute(&mut self.buffer);
     }
 
-    #[inline]
-    pub fn keccakf(&mut self) {
-        keccakf(self.buffer.inner());
-    }
-
-    pub fn finalize(mut self, output: &mut [u8]) {
-        self.pad();
-
-        // apply keccakf
-        keccakf(self.buffer.inner());
-
-        // squeeze output
-        self.squeeze(output);
-    }
-
-    // Absorb input
-    pub fn absorb(&mut self, input: &[u8]) {
+    fn update(&mut self, input: &[u8]) {
         //first foldp
         let mut ip = 0;
         let mut l = input.len();
@@ -321,7 +219,7 @@ impl Keccak {
         let mut offset = self.offset;
         while l >= rate {
             self.buffer.xorin(&input[ip..], offset, rate);
-            keccakf(self.buffer.inner());
+            self.keccakf();
             ip += rate;
             l -= rate;
             rate = self.rate;
@@ -333,23 +231,17 @@ impl Keccak {
         self.offset = offset + l;
     }
 
-    pub fn pad(&mut self) {
+    fn pad(&mut self) {
         self.buffer.pad(self.offset, self.delim, self.rate);
     }
 
-    pub fn fill_block(&mut self) {
-        self.keccakf();
-        self.offset = 0;
-    }
-
-    // squeeze output
-    pub fn squeeze(&mut self, output: &mut [u8]) {
+    fn squeeze(&mut self, output: &mut [u8]) {
         // second foldp
         let mut op = 0;
         let mut l = output.len();
         while l >= self.rate {
             self.buffer.setout(&mut output[op..], 0, self.rate);
-            keccakf(self.buffer.inner());
+            self.keccakf();
             op += self.rate;
             l -= self.rate;
         }
@@ -357,41 +249,13 @@ impl Keccak {
         self.buffer.setout(&mut output[op..], 0, l);
     }
 
-    #[inline]
-    pub fn xof(mut self) -> XofReader {
+    fn finalize(mut self, output: &mut [u8]) {
         self.pad();
 
-        keccakf(self.buffer.inner());
-
-        XofReader {
-            keccak: self,
-            offset: 0,
-        }
-    }
-}
-
-pub struct XofReader {
-    keccak: Keccak,
-    offset: usize,
-}
-
-impl XofReader {
-    pub fn squeeze(&mut self, output: &mut [u8]) {
-        // second foldp
-        let mut op = 0;
-        let mut l = output.len();
-        let mut rate = self.keccak.rate - self.offset;
-        let mut offset = self.offset;
-        while l >= rate {
-            self.keccak.buffer.setout(&mut output[op..], offset, rate);
-            self.keccak.keccakf();
-            op += rate;
-            l -= rate;
-            rate = self.keccak.rate;
-            offset = 0;
-        }
+        // apply keccakf
+        self.keccakf();
 
-        self.keccak.buffer.setout(&mut output[op..], offset, l);
-        self.offset = offset + l;
+        // squeeze output
+        self.squeeze(output);
     }
 }


Test patch:
diff --git a/tests/kangaroo.rs b/tests/kangaroo.rs
new file mode 100644
index 0000000..70d2d0d
--- /dev/null
+++ b/tests/kangaroo.rs
@@ -0,0 +1,83 @@
+use tiny_keccak::*;
+
+fn pattern(len: usize) -> Vec<u8> {
+    (0..len).map(|j| (j % 251) as u8).collect()
+}
+
+fn test_kangaroo_twelve<A: AsRef<[u8]>, B: AsRef<[u8]>>(custom_string: A, message: B, output_len: usize, expected: &[u8]) {
+    let mut kangaroo = KangarooTwelve::new(custom_string);
+    kangaroo.update(message.as_ref());
+    let mut res = vec![0; output_len];
+    kangaroo.finalize(&mut res);
+    assert_eq!(&res[output_len - expected.len()..], expected);
+}
+
+#[test]
+fn empty_kangaroo_twelve() {
+    let expected = vec![
+        0x1a, 0xc2, 0xd4, 0x50, 0xfc, 0x3b, 0x42, 0x05, 0xd1, 0x9d, 0xa7, 0xbf, 0xca, 0x1b, 0x37,
+        0x51, 0x3c, 0x08, 0x03, 0x57, 0x7a, 0xc7, 0x16, 0x7f, 0x06, 0xfe, 0x2c, 0xe1, 0xf0, 0xef,
+        0x39, 0xe5,
+    ];
+    test_kangaroo_twelve("", "", 32, &expected);
+}
+
+#[test]
+fn kangaroo_twelve_long() {
+    let expected = vec![
+        0xe8, 0xdc, 0x56, 0x36, 0x42, 0xf7, 0x22, 0x8c, 0x84, 0x68, 0x4c, 0x89, 0x84, 0x05, 0xd3,
+        0xa8, 0x34, 0x79, 0x91, 0x58, 0xc0, 0x79, 0xb1, 0x28, 0x80, 0x27, 0x7a, 0x1d, 0x28, 0xe2,
+        0xff, 0x6d,
+    ];
+    test_kangaroo_twelve("", "", 10032, &expected);
+}
+
+#[test]
+fn kangaroo_twelve_with_message() {
+    let expected = vec![
+        0x2b, 0xda, 0x92, 0x45, 0x0e, 0x8b, 0x14, 0x7f, 0x8a, 0x7c, 0xb6, 0x29, 0xe7, 0x84, 0xa0,
+        0x58, 0xef, 0xca, 0x7c, 0xf7, 0xd8, 0x21, 0x8e, 0x02, 0xd3, 0x45, 0xdf, 0xaa, 0x65, 0x24,
+        0x4a, 0x1f,
+    ];
+    test_kangaroo_twelve("", pattern(1), 32, &expected);
+}
+
+#[test]
+fn kangaroo_twelve_with_message2() {
+    let expected = vec![
+        0x6b, 0xf7, 0x5f, 0xa2, 0x23, 0x91, 0x98, 0xdb, 0x47, 0x72, 0xe3, 0x64, 0x78, 0xf8, 0xe1,
+        0x9b, 0x0f, 0x37, 0x12, 0x05, 0xf6, 0xa9, 0xa9, 0x3a, 0x27, 0x3f, 0x51, 0xdf, 0x37, 0x12,
+        0x28, 0x88,
+    ];
+    test_kangaroo_twelve("", pattern(17), 32, &expected);
+}
+
+#[test]
+fn kangaroo_twelve_with_custom_string() {
+    let expected = vec![
+        0xfa, 0xb6, 0x58, 0xdb, 0x63, 0xe9, 0x4a, 0x24, 0x61, 0x88, 0xbf, 0x7a, 0xf6, 0x9a, 0x13,
+        0x30, 0x45, 0xf4, 0x6e, 0xe9, 0x84, 0xc5, 0x6e, 0x3c, 0x33, 0x28, 0xca, 0xaf, 0x1a, 0xa1,
+        0xa5, 0x83,
+    ];
+    test_kangaroo_twelve(pattern(1), "", 32, &expected);
+}
+
+#[test]
+fn kangaroo_twelve_with_custom_string_and_message() {
+    let expected = vec![
+        0xd8, 0x48, 0xc5, 0x06, 0x8c, 0xed, 0x73, 0x6f, 0x44, 0x62, 0x15, 0x9b, 0x98, 0x67, 0xfd,
+        0x4c, 0x20, 0xb8, 0x08, 0xac, 0xc3, 0xd5, 0xbc, 0x48, 0xe0, 0xb0, 0x6b, 0xa0, 0xa3, 0x76,
+        0x2e, 0xc4,
+    ];
+    test_kangaroo_twelve(pattern(41), &[0xff], 32, &expected);
+}
+
+#[test]
+fn kangaroo_twelve_with_custom_string_and_message2() {
+    let expected = vec![
+        0x75, 0xd2, 0xf8, 0x6a, 0x2e, 0x64, 0x45, 0x66, 0x72, 0x6b, 0x4f, 0xbc, 0xfc, 0x56, 0x57,
+        0xb9, 0xdb, 0xcf, 0x07, 0x0c, 0x7b, 0x0d, 0xca, 0x06, 0x45, 0x0a, 0xb2, 0x91, 0xd7, 0x44,
+        0x3b, 0xcf
+    ];
+    test_kangaroo_twelve(pattern(68921), &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], 32, &expected);
+}
diff --git a/tests/test.rs b/tests/keccak.rs
similarity index 100%
rename from tests/test.rs
rename to tests/keccak.rs