use alloc::vec::Vec;
use rns_crypto::sha256::Sha256;
use super::types::ResourceError;
pub(crate) fn data_hash_state(unencrypted_data: &[u8]) -> Sha256 {
let mut state = Sha256::new();
state.update(unencrypted_data);
state
}
pub(crate) fn finish_with(state: &Sha256, suffix: &[u8]) -> [u8; 32] {
let mut hasher = state.clone();
hasher.update(suffix);
hasher.digest()
}
pub fn compute_resource_hash(unencrypted_data: &[u8], random_hash: &[u8]) -> [u8; 32] {
finish_with(&data_hash_state(unencrypted_data), random_hash)
}
pub fn compute_expected_proof(unencrypted_data: &[u8], resource_hash: &[u8; 32]) -> [u8; 32] {
finish_with(&data_hash_state(unencrypted_data), resource_hash)
}
pub fn build_proof_data(resource_hash: &[u8; 32], proof: &[u8; 32]) -> Vec<u8> {
let mut data = Vec::with_capacity(64);
data.extend_from_slice(resource_hash);
data.extend_from_slice(proof);
data
}
pub fn validate_proof(
proof_data: &[u8],
_expected_resource_hash: &[u8; 32],
expected_proof: &[u8; 32],
) -> Result<bool, ResourceError> {
if proof_data.len() != 64 {
return Err(ResourceError::InvalidProof);
}
let recv_proof = &proof_data[32..64];
Ok(recv_proof == expected_proof.as_slice())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::full_hash;
fn concatenated(data: &[u8], suffix: &[u8]) -> [u8; 32] {
let mut input = data.to_vec();
input.extend_from_slice(suffix);
full_hash(&input)
}
#[test]
fn streamed_hash_and_proof_match_concatenation() {
for len in [0usize, 1, 55, 56, 63, 64, 65, 127, 128, 1000, 70_001] {
let data: Vec<u8> = (0..len).map(|i| (i * 7 + 3) as u8).collect();
let random = [0x5a, 0x01, 0xfe, 0x33];
let hash = compute_resource_hash(&data, &random);
assert_eq!(hash, concatenated(&data, &random), "hash, len {len}");
let proof = compute_expected_proof(&data, &hash);
assert_eq!(proof, concatenated(&data, &hash), "proof, len {len}");
let state = data_hash_state(&data);
assert_eq!(finish_with(&state, &hash), proof);
assert_eq!(finish_with(&state, &random), hash);
assert_eq!(
finish_with(&state, &[0x5a, 0x01, 0xfe, 0x34]),
concatenated(&data, &[0x5a, 0x01, 0xfe, 0x34])
);
}
}
#[test]
fn test_compute_resource_hash() {
let data = b"test resource data";
let random = [0xAA, 0xBB, 0xCC, 0xDD];
let hash = compute_resource_hash(data, &random);
assert_eq!(hash.len(), 32);
let hash2 = compute_resource_hash(data, &random);
assert_eq!(hash, hash2);
}
#[test]
fn test_compute_expected_proof() {
let data = b"test resource data";
let random = [0xAA, 0xBB, 0xCC, 0xDD];
let resource_hash = compute_resource_hash(data, &random);
let proof = compute_expected_proof(data, &resource_hash);
assert_eq!(proof.len(), 32);
assert_ne!(proof, resource_hash); }
#[test]
fn test_build_proof_data() {
let hash = [0x11u8; 32];
let proof = [0x22u8; 32];
let data = build_proof_data(&hash, &proof);
assert_eq!(data.len(), 64);
assert_eq!(&data[..32], &hash);
assert_eq!(&data[32..], &proof);
}
#[test]
fn test_validate_proof_valid() {
let data = b"resource data here";
let random = [0x11, 0x22, 0x33, 0x44];
let resource_hash = compute_resource_hash(data, &random);
let expected = compute_expected_proof(data, &resource_hash);
let proof_data = build_proof_data(&resource_hash, &expected);
assert_eq!(
validate_proof(&proof_data, &resource_hash, &expected),
Ok(true)
);
}
#[test]
fn test_validate_proof_wrong_hash_prefix_still_valid() {
let data = b"resource data";
let random = [0x11; 4];
let resource_hash = compute_resource_hash(data, &random);
let expected = compute_expected_proof(data, &resource_hash);
let wrong_hash = [0xFF; 32];
let proof_data = build_proof_data(&wrong_hash, &expected);
assert_eq!(
validate_proof(&proof_data, &resource_hash, &expected),
Ok(true)
);
}
#[test]
fn test_validate_proof_invalid_proof() {
let data = b"resource data";
let random = [0x11; 4];
let resource_hash = compute_resource_hash(data, &random);
let expected = compute_expected_proof(data, &resource_hash);
let wrong_proof = [0xFF; 32];
let proof_data = build_proof_data(&resource_hash, &wrong_proof);
assert_eq!(
validate_proof(&proof_data, &resource_hash, &expected),
Ok(false)
);
}
#[test]
fn test_validate_proof_wrong_length() {
let hash = [0x11; 32];
let proof = [0x22; 32];
assert!(validate_proof(&[0; 50], &hash, &proof).is_err());
assert!(validate_proof(&[], &hash, &proof).is_err());
}
#[test]
fn test_proof_uses_unencrypted_data() {
let random = [0xAA; 4];
let hash1 = compute_resource_hash(b"data1", &random);
let hash2 = compute_resource_hash(b"data2", &random);
assert_ne!(hash1, hash2);
let proof1 = compute_expected_proof(b"data1", &hash1);
let proof2 = compute_expected_proof(b"data2", &hash2);
assert_ne!(proof1, proof2);
}
}