CanMan/src/hash.rs
Jason Tudisco 360ecbdad0 Initial commit: CAN Service + examples (can-sync v1, canfs, filemanager, paste)
CAN Service: content-addressable storage with HTTP API, SQLite metadata,
file-based blob storage, thumbnail generation, and integrity verification.

can-sync v1: P2P sync sidecar using iroh-docs for encrypted peer-to-peer
replication with library/filter-based selective sync. Fully builds but
being superseded by v2 (simplified full-mirror approach).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 10:32:04 -06:00

49 lines
1.4 KiB
Rust

use sha2::{Digest, Sha256};
/// Compute SHA-256 hash as: SHA256(timestamp_bytes + content_bytes).
/// The timestamp is serialized as its string representation's bytes,
/// matching the spec: `SHA256([timestamp_bytes] + [raw_file_content_bytes])`.
pub fn compute_hash(timestamp_ms: i64, content: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(timestamp_ms.to_be_bytes());
hasher.update(content);
hex::encode(hasher.finalize())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deterministic_hash() {
let ts = 1773014400123i64;
let content = b"hello world";
let h1 = compute_hash(ts, content);
let h2 = compute_hash(ts, content);
assert_eq!(h1, h2);
assert_eq!(h1.len(), 64); // SHA-256 hex = 64 chars
}
#[test]
fn test_different_timestamp_different_hash() {
let content = b"same content";
let h1 = compute_hash(1000, content);
let h2 = compute_hash(2000, content);
assert_ne!(h1, h2);
}
#[test]
fn test_different_content_different_hash() {
let ts = 1234567890i64;
let h1 = compute_hash(ts, b"content A");
let h2 = compute_hash(ts, b"content B");
assert_ne!(h1, h2);
}
#[test]
fn test_empty_content() {
let h = compute_hash(0, b"");
assert_eq!(h.len(), 64);
}
}