1use crate::seqstring::global_string;
25use crate::stack::{Stack, pop, push};
26use crate::value::Value;
27
28use hmac::{Hmac, Mac};
29use rand::RngCore;
30use sha2::{Digest, Sha256};
31use subtle::ConstantTimeEq;
32use uuid::Uuid;
33
34type HmacSha256 = Hmac<Sha256>;
35
36#[unsafe(no_mangle)]
45pub unsafe extern "C" fn patch_seq_sha256(stack: Stack) -> Stack {
46 assert!(!stack.is_null(), "sha256: stack is empty");
47
48 let (stack, value) = unsafe { pop(stack) };
49
50 match value {
51 Value::String(s) => {
52 let mut hasher = Sha256::new();
53 hasher.update(s.as_str().as_bytes());
54 let result = hasher.finalize();
55 let hex_digest = hex::encode(result);
56 unsafe { push(stack, Value::String(global_string(hex_digest))) }
57 }
58 _ => panic!("sha256: expected String on stack, got {:?}", value),
59 }
60}
61
62#[unsafe(no_mangle)]
72pub unsafe extern "C" fn patch_seq_hmac_sha256(stack: Stack) -> Stack {
73 assert!(!stack.is_null(), "hmac-sha256: stack is empty");
74
75 let (stack, key_value) = unsafe { pop(stack) };
76 let (stack, msg_value) = unsafe { pop(stack) };
77
78 match (msg_value, key_value) {
79 (Value::String(msg), Value::String(key)) => {
80 let mut mac =
81 HmacSha256::new_from_slice(key.as_str().as_bytes()).expect("HMAC can take any key");
82 mac.update(msg.as_str().as_bytes());
83 let result = mac.finalize();
84 let hex_sig = hex::encode(result.into_bytes());
85 unsafe { push(stack, Value::String(global_string(hex_sig))) }
86 }
87 (msg, key) => panic!(
88 "hmac-sha256: expected (String, String) on stack, got ({:?}, {:?})",
89 msg, key
90 ),
91 }
92}
93
94#[unsafe(no_mangle)]
108pub unsafe extern "C" fn patch_seq_constant_time_eq(stack: Stack) -> Stack {
109 assert!(!stack.is_null(), "constant-time-eq: stack is empty");
110
111 let (stack, b_value) = unsafe { pop(stack) };
112 let (stack, a_value) = unsafe { pop(stack) };
113
114 match (a_value, b_value) {
115 (Value::String(a), Value::String(b)) => {
116 let a_bytes = a.as_str().as_bytes();
117 let b_bytes = b.as_str().as_bytes();
118
119 let eq = a_bytes.ct_eq(b_bytes);
122
123 unsafe { push(stack, Value::Bool(bool::from(eq))) }
124 }
125 (a, b) => panic!(
126 "constant-time-eq: expected (String, String) on stack, got ({:?}, {:?})",
127 a, b
128 ),
129 }
130}
131
132#[unsafe(no_mangle)]
146pub unsafe extern "C" fn patch_seq_random_bytes(stack: Stack) -> Stack {
147 assert!(!stack.is_null(), "random-bytes: stack is empty");
148
149 let (stack, value) = unsafe { pop(stack) };
150
151 match value {
152 Value::Int(n) => {
153 if n < 0 {
154 panic!("random-bytes: byte count must be non-negative, got {}", n);
155 }
156 if n > 1024 {
157 panic!("random-bytes: byte count too large (max 1024), got {}", n);
158 }
159
160 let mut bytes = vec![0u8; n as usize];
161 rand::thread_rng().fill_bytes(&mut bytes);
162 let hex_str = hex::encode(&bytes);
163 unsafe { push(stack, Value::String(global_string(hex_str))) }
164 }
165 _ => panic!("random-bytes: expected Int on stack, got {:?}", value),
166 }
167}
168
169#[unsafe(no_mangle)]
178pub unsafe extern "C" fn patch_seq_uuid4(stack: Stack) -> Stack {
179 assert!(!stack.is_null(), "uuid4: stack is empty");
180
181 let uuid = Uuid::new_v4();
182 unsafe { push(stack, Value::String(global_string(uuid.to_string()))) }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use crate::stack::pop;
189
190 #[test]
191 fn test_sha256() {
192 unsafe {
193 let stack = crate::stack::alloc_test_stack();
194 let stack = push(stack, Value::String(global_string("hello".to_string())));
195 let stack = patch_seq_sha256(stack);
196 let (_, value) = pop(stack);
197
198 match value {
199 Value::String(s) => {
200 assert_eq!(
202 s.as_str(),
203 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
204 );
205 }
206 _ => panic!("Expected String"),
207 }
208 }
209 }
210
211 #[test]
212 fn test_sha256_empty() {
213 unsafe {
214 let stack = crate::stack::alloc_test_stack();
215 let stack = push(stack, Value::String(global_string(String::new())));
216 let stack = patch_seq_sha256(stack);
217 let (_, value) = pop(stack);
218
219 match value {
220 Value::String(s) => {
221 assert_eq!(
223 s.as_str(),
224 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
225 );
226 }
227 _ => panic!("Expected String"),
228 }
229 }
230 }
231
232 #[test]
233 fn test_hmac_sha256() {
234 unsafe {
235 let stack = crate::stack::alloc_test_stack();
236 let stack = push(stack, Value::String(global_string("message".to_string())));
237 let stack = push(stack, Value::String(global_string("secret".to_string())));
238 let stack = patch_seq_hmac_sha256(stack);
239 let (_, value) = pop(stack);
240
241 match value {
242 Value::String(s) => {
243 assert_eq!(
245 s.as_str(),
246 "8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"
247 );
248 }
249 _ => panic!("Expected String"),
250 }
251 }
252 }
253
254 #[test]
255 fn test_constant_time_eq_equal() {
256 unsafe {
257 let stack = crate::stack::alloc_test_stack();
258 let stack = push(stack, Value::String(global_string("hello".to_string())));
259 let stack = push(stack, Value::String(global_string("hello".to_string())));
260 let stack = patch_seq_constant_time_eq(stack);
261 let (_, value) = pop(stack);
262
263 match value {
264 Value::Bool(b) => assert!(b),
265 _ => panic!("Expected Bool"),
266 }
267 }
268 }
269
270 #[test]
271 fn test_constant_time_eq_different() {
272 unsafe {
273 let stack = crate::stack::alloc_test_stack();
274 let stack = push(stack, Value::String(global_string("hello".to_string())));
275 let stack = push(stack, Value::String(global_string("world".to_string())));
276 let stack = patch_seq_constant_time_eq(stack);
277 let (_, value) = pop(stack);
278
279 match value {
280 Value::Bool(b) => assert!(!b),
281 _ => panic!("Expected Bool"),
282 }
283 }
284 }
285
286 #[test]
287 fn test_constant_time_eq_different_lengths() {
288 unsafe {
289 let stack = crate::stack::alloc_test_stack();
290 let stack = push(stack, Value::String(global_string("hello".to_string())));
291 let stack = push(stack, Value::String(global_string("hi".to_string())));
292 let stack = patch_seq_constant_time_eq(stack);
293 let (_, value) = pop(stack);
294
295 match value {
296 Value::Bool(b) => assert!(!b),
297 _ => panic!("Expected Bool"),
298 }
299 }
300 }
301
302 #[test]
303 fn test_random_bytes() {
304 unsafe {
305 let stack = crate::stack::alloc_test_stack();
306 let stack = push(stack, Value::Int(16));
307 let stack = patch_seq_random_bytes(stack);
308 let (_, value) = pop(stack);
309
310 match value {
311 Value::String(s) => {
312 assert_eq!(s.as_str().len(), 32);
314 assert!(hex::decode(s.as_str()).is_ok());
316 }
317 _ => panic!("Expected String"),
318 }
319 }
320 }
321
322 #[test]
323 fn test_random_bytes_zero() {
324 unsafe {
325 let stack = crate::stack::alloc_test_stack();
326 let stack = push(stack, Value::Int(0));
327 let stack = patch_seq_random_bytes(stack);
328 let (_, value) = pop(stack);
329
330 match value {
331 Value::String(s) => {
332 assert_eq!(s.as_str(), "");
333 }
334 _ => panic!("Expected String"),
335 }
336 }
337 }
338
339 #[test]
340 fn test_uuid4() {
341 unsafe {
342 let stack = crate::stack::alloc_test_stack();
343 let stack = patch_seq_uuid4(stack);
344 let (_, value) = pop(stack);
345
346 match value {
347 Value::String(s) => {
348 assert_eq!(s.as_str().len(), 36);
350 assert_eq!(s.as_str().chars().filter(|c| *c == '-').count(), 4);
351 assert_eq!(s.as_str().chars().nth(14), Some('4'));
353 }
354 _ => panic!("Expected String"),
355 }
356 }
357 }
358
359 #[test]
360 fn test_uuid4_unique() {
361 unsafe {
362 let stack = crate::stack::alloc_test_stack();
363 let stack = patch_seq_uuid4(stack);
364 let (stack, value1) = pop(stack);
365 let stack = patch_seq_uuid4(stack);
366 let (_, value2) = pop(stack);
367
368 match (value1, value2) {
369 (Value::String(s1), Value::String(s2)) => {
370 assert_ne!(s1.as_str(), s2.as_str());
371 }
372 _ => panic!("Expected Strings"),
373 }
374 }
375 }
376
377 #[test]
378 fn test_random_bytes_max_limit() {
379 unsafe {
380 let stack = crate::stack::alloc_test_stack();
381 let stack = push(stack, Value::Int(1024)); let stack = patch_seq_random_bytes(stack);
383 let (_, value) = pop(stack);
384
385 match value {
386 Value::String(s) => {
387 assert_eq!(s.as_str().len(), 2048);
389 }
390 _ => panic!("Expected String"),
391 }
392 }
393 }
394 }