Skip to main content

rns_core/
hash.rs

1use rns_crypto::Rng;
2
3use crate::constants;
4
5/// Compute full SHA-256 hash of data.
6pub fn full_hash(data: &[u8]) -> [u8; 32] {
7    rns_crypto::sha256::sha256(data)
8}
9
10/// Compute truncated SHA-256 hash (first 16 bytes).
11pub fn truncated_hash(data: &[u8]) -> [u8; 16] {
12    let full = full_hash(data);
13    let mut result = [0u8; 16];
14    result.copy_from_slice(&full[..16]);
15    result
16}
17
18/// Generate a random truncated hash: truncated_hash(random(16)).
19pub fn get_random_hash(rng: &mut dyn Rng) -> [u8; 16] {
20    let mut random_bytes = [0u8; 16];
21    rng.fill_bytes(&mut random_bytes);
22    truncated_hash(&random_bytes)
23}
24
25/// Compute name hash from app_name and aspects.
26/// = SHA-256("app_name.aspect1.aspect2".as_bytes())[:10]
27///
28/// Panics if app_name or any aspect contains a dot, matching Python's
29/// `ValueError("Dots can't be used in app names/aspects")`.
30pub fn name_hash(app_name: &str, aspects: &[&str]) -> [u8; constants::NAME_HASH_LENGTH / 8] {
31    assert!(!app_name.contains('.'), "Dots can't be used in app names");
32    for aspect in aspects {
33        assert!(!aspect.contains('.'), "Dots can't be used in aspects");
34    }
35    let mut name = alloc::string::String::from(app_name);
36    for aspect in aspects {
37        name.push('.');
38        name.push_str(aspect);
39    }
40    let full = full_hash(name.as_bytes());
41    let mut result = [0u8; constants::NAME_HASH_LENGTH / 8];
42    result.copy_from_slice(&full[..constants::NAME_HASH_LENGTH / 8]);
43    result
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn test_truncated_hash_is_prefix_of_full_hash() {
52        let data = b"test data";
53        let full = full_hash(data);
54        let trunc = truncated_hash(data);
55        assert_eq!(&full[..16], &trunc);
56    }
57
58    #[test]
59    fn test_name_hash_basic() {
60        let nh = name_hash("app", &["aspect"]);
61        assert_eq!(nh.len(), 10);
62        // Verify it's deterministic
63        let nh2 = name_hash("app", &["aspect"]);
64        assert_eq!(nh, nh2);
65    }
66
67    #[test]
68    fn test_name_hash_multiple_aspects() {
69        // name_hash("app", &["a", "b"]) should hash "app.a.b"
70        let nh = name_hash("app", &["a", "b"]);
71        let expected = full_hash(b"app.a.b");
72        assert_eq!(nh, expected[..10]);
73    }
74
75    #[test]
76    fn test_get_random_hash() {
77        let mut rng = rns_crypto::FixedRng::new(&[0x42; 32]);
78        let h = get_random_hash(&mut rng);
79        assert_eq!(h.len(), 16);
80    }
81
82    #[test]
83    #[should_panic(expected = "Dots can't be used in app names")]
84    fn test_name_hash_rejects_dot_in_app_name() {
85        name_hash("app.bad", &["aspect"]);
86    }
87
88    #[test]
89    #[should_panic(expected = "Dots can't be used in aspects")]
90    fn test_name_hash_rejects_dot_in_aspect() {
91        name_hash("app", &["bad.aspect"]);
92    }
93}