pub mod cubehash;
mod scalar;
mod sse2;
mod avx2;
mod neon;
mod wasm32;
#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
pub mod wasm;
pub use cubehash::{CubeHashAuto, CubeHashBest, CubeHash256, CubeHash384, CubeHash512};
use std::cmp;
pub const BLOCKSIZE: usize = 32;
pub const ROUNDS: i32 = 16;
pub const MAXHASHLEN: i32 = 512;
#[derive(Clone, Copy, Debug)]
pub struct CubeHashParams {
pub revision: i32,
pub hash_len_bits: i32,
}
impl Default for CubeHashParams {
fn default() -> Self { Self { revision: 3, hash_len_bits: 256 } }
}
impl CubeHashParams {
pub fn validate(self) -> Result<(), &'static str> {
if self.revision != 2 && self.revision != 3 {
return Err("revision must be 2 or 3");
}
if self.hash_len_bits <= 0 || self.hash_len_bits > MAXHASHLEN || self.hash_len_bits % 8 != 0 {
return Err("hash length must be between 8 and 512 bits and divisible by 8");
}
Ok(())
}
}
#[inline(always)]
fn rounds_for_rev(revision: i32) -> (i32, i32) {
match revision {
3 => (16, 32), 2 => (160, 160),
_ => (0, 0),
}
}
pub trait Backend {
fn new(params: CubeHashParams) -> Self;
fn absorb_block(&mut self, block32: &[u8]);
fn set_finalize_flag(&mut self);
fn rounds_only(&mut self);
fn output_full(&self) -> [u8; 64];
}
pub struct CubeHash<B: Backend> {
backend: B,
block: [u8; BLOCKSIZE],
used: usize,
out_bits: i32,
frounds: i32,
}
impl<B: Backend> CubeHash<B> {
pub fn new(params: CubeHashParams) -> Self {
params.validate().expect("invalid CubeHashParams");
let (_ir, fr) = rounds_for_rev(params.revision);
Self {
backend: B::new(params),
block: [0u8; BLOCKSIZE],
used: 0,
out_bits: params.hash_len_bits,
frounds: fr,
}
}
#[inline]
pub fn update(&mut self, mut data: &[u8]) {
if self.used > 0 {
let need = BLOCKSIZE - self.used;
let take = cmp::min(need, data.len());
self.block[self.used..self.used + take].copy_from_slice(&data[..take]);
self.used += take;
data = &data[take..];
if self.used == BLOCKSIZE {
self.backend.absorb_block(&self.block);
self.used = 0;
}
}
while data.len() >= BLOCKSIZE {
self.backend.absorb_block(&data[..BLOCKSIZE]);
data = &data[BLOCKSIZE..];
}
if !data.is_empty() {
self.block[..data.len()].copy_from_slice(data);
self.used = data.len();
}
}
#[inline]
pub fn finalize(mut self) -> Vec<u8> {
self.block[self.used] = 0x80;
self.block[self.used + 1..].fill(0);
self.backend.absorb_block(&self.block);
self.backend.set_finalize_flag();
for _ in 0..(self.frounds / ROUNDS) {
self.backend.rounds_only();
}
let full = self.backend.output_full();
let out_len = (self.out_bits / 8) as usize;
full[..out_len].to_vec()
}
}