use alloc::vec::Vec;
use dusk_curves::bls12_381::BlsScalar;
use dusk_jubjub::JubJubScalar;
use dusk_safe::{Call, Sponge};
use crate::Error;
use crate::hades::ScalarPermutation;
#[cfg(feature = "zk")]
pub(crate) mod gadget;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Domain {
Merkle4,
Merkle2,
Encryption,
Other,
}
impl From<Domain> for u64 {
fn from(domain: Domain) -> Self {
match domain {
Domain::Merkle4 => 0x0000_0000_0000_000f,
Domain::Merkle2 => 0x0000_0000_0000_0003,
Domain::Encryption => 0x0000_0001_0000_0000,
Domain::Other => 0x0000_0000_0000_0000,
}
}
}
fn io_pattern<T>(
domain: Domain,
input: &[&[T]],
output_len: usize,
) -> Result<Vec<Call>, Error> {
let mut io_pattern = Vec::new();
let input_len = input.iter().fold(0, |acc, input| acc + input.len());
match domain {
Domain::Merkle2 if input_len != 2 || output_len != 1 => {
return Err(Error::IOPatternViolation);
}
Domain::Merkle4 if input_len != 4 || output_len != 1 => {
return Err(Error::IOPatternViolation);
}
_ => {}
}
for input in input.iter() {
io_pattern.push(Call::Absorb(input.len()));
}
io_pattern.push(Call::Squeeze(output_len));
Ok(io_pattern)
}
pub struct Hash<'a> {
domain: Domain,
input: Vec<&'a [BlsScalar]>,
output_len: usize,
}
impl<'a> Hash<'a> {
pub fn new(domain: Domain) -> Self {
Self {
domain,
input: Vec::new(),
output_len: 1,
}
}
pub fn output_len(&mut self, output_len: usize) {
if self.domain == Domain::Other && output_len > 0 {
self.output_len = output_len;
}
}
pub fn update(&mut self, input: &'a [BlsScalar]) {
self.input.push(input);
}
pub fn finalize(&self) -> Vec<BlsScalar> {
let mut sponge = Sponge::start(
ScalarPermutation::new(),
io_pattern(self.domain, &self.input, self.output_len)
.expect("io-pattern should be valid"),
self.domain.into(),
)
.expect("at this point the io-pattern is valid");
for input in self.input.iter() {
sponge
.absorb(input.len(), input)
.expect("at this point the io-pattern is valid");
}
sponge
.squeeze(self.output_len)
.expect("at this point the io-pattern is valid");
sponge
.finish()
.expect("at this point the io-pattern is valid")
}
pub fn finalize_truncated(&self) -> Vec<JubJubScalar> {
const TRUNCATION_MASK: BlsScalar = BlsScalar::from_raw([
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
0x03ff_ffff_ffff_ffff,
]);
let bls_output = self.finalize();
bls_output
.iter()
.map(|bls| {
JubJubScalar::from_raw((bls & &TRUNCATION_MASK).reduce().0)
})
.collect()
}
pub fn digest(domain: Domain, input: &'a [BlsScalar]) -> Vec<BlsScalar> {
let mut hash = Self::new(domain);
hash.update(input);
hash.finalize()
}
pub fn digest_truncated(
domain: Domain,
input: &'a [BlsScalar],
) -> Vec<JubJubScalar> {
let mut hash = Self::new(domain);
hash.update(input);
hash.finalize_truncated()
}
}