1use aurora_core::{AuroraResult, Pipeline, Value, AuroraError};
2use std::process::Command;
3use std::collections::hash_map::DefaultHasher;
4use std::hash::{Hash, Hasher};
5use std::io::Write;
6
7fn find_tool(name: &str) -> bool {
8 Command::new("which")
9 .arg(name)
10 .output()
11 .ok()
12 .is_some_and(|o| o.status.success())
13}
14
15pub fn crypto_hash(input: &str, algo: Option<&str>) -> AuroraResult<Pipeline> {
16 let algo_name = algo.unwrap_or("sha256");
17 let is_file = std::path::Path::new(input).exists() && std::path::Path::new(input).is_file();
18
19 match algo_name {
20 "sha256" => {
21 if is_file && find_tool("sha256sum") {
22 hash_file_cmd("sha256sum", input, "sha256")
23 } else if is_file && find_tool("shasum") {
24 hash_file_cmd("shasum", input, "sha256")
25 } else {
26 hash_string_cmd("sha256sum", input, "sha256")
27 }
28 }
29 "md5" => {
30 if is_file && find_tool("md5sum") {
31 hash_file_cmd("md5sum", input, "md5")
32 } else if find_tool("md5sum") {
33 hash_string_cmd("md5sum", input, "md5")
34 } else if find_tool("shasum") {
35 hash_string_cmd("shasum", input, "md5")
36 } else {
37 return Err(AuroraError::ModuleError(
38 "md5 requires md5sum or shasum command".into()
39 ));
40 }
41 }
42 _ => {
43 if find_tool(algo_name) {
44 hash_string_cmd(algo_name, input, algo_name)
45 } else {
46 return Err(AuroraError::InvalidInput(
47 format!("unknown algorithm: {algo_name}")
48 ));
49 }
50 }
51 }
52}
53
54fn hash_file_cmd(tool: &str, path: &str, algo: &str) -> AuroraResult<Pipeline> {
55 let output = Command::new(tool)
56 .arg(path)
57 .output()
58 .map_err(|e| AuroraError::ModuleError(format!("{tool} failed: {e}")))?;
59
60 let stdout = String::from_utf8_lossy(&output.stdout);
61 let hash = stdout.split_whitespace().next().unwrap_or("").to_string();
62
63 Ok(Pipeline::table(
64 vec!["algorithm".into(), "hash".into()],
65 vec![vec![Value::String(algo.into()), Value::String(hash)]],
66 ))
67}
68
69fn hash_string_cmd(tool: &str, input: &str, algo: &str) -> AuroraResult<Pipeline> {
70 let mut child = Command::new(tool)
71 .stdin(std::process::Stdio::piped())
72 .stdout(std::process::Stdio::piped())
73 .stderr(std::process::Stdio::null())
74 .spawn()
75 .map_err(|e| AuroraError::ModuleError(format!("{tool} failed: {e}")))?;
76
77 if let Some(ref mut stdin) = child.stdin {
78 stdin.write_all(input.as_bytes())
79 .map_err(|e| AuroraError::ModuleError(format!("Write failed: {e}")))?;
80 }
81 drop(child.stdin.take());
82
83 let output = child.wait_with_output()
84 .map_err(|e| AuroraError::ModuleError(format!("{tool} failed: {e}")))?;
85 let hash = String::from_utf8_lossy(&output.stdout)
86 .split_whitespace()
87 .next()
88 .unwrap_or("")
89 .to_string();
90
91 Ok(Pipeline::table(
92 vec!["algorithm".into(), "hash".into()],
93 vec![vec![Value::String(algo.into()), Value::String(hash)]],
94 ))
95}
96
97pub fn crypto_genkey(algo: Option<&str>) -> AuroraResult<Pipeline> {
98 let algo_name = algo.unwrap_or("aes-256");
99 use std::time::{SystemTime, UNIX_EPOCH};
100
101 let seed = SystemTime::now()
102 .duration_since(UNIX_EPOCH)
103 .map(|d| d.as_nanos())
104 .unwrap_or(0);
105
106 let mut hasher = DefaultHasher::new();
107 seed.hash(&mut hasher);
108 let hash = hasher.finish();
109
110 let key = format!("{:016x}{:016x}", hash, hash);
111
112 Ok(Pipeline::table(
113 vec!["algorithm".into(), "key".into()],
114 vec![vec![Value::String(algo_name.into()), Value::String(key)]],
115 ))
116}