use hybrid_array::Array;
use subtle::ConstantTimeEq;
use super::CommitmentScheme;
use crate::{
constants::CollisionResistanceBytes,
errors::PrimitiveError,
hashing::{hash, Digest},
random::CryptoRngCore,
types::SessionId,
};
pub type Commitment = Digest;
pub type Witness = Array<u8, CollisionResistanceBytes>;
#[derive(Default, Copy, Clone, Debug)]
pub struct HashBasedCommitment {
session_id: Option<SessionId>,
}
impl HashBasedCommitment {
pub fn new() -> Self {
Self { session_id: None }
}
pub fn new_with_session_id(session_id: SessionId) -> Self {
Self {
session_id: Some(session_id),
}
}
}
impl<T> CommitmentScheme<T> for HashBasedCommitment
where
T: AsRef<[u8]>,
{
type Commitment = Commitment;
type Witness = Witness;
fn commit<R: CryptoRngCore>(&self, input: &T, mut rng: R) -> (Self::Commitment, Self::Witness) {
let mut w = Witness::default();
rng.fill_bytes(&mut w);
let c = match self.session_id {
Some(session_id) => hash(&[w.as_ref(), input.as_ref(), session_id.as_ref()]),
None => hash(&[w.as_ref(), input.as_ref()]),
};
(c, w)
}
fn open(
&self,
c: &Self::Commitment,
w: &Self::Witness,
input: &T,
) -> Result<(), PrimitiveError> {
let c_prime = match self.session_id {
Some(session_id) => hash(&[w.as_ref(), input.as_ref(), session_id.as_ref()]),
None => hash(&[w.as_ref(), input.as_ref()]),
};
bool::from(c.ct_eq(&c_prime))
.then_some(())
.ok_or_else(|| PrimitiveError::WrongOpening(format!("{c:?}"), format!("{c_prime:?}")))
}
}