feat: update local-mini-kms

This commit is contained in:
2022-07-24 15:57:53 +08:00
parent 038d5546e0
commit 0b97850f65
6 changed files with 381 additions and 17 deletions

View File

@@ -1,5 +1,12 @@
use rusqlite::{Connection, params};
use rust_util::{debugging, information, opt_result, success, XResult};
use rust_util::{debugging, information, opt_result, simple_error, success, XResult};
pub const DEFAULT_MASTER_KEY_VERIFICATION_KEY: &'static str = "__master_verification_key";
pub struct Key {
name: String,
encrypted_key: String,
}
pub fn open_db(db: &str) -> XResult<Connection> {
let con = opt_result!(Connection::open(db), "Open sqlite db: {}, failed: {}", db);
@@ -24,4 +31,27 @@ pub fn init_db(conn: &Connection) -> XResult<bool> {
"##, ())?;
success!("Table keys created");
Ok(true)
}
pub fn insert_key(conn: &Connection, key: &Key) -> XResult<()> {
let _ = conn.execute(
"INSERT INTO keys (name, value) VALUES (?1, ?2)",
(&key.name, &key.encrypted_key),
)?;
Ok(())
}
pub fn find_key(conn: &Connection, name: &str) -> XResult<Option<Key>> {
let mut stmt = conn.prepare("SELECT id, name, value FROM keys WHERE name = ?1")?;
let mut key_iter = stmt.query_map(params![name], |row| {
Ok(Key {
name: row.get(1)?,
encrypted_key: row.get(2)?,
})
})?;
match key_iter.next() {
None => Ok(None),
Some(Ok(r)) => Ok(Some(r)),
Some(Err(e)) => simple_error!("Find key failed: {}", e),
}
}

57
src/jose.rs Normal file
View File

@@ -0,0 +1,57 @@
use josekit::jwe;
use josekit::jwe::alg::aeskw::AeskwJweAlgorithm;
use josekit::jwe::alg::rsaes::RsaesJweAlgorithm;
use josekit::jwe::JweHeader;
use josekit::jwk::{Jwk, KeyPair};
use josekit::jwk::alg::rsa::RsaKeyPair;
use rust_util::XResult;
pub fn generate_rsa_key(bits: u32) -> XResult<RsaKeyPair> {
Ok(RsaKeyPair::generate(bits)?)
}
pub fn serialize_jwe_rsa(payload: &[u8], jwk: &Jwk) -> XResult<String> {
let mut header = JweHeader::new();
header.set_content_encryption("A256GCM");
let encrypter = RsaesJweAlgorithm::RsaOaep.encrypter_from_jwk(&jwk)?;
Ok(jwe::serialize_compact(payload, &header, &encrypter)?)
}
pub fn deserialize_jwe_rsa(jwe: &str, jwk: &Jwk) -> XResult<(Vec<u8>, JweHeader)> {
let decrypter = RsaesJweAlgorithm::RsaOaep.decrypter_from_jwk(jwk)?;
Ok(jwe::deserialize_json(jwe, &decrypter)?)
}
pub fn serialize_jwe_aes(payload: &[u8], key: &[u8]) -> XResult<String> {
let mut header = JweHeader::new();
header.set_content_encryption("A256GCM");
let encrypter = AeskwJweAlgorithm::A256kw.encrypter_from_bytes(key)?;
Ok(jwe::serialize_compact(payload, &header, &encrypter)?)
}
pub fn deserialize_jwe_aes(jwe: &str, key: &[u8]) -> XResult<(Vec<u8>, JweHeader)> {
let decrypter = AeskwJweAlgorithm::A192kw.decrypter_from_bytes(key)?;
Ok(jwe::deserialize_json(jwe, &decrypter)?)
}
#[test]
fn a() {
let k = generate_rsa_key(4096).unwrap();
let k = k.to_jwk_key_pair();
let kk = k.to_public_key().unwrap();
println!("{:?}", k);
println!("{:?}", kk);
let mut header = JweHeader::new();
header.set_content_encryption("A256GCM");
let encrypter = RsaesJweAlgorithm::RsaOaep.encrypter_from_jwk(&kk).unwrap();
let payload = "helloworld";
let r = jwe::serialize_compact(payload.as_bytes(), &header, &encrypter);
println!("{:?}", r);
let k = "abcdefghijklmnopqrstuvwxyz123456";
let t = serialize_jwe_aes(payload.as_bytes(), k.as_bytes());
println!("{:?}", t);
}

View File

@@ -3,6 +3,7 @@ use rust_util::{failure_and_exit, information};
use rust_util::util_clap::{Command, CommandError};
mod db;
mod jose;
mod cli;
mod serve;

View File

@@ -1,11 +1,17 @@
use std::sync::RwLock;
use clap::{App, Arg, ArgMatches, SubCommand};
use hyper::{Body, Client, Method, Request, Response, Server, StatusCode};
use hyper::{Body, Client, header, Method, Request, Response, Server, StatusCode};
use hyper::body::Buf;
use hyper::client::HttpConnector;
use hyper::service::{make_service_fn, service_fn};
use rust_util::{failure_and_exit, iff, information, success};
use josekit::jwk::alg::rsa::RsaKeyPair;
use rust_util::{failure_and_exit, iff, information, success, XResult};
use rust_util::util_clap::{Command, CommandError};
use crate::db;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::jose;
type GenericError = Box<dyn std::error::Error + Send + Sync>;
type Result<T> = std::result::Result<T, GenericError>;
@@ -23,6 +29,12 @@ impl Command for CommandImpl {
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
match init_instance() {
Ok(true) => success!("Init server success"),
Ok(false) => failure_and_exit!("SHOULD NOT HAPPEN, server already init"),
Err(e) => failure_and_exit!("Init server failed: {}", e),
}
let listen = sub_arg_matches.value_of("listen").expect("Get argument listen error");
let rt = tokio::runtime::Runtime::new().expect("Create tokio runtime error");
rt.block_on(async {
@@ -48,41 +60,131 @@ impl Command for CommandImpl {
}
// ref: https://github.com/hyperium/hyper/blob/master/examples/web_api.rs
// ref: https://crates.io/crates/rusqlite
async fn response_requests(
req: Request<Body>,
_client: Client<HttpConnector>,
) -> Result<Response<Body>> {
match (req.method(), req.uri().path()) {
// (&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())),
// (&Method::GET, "/test.html") => client_request_response(&client).await,
(&Method::POST, "/init") => init(req).await,
(&Method::POST, "/decrypt") => decrypt(req).await,
(&Method::POST, "/encrypt") => encrypt(req).await,
(&Method::GET, "/status") => status().await,
(&Method::GET, "/version") => get_version().await,
_ => {
Ok(Response::builder()
.status(StatusCode::NOT_FOUND).body(NOTFOUND.into())
.expect("Response not found error"))
}
_ => Ok(Response::builder().status(StatusCode::NOT_FOUND).body(NOTFOUND.into())?),
}
}
// -------------------------------------------------------------------------------------------------
struct MemoryKey {
master_key: Vec<u8>,
instance_rsa_key_pair: RsaKeyPair,
master_key: Option<Vec<u8>>,
}
lazy_static::lazy_static! {
static ref STATUS_RW_LOCK: RwLock<bool> = RwLock::new(false);
static ref STATUP_RW_LOCK: RwLock<Option<MemoryKey>> = RwLock::new(None);
}
fn init_instance() -> XResult<bool> {
let mut startup_rw_lock = STATUP_RW_LOCK.write().expect("Lock write startup rw lock error");
match &*startup_rw_lock {
Some(_) => Ok(false),
None => {
let memory_key = MemoryKey {
instance_rsa_key_pair: jose::generate_rsa_key(4096)?,
master_key: None,
};
*startup_rw_lock = Some(memory_key);
Ok(true)
}
}
}
async fn decrypt(req: Request<Body>) -> Result<Response<Body>> {
let whole_body = hyper::body::aggregate(req).await?;
let data: serde_json::Value = serde_json::from_reader(whole_body.reader())?;
Ok(Response::builder().body(format!("{}", data).into())?)
}
async fn encrypt(req: Request<Body>) -> Result<Response<Body>> {
let whole_body = hyper::body::aggregate(req).await?;
let data: serde_json::Value = serde_json::from_reader(whole_body.reader())?;
Ok(Response::builder().body(format!("{}", data).into())?)
}
#[derive(Serialize, Deserialize)]
struct InitRequest {
clear_master_key_hex: Option<String>,
clear_master_key_base64: Option<String>,
encrypted_master_key: Option<String>,
}
async fn init(req: Request<Body>) -> Result<Response<Body>> {
let whole_body = hyper::body::aggregate(req).await?;
let init_request: InitRequest = serde_json::from_reader(whole_body.reader())?;
let mut startup_rw_lock = STATUP_RW_LOCK.write().expect("Lock read startup rw lock error");
let (status_code, body) = match &*startup_rw_lock {
None => (StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " })),
Some(memory_key) => match memory_key.master_key {
Some(_) => (StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "already init " })),
None => (StatusCode::OK, Value::Null),
},
};
if status_code != StatusCode::OK {
return Ok(Response::builder().status(status_code).body(serde_json::to_string_pretty(&body)?.into())?);
}
let (status_code, body) = if let Some(clear_master_key_base64) = init_request.clear_master_key_base64 {
let clear_master_key = base64::decode(clear_master_key_base64)?;
if clear_master_key.len() != 32 {
(StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "bad clear_master_key_hex length" }))
} else {
if let Some(k) = &mut *startup_rw_lock {
k.master_key = Some(clear_master_key);
}
(StatusCode::OK, json!({}))
}
} else if let Some(clear_master_key_hex) = init_request.clear_master_key_hex {
let clear_master_key = hex::decode(clear_master_key_hex)?;
if clear_master_key.len() != 32 {
(StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "bad clear_master_key_hex length" }))
} else {
if let Some(k) = &mut *startup_rw_lock {
k.master_key = Some(clear_master_key);
}
(StatusCode::OK, json!({}))
}
} else if let Some(encrypted_master_key) = init_request.encrypted_master_key {
// TODO ...
(StatusCode::BAD_REQUEST, json!({ "error": "not_implement", "error_message": "not_implement" }))
} else {
(StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "master key is not provided" }))
};
Ok(Response::builder().status(status_code).body(serde_json::to_string_pretty(&body)?.into())?)
}
async fn status() -> Result<Response<Body>> {
let status_rw_lock = STATUS_RW_LOCK.read().expect("Lock read status rw lock error");
let status_rw_lock_value = *status_rw_lock;
Ok(Response::builder().body(format!("{}\n", iff!(status_rw_lock_value, "init", "uninit")).into()).expect("x"))
let startup_rw_lock = STATUP_RW_LOCK.read().expect("Lock read startup rw lock error");
let body = match &*startup_rw_lock {
None => json!({ "status": "n/a" }),
Some(memory_key) => match memory_key.master_key {
None => json!({
"status": "not-ready",
"instance_public_key_jwk": memory_key.instance_rsa_key_pair.to_jwk_key_pair().to_public_key()?,
}),
Some(_) => json!({
"status": "ready",
"instance_public_key_jwk": memory_key.instance_rsa_key_pair.to_jwk_key_pair().to_public_key()?,
}),
}
};
Ok(Response::builder().body(serde_json::to_string_pretty(&body)?.into())?)
}
async fn get_version() -> Result<Response<Body>> {
Ok(Response::builder().body(format!(
"{} - {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")
).into()).expect("Response get_version error"))
).into())?)
}