#![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, missing_debug_implementations)]
#![warn(unreachable_pub)]
pub use digest;
use core::fmt;
use digest::{
CollisionResistance, CustomizedInit, ExtendableOutput, HashMarker, Update, XofReader,
common::AlgorithmName,
consts::{U16, U32},
};
use keccak::{Keccak, State1600};
use sponge_cursor::SpongeCursor;
const SHAKE_PAD: u8 = 0x1F;
const CSHAKE_PAD: u8 = 0x04;
pub type CShake128 = CShake<168>;
pub type CShake256 = CShake<136>;
pub type CShake128Reader = CShakeReader<168>;
pub type CShake256Reader = CShakeReader<136>;
#[derive(Clone)]
pub struct CShake<const RATE: usize> {
state: State1600,
cursor: SpongeCursor<RATE>,
pad: u8,
keccak: Keccak,
}
impl<const RATE: usize> Default for CShake<RATE> {
#[inline]
fn default() -> Self {
Self::new_with_function_name(b"", b"")
}
}
impl<const RATE: usize> CShake<RATE> {
pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self {
const {
assert!(RATE == 168 || RATE == 136, "unsupported rate");
}
let keccak = Keccak::new();
let mut state = State1600::default();
if function_name.is_empty() && customization.is_empty() {
return Self {
state,
cursor: Default::default(),
pad: SHAKE_PAD,
keccak,
};
}
#[inline(always)]
fn left_encode(val: u64, b: &mut [u8; 9]) -> &[u8] {
b[1..].copy_from_slice(&val.to_be_bytes());
let i = b[1..8].iter().take_while(|&&a| a == 0).count();
b[i] = (8 - i) as u8;
&b[i..]
}
keccak.with_f1600(|f1600| {
let mut cursor: SpongeCursor<RATE> = Default::default();
let state = &mut state;
let mut b = [0u8; 9];
let rate_u64 = u64::try_from(RATE).expect("RATE is smaller than 200");
cursor.absorb_u64_le(state, f1600, left_encode(rate_u64, &mut b));
let mut encode_str = |str: &[u8]| {
let str_bits_len = 8 * u64::try_from(str.len())
.expect("in practice strings can not be longer than u64::MAX");
let encoded_len = left_encode(str_bits_len, &mut b);
cursor.absorb_u64_le(state, f1600, encoded_len);
cursor.absorb_u64_le(state, f1600, str);
};
encode_str(function_name);
encode_str(customization);
if cursor.pos() != 0 {
f1600(state);
}
});
Self {
state,
cursor: Default::default(),
pad: CSHAKE_PAD,
keccak,
}
}
}
impl<const RATE: usize> CustomizedInit for CShake<RATE> {
#[inline]
fn new_customized(customization: &[u8]) -> Self {
Self::new_with_function_name(&[], customization)
}
}
impl<const RATE: usize> HashMarker for CShake<RATE> {}
impl<const RATE: usize> Update for CShake<RATE> {
fn update(&mut self, data: &[u8]) {
self.keccak.with_f1600(|f1600| {
self.cursor.absorb_u64_le(&mut self.state, f1600, data);
});
}
}
impl<const RATE: usize> ExtendableOutput for CShake<RATE> {
type Reader = CShakeReader<RATE>;
#[inline]
fn finalize_xof(mut self) -> Self::Reader {
let pos = self.cursor.pos();
let word_offset = pos / 8;
let byte_offset = pos % 8;
let pad = u64::from(self.pad) << (8 * byte_offset);
self.state[word_offset] ^= pad;
self.state[RATE / 8 - 1] ^= 1 << 63;
Self::Reader {
state: self.state,
cursor: Default::default(),
keccak: self.keccak,
}
}
}
impl<const RATE: usize> AlgorithmName for CShake<RATE> {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
let alg_name = match RATE {
168 => "cSHAKE128",
136 => "cSHAKE256",
_ => unreachable!(),
};
f.write_str(alg_name)
}
}
impl<const RATE: usize> fmt::Debug for CShake<RATE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let debug_str = match RATE {
168 => "CShake128 { ... }",
136 => "CShake256 { ... }",
_ => unreachable!(),
};
f.write_str(debug_str)
}
}
impl<const RATE: usize> Drop for CShake<RATE> {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
use digest::zeroize::Zeroize;
self.state.zeroize();
self.cursor.zeroize();
self.pad.zeroize();
}
}
}
#[cfg(feature = "zeroize")]
impl<const RATE: usize> digest::zeroize::ZeroizeOnDrop for CShake<RATE> {}
#[derive(Clone)]
pub struct CShakeReader<const RATE: usize> {
state: State1600,
cursor: SpongeCursor<RATE>,
keccak: Keccak,
}
impl<const RATE: usize> XofReader for CShakeReader<RATE> {
#[inline]
fn read(&mut self, buf: &mut [u8]) {
self.keccak.with_f1600(|f1600| {
self.cursor.squeeze_read_u64_le(&mut self.state, f1600, buf);
});
}
}
impl<const RATE: usize> fmt::Debug for CShakeReader<RATE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let debug_str = match RATE {
168 => "CShakeReader128 { ... }",
136 => "CShakeReader256 { ... }",
_ => unreachable!(),
};
f.write_str(debug_str)
}
}
impl<const RATE: usize> Drop for CShakeReader<RATE> {
fn drop(&mut self) {
#[cfg(feature = "zeroize")]
{
use digest::zeroize::Zeroize;
self.state.zeroize();
self.cursor.zeroize();
}
}
}
impl CollisionResistance for CShake128 {
type CollisionResistance = U16;
}
impl CollisionResistance for CShake256 {
type CollisionResistance = U32;
}