tari_tiny_keccak/
lib.rs

1//! Keccak derived functions specified in [`FIPS-202`], [`SP800-185`] and [`KangarooTwelve`].
2//!
3//! # Example
4//!
5//! ```
6//! # use tiny_keccak::Hasher;
7//! #
8//! # fn foo<H: Hasher>(mut hasher: H) {
9//! let input_a = b"hello world";
10//! let input_b = b"!";
11//! let mut output = [0u8; 32];
12//! hasher.update(input_a);
13//! hasher.update(input_b);
14//! hasher.finalize(&mut output);
15//! # }
16//! ```
17//!
18//! # Credits
19//!
20//! - [`coruus/keccak-tiny`] for C implementation of keccak function
21//! - [`@quininer`] for `no-std` support and rust implementation [`SP800-185`]
22//! - [`mimoo/GoKangarooTwelve`] for GO implementation of `KangarooTwelve`
23//! - [`@Vurich`] for optimizations
24//! - [`@oleganza`] for adding support for half-duplex use
25//!
26//! # License
27//!
28//! [`CC0`]. Attribution kindly requested. Blame taken too,
29//! but not liability.
30//!
31//! [`FIPS-202`]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
32//! [`SP800-185`]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf
33//! [`KangarooTwelve`]: https://eprint.iacr.org/2016/770.pdf
34//! [`coruus/keccak-tiny`]: https://github.com/coruus/keccak-tiny
35//! [`mimoo/GoKangarooTwelve`]: https://github.com/mimoo/GoKangarooTwelve
36//! [`@quininer`]: https://github.com/quininer
37//! [`@Vurich`]: https://github.com/Vurich
38//! [`@oleganza`]: https://github.com/oleganza
39//! [`CC0`]: https://github.com/debris/tiny-keccak/blob/master/LICENSE
40
41#![no_std]
42#![deny(missing_docs)]
43
44use borsh::io;
45use borsh::io::Write;
46use borsh::{BorshDeserialize, BorshSerialize};
47const RHO: [u32; 24] = [
48    1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
49];
50
51const PI: [usize; 24] = [
52    10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
53];
54
55const WORDS: usize = 25;
56
57macro_rules! keccak_function {
58    ($doc: expr, $name: ident, $rounds: expr, $rc: expr) => {
59        #[doc = $doc]
60        #[allow(unused_assignments)]
61        #[allow(non_upper_case_globals)]
62        pub fn $name(a: &mut [u64; $crate::WORDS]) {
63            use crunchy::unroll;
64
65            for i in 0..$rounds {
66                let mut array: [u64; 5] = [0; 5];
67
68                // Theta
69                unroll! {
70                    for x in 0..5 {
71                        unroll! {
72                            for y_count in 0..5 {
73                                let y = y_count * 5;
74                                array[x] ^= a[x + y];
75                            }
76                        }
77                    }
78                }
79
80                unroll! {
81                    for x in 0..5 {
82                        unroll! {
83                            for y_count in 0..5 {
84                                let y = y_count * 5;
85                                a[y + x] ^= array[(x + 4) % 5] ^ array[(x + 1) % 5].rotate_left(1);
86                            }
87                        }
88                    }
89                }
90
91                // Rho and pi
92                let mut last = a[1];
93                unroll! {
94                    for x in 0..24 {
95                        array[0] = a[$crate::PI[x]];
96                        a[$crate::PI[x]] = last.rotate_left($crate::RHO[x]);
97                        last = array[0];
98                    }
99                }
100
101                // Chi
102                unroll! {
103                    for y_step in 0..5 {
104                        let y = y_step * 5;
105
106                        unroll! {
107                            for x in 0..5 {
108                                array[x] = a[y + x];
109                            }
110                        }
111
112                        unroll! {
113                            for x in 0..5 {
114                                a[y + x] = array[x] ^ ((!array[(x + 1) % 5]) & (array[(x + 2) % 5]));
115                            }
116                        }
117                    }
118                };
119
120                // Iota
121                a[0] ^= $rc[i];
122            }
123        }
124    }
125}
126
127#[cfg(feature = "k12")]
128mod keccakp;
129
130#[cfg(feature = "k12")]
131pub use keccakp::keccakp;
132
133#[cfg(any(
134    feature = "keccak",
135    feature = "shake",
136    feature = "sha3",
137    feature = "cshake",
138    feature = "kmac",
139    feature = "tuple_hash",
140    feature = "parallel_hash"
141))]
142mod keccakf;
143
144#[cfg(any(
145    feature = "keccak",
146    feature = "shake",
147    feature = "sha3",
148    feature = "cshake",
149    feature = "kmac",
150    feature = "tuple_hash",
151    feature = "parallel_hash"
152))]
153pub use keccakf::keccakf;
154
155#[cfg(feature = "k12")]
156mod k12;
157
158#[cfg(feature = "k12")]
159pub use k12::{KangarooTwelve, KangarooTwelveXof};
160
161#[cfg(feature = "keccak")]
162mod keccak;
163
164#[cfg(feature = "keccak")]
165pub use keccak::Keccak;
166
167#[cfg(feature = "shake")]
168mod shake;
169
170#[cfg(feature = "shake")]
171pub use shake::Shake;
172
173#[cfg(feature = "sha3")]
174mod sha3;
175
176#[cfg(feature = "sha3")]
177pub use sha3::Sha3;
178
179#[cfg(feature = "cshake")]
180mod cshake;
181
182#[cfg(feature = "cshake")]
183pub use cshake::CShake;
184
185#[cfg(feature = "kmac")]
186mod kmac;
187
188#[cfg(feature = "kmac")]
189pub use kmac::{Kmac, KmacXof};
190
191#[cfg(feature = "tuple_hash")]
192mod tuple_hash;
193
194#[cfg(feature = "tuple_hash")]
195pub use tuple_hash::{TupleHash, TupleHashXof};
196
197#[cfg(feature = "parallel_hash")]
198mod parallel_hash;
199
200#[cfg(feature = "parallel_hash")]
201pub use parallel_hash::{ParallelHash, ParallelHashXof};
202
203#[cfg(test)]
204use quickcheck::{Arbitrary, Gen};
205
206/// A trait for hashing an arbitrary stream of bytes.
207///
208/// # Example
209///
210/// ```
211/// # use tiny_keccak::Hasher;
212/// #
213/// # fn foo<H: Hasher>(mut hasher: H) {
214/// let input_a = b"hello world";
215/// let input_b = b"!";
216/// let mut output = [0u8; 32];
217/// hasher.update(input_a);
218/// hasher.update(input_b);
219/// hasher.finalize(&mut output);
220/// # }
221/// ```
222pub trait Hasher {
223    /// Absorb additional input. Can be called multiple times.
224    fn update(&mut self, input: &[u8]);
225
226    /// Pad and squeeze the state to the output.
227    fn finalize(self, output: &mut [u8]);
228}
229
230/// A trait used to convert [`Hasher`] into it's [`Xof`] counterpart.
231///
232/// # Example
233///
234/// ```
235/// # use tiny_keccak::IntoXof;
236/// #
237/// # fn foo<H: IntoXof>(hasher: H) {
238/// let xof = hasher.into_xof();
239/// # }
240/// ```
241///
242/// [`Hasher`]: trait.Hasher.html
243/// [`Xof`]: trait.Xof.html
244pub trait IntoXof {
245    /// A type implementing [`Xof`], eXtendable-output function interface.
246    ///
247    /// [`Xof`]: trait.Xof.html
248    type Xof: Xof;
249
250    /// A method used to convert type into [`Xof`].
251    ///
252    /// [`Xof`]: trait.Xof.html
253    fn into_xof(self) -> Self::Xof;
254}
255
256/// Extendable-output function (`XOF`) is a function on bit strings in which the output can be
257/// extended to any desired length.
258///
259/// # Example
260///
261/// ```
262/// # use tiny_keccak::Xof;
263/// #
264/// # fn foo<X: Xof>(mut xof: X) {
265/// let mut output = [0u8; 64];
266/// xof.squeeze(&mut output[0..32]);
267/// xof.squeeze(&mut output[32..]);
268/// # }
269/// ```
270pub trait Xof {
271    /// A method used to retrieve another part of hash function output.
272    fn squeeze(&mut self, output: &mut [u8]);
273}
274
275struct EncodedLen {
276    offset: usize,
277    buffer: [u8; 9],
278}
279
280impl EncodedLen {
281    fn value(&self) -> &[u8] {
282        &self.buffer[self.offset..]
283    }
284}
285
286fn left_encode(len: usize) -> EncodedLen {
287    let mut buffer = [0u8; 9];
288    buffer[1..].copy_from_slice(&(len as u64).to_be_bytes());
289    let offset = buffer.iter().position(|i| *i != 0).unwrap_or(8);
290    buffer[offset - 1] = 9 - offset as u8;
291
292    EncodedLen {
293        offset: offset - 1,
294        buffer,
295    }
296}
297
298fn right_encode(len: usize) -> EncodedLen {
299    let mut buffer = [0u8; 9];
300    buffer[..8].copy_from_slice(&(len as u64).to_be_bytes());
301    let offset = buffer.iter().position(|i| *i != 0).unwrap_or(7);
302    buffer[8] = 8 - offset as u8;
303    EncodedLen { offset, buffer }
304}
305
306/// internal buffer for hashing keccak
307#[derive(Default, Debug, Clone)]
308pub struct Buffer([u64; WORDS]);
309
310impl Buffer {
311    fn words(&mut self) -> &mut [u64; WORDS] {
312        &mut self.0
313    }
314
315    #[cfg(target_endian = "little")]
316    #[inline]
317    fn execute<F: FnOnce(&mut [u8])>(&mut self, offset: usize, len: usize, f: F) {
318        let buffer: &mut [u8; WORDS * 8] = unsafe { core::mem::transmute(&mut self.0) };
319        f(&mut buffer[offset..][..len]);
320    }
321
322    #[cfg(target_endian = "big")]
323    #[inline]
324    fn execute<F: FnOnce(&mut [u8])>(&mut self, offset: usize, len: usize, f: F) {
325        fn swap_endianess(buffer: &mut [u64]) {
326            for item in buffer {
327                *item = item.swap_bytes();
328            }
329        }
330
331        let start = offset / 8;
332        let end = (offset + len + 7) / 8;
333        swap_endianess(&mut self.0[start..end]);
334        let buffer: &mut [u8; WORDS * 8] = unsafe { core::mem::transmute(&mut self.0) };
335        f(&mut buffer[offset..][..len]);
336        swap_endianess(&mut self.0[start..end]);
337    }
338
339    fn setout(&mut self, dst: &mut [u8], offset: usize, len: usize) {
340        self.execute(offset, len, |buffer| dst[..len].copy_from_slice(buffer));
341    }
342
343    fn xorin(&mut self, src: &[u8], offset: usize, len: usize) {
344        self.execute(offset, len, |dst| {
345            assert!(dst.len() <= src.len());
346            let len = dst.len();
347            let mut dst_ptr = dst.as_mut_ptr();
348            let mut src_ptr = src.as_ptr();
349            for _ in 0..len {
350                unsafe {
351                    *dst_ptr ^= *src_ptr;
352                    src_ptr = src_ptr.offset(1);
353                    dst_ptr = dst_ptr.offset(1);
354                }
355            }
356        });
357    }
358
359    fn pad(&mut self, offset: usize, delim: u8, rate: usize) {
360        self.execute(offset, 1, |buff| buff[0] ^= delim);
361        self.execute(rate - 1, 1, |buff| buff[0] ^= 0x80);
362    }
363}
364
365#[cfg(test)]
366impl Arbitrary for Buffer {
367    fn arbitrary(g: &mut Gen) -> Self {
368        let mut buf = [0u64; WORDS];
369        for i in 0..WORDS {
370            buf[i] = u64::arbitrary(g);
371        }
372        Buffer(buf)
373    }
374}
375
376impl BorshSerialize for Buffer {
377    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
378        for word in self.0 {
379            BorshSerialize::serialize(&word, writer)?;
380        }
381        Ok(())
382    }
383}
384
385impl BorshDeserialize for Buffer {
386    fn deserialize_reader<R>(reader: &mut R) -> Result<Self, io::Error>
387    where
388        R: io::Read,
389    {
390        let mut buf = [0u64; WORDS];
391        for i in 0..WORDS {
392            buf[i] = BorshDeserialize::deserialize_reader(reader)?;
393        }
394        Ok(Self(buf))
395    }
396}
397
398trait Permutation {
399    fn execute(a: &mut Buffer);
400}
401
402/// internal hashing mode for keccak
403#[derive(Clone, Debug, Copy, BorshSerialize, BorshDeserialize)]
404#[borsh(use_discriminant = true)]
405#[repr(u8)]
406pub enum Mode {
407    /// absorbing state
408    Absorbing = 1u8,
409    /// squeezing state
410    Squeezing = 2u8,
411}
412
413#[derive(Debug)]
414struct KeccakState<P> {
415    buffer: Buffer,
416    offset: u8,
417    rate: u8,
418    delim: u8,
419    mode: Mode,
420    permutation: core::marker::PhantomData<P>,
421}
422
423impl<P> BorshSerialize for KeccakState<P> {
424    fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
425        BorshSerialize::serialize(&self.buffer, writer)?;
426        BorshSerialize::serialize(&self.offset, writer)?;
427        BorshSerialize::serialize(&self.rate, writer)?;
428        BorshSerialize::serialize(&self.mode, writer)?;
429        Ok(())
430    }
431}
432
433impl<P> BorshDeserialize for KeccakState<P> {
434    fn deserialize_reader<R>(reader: &mut R) -> Result<Self, io::Error>
435    where
436        R: io::Read,
437    {
438        let buffer = BorshDeserialize::deserialize_reader(reader)?;
439        let offset = BorshDeserialize::deserialize_reader(reader)?;
440        let rate = BorshDeserialize::deserialize_reader(reader)?;
441        let mode = BorshDeserialize::deserialize_reader(reader)?;
442        Ok(Self {
443            buffer,
444            offset,
445            rate,
446            delim: 0x01,
447            mode,
448            permutation: core::marker::PhantomData,
449        })
450    }
451}
452
453impl<P> Clone for KeccakState<P> {
454    fn clone(&self) -> Self {
455        KeccakState {
456            buffer: self.buffer.clone(),
457            offset: self.offset,
458            rate: self.rate,
459            delim: self.delim,
460            mode: self.mode,
461            permutation: core::marker::PhantomData,
462        }
463    }
464}
465
466impl<P: Permutation> KeccakState<P> {
467    fn new(rate: u8, delim: u8) -> Self {
468        assert!(rate != 0, "rate cannot be equal 0");
469        KeccakState {
470            buffer: Buffer::default(),
471            offset: 0,
472            rate,
473            delim,
474            mode: Mode::Absorbing,
475            permutation: core::marker::PhantomData,
476        }
477    }
478    #[cfg(test)]
479    pub fn new_with(buffer: Buffer, offset: u8, rate: u8, delim: u8, mode: Mode) -> Self {
480        KeccakState {
481            buffer,
482            offset,
483            rate,
484            delim,
485            mode,
486            permutation: core::marker::PhantomData,
487        }
488    }
489
490    fn keccak(&mut self) {
491        P::execute(&mut self.buffer);
492    }
493
494    fn update(&mut self, input: &[u8]) {
495        if self.rate >= 200 || self.rate <= 1 || self.rate <= self.offset {
496            return;
497        }
498        if let Mode::Squeezing = self.mode {
499            self.mode = Mode::Absorbing;
500            self.fill_block();
501        }
502        //first foldp
503        let mut ip = 0;
504        let mut l = input.len();
505        let mut rate = (self.rate - self.offset) as usize;
506        let mut offset = self.offset as usize;
507        while l >= rate as usize {
508            self.buffer.xorin(&input[ip..], offset, rate);
509            self.keccak();
510            ip += rate;
511            l -= rate;
512            rate = self.rate as usize;
513            offset = 0;
514        }
515
516        self.buffer.xorin(&input[ip..], offset, l);
517        // rate is less than 255 which is u8max, offset + l is smaller than rate.
518        self.offset = (offset + l) as u8;
519    }
520
521    fn pad(&mut self) {
522        self.buffer
523            .pad(self.offset as usize, self.delim, self.rate as usize);
524    }
525
526    fn squeeze(&mut self, output: &mut [u8]) {
527        if self.rate >= 200 || self.rate <= 1 || self.rate <= self.offset {
528            return;
529        }
530        if let Mode::Absorbing = self.mode {
531            self.mode = Mode::Squeezing;
532            self.pad();
533            self.fill_block();
534        }
535
536        // second foldp
537        let mut op = 0;
538        let mut l = output.len();
539        let mut rate = (self.rate - self.offset) as usize;
540        let mut offset = self.offset as usize;
541        while l >= rate {
542            self.buffer.setout(&mut output[op..], offset, rate);
543            self.keccak();
544            op += rate;
545            l -= rate;
546            rate = self.rate as usize;
547            offset = 0;
548        }
549
550        self.buffer.setout(&mut output[op..], offset, l);
551        self.offset = (offset + l) as u8;
552    }
553
554    fn finalize(mut self, output: &mut [u8]) {
555        self.squeeze(output);
556    }
557
558    fn fill_block(&mut self) {
559        self.keccak();
560        self.offset = 0;
561    }
562
563    fn reset(&mut self) {
564        self.buffer = Buffer::default();
565        self.offset = 0;
566        self.mode = Mode::Absorbing;
567    }
568}
569
570fn bits_to_rate(bits: u16) -> u8 {
571    //max size is 512 -> (200-512/4)<255
572    (200u16.saturating_sub(bits / 4)) as u8
573}
574
575#[cfg(test)]
576mod tests {
577    use crate::{left_encode, right_encode};
578
579    #[test]
580    fn test_left_encode() {
581        assert_eq!(left_encode(0).value(), &[1, 0]);
582        assert_eq!(left_encode(128).value(), &[1, 128]);
583        assert_eq!(left_encode(65536).value(), &[3, 1, 0, 0]);
584        assert_eq!(left_encode(4096).value(), &[2, 16, 0]);
585        assert_eq!(left_encode(54321).value(), &[2, 212, 49]);
586    }
587
588    #[test]
589    fn test_right_encode() {
590        assert_eq!(right_encode(0).value(), &[0, 1]);
591        assert_eq!(right_encode(128).value(), &[128, 1]);
592        assert_eq!(right_encode(65536).value(), &[1, 0, 0, 3]);
593        assert_eq!(right_encode(4096).value(), &[16, 0, 2]);
594        assert_eq!(right_encode(54321).value(), &[212, 49, 2]);
595    }
596
597    mod quicktest {
598        use crate::{Buffer, Hasher, Keccak, Mode};
599        use quickcheck::{quickcheck, Arbitrary, Gen};
600
601        #[derive(Clone, Debug)]
602
603        struct Data([u8; 25]);
604
605        impl Data {
606            pub fn as_slice(&self) -> &[u8] {
607                &self.0
608            }
609        }
610        impl Arbitrary for Data {
611            fn arbitrary(g: &mut Gen) -> Data {
612                let mut buf = [0u8; 25];
613                for i in 0..25 {
614                    buf[i] = u8::arbitrary(g);
615                }
616                Data(buf)
617            }
618        }
619        #[test]
620        fn test_ser_deserialize() {
621            fn test_hashing_a(buffer: Buffer, offset: u8, rate: u8, hash: Data) -> bool {
622                let mode = Mode::Absorbing;
623                let mut keccak = Keccak::new_with(buffer, offset, rate, mode);
624                keccak.update(hash.as_slice());
625                let mut out: [u8; 32] = [0; 32];
626                keccak.finalize(&mut out);
627                true
628            }
629
630            fn test_hashing_s(buffer: Buffer, offset: u8, rate: u8, hash: Data) -> bool {
631                let mode = Mode::Squeezing;
632                let mut keccak = Keccak::new_with(buffer, offset, rate, mode);
633                keccak.update(hash.as_slice());
634                let mut out: [u8; 32] = [0; 32];
635                keccak.finalize(&mut out);
636                true
637            }
638            quickcheck(test_hashing_a as fn(Buffer, u8, u8, Data) -> bool);
639            quickcheck(test_hashing_s as fn(Buffer, u8, u8, Data) -> bool);
640        }
641    }
642}