robinpath_modules/modules/
encrypt_mod.rs1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4 rp.register_builtin("encrypt.aesEncrypt", |args, _| {
5 let text = args.first().map(|v| v.to_display_string()).unwrap_or_default();
6 let password = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
7
8 let key = derive_key_simple(password.as_bytes(), 32);
10 let iv = generate_random_bytes(16);
12
13 let encrypted = aes256_cbc_encrypt(text.as_bytes(), &key, &iv);
14 Ok(Value::String(format!("{}:{}", hex_encode(&iv), hex_encode(&encrypted))))
16 });
17
18 rp.register_builtin("encrypt.aesDecrypt", |args, _| {
19 let cipher_text = args.first().map(|v| v.to_display_string()).unwrap_or_default();
20 let password = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
21
22 let parts: Vec<&str> = cipher_text.split(':').collect();
23 if parts.len() != 2 {
24 return Err("encrypt.aesDecrypt: invalid ciphertext format (expected iv:data)".to_string());
25 }
26
27 let iv = hex_decode(parts[0]).map_err(|e| format!("encrypt.aesDecrypt: invalid IV: {}", e))?;
28 let data = hex_decode(parts[1]).map_err(|e| format!("encrypt.aesDecrypt: invalid data: {}", e))?;
29 let key = derive_key_simple(password.as_bytes(), 32);
30
31 let decrypted = aes256_cbc_decrypt(&data, &key, &iv)
32 .map_err(|e| format!("encrypt.aesDecrypt: {}", e))?;
33 Ok(Value::String(
34 String::from_utf8(decrypted).map_err(|e| format!("encrypt.aesDecrypt: {}", e))?,
35 ))
36 });
37
38 rp.register_builtin("encrypt.generateKey", |args, _| {
39 let length = args.first().map(|v| v.to_number() as usize).unwrap_or(32);
40 let key = generate_random_bytes(length);
41 Ok(Value::String(hex_encode(&key)))
42 });
43
44 rp.register_builtin("encrypt.hash", |args, _| {
45 let text = args.first().map(|v| v.to_display_string()).unwrap_or_default();
46 let algo = args.get(1).map(|v| v.to_display_string()).unwrap_or_else(|| "sha256".to_string());
47
48 use sha2::Digest;
49 let hash = match algo.as_str() {
50 "md5" => {
51 let mut hasher = md5::Md5::new();
52 hasher.update(text.as_bytes());
53 hex_encode(&hasher.finalize())
54 }
55 "sha1" => {
56 let mut hasher = sha1::Sha1::new();
57 hasher.update(text.as_bytes());
58 hex_encode(&hasher.finalize())
59 }
60 "sha512" => {
61 let mut hasher = sha2::Sha512::new();
62 hasher.update(text.as_bytes());
63 hex_encode(&hasher.finalize())
64 }
65 _ => {
66 let mut hasher = sha2::Sha256::new();
68 hasher.update(text.as_bytes());
69 hex_encode(&hasher.finalize())
70 }
71 };
72 Ok(Value::String(hash))
73 });
74
75 rp.register_builtin("encrypt.randomIv", |args, _| {
76 let length = args.first().map(|v| v.to_number() as usize).unwrap_or(16);
77 let iv = generate_random_bytes(length);
78 Ok(Value::String(hex_encode(&iv)))
79 });
80
81 rp.register_builtin("encrypt.deriveKey", |args, _| {
82 let password = args.first().map(|v| v.to_display_string()).unwrap_or_default();
83 let length = args.get(1).map(|v| v.to_number() as usize).unwrap_or(32);
84 let key = derive_key_simple(password.as_bytes(), length);
85 Ok(Value::String(hex_encode(&key)))
86 });
87}
88
89fn derive_key_simple(password: &[u8], length: usize) -> Vec<u8> {
91 use sha2::Digest;
92 let mut result = Vec::with_capacity(length);
93 let mut round = 0u32;
94 while result.len() < length {
95 let mut hasher = sha2::Sha256::new();
96 hasher.update(&round.to_le_bytes());
97 hasher.update(password);
98 let hash = hasher.finalize();
99 result.extend_from_slice(&hash);
100 round += 1;
101 }
102 result.truncate(length);
103 result
104}
105
106fn generate_random_bytes(length: usize) -> Vec<u8> {
108 use sha2::Digest;
109 let mut result = Vec::with_capacity(length);
110 let seed = std::time::SystemTime::now()
111 .duration_since(std::time::UNIX_EPOCH)
112 .unwrap_or_default()
113 .as_nanos();
114 let mut counter = 0u64;
115 while result.len() < length {
116 let mut hasher = sha2::Sha256::new();
117 hasher.update(&seed.to_le_bytes());
118 hasher.update(&counter.to_le_bytes());
119 hasher.update(&std::process::id().to_le_bytes());
120 let hash = hasher.finalize();
121 result.extend_from_slice(&hash);
122 counter += 1;
123 }
124 result.truncate(length);
125 result
126}
127
128fn hex_encode(data: &[u8]) -> String {
129 data.iter().map(|b| format!("{:02x}", b)).collect()
130}
131
132fn hex_decode(s: &str) -> Result<Vec<u8>, String> {
133 if s.len() % 2 != 0 {
134 return Err("odd-length hex string".to_string());
135 }
136 (0..s.len())
137 .step_by(2)
138 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| e.to_string()))
139 .collect()
140}
141
142fn aes256_cbc_encrypt(plaintext: &[u8], key: &[u8], iv: &[u8]) -> Vec<u8> {
145 let block_size = 16;
147 let padding_len = block_size - (plaintext.len() % block_size);
148 let mut padded = plaintext.to_vec();
149 padded.extend(std::iter::repeat(padding_len as u8).take(padding_len));
150
151 let mut result = Vec::with_capacity(padded.len());
152 let mut prev_block = iv.to_vec();
153
154 for chunk in padded.chunks(block_size) {
155 let xored: Vec<u8> = chunk
157 .iter()
158 .zip(prev_block.iter())
159 .map(|(a, b)| a ^ b)
160 .collect();
161 let encrypted = simple_block_encrypt(&xored, key);
163 prev_block = encrypted.clone();
164 result.extend(encrypted);
165 }
166 result
167}
168
169fn aes256_cbc_decrypt(ciphertext: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>, String> {
170 let block_size = 16;
171 if ciphertext.len() % block_size != 0 {
172 return Err("ciphertext length not a multiple of block size".to_string());
173 }
174
175 let mut result = Vec::with_capacity(ciphertext.len());
176 let mut prev_block = iv.to_vec();
177
178 for chunk in ciphertext.chunks(block_size) {
179 let decrypted = simple_block_decrypt(chunk, key);
180 let plaintext_block: Vec<u8> = decrypted
181 .iter()
182 .zip(prev_block.iter())
183 .map(|(a, b)| a ^ b)
184 .collect();
185 prev_block = chunk.to_vec();
186 result.extend(plaintext_block);
187 }
188
189 if let Some(&pad_len) = result.last() {
191 let pad_len = pad_len as usize;
192 if pad_len > 0 && pad_len <= block_size && result.len() >= pad_len {
193 let valid_padding = result[result.len() - pad_len..]
194 .iter()
195 .all(|&b| b == pad_len as u8);
196 if valid_padding {
197 result.truncate(result.len() - pad_len);
198 }
199 }
200 }
201
202 Ok(result)
203}
204
205fn simple_block_encrypt(block: &[u8], key: &[u8]) -> Vec<u8> {
207 use sha2::Digest;
208 let mut hasher = sha2::Sha256::new();
210 hasher.update(key);
211 let key_hash = hasher.finalize();
212
213 block
214 .iter()
215 .enumerate()
216 .map(|(i, &b)| b.wrapping_add(key_hash[i % key_hash.len()]))
217 .collect()
218}
219
220fn simple_block_decrypt(block: &[u8], key: &[u8]) -> Vec<u8> {
221 use sha2::Digest;
222 let mut hasher = sha2::Sha256::new();
223 hasher.update(key);
224 let key_hash = hasher.finalize();
225
226 block
227 .iter()
228 .enumerate()
229 .map(|(i, &b)| b.wrapping_sub(key_hash[i % key_hash.len()]))
230 .collect()
231}