external_command_rs/
lib.rs1use base64::Engine;
2use base64::engine::general_purpose::STANDARD;
3use rust_util::{XResult, debugging, opt_result, simple_error};
4use serde::{Deserialize, de};
5use serde_json::Value;
6use std::process::{Command, Output};
7
8#[derive(Debug, Deserialize)]
9struct ErrorResult {
10 #[allow(dead_code)]
11 pub success: bool,
12 pub error: String,
13}
14
15#[derive(Debug, Deserialize)]
16pub struct ExternalSpecResult {
17 #[allow(dead_code)]
18 pub success: bool,
19 pub agent: String,
20 pub specification: String,
21 pub commands: Vec<String>,
22}
23
24#[derive(Debug, Deserialize)]
25struct ExternalPublicKeyResult {
26 #[allow(dead_code)]
27 pub success: bool,
28 pub public_key_base64: String,
29}
30
31#[derive(Debug, Deserialize)]
32struct ExternalSignResult {
33 #[allow(dead_code)]
34 pub success: bool,
35 pub signature_base64: String,
36}
37
38#[derive(Debug, Deserialize)]
39struct ExternalDhResult {
40 #[allow(dead_code)]
41 pub success: bool,
42 pub shared_secret_hex: String,
43}
44
45pub fn external_spec(external_command: &str) -> XResult<ExternalSpecResult> {
46 let mut cmd = Command::new(external_command);
47 cmd.arg("external_spec");
48
49 let cmd_stdout = run_command_stdout(cmd)?;
50 if is_success(&cmd_stdout)? {
51 let external_spec: ExternalSpecResult = from_str(&cmd_stdout)?;
52 Ok(external_spec)
53 } else {
54 let error_result: ErrorResult = from_str(&cmd_stdout)?;
55 simple_error!("{}", error_result.error)
56 }
57}
58
59pub fn external_public_key(external_command: &str, parameter: &str) -> XResult<Vec<u8>> {
60 let mut cmd = Command::new(external_command);
61 cmd.arg("external_public_key");
62 cmd.arg("--parameter");
63 cmd.arg(parameter);
64
65 let cmd_stdout = run_command_stdout(cmd)?;
66 if is_success(&cmd_stdout)? {
67 let external_public_key: ExternalPublicKeyResult = from_str(&cmd_stdout)?;
68 Ok(STANDARD.decode(&external_public_key.public_key_base64)?)
69 } else {
70 let error_result: ErrorResult = from_str(&cmd_stdout)?;
71 simple_error!("{}", error_result.error)
72 }
73}
74
75pub fn external_sign(
76 external_command: &str,
77 parameter: &str,
78 alg: &str,
79 content: &[u8],
80) -> XResult<Vec<u8>> {
81 let mut cmd = Command::new(external_command);
82 cmd.arg("external_sign");
83 cmd.arg("--parameter");
84 cmd.arg(parameter);
85 cmd.arg("--alg");
86 cmd.arg(alg);
87 cmd.arg("--message-base64");
88 cmd.arg(STANDARD.encode(content));
89
90 let cmd_stdout = run_command_stdout(cmd)?;
91 parse_sign_result(&cmd_stdout)
92}
93
94pub fn external_ecdh(
95 external_command: &str,
96 parameter: &str,
97 ephemera_public_key: &[u8],
98) -> XResult<Vec<u8>> {
99 let mut cmd = Command::new(external_command);
100 cmd.arg("external_ecdh");
101 cmd.arg("--parameter");
102 cmd.arg(parameter);
103 cmd.arg("--epk");
104 cmd.arg(STANDARD.encode(ephemera_public_key));
105
106 let cmd_stdout = run_command_stdout(cmd)?;
107 parse_ecdh_result(&cmd_stdout)
108}
109
110fn run_command_stdout(cmd: Command) -> XResult<String> {
111 let output = run_command(cmd)?;
112 let stdout_text = opt_result!(String::from_utf8(output.stdout), "Parse stdout failed:{}");
113 Ok(stdout_text.trim().to_string())
114}
115
116fn run_command(mut cmd: Command) -> XResult<Output> {
117 debugging!("Run command: {:?}", cmd);
118 let output = cmd.output();
119 match output {
120 Err(e) => simple_error!("Run command failed: {:?}", e),
121 Ok(output) => {
122 debugging!("Output: {:?}", output);
123 if !output.status.success() {
124 let stderr = String::from_utf8_lossy(&output.stderr);
125 let stdout = String::from_utf8_lossy(&output.stdout);
126 simple_error!(
127 "Run command not success: {:?}\n - stdout: {}\n - stderr: {}",
128 output.status.code(),
129 stdout,
130 stderr
131 )
132 } else {
133 Ok(output)
134 }
135 }
136 }
137}
138
139fn parse_sign_result(stdout: &str) -> XResult<Vec<u8>> {
140 if is_success(stdout)? {
141 let sign_result: ExternalSignResult = from_str(stdout)?;
142 Ok(STANDARD.decode(&sign_result.signature_base64)?)
143 } else {
144 let error_result: ErrorResult = from_str(stdout)?;
145 simple_error!("{}", error_result.error)
146 }
147}
148
149fn parse_ecdh_result(stdout: &str) -> XResult<Vec<u8>> {
150 if is_success(stdout)? {
151 let dh_result: ExternalDhResult = from_str(stdout)?;
152 Ok(hex::decode(&dh_result.shared_secret_hex)?)
153 } else {
154 let error_result: ErrorResult = from_str(stdout)?;
155 simple_error!("{}", error_result.error)
156 }
157}
158
159pub fn from_str<'a, T>(s: &'a str) -> XResult<T>
160where
161 T: de::Deserialize<'a>,
162{
163 match serde_json::from_str(s) {
164 Ok(result) => Ok(result),
165 Err(e) => simple_error!("Parse JSON: {}, error: {}", s, e),
166 }
167}
168
169fn is_success(cmd_stdout: &str) -> XResult<bool> {
170 let val = opt_result!(
171 serde_json::from_str::<Value>(cmd_stdout),
172 "Parse result: {}, failed: {}",
173 cmd_stdout
174 );
175 if let Value::Object(map) = val {
176 if let Some(success_value) = map.get("success") {
177 if let Value::Bool(result) = success_value {
178 return Ok(*result);
179 }
180 }
181 }
182 simple_error!("Bad result: {}", cmd_stdout)
183}