#![no_std]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
pub use digest::{self, ExtendableOutput, Update, XofReader};
use core::fmt;
use digest::{
CollisionResistance, ExtendableOutputReset, HashMarker, Reset,
common::AlgorithmName,
consts::{U16, U32},
};
mod consts;
pub mod custom;
mod node_turbo_shake;
mod reader;
mod turbo_shake;
mod update;
mod utils;
pub use custom::*;
pub use reader::KtReader;
use consts::{CHUNK_SIZE_U64, FINAL_NODE_DS, ROUNDS, SINGLE_NODE_DS};
use turbo_shake::TurboShake;
use utils::length_encode;
#[derive(Clone)]
pub struct Kt<const RATE: usize> {
accum_tshk: TurboShake<RATE>,
node_tshk: TurboShake<RATE>,
consumed_len: u64,
keccak: keccak::Keccak,
}
impl<const RATE: usize> Default for Kt<RATE> {
#[inline]
fn default() -> Self {
const { assert!(matches!(RATE, 136 | 168)) }
Self {
accum_tshk: Default::default(),
node_tshk: Default::default(),
consumed_len: 0,
keccak: Default::default(),
}
}
}
impl<const RATE: usize> fmt::Debug for Kt<RATE> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "Kt{} {{ ... }}", 4 * (200 - RATE))
}
}
impl<const RATE: usize> AlgorithmName for Kt<RATE> {
#[inline]
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "KT{}", 4 * (200 - RATE))
}
}
impl<const RATE: usize> HashMarker for Kt<RATE> {}
impl<const RATE: usize> Update for Kt<RATE> {
#[inline]
fn update(&mut self, data: &[u8]) {
let keccak = self.keccak;
let closure = update::Closure::<'_, RATE> { data, kt: self };
keccak.with_backend(closure);
}
}
impl<const RATE: usize> Reset for Kt<RATE> {
#[inline]
fn reset(&mut self) {
self.accum_tshk.reset();
self.node_tshk.reset();
self.consumed_len = 0;
}
}
impl<const RATE: usize> Kt<RATE> {
#[inline]
fn raw_finalize(&mut self) -> KtReader<RATE> {
let keccak = self.keccak;
if self.consumed_len <= CHUNK_SIZE_U64 {
self.accum_tshk.pad::<SINGLE_NODE_DS>();
} else {
keccak.with_p1600::<ROUNDS>(|p1600| {
let nodes_len = (self.consumed_len - 1) / CHUNK_SIZE_U64;
let partial_node_len = self.consumed_len % CHUNK_SIZE_U64;
if partial_node_len != 0 {
let cv_dst = &mut [0u8; 200][..200 - RATE];
self.node_tshk.finalize_intermediate_node(p1600, cv_dst);
self.accum_tshk.absorb(p1600, cv_dst);
}
length_encode(nodes_len, |enc_len| self.accum_tshk.absorb(p1600, enc_len));
self.accum_tshk.absorb(p1600, b"\xFF\xFF");
self.accum_tshk.pad::<FINAL_NODE_DS>();
});
};
KtReader::new(self.accum_tshk.state(), keccak)
}
}
impl<const RATE: usize> ExtendableOutput for Kt<RATE> {
type Reader = KtReader<RATE>;
#[inline]
fn finalize_xof(mut self) -> Self::Reader {
self.update(&[0x00]);
self.raw_finalize()
}
}
impl<const RATE: usize> ExtendableOutputReset for Kt<RATE> {
#[inline]
fn finalize_xof_reset(&mut self) -> Self::Reader {
self.update(&[0x00]);
let reader = self.raw_finalize();
self.reset();
reader
}
}
impl<const RATE: usize> Drop for Kt<RATE> {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
use digest::zeroize::Zeroize;
self.consumed_len.zeroize();
}
}
}
#[cfg(feature = "zeroize")]
impl<const RATE: usize> digest::zeroize::ZeroizeOnDrop for Kt<RATE> {}
pub type Kt128 = Kt<168>;
pub type Kt256 = Kt<136>;
pub type Kt128Reader = KtReader<168>;
pub type Kt256Reader = KtReader<136>;
impl CollisionResistance for Kt128 {
type CollisionResistance = U16;
}
impl CollisionResistance for Kt256 {
type CollisionResistance = U32;
}