184 lines
5.2 KiB
Rust
184 lines
5.2 KiB
Rust
use base64::Engine;
|
|
use base64::engine::general_purpose::STANDARD;
|
|
use rust_util::{XResult, debugging, opt_result, simple_error};
|
|
use serde::{Deserialize, de};
|
|
use serde_json::Value;
|
|
use std::process::{Command, Output};
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct ErrorResult {
|
|
#[allow(dead_code)]
|
|
pub success: bool,
|
|
pub error: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct ExternalSpecResult {
|
|
#[allow(dead_code)]
|
|
pub success: bool,
|
|
pub agent: String,
|
|
pub specification: String,
|
|
pub commands: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct ExternalPublicKeyResult {
|
|
#[allow(dead_code)]
|
|
pub success: bool,
|
|
pub public_key_base64: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct ExternalSignResult {
|
|
#[allow(dead_code)]
|
|
pub success: bool,
|
|
pub signature_base64: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct ExternalDhResult {
|
|
#[allow(dead_code)]
|
|
pub success: bool,
|
|
pub shared_secret_hex: String,
|
|
}
|
|
|
|
pub fn external_spec(external_command: &str) -> XResult<ExternalSpecResult> {
|
|
let mut cmd = Command::new(external_command);
|
|
cmd.arg("external_spec");
|
|
|
|
let cmd_stdout = run_command_stdout(cmd)?;
|
|
if is_success(&cmd_stdout)? {
|
|
let external_spec: ExternalSpecResult = from_str(&cmd_stdout)?;
|
|
Ok(external_spec)
|
|
} else {
|
|
let error_result: ErrorResult = from_str(&cmd_stdout)?;
|
|
simple_error!("{}", error_result.error)
|
|
}
|
|
}
|
|
|
|
pub fn external_public_key(external_command: &str, parameter: &str) -> XResult<Vec<u8>> {
|
|
let mut cmd = Command::new(external_command);
|
|
cmd.arg("external_public_key");
|
|
cmd.arg("--parameter");
|
|
cmd.arg(parameter);
|
|
|
|
let cmd_stdout = run_command_stdout(cmd)?;
|
|
if is_success(&cmd_stdout)? {
|
|
let external_public_key: ExternalPublicKeyResult = from_str(&cmd_stdout)?;
|
|
Ok(STANDARD.decode(&external_public_key.public_key_base64)?)
|
|
} else {
|
|
let error_result: ErrorResult = from_str(&cmd_stdout)?;
|
|
simple_error!("{}", error_result.error)
|
|
}
|
|
}
|
|
|
|
pub fn external_sign(
|
|
external_command: &str,
|
|
parameter: &str,
|
|
alg: &str,
|
|
content: &[u8],
|
|
) -> XResult<Vec<u8>> {
|
|
let mut cmd = Command::new(external_command);
|
|
cmd.arg("external_sign");
|
|
cmd.arg("--parameter");
|
|
cmd.arg(parameter);
|
|
cmd.arg("--alg");
|
|
cmd.arg(alg);
|
|
cmd.arg("--message-base64");
|
|
cmd.arg(STANDARD.encode(content));
|
|
|
|
let cmd_stdout = run_command_stdout(cmd)?;
|
|
parse_sign_result(&cmd_stdout)
|
|
}
|
|
|
|
pub fn external_ecdh(
|
|
external_command: &str,
|
|
parameter: &str,
|
|
ephemera_public_key: &[u8],
|
|
) -> XResult<Vec<u8>> {
|
|
let mut cmd = Command::new(external_command);
|
|
cmd.arg("external_ecdh");
|
|
cmd.arg("--parameter");
|
|
cmd.arg(parameter);
|
|
cmd.arg("--epk");
|
|
cmd.arg(STANDARD.encode(ephemera_public_key));
|
|
|
|
let cmd_stdout = run_command_stdout(cmd)?;
|
|
parse_ecdh_result(&cmd_stdout)
|
|
}
|
|
|
|
fn run_command_stdout(cmd: Command) -> XResult<String> {
|
|
let output = run_command(cmd)?;
|
|
let stdout_text = opt_result!(String::from_utf8(output.stdout), "Parse stdout failed:{}");
|
|
Ok(stdout_text.trim().to_string())
|
|
}
|
|
|
|
fn run_command(mut cmd: Command) -> XResult<Output> {
|
|
debugging!("Run command: {:?}", cmd);
|
|
let output = cmd.output();
|
|
match output {
|
|
Err(e) => simple_error!("Run command failed: {:?}", e),
|
|
Ok(output) => {
|
|
debugging!("Output: {:?}", output);
|
|
if !output.status.success() {
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
simple_error!(
|
|
"Run command not success: {:?}\n - stdout: {}\n - stderr: {}",
|
|
output.status.code(),
|
|
stdout,
|
|
stderr
|
|
)
|
|
} else {
|
|
Ok(output)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_sign_result(stdout: &str) -> XResult<Vec<u8>> {
|
|
if is_success(stdout)? {
|
|
let sign_result: ExternalSignResult = from_str(stdout)?;
|
|
Ok(STANDARD.decode(&sign_result.signature_base64)?)
|
|
} else {
|
|
let error_result: ErrorResult = from_str(stdout)?;
|
|
simple_error!("{}", error_result.error)
|
|
}
|
|
}
|
|
|
|
fn parse_ecdh_result(stdout: &str) -> XResult<Vec<u8>> {
|
|
if is_success(stdout)? {
|
|
let dh_result: ExternalDhResult = from_str(stdout)?;
|
|
Ok(hex::decode(&dh_result.shared_secret_hex)?)
|
|
} else {
|
|
let error_result: ErrorResult = from_str(stdout)?;
|
|
simple_error!("{}", error_result.error)
|
|
}
|
|
}
|
|
|
|
pub fn from_str<'a, T>(s: &'a str) -> XResult<T>
|
|
where
|
|
T: de::Deserialize<'a>,
|
|
{
|
|
match serde_json::from_str(s) {
|
|
Ok(result) => Ok(result),
|
|
Err(e) => simple_error!("Parse JSON: {}, error: {}", s, e),
|
|
}
|
|
}
|
|
|
|
fn is_success(cmd_stdout: &str) -> XResult<bool> {
|
|
let val = opt_result!(
|
|
serde_json::from_str::<Value>(cmd_stdout),
|
|
"Parse result: {}, failed: {}",
|
|
cmd_stdout
|
|
);
|
|
if let Value::Object(map) = val {
|
|
if let Some(success_value) = map.get("success") {
|
|
if let Value::Bool(result) = success_value {
|
|
return Ok(*result);
|
|
}
|
|
}
|
|
}
|
|
simple_error!("Bad result: {}", cmd_stdout)
|
|
}
|