1use blake3;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub struct HashDigest([u8; 32]);
7
8impl HashDigest {
9 pub fn from_bytes(bytes: [u8; 32]) -> Self {
11 Self(bytes)
12 }
13
14 pub fn as_bytes(&self) -> &[u8; 32] {
16 &self.0
17 }
18
19 pub fn to_hex(&self) -> String {
21 hex::encode(&self.0)
22 }
23
24 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
38pub fn hash_data(data: &[u8]) -> HashDigest {
40 let hash = blake3::hash(data);
41 HashDigest(*hash.as_bytes())
42}
43
44pub 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
53pub fn keyed_hash(key: &[u8; 32], data: &[u8]) -> HashDigest {
55 let hash = blake3::keyed_hash(key, data);
56 HashDigest(*hash.as_bytes())
57}
58
59pub 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}