tatara_lisp_script/stdlib/
crypto_extra.rs1use std::sync::Arc;
11
12use hmac::{Hmac, Mac};
13use sha1::Sha1;
14use sha2::{Digest, Sha256, Sha512};
15use tatara_lisp_eval::{Arity, EvalError, Interpreter, Value};
16
17use crate::script_ctx::ScriptCtx;
18use crate::stdlib::env::{int_arg, str_arg};
19
20type HmacSha256 = Hmac<Sha256>;
21
22pub fn install(interp: &mut Interpreter<ScriptCtx>) {
23 interp.register_fn(
24 "sha1",
25 Arity::Exact(1),
26 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
27 let s = str_arg(&args[0], "sha1", sp)?;
28 Ok(Value::Str(Arc::from(hex::encode(Sha1::digest(
29 s.as_bytes(),
30 )))))
31 },
32 );
33
34 interp.register_fn(
35 "sha512",
36 Arity::Exact(1),
37 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
38 let s = str_arg(&args[0], "sha512", sp)?;
39 Ok(Value::Str(Arc::from(hex::encode(Sha512::digest(
40 s.as_bytes(),
41 )))))
42 },
43 );
44
45 interp.register_fn(
46 "hmac-sha256",
47 Arity::Exact(2),
48 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
49 let key = str_arg(&args[0], "hmac-sha256", sp)?;
50 let msg = str_arg(&args[1], "hmac-sha256", sp)?;
51 let mut mac = HmacSha256::new_from_slice(key.as_bytes())
52 .map_err(|e| EvalError::native_fn("hmac-sha256", e.to_string(), sp))?;
53 mac.update(msg.as_bytes());
54 Ok(Value::Str(Arc::from(hex::encode(
55 mac.finalize().into_bytes(),
56 ))))
57 },
58 );
59
60 interp.register_fn(
61 "uuid-v4",
62 Arity::Exact(0),
63 |_args: &[Value], _ctx: &mut ScriptCtx, _sp| Ok(Value::Str(Arc::from(uuid_v4()))),
64 );
65
66 interp.register_fn(
67 "random-hex",
68 Arity::Exact(1),
69 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
70 let n = int_arg(&args[0], "random-hex", sp)?;
71 if n < 0 {
72 return Err(EvalError::native_fn(
73 "random-hex",
74 format!("need >= 0 bytes, got {n}"),
75 sp,
76 ));
77 }
78 let bytes = random_bytes(n as usize);
79 Ok(Value::Str(Arc::from(hex::encode(bytes))))
80 },
81 );
82
83 interp.register_fn(
84 "random-int",
85 Arity::Exact(2),
86 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
87 let lo = int_arg(&args[0], "random-int", sp)?;
88 let hi = int_arg(&args[1], "random-int", sp)?;
89 if hi <= lo {
90 return Err(EvalError::native_fn(
91 "random-int",
92 format!("hi ({hi}) must be > lo ({lo})"),
93 sp,
94 ));
95 }
96 let range = (hi - lo) as u64;
97 let r = {
98 let bytes = random_bytes(8);
99 let mut u = 0u64;
100 for b in bytes {
101 u = (u << 8) | b as u64;
102 }
103 lo + (u % range) as i64
104 };
105 Ok(Value::Int(r))
106 },
107 );
108}
109
110fn random_bytes(n: usize) -> Vec<u8> {
111 let mut out = vec![0u8; n];
113 if let Ok(mut f) = std::fs::File::open("/dev/urandom") {
114 use std::io::Read;
115 if f.read_exact(&mut out).is_ok() {
116 return out;
117 }
118 }
119 let ts = std::time::SystemTime::now()
122 .duration_since(std::time::UNIX_EPOCH)
123 .map(|d| d.as_nanos() as u64)
124 .unwrap_or(0);
125 let pid = std::process::id() as u64;
126 for (i, b) in out.iter_mut().enumerate() {
127 let seed = ts
128 .wrapping_add(pid)
129 .wrapping_add(i as u64)
130 .wrapping_mul(2_862_933_555_777_941_757);
131 *b = (seed >> 32) as u8;
132 }
133 out
134}
135
136fn uuid_v4() -> String {
137 let mut bytes = random_bytes(16);
138 bytes[6] = (bytes[6] & 0x0f) | 0x40;
140 bytes[8] = (bytes[8] & 0x3f) | 0x80;
141 format!(
142 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
143 bytes[0], bytes[1], bytes[2], bytes[3],
144 bytes[4], bytes[5],
145 bytes[6], bytes[7],
146 bytes[8], bytes[9],
147 bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]
148 )
149}