use crate::{Result, SodiumError};
pub mod shake128 {
use super::*;
pub const BLOCKBYTES: usize = libsodium_sys::crypto_xof_shake128_BLOCKBYTES as usize;
pub const STATEBYTES: usize = libsodium_sys::crypto_xof_shake128_STATEBYTES as usize;
pub const DOMAIN_STANDARD: u8 = libsodium_sys::crypto_xof_shake128_DOMAIN_STANDARD as u8;
pub fn blockbytes() -> usize {
unsafe { libsodium_sys::crypto_xof_shake128_blockbytes() }
}
pub fn statebytes() -> usize {
unsafe { libsodium_sys::crypto_xof_shake128_statebytes() }
}
pub fn domain_standard() -> u8 {
unsafe { libsodium_sys::crypto_xof_shake128_domain_standard() }
}
pub fn hash(input: &[u8], output_len: usize) -> Result<Vec<u8>> {
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_shake128(
output.as_mut_ptr(),
output_len,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError("SHAKE128 hash failed".into()));
}
Ok(output)
}
pub struct State {
state: libsodium_sys::crypto_xof_shake128_state,
squeezed: bool,
}
impl State {
pub fn new() -> Result<Self> {
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe { libsodium_sys::crypto_xof_shake128_init(&mut state.state) };
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE128 state initialization failed".into(),
));
}
Ok(state)
}
pub fn new_with_domain(domain: u8) -> Result<Self> {
if domain == 0 || domain > 0x7F {
return Err(SodiumError::InvalidInput(
"domain must be between 0x01 and 0x7F".into(),
));
}
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe {
libsodium_sys::crypto_xof_shake128_init_with_domain(&mut state.state, domain)
};
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE128 state initialization with domain failed".into(),
));
}
Ok(state)
}
pub fn update(&mut self, input: &[u8]) -> Result<()> {
if self.squeezed {
return Err(SodiumError::OperationError(
"cannot absorb after squeezing".into(),
));
}
let result = unsafe {
libsodium_sys::crypto_xof_shake128_update(
&mut self.state,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError("SHAKE128 update failed".into()));
}
Ok(())
}
pub fn squeeze(&mut self, output_len: usize) -> Result<Vec<u8>> {
self.squeezed = true;
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_shake128_squeeze(
&mut self.state,
output.as_mut_ptr(),
output_len,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE128 squeeze failed".into(),
));
}
Ok(output)
}
pub fn squeeze_into(&mut self, output: &mut [u8]) -> Result<()> {
self.squeezed = true;
let result = unsafe {
libsodium_sys::crypto_xof_shake128_squeeze(
&mut self.state,
output.as_mut_ptr(),
output.len(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE128 squeeze failed".into(),
));
}
Ok(())
}
}
impl Default for State {
fn default() -> Self {
Self::new().expect("SHAKE128 state initialization should not fail")
}
}
}
pub mod shake256 {
use super::*;
pub const BLOCKBYTES: usize = libsodium_sys::crypto_xof_shake256_BLOCKBYTES as usize;
pub const STATEBYTES: usize = libsodium_sys::crypto_xof_shake256_STATEBYTES as usize;
pub const DOMAIN_STANDARD: u8 = libsodium_sys::crypto_xof_shake256_DOMAIN_STANDARD as u8;
pub fn blockbytes() -> usize {
unsafe { libsodium_sys::crypto_xof_shake256_blockbytes() }
}
pub fn statebytes() -> usize {
unsafe { libsodium_sys::crypto_xof_shake256_statebytes() }
}
pub fn domain_standard() -> u8 {
unsafe { libsodium_sys::crypto_xof_shake256_domain_standard() }
}
pub fn hash(input: &[u8], output_len: usize) -> Result<Vec<u8>> {
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_shake256(
output.as_mut_ptr(),
output_len,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError("SHAKE256 hash failed".into()));
}
Ok(output)
}
pub struct State {
state: libsodium_sys::crypto_xof_shake256_state,
squeezed: bool,
}
impl State {
pub fn new() -> Result<Self> {
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe { libsodium_sys::crypto_xof_shake256_init(&mut state.state) };
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE256 state initialization failed".into(),
));
}
Ok(state)
}
pub fn new_with_domain(domain: u8) -> Result<Self> {
if domain == 0 || domain > 0x7F {
return Err(SodiumError::InvalidInput(
"domain must be between 0x01 and 0x7F".into(),
));
}
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe {
libsodium_sys::crypto_xof_shake256_init_with_domain(&mut state.state, domain)
};
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE256 state initialization with domain failed".into(),
));
}
Ok(state)
}
pub fn update(&mut self, input: &[u8]) -> Result<()> {
if self.squeezed {
return Err(SodiumError::OperationError(
"cannot absorb after squeezing".into(),
));
}
let result = unsafe {
libsodium_sys::crypto_xof_shake256_update(
&mut self.state,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError("SHAKE256 update failed".into()));
}
Ok(())
}
pub fn squeeze(&mut self, output_len: usize) -> Result<Vec<u8>> {
self.squeezed = true;
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_shake256_squeeze(
&mut self.state,
output.as_mut_ptr(),
output_len,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE256 squeeze failed".into(),
));
}
Ok(output)
}
pub fn squeeze_into(&mut self, output: &mut [u8]) -> Result<()> {
self.squeezed = true;
let result = unsafe {
libsodium_sys::crypto_xof_shake256_squeeze(
&mut self.state,
output.as_mut_ptr(),
output.len(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"SHAKE256 squeeze failed".into(),
));
}
Ok(())
}
}
impl Default for State {
fn default() -> Self {
Self::new().expect("SHAKE256 state initialization should not fail")
}
}
}
pub mod turboshake128 {
use super::*;
pub const BLOCKBYTES: usize = libsodium_sys::crypto_xof_turboshake128_BLOCKBYTES as usize;
pub const STATEBYTES: usize = libsodium_sys::crypto_xof_turboshake128_STATEBYTES as usize;
pub const DOMAIN_STANDARD: u8 = libsodium_sys::crypto_xof_turboshake128_DOMAIN_STANDARD as u8;
pub fn blockbytes() -> usize {
unsafe { libsodium_sys::crypto_xof_turboshake128_blockbytes() }
}
pub fn statebytes() -> usize {
unsafe { libsodium_sys::crypto_xof_turboshake128_statebytes() }
}
pub fn domain_standard() -> u8 {
unsafe { libsodium_sys::crypto_xof_turboshake128_domain_standard() }
}
pub fn hash(input: &[u8], output_len: usize) -> Result<Vec<u8>> {
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_turboshake128(
output.as_mut_ptr(),
output_len,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE128 hash failed".into(),
));
}
Ok(output)
}
pub struct State {
state: libsodium_sys::crypto_xof_turboshake128_state,
squeezed: bool,
}
impl State {
pub fn new() -> Result<Self> {
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe { libsodium_sys::crypto_xof_turboshake128_init(&mut state.state) };
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE128 state initialization failed".into(),
));
}
Ok(state)
}
pub fn new_with_domain(domain: u8) -> Result<Self> {
if domain == 0 || domain > 0x7F {
return Err(SodiumError::InvalidInput(
"domain must be between 0x01 and 0x7F".into(),
));
}
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe {
libsodium_sys::crypto_xof_turboshake128_init_with_domain(&mut state.state, domain)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE128 state initialization with domain failed".into(),
));
}
Ok(state)
}
pub fn update(&mut self, input: &[u8]) -> Result<()> {
if self.squeezed {
return Err(SodiumError::OperationError(
"cannot absorb after squeezing".into(),
));
}
let result = unsafe {
libsodium_sys::crypto_xof_turboshake128_update(
&mut self.state,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE128 update failed".into(),
));
}
Ok(())
}
pub fn squeeze(&mut self, output_len: usize) -> Result<Vec<u8>> {
self.squeezed = true;
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_turboshake128_squeeze(
&mut self.state,
output.as_mut_ptr(),
output_len,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE128 squeeze failed".into(),
));
}
Ok(output)
}
pub fn squeeze_into(&mut self, output: &mut [u8]) -> Result<()> {
self.squeezed = true;
let result = unsafe {
libsodium_sys::crypto_xof_turboshake128_squeeze(
&mut self.state,
output.as_mut_ptr(),
output.len(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE128 squeeze failed".into(),
));
}
Ok(())
}
}
impl Default for State {
fn default() -> Self {
Self::new().expect("TurboSHAKE128 state initialization should not fail")
}
}
}
pub mod turboshake256 {
use super::*;
pub const BLOCKBYTES: usize = libsodium_sys::crypto_xof_turboshake256_BLOCKBYTES as usize;
pub const STATEBYTES: usize = libsodium_sys::crypto_xof_turboshake256_STATEBYTES as usize;
pub const DOMAIN_STANDARD: u8 = libsodium_sys::crypto_xof_turboshake256_DOMAIN_STANDARD as u8;
pub fn blockbytes() -> usize {
unsafe { libsodium_sys::crypto_xof_turboshake256_blockbytes() }
}
pub fn statebytes() -> usize {
unsafe { libsodium_sys::crypto_xof_turboshake256_statebytes() }
}
pub fn domain_standard() -> u8 {
unsafe { libsodium_sys::crypto_xof_turboshake256_domain_standard() }
}
pub fn hash(input: &[u8], output_len: usize) -> Result<Vec<u8>> {
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_turboshake256(
output.as_mut_ptr(),
output_len,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE256 hash failed".into(),
));
}
Ok(output)
}
pub struct State {
state: libsodium_sys::crypto_xof_turboshake256_state,
squeezed: bool,
}
impl State {
pub fn new() -> Result<Self> {
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe { libsodium_sys::crypto_xof_turboshake256_init(&mut state.state) };
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE256 state initialization failed".into(),
));
}
Ok(state)
}
pub fn new_with_domain(domain: u8) -> Result<Self> {
if domain == 0 || domain > 0x7F {
return Err(SodiumError::InvalidInput(
"domain must be between 0x01 and 0x7F".into(),
));
}
let mut state = Self {
state: unsafe { std::mem::zeroed() },
squeezed: false,
};
let result = unsafe {
libsodium_sys::crypto_xof_turboshake256_init_with_domain(&mut state.state, domain)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE256 state initialization with domain failed".into(),
));
}
Ok(state)
}
pub fn update(&mut self, input: &[u8]) -> Result<()> {
if self.squeezed {
return Err(SodiumError::OperationError(
"cannot absorb after squeezing".into(),
));
}
let result = unsafe {
libsodium_sys::crypto_xof_turboshake256_update(
&mut self.state,
input.as_ptr(),
input.len() as u64,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE256 update failed".into(),
));
}
Ok(())
}
pub fn squeeze(&mut self, output_len: usize) -> Result<Vec<u8>> {
self.squeezed = true;
let mut output = vec![0u8; output_len];
let result = unsafe {
libsodium_sys::crypto_xof_turboshake256_squeeze(
&mut self.state,
output.as_mut_ptr(),
output_len,
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE256 squeeze failed".into(),
));
}
Ok(output)
}
pub fn squeeze_into(&mut self, output: &mut [u8]) -> Result<()> {
self.squeezed = true;
let result = unsafe {
libsodium_sys::crypto_xof_turboshake256_squeeze(
&mut self.state,
output.as_mut_ptr(),
output.len(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"TurboSHAKE256 squeeze failed".into(),
));
}
Ok(())
}
}
impl Default for State {
fn default() -> Self {
Self::new().expect("TurboSHAKE256 state initialization should not fail")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shake128_constants() {
assert_eq!(shake128::BLOCKBYTES, 168);
assert_eq!(shake128::STATEBYTES, 256);
assert_eq!(shake128::DOMAIN_STANDARD, 0x1F);
assert_eq!(shake128::blockbytes(), 168);
assert_eq!(shake128::statebytes(), 256);
assert_eq!(shake128::domain_standard(), 0x1F);
}
#[test]
fn test_shake256_constants() {
assert_eq!(shake256::BLOCKBYTES, 136);
assert_eq!(shake256::STATEBYTES, 256);
assert_eq!(shake256::DOMAIN_STANDARD, 0x1F);
assert_eq!(shake256::blockbytes(), 136);
assert_eq!(shake256::statebytes(), 256);
assert_eq!(shake256::domain_standard(), 0x1F);
}
#[test]
fn test_turboshake128_constants() {
assert_eq!(turboshake128::BLOCKBYTES, 168);
assert_eq!(turboshake128::STATEBYTES, 256);
assert_eq!(turboshake128::DOMAIN_STANDARD, 0x1F);
assert_eq!(turboshake128::blockbytes(), 168);
assert_eq!(turboshake128::statebytes(), 256);
assert_eq!(turboshake128::domain_standard(), 0x1F);
}
#[test]
fn test_turboshake256_constants() {
assert_eq!(turboshake256::BLOCKBYTES, 136);
assert_eq!(turboshake256::STATEBYTES, 256);
assert_eq!(turboshake256::DOMAIN_STANDARD, 0x1F);
assert_eq!(turboshake256::blockbytes(), 136);
assert_eq!(turboshake256::statebytes(), 256);
assert_eq!(turboshake256::domain_standard(), 0x1F);
}
#[test]
fn test_shake128_hash() {
let message = b"Hello, World!";
let hash = shake128::hash(message, 32).unwrap();
assert_eq!(hash.len(), 32);
let hash2 = shake128::hash(message, 32).unwrap();
assert_eq!(hash, hash2);
let hash_short = shake128::hash(message, 16).unwrap();
let hash_long = shake128::hash(message, 32).unwrap();
assert_eq!(&hash_short[..], &hash_long[..16]);
}
#[test]
fn test_shake256_hash() {
let message = b"Hello, World!";
let hash = shake256::hash(message, 64).unwrap();
assert_eq!(hash.len(), 64);
let hash_short = shake256::hash(message, 32).unwrap();
assert_eq!(&hash_short[..], &hash[..32]);
}
#[test]
fn test_turboshake128_hash() {
let message = b"Hello, World!";
let hash = turboshake128::hash(message, 32).unwrap();
assert_eq!(hash.len(), 32);
let shake_hash = shake128::hash(message, 32).unwrap();
assert_ne!(hash, shake_hash);
}
#[test]
fn test_turboshake256_hash() {
let message = b"Hello, World!";
let hash = turboshake256::hash(message, 64).unwrap();
assert_eq!(hash.len(), 64);
let shake_hash = shake256::hash(message, 64).unwrap();
assert_ne!(hash, shake_hash);
}
#[test]
fn test_shake128_incremental() {
let message = b"Hello, World!";
let hash1 = shake128::hash(message, 32).unwrap();
let mut state = shake128::State::new().unwrap();
state.update(b"Hello, ").unwrap();
state.update(b"World!").unwrap();
let hash2 = state.squeeze(32).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_shake256_incremental() {
let message = b"Hello, World!";
let hash1 = shake256::hash(message, 64).unwrap();
let mut state = shake256::State::new().unwrap();
state.update(message).unwrap();
let hash2 = state.squeeze(64).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_turboshake128_incremental() {
let message = b"Hello, World!";
let hash1 = turboshake128::hash(message, 32).unwrap();
let mut state = turboshake128::State::new().unwrap();
state.update(b"Hello, ").unwrap();
state.update(b"World!").unwrap();
let hash2 = state.squeeze(32).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_turboshake256_incremental() {
let message = b"Hello, World!";
let hash1 = turboshake256::hash(message, 64).unwrap();
let mut state = turboshake256::State::new().unwrap();
state.update(message).unwrap();
let hash2 = state.squeeze(64).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_incremental_squeeze() {
let message = b"seed";
let full = turboshake128::hash(message, 96).unwrap();
let mut state = turboshake128::State::new().unwrap();
state.update(message).unwrap();
let part1 = state.squeeze(32).unwrap();
let part2 = state.squeeze(32).unwrap();
let part3 = state.squeeze(32).unwrap();
let mut combined = Vec::new();
combined.extend_from_slice(&part1);
combined.extend_from_slice(&part2);
combined.extend_from_slice(&part3);
assert_eq!(full, combined);
}
#[test]
fn test_domain_separation() {
let message = b"same input";
let mut state1 = turboshake128::State::new_with_domain(0x01).unwrap();
let mut state2 = turboshake128::State::new_with_domain(0x02).unwrap();
state1.update(message).unwrap();
state2.update(message).unwrap();
let hash1 = state1.squeeze(32).unwrap();
let hash2 = state2.squeeze(32).unwrap();
assert_ne!(hash1, hash2);
let mut state3 = turboshake128::State::new().unwrap();
state3.update(message).unwrap();
let hash3 = state3.squeeze(32).unwrap();
assert_ne!(hash1, hash3);
assert_ne!(hash2, hash3);
}
#[test]
fn test_invalid_domain() {
assert!(shake128::State::new_with_domain(0x00).is_err());
assert!(shake256::State::new_with_domain(0x00).is_err());
assert!(turboshake128::State::new_with_domain(0x00).is_err());
assert!(turboshake256::State::new_with_domain(0x00).is_err());
assert!(shake128::State::new_with_domain(0x80).is_err());
assert!(shake256::State::new_with_domain(0x80).is_err());
assert!(turboshake128::State::new_with_domain(0x80).is_err());
assert!(turboshake256::State::new_with_domain(0x80).is_err());
assert!(turboshake128::State::new_with_domain(0x01).is_ok());
assert!(turboshake128::State::new_with_domain(0x7F).is_ok());
}
#[test]
fn test_no_update_after_squeeze() {
let mut state = turboshake128::State::new().unwrap();
state.update(b"data").unwrap();
state.squeeze(32).unwrap();
assert!(state.update(b"more data").is_err());
}
#[test]
fn test_squeeze_into() {
let message = b"test";
let mut state = turboshake128::State::new().unwrap();
state.update(message).unwrap();
let mut output = [0u8; 32];
state.squeeze_into(&mut output).unwrap();
let expected = turboshake128::hash(message, 32).unwrap();
assert_eq!(&output[..], &expected[..]);
}
#[test]
fn test_variable_output_lengths() {
let message = b"test";
for len in [1, 16, 32, 64, 100, 256, 1000] {
let output = turboshake128::hash(message, len).unwrap();
assert_eq!(output.len(), len);
}
}
#[test]
fn test_empty_input() {
let hash = turboshake128::hash(&[], 32).unwrap();
assert_eq!(hash.len(), 32);
let mut state = turboshake128::State::new().unwrap();
let hash2 = state.squeeze(32).unwrap();
assert_eq!(hash, hash2);
}
}