Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Zeros

Copyright (C) 2019-2025  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2019-2025".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

#![cfg(target_endian="little")]
#![doc(cfg(target_endian="little"))]

//! # Keccak
//!
//! ## Notes
//!
//! - In addition to NIST's test data, this implementation has passed millions of sample hashes generated by [OpenSSL][site:openssl].
//! - 8-bit machines are not supported.
//!
//! ## References
//!
//! - Specifications: <https://keccak.team/keccak_specs_summary.html>
//! - Test data: <https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values>
//!
//! ## Examples
//!
//! ```
//! use zeros::keccak::Hash;
//!
//! let mut keccak = Hash::Shake128.new_keccak();
//! keccak.update("test");
//! assert_eq!(
//!     keccak.finish(),
//!     &[
//!         0xd3, 0xb0, 0xaa, 0x9c, 0xd8, 0xb7, 0x25, 0x56,
//!         0x22, 0xce, 0xbc, 0x63, 0x1e, 0x86, 0x7d, 0x40,
//!     ],
//! );
//!
//! ```
//!
//! ### Pseudorandom byte generator
//!
//! ```
//! use zeros::keccak::Hash;
//!
//! let mut keccak_x = Hash::Shake256.make_unlimited_keccak_x([
//!     "This is just an example.",
//!     "You should feed it data from some secure source,",
//!     "such as /dev/urandom",
//! ])?;
//! assert_eq!(keccak_x.size_hint(), (usize::MAX, Some(usize::MAX)));
//!
//! // Now read some bytes
//! let number = keccak_x.next_u64();
//!
//! // For what we feed above, we'll always get `0x_0f68_60c9_b7c2_cde6`
//! assert_eq!(number, 0x_0f68_60c9_b7c2_cde6);
//!
//! // Check its size again
//! assert_eq!(keccak_x.size_hint(), (usize::MAX, Some(usize::MAX)));
//!
//! # zeros::Result::Ok(())
//! ```
//!
//! [site:openssl]: https://www.openssl.org

use {
    core::mem,
    alloc::{
        string::String,
        vec::Vec,
    },
    crate::{Bytes, Result},
    self::{
        bits::Bits,
        state::State,
    },
};

#[cfg(feature="std")]
use {
    std::io::Write,
    crate::IoResult,
};

pub use self::{
    hash::Hash,
    keccak_x::{KeccakX, XType},
    r#type::Type,
};

#[cfg(feature="std")]
pub use self::{
    hash_reader::*,
    hash_writer::*,
};

macro_rules! u8_bits { () => { 8 }}

mod bits;
mod hash;
mod keccak_x;
mod state;
mod tests;
mod r#type;

#[cfg(feature="std")]
mod hash_reader;
#[cfg(feature="std")]
mod hash_writer;

type OneLane = u64;
const SIZE_OF_ONE_LANE: usize = mem::size_of::<OneLane>();

type Lanes = [OneLane; NUMBER_OF_LANES];
const NUMBER_OF_LANES: usize = 25;
const LANES_OF_ZEROS: Lanes = [OneLane::MIN; NUMBER_OF_LANES];

// TODO: replace this with `Type`
// - Unstable feature: [`adt_const_params`](https://github.com/rust-lang/rust/issues/95174)
type InnerType = char;

/// # Keccak
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Keccak<const TYPE: InnerType> {
    hash: Hash,
    bits: Bits,
    buf: Vec<u8>,
    buf_chunk: usize,
    lanes: Lanes,
    output_bytes: Option<usize>,
}

impl<const TYPE: InnerType> Keccak<TYPE> {

    /// # Makes new instance
    fn with(hash: Hash) -> Keccak<TYPE> {
        let bits = Bits::B1600;
        let buf_chunk = hash.rate() / u8_bits!();
        let buf = Vec::with_capacity(buf_chunk);
        let output_bytes = Some(hash.output_bytes());

        Self {
            hash,
            bits,
            buf,
            buf_chunk,
            lanes: LANES_OF_ZEROS,
            output_bytes,
        }
    }

    /// # Makes new instance
    pub (crate) fn new(hash: Hash) -> Keccak<{Type::Function.id()}> {
        Keccak::with(hash)
    }

    /// # Makes new instance with limited output *bytes*
    pub (crate) fn make_with_output_bytes(hash: Hash, output_bytes: usize) -> Result<Keccak<{Type::LimitedXOF.id()}>> {
        if hash.is_extendable_output_function() {
            if output_bytes >= Hash::MIN_CUSTOM_OUTPUT_BYTES {
                let mut result = Keccak::with(hash);
                result.output_bytes = Some(output_bytes);
                Ok(result)
            } else {
                Err(err!("Custom output bytes must be equal to or larger than {min}", min=Hash::MIN_CUSTOM_OUTPUT_BYTES))
            }
        } else {
            Err(err!("{hash} does not support custom output bits", hash=hash))
        }
    }

    /// # Makes new instance with limited output *bytes*
    pub (crate) fn make_with_unlimited_output_bytes(hash: Hash) -> Result<Keccak<{Type::UnlimitedXOF.id()}>> {
        if hash.is_extendable_output_function() {
            let mut result = Keccak::with(hash);
            result.output_bytes = None;
            Ok(result)
        } else {
            Err(err!("{hash} does not support custom output bits", hash=hash))
        }
    }

    /// # Gets hash
    pub const fn hash(&self) -> &Hash {
        &self.hash
    }

    /// # Updates new data
    ///
    /// Returns length of input bytes.
    pub fn update<B>(&mut self, bytes: B) -> usize where B: AsRef<[u8]> {
        macro_rules! proc { ($buf: expr) => {{
            let buf = $buf.unwrap_or(&self.buf);
            // Caller should guarantee that we have exactly 8 bytes for the *last* chunk
            for (idx, c) in {
                let chunks = buf.chunks_exact(SIZE_OF_ONE_LANE);
                #[cfg(test)]
                assert!(chunks.remainder().is_empty());
                chunks.enumerate()
            } {
                self.lanes[idx] ^= u64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]);
            }
            // Update
            keccak_p_1600_24(&mut self.lanes);
        }}}

        let mut bytes = bytes.as_ref();
        let result = bytes.len();

        // Process buffer first
        if crate::io::fill_buffer(&mut self.buf, self.buf_chunk, &mut bytes) {
            proc!(None);
            self.buf.clear();
        }

        // Now process bytes
        let chunks = bytes.chunks_exact(self.buf_chunk);
        self.buf.extend(chunks.remainder());
        chunks.for_each(|c| proc!(Some(c)));

        result
    }

    /// # Updates new data
    ///
    /// Returns length of input bytes. If overflow happens, `None` will be returned.
    pub fn update_bytes<'a, const N: usize, B, B0>(&mut self, bytes: B) -> Option<usize> where B: Into<Bytes<'a, N, B0>>, B0: AsRef<[u8]> + 'a {
        let mut result = Some(usize::MIN);
        for bytes in bytes.into().as_slice() {
            let size = self.update(bytes);
            if let Some(current) = result.as_mut() {
                match current.checked_add(size) {
                    Some(new) => *current = new,
                    None => result = None,
                };
            }
        }

        result
    }

    /// # Finishes as `KeccakX`
    fn finish_as_keccak_x<const T: keccak_x::InnerXType>(mut self) -> KeccakX<T> {
        const ZEROS: &[u8] = &[u8::MIN; 128];

        // Padding form: buf + suffix + 1 + j*0 + 1
        let suffix = self.hash.suffix();
        let suffix_len = (u8_bits!() - suffix.leading_zeros()) as u8;
        let bits = {
            let j = (|| {
                let j = isize::try_from(self.buf.len()).ok()?.checked_mul(u8_bits!())?.checked_add(suffix_len.into())?
                    .checked_neg()?.checked_sub(2)?.rem_euclid(isize::try_from(self.hash.rate()).ok()?);
                usize::try_from(j).ok()
            })().unwrap();
            match j.checked_add(2).map(|n| n.checked_add(suffix_len.into())) {
                Some(Some(bits)) => bits,
                _ => panic!("{}", e!()),
            }
        };

        let bytes = if bits % u8_bits!() == usize::MIN { bits / u8_bits!() } else { panic!("{}", err!("Invalid bits: {bits}")); };
        self.buf.reserve(bytes);

        // Padding will make data length a positive multiple of rate. While rate is tested that: rate % 64 == 0
        self.buf.push(suffix | (1 << u32::from(suffix_len)));
        if let Some(mut bytes) = bytes.checked_sub(1) {
            while bytes > usize::MIN {
                let count = bytes.min(ZEROS.len());
                self.buf.extend(&ZEROS[..count]);
                bytes -= count;
            }
        }
        *self.buf.last_mut().unwrap() |= 0b_1000_0000;

        self.update(&[]);

        // Tried to use an external vector. Times taken were similar. See: tests/hash_again.rs
        KeccakX::new(self.hash, self.lanes, self.buf_chunk, self.output_bytes)
    }

}

impl Keccak<{Type::Function.id()}> {

    /// # Finishes and returns hash
    pub fn finish(self) -> Vec<u8> {
        self.finish_as_keccak_x::<{XType::Limited.id()}>().finish()
    }

    /// # Finishes and returns hash as a hexadecimal string, in lower-case
    pub fn finish_as_hex(self) -> String {
        crate::bytes_to_hex(self.finish())
    }

}

/// # Extendable-Output Function (limited output)
impl Keccak<{Type::LimitedXOF.id()}> {

    /// # Finishes as `KeccakX`
    pub fn finish(self) -> KeccakX<{XType::Limited.id()}> {
        self.finish_as_keccak_x()
    }

}

/// # Extendable-Output Function (unlimited output)
impl Keccak<{Type::UnlimitedXOF.id()}> {

    /// # Finishes as `KeccakX`
    pub fn finish(self) -> KeccakX<{XType::Unlimited.id()}> {
        self.finish_as_keccak_x()
    }

}

impl From<Hash> for Keccak<{Type::Function.id()}> {

    fn from(hash: Hash) -> Self {
        Self::new(hash)
    }

}

impl From<&Hash> for Keccak<{Type::Function.id()}> {

    fn from(hash: &Hash) -> Self {
        Self::new(hash.clone())
    }

}

#[cfg(feature="std")]
#[doc(cfg(feature="std"))]
impl<const TYPE: InnerType> Write for Keccak<TYPE> {

    #[inline(always)]
    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
        Ok(self.update(buf))
    }

    #[inline(always)]
    fn flush(&mut self) -> IoResult<()> {
        Ok(())
    }

}

/// # Calls `keccak_p()` with `1600` bits and `24` rounds
#[inline(always)]
fn keccak_p_1600_24(lanes: &mut Lanes) {
    keccak_p(&Bits::B1600, 24, lanes);
}

/// # (See references)
#[inline(always)]
fn keccak_p(bits: &Bits, rounds: usize, lanes: &mut Lanes) {
    let mut state = State::new(lanes);

    let end = 12 + 2 * bits.l();
    for round_index in end - rounds .. end {
        state.theta();
        state.rho_and_pi();
        state.chi();
        state.iota(round_index);
    }
}

/// # (See references)
#[allow(unused)]
fn keccak_f(bits: &Bits, lanes: &mut Lanes) {
    let rounds = 12 + 2 * bits.l();
    keccak_p(bits, rounds, lanes);
}