use super::hashing::sha512_256i;
use purecrypto::rng::RngCore;
pub fn commit(rng: &mut impl RngCore, secrets: &[Vec<u8>]) -> (Vec<u8>, Vec<Vec<u8>>) {
let mut r = [0u8; 32];
rng.fill_bytes(&mut r);
let mut d: Vec<Vec<u8>> = Vec::with_capacity(secrets.len() + 1);
d.push(strip(&r).to_vec());
d.extend(secrets.iter().cloned());
let c = digest(&d);
(c, d)
}
pub fn decommit(c: &[u8], d: &[Vec<u8>]) -> Option<Vec<Vec<u8>>> {
if d.is_empty() {
return None;
}
if digest(d) != strip(c) {
return None;
}
Some(d[1..].to_vec())
}
fn digest(parts: &[Vec<u8>]) -> Vec<u8> {
let refs: Vec<&[u8]> = parts.iter().map(|p| p.as_slice()).collect();
strip(&sha512_256i(&refs)).to_vec()
}
fn strip(b: &[u8]) -> &[u8] {
let start = b.iter().position(|&x| x != 0).unwrap_or(b.len());
&b[start..]
}
#[cfg(test)]
mod tests {
use super::*;
use purecrypto::rng::OsRng;
#[test]
fn commit_decommit_roundtrip() {
let secrets = vec![vec![1, 2, 3], vec![0xff, 0x00, 0x11], vec![42]];
let (c, d) = commit(&mut OsRng, &secrets);
let opened = decommit(&c, &d).unwrap();
assert_eq!(opened, secrets);
}
#[test]
fn tampered_commitment_fails() {
let secrets = vec![vec![1, 2, 3]];
let (mut c, d) = commit(&mut OsRng, &secrets);
c[0] ^= 0x01;
assert!(decommit(&c, &d).is_none());
}
#[test]
fn tampered_decommitment_fails() {
let secrets = vec![vec![1, 2, 3]];
let (c, mut d) = commit(&mut OsRng, &secrets);
d[1][0] ^= 0x01;
assert!(decommit(&c, &d).is_none());
}
}