1use crate::marshal::{register_typed_fn_0, register_typed_fn_1, register_typed_fn_2, register_typed_fn_3};
12use crate::module_exports::ModuleExports;
13use crate::typed_module_exports::{ConcreteReturn, ConcreteType, TypedReturn};
14use std::sync::Arc;
15
16pub fn create_crypto_module() -> ModuleExports {
18 let mut module = ModuleExports::new("std::core::crypto");
19 module.description = "Cryptographic hashing and encoding utilities".to_string();
20
21 register_typed_fn_1::<_, Arc<String>>(
23 &mut module,
24 "sha256",
25 "Compute the SHA-256 hash of a string, returning a hex-encoded digest",
26 "data",
27 "string",
28 ConcreteType::String,
29 |data, _ctx| {
30 use sha2::{Digest, Sha256};
31 let mut hasher = Sha256::new();
32 hasher.update(data.as_bytes());
33 let result = hasher.finalize();
34 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(result))))
35 },
36 );
37
38 register_typed_fn_2::<_, Arc<String>, Arc<String>>(
40 &mut module,
41 "hmac_sha256",
42 "Compute HMAC-SHA256 of data with the given key, returning hex digest",
43 [("data", "string"), ("key", "string")],
44 ConcreteType::String,
45 |data, key, _ctx| {
46 use hmac::{Hmac, Mac};
47 use sha2::Sha256;
48 type HmacSha256 = Hmac<Sha256>;
49 let mut mac = HmacSha256::new_from_slice(key.as_bytes())
50 .map_err(|e| format!("crypto.hmac_sha256() key error: {}", e))?;
51 mac.update(data.as_bytes());
52 let result = mac.finalize();
53 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(result.into_bytes()))))
54 },
55 );
56
57 register_typed_fn_1::<_, Arc<String>>(
59 &mut module,
60 "base64_encode",
61 "Encode a string to Base64",
62 "data",
63 "string",
64 ConcreteType::String,
65 |data, _ctx| {
66 use base64::Engine;
67 let encoded = base64::engine::general_purpose::STANDARD.encode(data.as_bytes());
68 Ok(TypedReturn::Concrete(ConcreteReturn::String(encoded)))
69 },
70 );
71
72 register_typed_fn_1::<_, Arc<String>>(
74 &mut module,
75 "base64_decode",
76 "Decode a Base64 string",
77 "encoded",
78 "string",
79 ConcreteType::Result(Box::new(ConcreteType::String)),
80 |encoded, _ctx| {
81 use base64::Engine;
82 let bytes = base64::engine::general_purpose::STANDARD
83 .decode(encoded.as_str())
84 .map_err(|e| format!("crypto.base64_decode() failed: {}", e))?;
85 let decoded = String::from_utf8(bytes)
86 .map_err(|e| format!("crypto.base64_decode() invalid UTF-8: {}", e))?;
87 Ok(TypedReturn::Ok(ConcreteReturn::String(decoded)))
88 },
89 );
90
91 register_typed_fn_1::<_, Arc<String>>(
93 &mut module,
94 "hex_encode",
95 "Encode a string as hexadecimal",
96 "data",
97 "string",
98 ConcreteType::String,
99 |data, _ctx| {
100 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(data.as_bytes()))))
101 },
102 );
103
104 register_typed_fn_1::<_, Arc<String>>(
106 &mut module,
107 "hex_decode",
108 "Decode a hexadecimal string",
109 "hex",
110 "string",
111 ConcreteType::Result(Box::new(ConcreteType::String)),
112 |hex_str, _ctx| {
113 let bytes = hex::decode(hex_str.as_str())
114 .map_err(|e| format!("crypto.hex_decode() failed: {}", e))?;
115 let decoded = String::from_utf8(bytes)
116 .map_err(|e| format!("crypto.hex_decode() invalid UTF-8: {}", e))?;
117 Ok(TypedReturn::Ok(ConcreteReturn::String(decoded)))
118 },
119 );
120
121 register_typed_fn_1::<_, Arc<String>>(
123 &mut module,
124 "sha512",
125 "Compute the SHA-512 hash of a string, returning a hex-encoded digest",
126 "data",
127 "string",
128 ConcreteType::String,
129 |data, _ctx| {
130 use sha2::{Digest, Sha512};
131 let mut hasher = Sha512::new();
132 hasher.update(data.as_bytes());
133 let result = hasher.finalize();
134 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(result))))
135 },
136 );
137
138 register_typed_fn_1::<_, Arc<String>>(
140 &mut module,
141 "sha1",
142 "Compute the SHA-1 hash of a string, returning a hex-encoded digest (legacy)",
143 "data",
144 "string",
145 ConcreteType::String,
146 |data, _ctx| {
147 use sha1::Digest;
148 let mut hasher = sha1::Sha1::new();
149 hasher.update(data.as_bytes());
150 let result = hasher.finalize();
151 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(result))))
152 },
153 );
154
155 register_typed_fn_1::<_, Arc<String>>(
157 &mut module,
158 "md5",
159 "Compute the MD5 hash of a string, returning a hex-encoded digest (legacy)",
160 "data",
161 "string",
162 ConcreteType::String,
163 |data, _ctx| {
164 use md5::Digest;
165 let mut hasher = md5::Md5::new();
166 hasher.update(data.as_bytes());
167 let result = hasher.finalize();
168 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(result))))
169 },
170 );
171
172 register_typed_fn_1::<_, i64>(
174 &mut module,
175 "random_bytes",
176 "Generate n random bytes, returned as a hex-encoded string",
177 "n",
178 "int",
179 ConcreteType::String,
180 |n, ctx| {
181 crate::module_exports::check_permission(ctx, shape_abi_v1::Permission::Random)?;
182 use rand::RngCore;
183 if n < 0 || n > 65536 {
184 return Err("crypto.random_bytes() n must be between 0 and 65536".to_string());
185 }
186 let mut buf = vec![0u8; n as usize];
187 rand::thread_rng().fill_bytes(&mut buf);
188 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(buf))))
189 },
190 );
191
192 register_typed_fn_0(
194 &mut module,
195 "ed25519_generate_keypair",
196 "Generate an Ed25519 keypair, returning an object with hex-encoded public_key and secret_key",
197 ConcreteType::Object,
198 |ctx| {
199 crate::module_exports::check_permission(ctx, shape_abi_v1::Permission::Random)?;
200 use rand::RngCore;
201 let mut secret = [0u8; 32];
202 rand::thread_rng().fill_bytes(&mut secret);
203 let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret);
204 let verifying_key = signing_key.verifying_key();
205 Ok(TypedReturn::ObjectPairs(vec![
206 (
207 "public_key".to_string(),
208 ConcreteReturn::String(hex::encode(verifying_key.to_bytes())),
209 ),
210 (
211 "secret_key".to_string(),
212 ConcreteReturn::String(hex::encode(signing_key.to_bytes())),
213 ),
214 ]))
215 },
216 );
217
218 register_typed_fn_2::<_, Arc<String>, Arc<String>>(
220 &mut module,
221 "ed25519_sign",
222 "Sign a message with an Ed25519 secret key, returning a hex-encoded signature",
223 [("message", "string"), ("secret_key", "string")],
224 ConcreteType::String,
225 |message, secret_hex, _ctx| {
226 use ed25519_dalek::Signer;
227 let secret_bytes = hex::decode(secret_hex.as_str())
228 .map_err(|e| format!("crypto.ed25519_sign() invalid secret_key hex: {}", e))?;
229 let secret_arr: [u8; 32] = secret_bytes.as_slice().try_into().map_err(|_| {
230 format!(
231 "crypto.ed25519_sign() secret_key must be 32 bytes (got {})",
232 secret_bytes.len()
233 )
234 })?;
235 let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret_arr);
236 let signature = signing_key.sign(message.as_bytes());
237 Ok(TypedReturn::Concrete(ConcreteReturn::String(hex::encode(signature.to_bytes()))))
238 },
239 );
240
241 register_typed_fn_3::<_, Arc<String>, Arc<String>, Arc<String>>(
243 &mut module,
244 "ed25519_verify",
245 "Verify an Ed25519 signature against a message and public key",
246 [("message", "string"), ("signature", "string"), ("public_key", "string")],
247 ConcreteType::Bool,
248 |message, sig_hex, pub_hex, _ctx| {
249 use ed25519_dalek::Verifier;
250 let sig_bytes = hex::decode(sig_hex.as_str())
251 .map_err(|e| format!("crypto.ed25519_verify() invalid signature hex: {}", e))?;
252 let sig_arr: [u8; 64] = sig_bytes.as_slice().try_into().map_err(|_| {
253 format!(
254 "crypto.ed25519_verify() signature must be 64 bytes (got {})",
255 sig_bytes.len()
256 )
257 })?;
258 let pub_bytes = hex::decode(pub_hex.as_str())
259 .map_err(|e| format!("crypto.ed25519_verify() invalid public_key hex: {}", e))?;
260 let pub_arr: [u8; 32] = pub_bytes.as_slice().try_into().map_err(|_| {
261 format!(
262 "crypto.ed25519_verify() public_key must be 32 bytes (got {})",
263 pub_bytes.len()
264 )
265 })?;
266 let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&pub_arr)
267 .map_err(|e| format!("crypto.ed25519_verify() invalid public key: {}", e))?;
268 let signature = ed25519_dalek::Signature::from_bytes(&sig_arr);
269 let valid = verifying_key.verify(message.as_bytes(), &signature).is_ok();
270 Ok(TypedReturn::Concrete(ConcreteReturn::Bool(valid)))
271 },
272 );
273
274 module
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn test_crypto_module_creation() {
283 let module = create_crypto_module();
284 assert_eq!(module.name, "std::core::crypto");
285 assert!(module.has_export("sha256"));
286 assert!(module.has_export("hmac_sha256"));
287 assert!(module.has_export("base64_encode"));
288 assert!(module.has_export("base64_decode"));
289 assert!(module.has_export("hex_encode"));
290 assert!(module.has_export("hex_decode"));
291 assert!(module.has_export("sha512"));
292 assert!(module.has_export("sha1"));
293 assert!(module.has_export("md5"));
294 assert!(module.has_export("random_bytes"));
295 assert!(module.has_export("ed25519_generate_keypair"));
296 assert!(module.has_export("ed25519_sign"));
297 assert!(module.has_export("ed25519_verify"));
298 }
299
300 #[test]
301 fn test_crypto_schemas() {
302 let module = create_crypto_module();
303
304 let sha_schema = module.get_schema("sha256").unwrap();
305 assert_eq!(sha_schema.params.len(), 1);
306 assert_eq!(sha_schema.return_type.as_deref(), Some("string"));
307
308 let hmac_schema = module.get_schema("hmac_sha256").unwrap();
309 assert_eq!(hmac_schema.params.len(), 2);
310
311 let b64d_schema = module.get_schema("base64_decode").unwrap();
312 assert_eq!(b64d_schema.return_type.as_deref(), Some("Result<string>"));
313
314 let rb_schema = module.get_schema("random_bytes").unwrap();
315 assert_eq!(rb_schema.params[0].type_name, "int");
316
317 let gen_schema = module.get_schema("ed25519_generate_keypair").unwrap();
318 assert_eq!(gen_schema.params.len(), 0);
319
320 let verify_schema = module.get_schema("ed25519_verify").unwrap();
321 assert_eq!(verify_schema.params.len(), 3);
322 }
323
324 #[test]
325 fn test_crypto_typed_registry() {
326 let module = create_crypto_module();
327 let typed = module.typed_exports();
328 assert_eq!(typed.functions.len(), 13);
329
330 let sha = typed.get("sha256").unwrap();
331 assert_eq!(sha.arg_kinds.len(), 1);
332 assert_eq!(sha.arg_kinds[0], shape_value::NativeKind::String);
333
334 let rb = typed.get("random_bytes").unwrap();
335 assert_eq!(rb.arg_kinds[0], shape_value::NativeKind::Int64);
336
337 let keygen = typed.get("ed25519_generate_keypair").unwrap();
338 assert!(keygen.arg_kinds.is_empty());
339 }
340
341 }