external_command_rs/
lib.rs

1use 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}