feat: v0.1.0

This commit is contained in:
2022-07-24 23:44:05 +08:00
parent 3c8e12a8d2
commit b15da58154
5 changed files with 307 additions and 18 deletions

View File

@@ -1,6 +1,14 @@
use std::io::Write;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::simple_error;
use hyper::{Body, Client, Method, Request, Response, StatusCode};
use hyper::body::Buf;
use josekit::jwk::Jwk;
use rust_util::{debugging, opt_value_result, simple_error, success, XResult};
use rust_util::util_clap::{Command, CommandError};
use serde_json::{json, Value};
use crate::jose;
pub struct CommandImpl;
@@ -9,10 +17,175 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Local mini KMS cli")
.arg(Arg::with_name("connect").long("connect").takes_value(true).default_value("127.0.0.1:6567").help("Connect server"))
.arg(Arg::with_name("connect").long("connect").takes_value(true).default_value("127.0.0.1:5567").help("Connect server"))
.arg(Arg::with_name("init").long("init").help("Init server"))
.arg(Arg::with_name("offline-init").long("offline-init").help("Offline init server"))
.arg(Arg::with_name("encrypt").long("encrypt").help("Encrypt text"))
.arg(Arg::with_name("decrypt").long("decrypt").help("Decrypt text"))
.arg(Arg::with_name("value").long("value").takes_value(true).help("Value, for encrypt or decrypt"))
.arg(Arg::with_name("value-hex").long("value-hex").takes_value(true).help("Value(hex), for encrypt"))
.arg(Arg::with_name("value-base64").long("value-base64").takes_value(true).help("Value(base64), for encrypt"))
}
fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError {
simple_error!("Not implemented")
fn run(&self, arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let init = sub_arg_matches.is_present("init");
let offline_init = sub_arg_matches.is_present("offline-init");
let encrypt = sub_arg_matches.is_present("encrypt");
let decrypt = sub_arg_matches.is_present("decrypt");
let rt = tokio::runtime::Runtime::new().expect("Create tokio runtime error");
if init {
rt.block_on(async {
do_init(arg_matches, sub_arg_matches).await
})
} else if offline_init {
do_offline_init(arg_matches, sub_arg_matches)
} else if encrypt {
rt.block_on(async {
do_encrypt(arg_matches, sub_arg_matches).await
})
} else if decrypt {
rt.block_on(async {
do_decrypt(arg_matches, sub_arg_matches).await
})
} else {
simple_error!("Need a flag")
}
}
}
async fn do_init(_arg_matches: &ArgMatches<'_>, sub_arg_matches: &ArgMatches<'_>) -> CommandError {
let connect = sub_arg_matches.value_of("connect").expect("Get argument listen error");
let client = Client::new();
let uri = format!("http://{}/status", connect);
debugging!("Request uri: {}", &uri);
let req = Request::builder().method(Method::GET).uri(uri).body(Body::empty())?;
let req_response = client.request(req).await?;
if req_response.status() != StatusCode::OK {
return simple_error!("Server status is not success: {}", req_response.status().as_u16());
}
let data = response_to_value(req_response).await?;
debugging!("Get status: {}", &data);
let status = &data["status"];
if let Some(status) = status.as_str() {
if status == "ready" {
success!("Server is already init");
return Ok(Some(0));
}
if status != "not-ready" {
return simple_error!("Server status is NOT not-ready");
}
}
let instance_public_key_jwk = &data["instance_public_key_jwk"];
println!("Instance server public key JWK: {}", instance_public_key_jwk);
let line = read_line("Input encrypted master key: ")?;
let uri = format!("http://{}/init", connect);
debugging!("Request uri: {}", &uri);
let body = json!({
"encrypted_master_key": line,
});
let body = serde_json::to_string(&body)?;
let req = Request::builder().method(Method::POST).uri(uri).body(Body::from(body))?;
let req_response = client.request(req).await?;
if req_response.status() != StatusCode::OK {
let status = req_response.status().as_u16();
let data = response_to_value(req_response).await?;
return simple_error!("Server status is not success: {}, response: {}", status, data);
}
success!("Init finished");
Ok(Some(0))
}
async fn do_encrypt(_arg_matches: &ArgMatches<'_>, sub_arg_matches: &ArgMatches<'_>) -> CommandError {
let connect = sub_arg_matches.value_of("connect").expect("Get argument listen error");
let value = sub_arg_matches.value_of("value");
let value_hex = sub_arg_matches.value_of("value-hex");
let value_base64 = sub_arg_matches.value_of("value-base64");
let client = Client::new();
let uri = format!("http://{}/encrypt", connect);
debugging!("Request uri: {}", &uri);
let body = if let Some(value) = value {
json!({ "value": value })
} else if let Some(value_hex) = value_hex {
json!({ "value_hex": value_hex })
} else if let Some(value_base64) = value_base64 {
json!({ "value_base64": value_base64 })
} else {
return simple_error!("Require one of value, value-hex, value-base64");
};
let body = serde_json::to_string(&body)?;
let req = Request::builder().method(Method::POST).uri(uri).body(Body::from(body))?;
let req_response = client.request(req).await?;
if req_response.status() != StatusCode::OK {
let status = req_response.status().as_u16();
let data = response_to_value(req_response).await?;
return simple_error!("Server status is not success: {}, response: {}", status, data);
}
let data = response_to_value(req_response).await?;
success!("Encrypted value: {}", data["encrypted_value"].as_str().expect("Get encrypted_value error"));
Ok(Some(0))
}
async fn do_decrypt(_arg_matches: &ArgMatches<'_>, sub_arg_matches: &ArgMatches<'_>) -> CommandError {
let connect = sub_arg_matches.value_of("connect").expect("Get argument listen error");
let value = opt_value_result!(sub_arg_matches.value_of("value"), "Argument value required");
let client = Client::new();
let uri = format!("http://{}/decrypt", connect);
debugging!("Request uri: {}", &uri);
let body = json!({ "encrypted_value": value });
let body = serde_json::to_string(&body)?;
let req = Request::builder().method(Method::POST).uri(uri).body(Body::from(body))?;
let req_response = client.request(req).await?;
if req_response.status() != StatusCode::OK {
let status = req_response.status().as_u16();
let data = response_to_value(req_response).await?;
return simple_error!("Server status is not success: {}, response: {}", status, data);
}
let data = response_to_value(req_response).await?;
success!("Encrypted value(hex): {}", data["value_hex"].as_str().expect("Get value_hex error"));
success!("Encrypted value(base64): {}", data["value_base64"].as_str().expect("Get value_base64 error"));
success!("Encrypted value: {}", String::from_utf8_lossy(&hex::decode(data["value_hex"].as_str().expect("Get value_hex error"))?).to_string());
Ok(Some(0))
}
fn do_offline_init(_arg_matches: &ArgMatches<'_>, _sub_arg_matches: &ArgMatches<'_>) -> CommandError {
let line = read_line("Input master key: ")?;
let master_key = if line.starts_with("hex:") {
let hex: String = line.chars().skip(4).collect();
hex::decode(&hex)?
} else if line.starts_with("base64:") {
let base64: String = line.chars().skip(7).collect();
base64::decode(&base64)?
} else {
line.as_bytes().to_vec()
};
let jwk = read_line("Input JWK: ")?;
let jwk = Jwk::from_bytes(jwk.as_bytes())?;
let encrypted_master_key = jose::serialize_jwe_rsa(&master_key, &jwk)?;
success!("Encrypted master key: {}", encrypted_master_key);
Ok(Some(0))
}
fn read_line(prompt: &str) -> XResult<String> {
std::io::stdout().write(prompt.as_bytes()).ok();
std::io::stdout().flush().ok();
let mut line = String::new();
if let Err(e) = std::io::stdin().read_line(&mut line) {
return simple_error!("Read from terminal failed: {}", e);
}
Ok(line.trim().to_string())
}
async fn response_to_value(response: Response<Body>) -> XResult<Value> {
let req_body = response.into_body();
let whole_body = hyper::body::aggregate(req_body).await?;
let data: Value = serde_json::from_reader(whole_body.reader())?;
Ok(data)
}