use purecrypto::hash::sha512_256;
const DELIMITER: u8 = b'$';
pub fn sha512_256_parts(parts: &[&[u8]]) -> [u8; 32] {
let mut data = Vec::new();
data.extend_from_slice(&(parts.len() as u64).to_le_bytes());
for p in parts {
data.extend_from_slice(p);
data.push(DELIMITER);
data.extend_from_slice(&(p.len() as u64).to_le_bytes());
}
sha512_256(&data)
}
pub fn sha512_256i_tagged(tag: &[u8], operands: &[&[u8]]) -> [u8; 32] {
let tag_bz = sha512_256_parts(&[tag]);
let data = operands_data(operands);
let mut input = Vec::with_capacity(64 + data.len());
input.extend_from_slice(&tag_bz);
input.extend_from_slice(&tag_bz);
input.extend_from_slice(&data);
sha512_256(&input)
}
pub fn sha512_256i(operands: &[&[u8]]) -> [u8; 32] {
sha512_256(&operands_data(operands))
}
fn operands_data(operands: &[&[u8]]) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&(operands.len() as u64).to_le_bytes());
for op in operands {
data.push(0x00); data.extend_from_slice(op);
data.push(DELIMITER);
data.extend_from_slice(&(op.len() as u64).to_le_bytes());
}
data
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic() {
let a = sha512_256i_tagged(b"tag", &[&[1, 2, 3], &[4, 5]]);
let b = sha512_256i_tagged(b"tag", &[&[1, 2, 3], &[4, 5]]);
assert_eq!(a, b);
}
#[test]
fn tag_and_operands_matter() {
let base = sha512_256i_tagged(b"tag", &[&[1]]);
assert_ne!(base, sha512_256i_tagged(b"other", &[&[1]]));
assert_ne!(base, sha512_256i_tagged(b"tag", &[&[2]]));
assert_ne!(
sha512_256i_tagged(b"tag", &[&[1, 2], &[3]]),
sha512_256i_tagged(b"tag", &[&[1], &[2, 3]])
);
}
}