Skip to main content

shadow_crypto/
hash.rs

1use blake3;
2use serde::{Deserialize, Serialize};
3
4/// Hash digest (256-bit BLAKE3)
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub struct HashDigest([u8; 32]);
7
8impl HashDigest {
9    /// Create from bytes
10    pub fn from_bytes(bytes: [u8; 32]) -> Self {
11        Self(bytes)
12    }
13
14    /// Get as bytes
15    pub fn as_bytes(&self) -> &[u8; 32] {
16        &self.0
17    }
18
19    /// Convert to hex string
20    pub fn to_hex(&self) -> String {
21        hex::encode(&self.0)
22    }
23
24    /// Parse from hex string
25    pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
26        let mut bytes = [0u8; 32];
27        hex::decode_to_slice(s, &mut bytes)?;
28        Ok(Self(bytes))
29    }
30}
31
32impl std::fmt::Display for HashDigest {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.to_hex())
35    }
36}
37
38/// Hash data using BLAKE3
39pub fn hash_data(data: &[u8]) -> HashDigest {
40    let hash = blake3::hash(data);
41    HashDigest(*hash.as_bytes())
42}
43
44/// Hash multiple pieces of data
45pub fn hash_multiple(parts: &[&[u8]]) -> HashDigest {
46    let mut hasher = blake3::Hasher::new();
47    for part in parts {
48        hasher.update(part);
49    }
50    HashDigest(*hasher.finalize().as_bytes())
51}
52
53/// Keyed hash (MAC) using BLAKE3
54pub fn keyed_hash(key: &[u8; 32], data: &[u8]) -> HashDigest {
55    let hash = blake3::keyed_hash(key, data);
56    HashDigest(*hash.as_bytes())
57}
58
59/// Derive key from data using BLAKE3 KDF
60pub fn derive_key(context: &str, key_material: &[u8]) -> [u8; 32] {
61    let mut hasher = blake3::Hasher::new_derive_key(context);
62    hasher.update(key_material);
63    *hasher.finalize().as_bytes()
64}
65
66mod hex {
67    use std::fmt;
68
69    #[derive(Debug)]
70    pub enum FromHexError {
71        InvalidHexCharacter { c: char, index: usize },
72        OddLength,
73        InvalidLength,
74    }
75
76    impl fmt::Display for FromHexError {
77        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78            match self {
79                Self::InvalidHexCharacter { c, index } => {
80                    write!(f, "Invalid hex character '{}' at position {}", c, index)
81                }
82                Self::OddLength => write!(f, "Odd length hex string"),
83                Self::InvalidLength => write!(f, "Invalid length"),
84            }
85        }
86    }
87
88    impl std::error::Error for FromHexError {}
89
90    pub fn encode(data: &[u8]) -> String {
91        data.iter().map(|b| format!("{:02x}", b)).collect()
92    }
93
94    pub fn decode_to_slice(s: &str, target: &mut [u8]) -> Result<(), FromHexError> {
95        if s.len() % 2 != 0 {
96            return Err(FromHexError::OddLength);
97        }
98        if s.len() / 2 != target.len() {
99            return Err(FromHexError::InvalidLength);
100        }
101
102        for (i, chunk) in s.as_bytes().chunks(2).enumerate() {
103            target[i] = parse_hex_byte(chunk, i * 2)?;
104        }
105
106        Ok(())
107    }
108
109    fn parse_hex_byte(bytes: &[u8], index: usize) -> Result<u8, FromHexError> {
110        let high = parse_hex_digit(bytes[0] as char, index)?;
111        let low = parse_hex_digit(bytes[1] as char, index + 1)?;
112        Ok((high << 4) | low)
113    }
114
115    fn parse_hex_digit(c: char, index: usize) -> Result<u8, FromHexError> {
116        match c {
117            '0'..='9' => Ok(c as u8 - b'0'),
118            'a'..='f' => Ok(c as u8 - b'a' + 10),
119            'A'..='F' => Ok(c as u8 - b'A' + 10),
120            _ => Err(FromHexError::InvalidHexCharacter { c, index }),
121        }
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_hash_data() {
131        let data = b"Hello, Shadow Network!";
132        let hash1 = hash_data(data);
133        let hash2 = hash_data(data);
134        
135        assert_eq!(hash1, hash2);
136        
137        let different = b"Different data";
138        let hash3 = hash_data(different);
139        assert_ne!(hash1, hash3);
140    }
141
142    #[test]
143    fn test_hash_hex() {
144        let data = b"test";
145        let hash = hash_data(data);
146        let hex = hash.to_hex();
147        let parsed = HashDigest::from_hex(&hex).unwrap();
148        
149        assert_eq!(hash, parsed);
150    }
151
152    #[test]
153    fn test_keyed_hash() {
154        let key = [0u8; 32];
155        let data = b"message";
156        
157        let mac1 = keyed_hash(&key, data);
158        let mac2 = keyed_hash(&key, data);
159        
160        assert_eq!(mac1, mac2);
161        
162        let different_key = [1u8; 32];
163        let mac3 = keyed_hash(&different_key, data);
164        assert_ne!(mac1, mac3);
165    }
166}