#![no_std]
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
extern crate alloc;
use alloc::vec::Vec;
mod error;
mod sponge;
pub use error::Error;
pub use sponge::{Safe, Sponge};
#[cfg(feature = "encryption")]
mod encryption;
#[cfg(feature = "encryption")]
pub use encryption::{decrypt, encrypt, Encryption};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Call {
Absorb(usize),
Squeeze(usize),
}
impl Call {
pub fn call_len(&self) -> &usize {
match self {
Call::Absorb(len) => len,
Call::Squeeze(len) => len,
}
}
}
fn tag_input(
iopattern: impl AsRef<[Call]>,
domain_sep: u64,
) -> Result<Vec<u8>, Error> {
validate_io_pattern(iopattern.as_ref())?;
const ABSORB_MASK: u32 = 0x8000_0000;
let mut input_u32 = Vec::new();
input_u32.push(ABSORB_MASK);
iopattern.as_ref().iter().for_each(|call| {
let l = input_u32.len();
let prev = &mut input_u32[l - 1];
match call {
Call::Absorb(len) if *prev & ABSORB_MASK != 0 => {
*prev += *len as u32
}
Call::Absorb(len) => input_u32.push(ABSORB_MASK + *len as u32),
Call::Squeeze(len) if *prev & ABSORB_MASK == 0 => {
*prev += *len as u32
}
Call::Squeeze(len) => input_u32.push(*len as u32),
}
});
let mut input: Vec<u8> = input_u32
.iter()
.flat_map(|u32_int| u32_int.to_be_bytes().into_iter())
.collect();
input.extend(domain_sep.to_be_bytes());
Ok(input)
}
fn validate_io_pattern(iopattern: impl AsRef<[Call]>) -> Result<(), Error> {
match (iopattern.as_ref().first(), iopattern.as_ref().last()) {
(Some(Call::Absorb(_)), Some(Call::Squeeze(_))) => {}
_ => return Err(Error::InvalidIOPattern),
}
const MAX_LEN: usize = u32::MAX as usize >> 1;
if iopattern.as_ref().iter().any(|call| {
let call_len = *call.call_len();
call_len == 0 || call_len > MAX_LEN
}) {
Err(Error::InvalidIOPattern)
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::vec;
use super::*;
#[test]
fn test_validate_io_pattern() {
let iopattern = vec![Call::Absorb(42), Call::Squeeze(3)];
assert!(validate_io_pattern(&iopattern).is_ok());
let iopattern = vec![
Call::Absorb(42),
Call::Absorb(5),
Call::Squeeze(4),
Call::Squeeze(3),
];
assert!(validate_io_pattern(&iopattern).is_ok());
let iopattern = vec![
Call::Absorb(42),
Call::Absorb(5),
Call::Squeeze(4),
Call::Absorb(5),
Call::Squeeze(3),
Call::Squeeze(3),
];
assert!(validate_io_pattern(&iopattern).is_ok());
let iopattern = vec![
Call::Absorb(42),
Call::Squeeze(4),
Call::Absorb(5),
Call::Squeeze(4),
Call::Absorb(5),
Call::Squeeze(3),
Call::Absorb(5),
Call::Squeeze(3),
];
assert!(validate_io_pattern(&iopattern).is_ok());
let iopattern = vec![];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern = vec![Call::Absorb(2)];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern = vec![Call::Squeeze(2)];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern = vec![Call::Absorb(0), Call::Squeeze(2)];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern = vec![Call::Absorb(42), Call::Squeeze(0)];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern =
vec![Call::Squeeze(42), Call::Absorb(3), Call::Squeeze(4)];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern = vec![
Call::Absorb(42),
Call::Absorb(3),
Call::Squeeze(4),
Call::Absorb(3),
];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern = vec![
Call::Absorb(42),
Call::Absorb(3),
Call::Squeeze(0),
Call::Absorb(3),
Call::Squeeze(4),
];
assert!(validate_io_pattern(&iopattern).is_err());
let iopattern =
vec![Call::Absorb(3), Call::Absorb(1 << 31), Call::Squeeze(1)];
assert!(validate_io_pattern(&iopattern).is_err());
}
#[test]
fn test_tag_input() -> Result<(), Error> {
let domain_sep = 42;
let pattern1 = vec![Call::Absorb(2), Call::Squeeze(10)];
let pattern2 = vec![Call::Absorb(2), Call::Squeeze(1)];
assert_ne!(
tag_input(&pattern1, domain_sep)?,
tag_input(&pattern2, domain_sep)?
);
let pattern1 = vec![Call::Absorb(2), Call::Squeeze(1)];
let pattern2 = vec![Call::Absorb(2), Call::Squeeze(1)];
assert_eq!(
tag_input(&pattern1, domain_sep)?,
tag_input(&pattern2, domain_sep)?
);
let pattern1 = vec![Call::Absorb(1), Call::Absorb(1), Call::Squeeze(1)];
let pattern2 = vec![Call::Absorb(2), Call::Squeeze(1)];
assert_eq!(
tag_input(&pattern1, domain_sep)?,
tag_input(&pattern2, domain_sep)?
);
let pattern1 = vec![Call::Absorb(2), Call::Squeeze(10)];
let pattern2 = vec![
Call::Absorb(2),
Call::Squeeze(1),
Call::Squeeze(1),
Call::Squeeze(8),
];
assert_eq!(
tag_input(&pattern1, domain_sep)?,
tag_input(&pattern2, domain_sep)?
);
let pattern1 = vec![
Call::Absorb(2),
Call::Absorb(2),
Call::Squeeze(1),
Call::Squeeze(1),
Call::Squeeze(1),
Call::Absorb(2),
Call::Absorb(2),
Call::Squeeze(1),
Call::Squeeze(8),
];
let pattern2 = vec![
Call::Absorb(3),
Call::Absorb(1),
Call::Squeeze(2),
Call::Squeeze(1),
Call::Absorb(1),
Call::Absorb(3),
Call::Squeeze(5),
Call::Squeeze(4),
];
assert_eq!(
tag_input(&pattern1, domain_sep)?,
tag_input(&pattern2, domain_sep)?
);
Ok(())
}
}