Skip to main content

jpx_core/extensions/
hash.rs

1//! Cryptographic hash functions.
2
3use std::collections::HashSet;
4
5use serde_json::Value;
6
7use crate::functions::Function;
8use crate::interpreter::SearchResult;
9use crate::registry::register_if_enabled;
10use crate::{Context, Runtime, arg, defn};
11
12use crc32fast::Hasher as Crc32Hasher;
13use hmac::{Hmac, Mac};
14use md5::{Digest, Md5};
15use sha1::Sha1;
16use sha2::{Sha256, Sha512};
17
18// Type aliases for HMAC variants
19type HmacMd5 = Hmac<Md5>;
20type HmacSha1 = Hmac<Sha1>;
21type HmacSha256 = Hmac<Sha256>;
22type HmacSha512 = Hmac<Sha512>;
23
24/// Register hash functions with the runtime, filtered by the enabled set.
25pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
26    // Hash functions
27    register_if_enabled(runtime, "md5", enabled, Box::new(Md5Fn::new()));
28    register_if_enabled(runtime, "sha1", enabled, Box::new(Sha1Fn::new()));
29    register_if_enabled(runtime, "sha256", enabled, Box::new(Sha256Fn::new()));
30    register_if_enabled(runtime, "sha512", enabled, Box::new(Sha512Fn::new()));
31
32    // HMAC functions
33    register_if_enabled(runtime, "hmac_md5", enabled, Box::new(HmacMd5Fn::new()));
34    register_if_enabled(runtime, "hmac_sha1", enabled, Box::new(HmacSha1Fn::new()));
35    register_if_enabled(
36        runtime,
37        "hmac_sha256",
38        enabled,
39        Box::new(HmacSha256Fn::new()),
40    );
41    register_if_enabled(
42        runtime,
43        "hmac_sha512",
44        enabled,
45        Box::new(HmacSha512Fn::new()),
46    );
47
48    // Checksum functions
49    register_if_enabled(runtime, "crc32", enabled, Box::new(Crc32Fn::new()));
50}
51
52// =============================================================================
53// md5(string) -> string (hex-encoded MD5 hash)
54// =============================================================================
55
56defn!(Md5Fn, vec![arg!(string)], None);
57
58impl Function for Md5Fn {
59    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
60        self.signature.validate(args, ctx)?;
61
62        let input = args[0].as_str().ok_or_else(|| {
63            crate::JmespathError::from_ctx(
64                ctx,
65                crate::ErrorReason::Parse("Expected string argument".to_owned()),
66            )
67        })?;
68
69        let mut hasher = Md5::new();
70        hasher.update(input.as_bytes());
71        let result = hasher.finalize();
72        let hex_string = format!("{:x}", result);
73
74        Ok(Value::String(hex_string))
75    }
76}
77
78// =============================================================================
79// sha1(string) -> string (hex-encoded SHA-1 hash)
80// =============================================================================
81
82defn!(Sha1Fn, vec![arg!(string)], None);
83
84impl Function for Sha1Fn {
85    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
86        self.signature.validate(args, ctx)?;
87
88        let input = args[0].as_str().ok_or_else(|| {
89            crate::JmespathError::from_ctx(
90                ctx,
91                crate::ErrorReason::Parse("Expected string argument".to_owned()),
92            )
93        })?;
94
95        let mut hasher = Sha1::new();
96        hasher.update(input.as_bytes());
97        let result = hasher.finalize();
98        let hex_string = format!("{:x}", result);
99
100        Ok(Value::String(hex_string))
101    }
102}
103
104// =============================================================================
105// sha256(string) -> string (hex-encoded SHA-256 hash)
106// =============================================================================
107
108defn!(Sha256Fn, vec![arg!(string)], None);
109
110impl Function for Sha256Fn {
111    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
112        self.signature.validate(args, ctx)?;
113
114        let input = args[0].as_str().ok_or_else(|| {
115            crate::JmespathError::from_ctx(
116                ctx,
117                crate::ErrorReason::Parse("Expected string argument".to_owned()),
118            )
119        })?;
120
121        let mut hasher = Sha256::new();
122        hasher.update(input.as_bytes());
123        let result = hasher.finalize();
124        let hex_string = format!("{:x}", result);
125
126        Ok(Value::String(hex_string))
127    }
128}
129
130// =============================================================================
131// sha512(string) -> string (hex-encoded SHA-512 hash)
132// =============================================================================
133
134defn!(Sha512Fn, vec![arg!(string)], None);
135
136impl Function for Sha512Fn {
137    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
138        self.signature.validate(args, ctx)?;
139
140        let input = args[0].as_str().ok_or_else(|| {
141            crate::JmespathError::from_ctx(
142                ctx,
143                crate::ErrorReason::Parse("Expected string argument".to_owned()),
144            )
145        })?;
146
147        let mut hasher = Sha512::new();
148        hasher.update(input.as_bytes());
149        let result = hasher.finalize();
150        let hex_string = format!("{:x}", result);
151
152        Ok(Value::String(hex_string))
153    }
154}
155
156// =============================================================================
157// hmac_md5(text, key) -> string (hex-encoded HMAC-MD5)
158// =============================================================================
159
160defn!(HmacMd5Fn, vec![arg!(string), arg!(string)], None);
161
162impl Function for HmacMd5Fn {
163    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
164        self.signature.validate(args, ctx)?;
165
166        let text = args[0].as_str().ok_or_else(|| {
167            crate::JmespathError::from_ctx(
168                ctx,
169                crate::ErrorReason::Parse("Expected string for text argument".to_owned()),
170            )
171        })?;
172
173        let key = args[1].as_str().ok_or_else(|| {
174            crate::JmespathError::from_ctx(
175                ctx,
176                crate::ErrorReason::Parse("Expected string for key argument".to_owned()),
177            )
178        })?;
179
180        let mut mac =
181            HmacMd5::new_from_slice(key.as_bytes()).expect("HMAC can take key of any size");
182        mac.update(text.as_bytes());
183        let result = mac.finalize();
184        let hex_string = format!("{:x}", result.into_bytes());
185
186        Ok(Value::String(hex_string))
187    }
188}
189
190// =============================================================================
191// hmac_sha1(text, key) -> string (hex-encoded HMAC-SHA1)
192// =============================================================================
193
194defn!(HmacSha1Fn, vec![arg!(string), arg!(string)], None);
195
196impl Function for HmacSha1Fn {
197    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
198        self.signature.validate(args, ctx)?;
199
200        let text = args[0].as_str().ok_or_else(|| {
201            crate::JmespathError::from_ctx(
202                ctx,
203                crate::ErrorReason::Parse("Expected string for text argument".to_owned()),
204            )
205        })?;
206
207        let key = args[1].as_str().ok_or_else(|| {
208            crate::JmespathError::from_ctx(
209                ctx,
210                crate::ErrorReason::Parse("Expected string for key argument".to_owned()),
211            )
212        })?;
213
214        let mut mac =
215            HmacSha1::new_from_slice(key.as_bytes()).expect("HMAC can take key of any size");
216        mac.update(text.as_bytes());
217        let result = mac.finalize();
218        let hex_string = format!("{:x}", result.into_bytes());
219
220        Ok(Value::String(hex_string))
221    }
222}
223
224// =============================================================================
225// hmac_sha256(text, key) -> string (hex-encoded HMAC-SHA256)
226// =============================================================================
227
228defn!(HmacSha256Fn, vec![arg!(string), arg!(string)], None);
229
230impl Function for HmacSha256Fn {
231    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
232        self.signature.validate(args, ctx)?;
233
234        let text = args[0].as_str().ok_or_else(|| {
235            crate::JmespathError::from_ctx(
236                ctx,
237                crate::ErrorReason::Parse("Expected string for text argument".to_owned()),
238            )
239        })?;
240
241        let key = args[1].as_str().ok_or_else(|| {
242            crate::JmespathError::from_ctx(
243                ctx,
244                crate::ErrorReason::Parse("Expected string for key argument".to_owned()),
245            )
246        })?;
247
248        let mut mac =
249            HmacSha256::new_from_slice(key.as_bytes()).expect("HMAC can take key of any size");
250        mac.update(text.as_bytes());
251        let result = mac.finalize();
252        let hex_string = format!("{:x}", result.into_bytes());
253
254        Ok(Value::String(hex_string))
255    }
256}
257
258// =============================================================================
259// hmac_sha512(text, key) -> string (hex-encoded HMAC-SHA512)
260// =============================================================================
261
262defn!(HmacSha512Fn, vec![arg!(string), arg!(string)], None);
263
264impl Function for HmacSha512Fn {
265    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
266        self.signature.validate(args, ctx)?;
267
268        let text = args[0].as_str().ok_or_else(|| {
269            crate::JmespathError::from_ctx(
270                ctx,
271                crate::ErrorReason::Parse("Expected string for text argument".to_owned()),
272            )
273        })?;
274
275        let key = args[1].as_str().ok_or_else(|| {
276            crate::JmespathError::from_ctx(
277                ctx,
278                crate::ErrorReason::Parse("Expected string for key argument".to_owned()),
279            )
280        })?;
281
282        let mut mac =
283            HmacSha512::new_from_slice(key.as_bytes()).expect("HMAC can take key of any size");
284        mac.update(text.as_bytes());
285        let result = mac.finalize();
286        let hex_string = format!("{:x}", result.into_bytes());
287
288        Ok(Value::String(hex_string))
289    }
290}
291
292// =============================================================================
293// crc32(string) -> number (CRC32 checksum as integer)
294// =============================================================================
295
296defn!(Crc32Fn, vec![arg!(string)], None);
297
298impl Function for Crc32Fn {
299    fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
300        self.signature.validate(args, ctx)?;
301
302        let input = args[0].as_str().ok_or_else(|| {
303            crate::JmespathError::from_ctx(
304                ctx,
305                crate::ErrorReason::Parse("Expected string argument".to_owned()),
306            )
307        })?;
308
309        let mut hasher = Crc32Hasher::new();
310        hasher.update(input.as_bytes());
311        let checksum = hasher.finalize();
312
313        Ok(Value::Number(serde_json::Number::from(checksum)))
314    }
315}