#![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)]
#![warn(missing_docs, unreachable_pub)]
pub use digest;
use core::fmt;
use digest::{
CollisionResistance, ExtendableOutput, ExtendableOutputReset, HashMarker, Reset, Update,
block_api::{AlgorithmName, BlockSizeUser},
block_buffer::BlockSizes,
consts::{U16, U32, U136, U168},
};
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, INTERMEDIATE_NODE_DS, ROUNDS, SINGLE_NODE_DS};
use turbo_shake::TurboShake;
use utils::{copy_cv, length_encode};
#[derive(Clone)]
pub struct Kt<Rate: BlockSizes> {
accum_tshk: TurboShake<Rate>,
node_tshk: TurboShake<Rate>,
consumed_len: u64,
keccak: keccak::Keccak,
}
impl<Rate: BlockSizes> Default for Kt<Rate> {
#[inline]
fn default() -> Self {
const { assert!(matches!(Rate::USIZE, 136 | 168)) }
Self {
accum_tshk: Default::default(),
node_tshk: Default::default(),
consumed_len: 0,
keccak: Default::default(),
}
}
}
impl<Rate: BlockSizes> fmt::Debug for Kt<Rate> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "Kt{} {{ ... }}", 4 * (200 - Rate::USIZE))
}
}
impl<Rate: BlockSizes> AlgorithmName for Kt<Rate> {
#[inline]
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "KT{}", 4 * (200 - Rate::USIZE))
}
}
impl<Rate: BlockSizes> HashMarker for Kt<Rate> {}
impl<Rate: BlockSizes> BlockSizeUser for Kt<Rate> {
type BlockSize = Rate;
}
impl<Rate: BlockSizes> 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<Rate: BlockSizes> Reset for Kt<Rate> {
#[inline]
fn reset(&mut self) {
self.accum_tshk.reset();
self.node_tshk.reset();
self.consumed_len = 0;
}
}
impl<Rate: BlockSizes> Kt<Rate> {
#[inline]
fn raw_finalize(&mut self) -> KtReader<Rate> {
let keccak = self.keccak;
keccak.with_p1600::<ROUNDS>(|p1600| {
if self.consumed_len <= CHUNK_SIZE_U64 {
self.accum_tshk.finalize::<SINGLE_NODE_DS>(p1600);
} else {
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 {
self.node_tshk.finalize::<INTERMEDIATE_NODE_DS>(p1600);
let cv_dst = &mut [0u8; 200][..200 - Rate::USIZE];
copy_cv(self.node_tshk.state(), 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.finalize::<FINAL_NODE_DS>(p1600);
};
});
KtReader {
state: *self.accum_tshk.state(),
buffer: Default::default(),
keccak,
}
}
}
impl<Rate: BlockSizes> ExtendableOutput for Kt<Rate> {
type Reader = KtReader<Rate>;
#[inline]
fn finalize_xof(mut self) -> Self::Reader {
self.update(&[0x00]);
self.raw_finalize()
}
}
impl<Rate: BlockSizes> 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<Rate: BlockSizes> Drop for Kt<Rate> {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
use digest::zeroize::Zeroize;
self.consumed_len.zeroize();
}
}
}
#[cfg(feature = "zeroize")]
impl<Rate: BlockSizes> digest::zeroize::ZeroizeOnDrop for Kt<Rate> {}
pub type Kt128 = Kt<U168>;
pub type Kt256 = Kt<U136>;
pub type Kt128Reader = KtReader<U168>;
pub type Kt256Reader = KtReader<U136>;
impl CollisionResistance for Kt128 {
type CollisionResistance = U16;
}
impl CollisionResistance for Kt256 {
type CollisionResistance = U32;
}