1use std::fmt::Write;
2
3use bstr::BString;
4use md5::Md5;
5use mlua::prelude::*;
6
7use blake3::Hasher as Blake3;
8use sha1::Sha1;
9use sha2::{Sha224, Sha256, Sha384, Sha512};
10use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512};
11
12pub struct HashOptions {
13 algorithm: HashAlgorithm,
14 message: BString,
15 secret: Option<BString>,
16 }
18
19#[derive(Debug, Clone, Copy)]
20enum HashAlgorithm {
21 Md5,
22 Sha1,
23 Sha2_224,
25 Sha2_256,
26 Sha2_384,
27 Sha2_512,
28 Sha3_224,
30 Sha3_256,
31 Sha3_384,
32 Sha3_512,
33 Blake3,
35}
36
37impl HashAlgorithm {
38 pub const ALL: [Self; 11] = [
39 Self::Md5,
40 Self::Sha1,
41 Self::Sha2_224,
42 Self::Sha2_256,
43 Self::Sha2_384,
44 Self::Sha2_512,
45 Self::Sha3_224,
46 Self::Sha3_256,
47 Self::Sha3_384,
48 Self::Sha3_512,
49 Self::Blake3,
50 ];
51
52 pub const fn name(self) -> &'static str {
53 match self {
54 Self::Md5 => "md5",
55 Self::Sha1 => "sha1",
56 Self::Sha2_224 => "sha224",
57 Self::Sha2_256 => "sha256",
58 Self::Sha2_384 => "sha384",
59 Self::Sha2_512 => "sha512",
60 Self::Sha3_224 => "sha3-224",
61 Self::Sha3_256 => "sha3-256",
62 Self::Sha3_384 => "sha3-384",
63 Self::Sha3_512 => "sha3-512",
64 Self::Blake3 => "blake3",
65 }
66 }
67}
68
69impl HashOptions {
70 #[inline]
75 #[must_use = "hashing a message is useless without using the resulting hash"]
76 pub fn hash(self) -> String {
77 use digest::Digest;
78
79 let message = self.message;
80 let bytes = match self.algorithm {
81 HashAlgorithm::Md5 => Md5::digest(message).to_vec(),
82 HashAlgorithm::Sha1 => Sha1::digest(message).to_vec(),
83 HashAlgorithm::Sha2_224 => Sha224::digest(message).to_vec(),
84 HashAlgorithm::Sha2_256 => Sha256::digest(message).to_vec(),
85 HashAlgorithm::Sha2_384 => Sha384::digest(message).to_vec(),
86 HashAlgorithm::Sha2_512 => Sha512::digest(message).to_vec(),
87
88 HashAlgorithm::Sha3_224 => Sha3_224::digest(message).to_vec(),
89 HashAlgorithm::Sha3_256 => Sha3_256::digest(message).to_vec(),
90 HashAlgorithm::Sha3_384 => Sha3_384::digest(message).to_vec(),
91 HashAlgorithm::Sha3_512 => Sha3_512::digest(message).to_vec(),
92
93 HashAlgorithm::Blake3 => Blake3::digest(message).to_vec(),
94 };
95
96 bytes
99 .iter()
100 .fold(String::with_capacity(bytes.len() * 2), |mut output, b| {
101 let _ = write!(output, "{b:02x}");
102 output
103 })
104 }
105
106 #[inline]
116 pub fn hmac(self) -> LuaResult<String> {
117 use hmac::{Hmac, Mac, SimpleHmac};
118
119 let secret = self
120 .secret
121 .ok_or_else(|| LuaError::FromLuaConversionError {
122 from: "nil",
123 to: "string or buffer".to_string(),
124 message: Some("Argument #3 missing or nil".to_string()),
125 })?;
126
127 macro_rules! hmac {
137 ($Type:ty) => {{
138 let mut mac: Hmac<$Type> = Hmac::new_from_slice(&secret).into_lua_err()?;
139 mac.update(&self.message);
140 mac.finalize().into_bytes().to_vec()
141 }};
142 }
143 macro_rules! hmac_no_blocks {
144 ($Type:ty) => {{
145 let mut mac: SimpleHmac<$Type> =
146 SimpleHmac::new_from_slice(&secret).into_lua_err()?;
147 mac.update(&self.message);
148 mac.finalize().into_bytes().to_vec()
149 }};
150 }
151
152 let bytes = match self.algorithm {
153 HashAlgorithm::Md5 => hmac!(Md5),
154 HashAlgorithm::Sha1 => hmac!(Sha1),
155
156 HashAlgorithm::Sha2_224 => hmac!(Sha224),
157 HashAlgorithm::Sha2_256 => hmac!(Sha256),
158 HashAlgorithm::Sha2_384 => hmac!(Sha384),
159 HashAlgorithm::Sha2_512 => hmac!(Sha512),
160
161 HashAlgorithm::Sha3_224 => hmac!(Sha3_224),
162 HashAlgorithm::Sha3_256 => hmac!(Sha3_256),
163 HashAlgorithm::Sha3_384 => hmac!(Sha3_384),
164 HashAlgorithm::Sha3_512 => hmac!(Sha3_512),
165
166 HashAlgorithm::Blake3 => hmac_no_blocks!(Blake3),
167 };
168 Ok(bytes
169 .iter()
170 .fold(String::with_capacity(bytes.len() * 2), |mut output, b| {
171 let _ = write!(output, "{b:02x}");
172 output
173 }))
174 }
175}
176
177impl FromLua for HashAlgorithm {
178 fn from_lua(value: LuaValue, _lua: &Lua) -> LuaResult<Self> {
179 if let LuaValue::String(str) = value {
180 let str = str.to_str()?.to_ascii_lowercase();
185 match str.as_str() {
186 "md5" => Ok(Self::Md5),
187 "sha1" => Ok(Self::Sha1),
188
189 "sha2-224" | "sha2_224" | "sha224" => Ok(Self::Sha2_224),
190 "sha2-256" | "sha2_256" | "sha256" => Ok(Self::Sha2_256),
191 "sha2-384" | "sha2_384" | "sha384" => Ok(Self::Sha2_384),
192 "sha2-512" | "sha2_512" | "sha512" => Ok(Self::Sha2_512),
193
194 "sha3-224" | "sha3_224" => Ok(Self::Sha3_224),
195 "sha3-256" | "sha3_256" => Ok(Self::Sha3_256),
196 "sha3-384" | "sha3_384" => Ok(Self::Sha3_384),
197 "sha3-512" | "sha3_512" => Ok(Self::Sha3_512),
198
199 "blake3" => Ok(Self::Blake3),
200
201 _ => Err(LuaError::FromLuaConversionError {
202 from: "string",
203 to: "HashAlgorithm".to_string(),
204 message: Some(format!(
205 "Invalid hashing algorithm '{str}', valid kinds are:\n{}",
206 HashAlgorithm::ALL
207 .into_iter()
208 .map(HashAlgorithm::name)
209 .collect::<Vec<_>>()
210 .join(", ")
211 )),
212 }),
213 }
214 } else {
215 Err(LuaError::FromLuaConversionError {
216 from: value.type_name(),
217 to: "HashAlgorithm".to_string(),
218 message: None,
219 })
220 }
221 }
222}
223
224impl FromLuaMulti for HashOptions {
225 fn from_lua_multi(mut values: LuaMultiValue, lua: &Lua) -> LuaResult<Self> {
226 let algorithm = values
227 .pop_front()
228 .map(|value| HashAlgorithm::from_lua(value, lua))
229 .transpose()?
230 .ok_or_else(|| LuaError::FromLuaConversionError {
231 from: "nil",
232 to: "HashOptions".to_string(),
233 message: Some("Argument #1 missing or nil".to_string()),
234 })?;
235 let message = values
236 .pop_front()
237 .map(|value| BString::from_lua(value, lua))
238 .transpose()?
239 .ok_or_else(|| LuaError::FromLuaConversionError {
240 from: "nil",
241 to: "string or buffer".to_string(),
242 message: Some("Argument #2 missing or nil".to_string()),
243 })?;
244 let secret = values
245 .pop_front()
246 .map(|value| BString::from_lua(value, lua))
247 .transpose()?;
248 Ok(HashOptions {
254 algorithm,
255 message,
256 secret,
257 })
259 }
260}