use sha3::{
digest::{ExtendableOutput, Update, XofReader},
Shake128,
};
#[derive(Clone)]
pub struct Transcript {
hasher: Shake128,
#[allow(dead_code)]
domain_separator: Vec<u8>,
}
impl Transcript {
pub fn new(domain_separator: &[u8]) -> Self {
let mut hasher = Shake128::default();
hasher.update(domain_separator);
hasher.update(&(domain_separator.len() as u64).to_le_bytes());
Self {
hasher,
domain_separator: domain_separator.to_vec(),
}
}
pub fn append_message(&mut self, label: &[u8], message: &[u8]) {
self.hasher.update(label);
self.hasher.update(&(label.len() as u64).to_le_bytes());
self.hasher.update(message);
self.hasher.update(&(message.len() as u64).to_le_bytes());
}
pub fn challenge_bytes(&mut self, label: &[u8], dest: &mut [u8]) {
self.hasher.update(label);
self.hasher.update(&(label.len() as u64).to_le_bytes());
let mut reader = self.hasher.clone().finalize_xof();
reader.read(dest);
self.hasher.update(dest);
}
}
#[derive(Clone)]
pub struct DuplexSponge {
state: Shake128,
}
impl DuplexSponge {
pub fn new(initialization_vector: &[u8; 32]) -> Self {
let mut state = Shake128::default();
state.update(initialization_vector);
Self { state }
}
pub fn absorb(&mut self, data: &[u8]) {
self.state.update(data);
self.state.update(&(data.len() as u64).to_le_bytes());
}
pub fn squeeze(&mut self, output_len: usize) -> Vec<u8> {
let mut output = vec![0u8; output_len];
let mut reader = self.state.clone().finalize_xof();
reader.read(&mut output);
self.state.update(&output);
output
}
pub fn prover_message(&mut self, commitment: &[u8]) -> &mut Self {
self.absorb(b"prover");
self.absorb(commitment);
self
}
pub fn verifier_challenge(&mut self, challenge_len: usize) -> Vec<u8> {
self.absorb(b"verifier");
self.squeeze(challenge_len)
}
}