robinpath_modules/modules/
hash_mod.rs1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4 rp.register_builtin("hash.md5", |args, _| {
6 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
7 use md5::Digest;
8 let result = md5::Md5::digest(input.as_bytes());
9 Ok(Value::String(hex::encode(result)))
10 });
11
12 rp.register_builtin("hash.sha1", |args, _| {
14 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
15 use sha1::Digest;
16 let result = sha1::Sha1::digest(input.as_bytes());
17 Ok(Value::String(hex::encode(result)))
18 });
19
20 rp.register_builtin("hash.sha256", |args, _| {
22 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
23 use sha2::Digest;
24 let result = sha2::Sha256::digest(input.as_bytes());
25 Ok(Value::String(hex::encode(result)))
26 });
27
28 rp.register_builtin("hash.sha512", |args, _| {
30 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
31 use sha2::Digest;
32 let result = sha2::Sha512::digest(input.as_bytes());
33 Ok(Value::String(hex::encode(result)))
34 });
35
36 rp.register_builtin("hash.hmac", |args, _| {
38 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
39 let key = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
40 let algo = args.get(2).map(|v| v.to_display_string()).unwrap_or_else(|| "sha256".to_string());
41 use hmac::{Hmac, Mac};
42 match algo.as_str() {
43 "sha256" => {
44 let mut mac = Hmac::<sha2::Sha256>::new_from_slice(key.as_bytes())
45 .map_err(|e| format!("HMAC error: {}", e))?;
46 mac.update(input.as_bytes());
47 Ok(Value::String(hex::encode(mac.finalize().into_bytes())))
48 }
49 "sha1" => {
50 let mut mac = Hmac::<sha1::Sha1>::new_from_slice(key.as_bytes())
51 .map_err(|e| format!("HMAC error: {}", e))?;
52 mac.update(input.as_bytes());
53 Ok(Value::String(hex::encode(mac.finalize().into_bytes())))
54 }
55 "sha512" => {
56 let mut mac = Hmac::<sha2::Sha512>::new_from_slice(key.as_bytes())
57 .map_err(|e| format!("HMAC error: {}", e))?;
58 mac.update(input.as_bytes());
59 Ok(Value::String(hex::encode(mac.finalize().into_bytes())))
60 }
61 "md5" => {
62 let mut mac = Hmac::<md5::Md5>::new_from_slice(key.as_bytes())
63 .map_err(|e| format!("HMAC error: {}", e))?;
64 mac.update(input.as_bytes());
65 Ok(Value::String(hex::encode(mac.finalize().into_bytes())))
66 }
67 _ => Err(format!("Unsupported algorithm: {}", algo)),
68 }
69 });
70
71 rp.register_builtin("hash.hashFile", |args, _| {
73 let path = args.first().map(|v| v.to_display_string()).unwrap_or_default();
74 let algo = args.get(1).map(|v| v.to_display_string()).unwrap_or_else(|| "sha256".to_string());
75 let data = std::fs::read(&path).map_err(|e| format!("hash.hashFile error: {}", e))?;
76 match algo.as_str() {
77 "sha256" => { use sha2::Digest; Ok(Value::String(hex::encode(sha2::Sha256::digest(&data)))) }
78 "sha512" => { use sha2::Digest; Ok(Value::String(hex::encode(sha2::Sha512::digest(&data)))) }
79 "sha1" => { use sha1::Digest; Ok(Value::String(hex::encode(sha1::Sha1::digest(&data)))) }
80 "md5" => { use md5::Digest; Ok(Value::String(hex::encode(md5::Md5::digest(&data)))) }
81 _ => Err(format!("Unsupported algorithm: {}", algo)),
82 }
83 });
84
85 rp.register_builtin("hash.crc32", |args, _| {
87 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
88 let crc = crc32_compute(input.as_bytes());
89 let as_hex = args.get(1).map(|v| match v { Value::Bool(b) => *b, _ => false }).unwrap_or(false);
90 if as_hex {
91 Ok(Value::String(format!("{:08x}", crc)))
92 } else {
93 Ok(Value::Number(crc as f64))
94 }
95 });
96
97 rp.register_builtin("hash.checksum", |args, _| {
99 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
100 let expected = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
101 let algo = args.get(2).map(|v| v.to_display_string()).unwrap_or_else(|| "sha256".to_string());
102 let actual = match algo.as_str() {
103 "sha256" => { use sha2::Digest; hex::encode(sha2::Sha256::digest(input.as_bytes())) }
104 "sha512" => { use sha2::Digest; hex::encode(sha2::Sha512::digest(input.as_bytes())) }
105 "sha1" => { use sha1::Digest; hex::encode(sha1::Sha1::digest(input.as_bytes())) }
106 "md5" => { use md5::Digest; hex::encode(md5::Md5::digest(input.as_bytes())) }
107 _ => return Err(format!("Unsupported algorithm: {}", algo)),
108 };
109 Ok(Value::Bool(actual == expected.to_lowercase()))
110 });
111
112 rp.register_builtin("hash.compare", |args, _| {
114 let a = args.first().map(|v| v.to_display_string()).unwrap_or_default();
115 let b = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
116 if a.len() != b.len() {
117 return Ok(Value::Bool(false));
118 }
119 let mut diff = 0u8;
120 for (x, y) in a.bytes().zip(b.bytes()) {
121 diff |= x ^ y;
122 }
123 Ok(Value::Bool(diff == 0))
124 });
125
126 rp.register_builtin("hash.randomHex", |args, _| {
128 let length = args.first().map(|v| v.to_number() as usize).unwrap_or(32);
129 let bytes = random_bytes(length / 2 + 1);
130 let hex: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
131 Ok(Value::String(hex[..length.min(hex.len())].to_string()))
132 });
133
134 rp.register_builtin("hash.fingerprint", |args, _| {
136 let input = args.first().map(|v| v.to_display_string()).unwrap_or_default();
137 let md5_hash = { use md5::Digest; hex::encode(md5::Md5::digest(input.as_bytes())) };
138 let sha256_hash = { use sha2::Digest; hex::encode(sha2::Sha256::digest(input.as_bytes())) };
139 let mut obj = indexmap::IndexMap::new();
140 obj.insert("md5".to_string(), Value::String(md5_hash));
141 obj.insert("sha256".to_string(), Value::String(sha256_hash));
142 Ok(Value::Object(obj))
143 });
144}
145
146fn crc32_compute(data: &[u8]) -> u32 {
147 let mut crc: u32 = 0xFFFFFFFF;
148 for &byte in data {
149 crc ^= byte as u32;
150 for _ in 0..8 {
151 if crc & 1 != 0 {
152 crc = (crc >> 1) ^ 0xEDB88320;
153 } else {
154 crc >>= 1;
155 }
156 }
157 }
158 !crc
159}
160
161fn random_bytes(n: usize) -> Vec<u8> {
162 use std::collections::hash_map::RandomState;
163 use std::hash::{BuildHasher, Hasher};
164 let mut bytes = Vec::with_capacity(n);
165 for i in 0..n {
166 let state = RandomState::new();
167 let mut hasher = state.build_hasher();
168 hasher.write_usize(i);
169 bytes.push((hasher.finish() & 0xFF) as u8);
170 }
171 bytes
172}