#![forbid(unsafe_code)]
use crate::core::extent::ChunkId;
use crate::core::limits::Limits;
use crate::core::materialize::{DecoderContext, MaterializeError, materialize_to_vec};
use crate::core::representation::Representation;
pub fn logical_content_hash(bytes: &[u8]) -> ChunkId {
ChunkId::of(bytes)
}
pub fn verify_content(bytes: &[u8], expected: &ChunkId) -> bool {
&logical_content_hash(bytes) == expected
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContentVerifyError {
Materialize(MaterializeError),
LengthMismatch {
declared: u64,
actual: u64,
},
HashMismatch {
key: ChunkId,
actual: ChunkId,
},
}
impl std::fmt::Display for ContentVerifyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ContentVerifyError {}
pub fn verify_descriptor(
descriptor: &Representation,
key: &ChunkId,
resolver: &dyn DecoderContext,
limits: &Limits,
) -> Result<Vec<u8>, ContentVerifyError> {
let bytes = materialize_to_vec(descriptor, resolver, limits)
.map_err(ContentVerifyError::Materialize)?;
if bytes.len() as u64 != descriptor.len() {
return Err(ContentVerifyError::LengthMismatch {
declared: descriptor.len(),
actual: bytes.len() as u64,
});
}
let actual = logical_content_hash(&bytes);
if &actual != key {
return Err(ContentVerifyError::HashMismatch { key: *key, actual });
}
Ok(bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn content_id_distinguishes_bytes() {
assert_ne!(logical_content_hash(b"a"), logical_content_hash(b"b"));
assert_eq!(logical_content_hash(b"same"), logical_content_hash(b"same"));
}
#[test]
fn verify_descriptor_rejects_wrong_key() {
let data = vec![0x42u8; 4096];
let rep = Representation::Fill {
value: 0x42,
len: 4096,
};
let resolver = crate::tests::helpers::MemResolver::empty();
let limits = Limits::default();
let key = ChunkId::of(&data);
let out = verify_descriptor(&rep, &key, &resolver, &limits).unwrap();
assert_eq!(out, data);
let bad = ChunkId::of(b"not the data");
assert!(matches!(
verify_descriptor(&rep, &bad, &resolver, &limits),
Err(ContentVerifyError::HashMismatch { .. })
));
}
}