diff --git a/.gitignore b/.gitignore index f6c5e32..8f8230d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +LOCAL_README.md *_gitignore.* local-mini-kms.db .idea/ diff --git a/Cargo.lock b/Cargo.lock index 23a7d71..fc4824a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,7 +653,7 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "local-mini-kms" -version = "0.2.3" +version = "0.3.0" dependencies = [ "base64 0.21.0", "clap", diff --git a/Cargo.toml b/Cargo.toml index 1038fad..0d7decc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "local-mini-kms" -version = "0.2.3" +version = "0.3.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 5bcbab8..7c4693e 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,18 @@ Mini-KMS runs local written by Rust ```shell ./local-mini-kms cli --offline-init ``` + +Write value: +```shell +curl -X POST http://127.0.0.1:5567/write \ + -H "Content-Type: application/json" \ + -d '{"name":"test","value":{"value":"hello"}}' +``` + +Read value: +```shell +curl -X POST http://127.0.0.1:5567/read \ + -H "Content-Type: application/json" \ + -d '{"name":"test"}' +``` + diff --git a/src/db.rs b/src/db.rs index adda78a..bdcda8a 100644 --- a/src/db.rs +++ b/src/db.rs @@ -8,6 +8,10 @@ pub struct Key { pub encrypted_key: String, } +pub fn make_db_key_name(name: &str) -> String { + format!("value:{}", name) +} + pub fn open_db(db: &str) -> XResult { let con = opt_result!(Connection::open(db), "Open sqlite db: {}, failed: {}", db); debugging!("Db auto commit: {}", con.is_autocommit()); @@ -41,6 +45,14 @@ pub fn insert_key(conn: &Connection, key: &Key) -> XResult<()> { Ok(()) } +pub fn update_key(conn: &Connection, key: &Key) -> XResult<()> { + let _ = conn.execute( + "UPDATE keys SET value = ?1 WHERE name = ?2", + (&key.encrypted_key, &key.name), + )?; + Ok(()) +} + pub fn find_key(conn: &Connection, name: &str) -> XResult> { let mut stmt = conn.prepare("SELECT id, name, value FROM keys WHERE name = ?1")?; let mut key_iter = stmt.query_map(params![name], |row| { diff --git a/src/main.rs b/src/main.rs index 0e01d19..6963f05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,11 @@ mod proc; mod jose; mod cli; mod serve; +mod serve_common; +mod serve_status; +mod serve_init; +mod serve_encrypt_decrypt; +mod serve_read_write; pub struct DefaultCommandImpl; diff --git a/src/serve.rs b/src/serve.rs index 51f3c7e..7da5b9d 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -1,29 +1,24 @@ use std::net::SocketAddr; -use std::sync::Mutex; -use base64::Engine; -use base64::engine::general_purpose::STANDARD; use clap::{App, Arg, ArgMatches, SubCommand}; use hyper::{Body, Client, Method, Request, Response, Server, StatusCode}; -use hyper::body::Buf; use hyper::client::HttpConnector; use hyper::server::conn::AddrStream; use hyper::service::{make_service_fn, service_fn}; -use josekit::jwk::alg::rsa::RsaKeyPair; -use josekit::jwk::KeyPair; -use rust_util::{debugging, failure_and_exit, information, opt_result, simple_error, success, XResult}; +use rust_util::{failure_and_exit, information, success, XResult}; use rust_util::util_clap::{Command, CommandError}; -use seckey::SecBytes; -use serde::{Deserialize, Serialize}; -use serde_json::{json, Map, Value}; -use zeroize::Zeroize; +use serde_json::{json, Value}; use crate::{db, jose, proc}; -use crate::db::Key; - -type GenericError = Box; -type Result = std::result::Result; - +use crate::do_response; +use crate::serve_common; +use crate::serve_common::GenericError; +use crate::serve_common::MemoryKey; +use crate::serve_common::Result; +use crate::serve_encrypt_decrypt; +use crate::serve_init; +use crate::serve_read_write; +use crate::serve_status; pub struct CommandImpl; @@ -79,11 +74,13 @@ async fn response_requests( let process = proc::get_process(remote_addr.port()); information!("Receive request: {}, from: {}, process: {:?}", req.uri(), remote_addr, process); match (req.method(), req.uri().path()) { - (&Method::POST, "/init") => init(req).await, + (&Method::POST, "/init") => serve_init::init(req).await, (&Method::POST, "/update") => update().await, - (&Method::POST, "/decrypt") => decrypt(req).await, - (&Method::POST, "/encrypt") => encrypt(req).await, - (&Method::GET, "/status") => status().await, + (&Method::POST, "/decrypt") => serve_encrypt_decrypt::decrypt(req).await, + (&Method::POST, "/encrypt") => serve_encrypt_decrypt::encrypt(req).await, + (&Method::POST, "/read") => serve_read_write::read(req).await, + (&Method::POST, "/write") => serve_read_write::write(req).await, + (&Method::GET, "/status") => serve_status::status().await, (&Method::GET, "/version") => get_version().await, (&Method::GET, "/") => get_root().await, _ => Ok(Response::builder() @@ -94,37 +91,13 @@ async fn response_requests( } } -macro_rules! do_response { - ($ex: expr) => ( - match $ex { - Ok((status_code, body)) => Ok(Response::builder().status(status_code).body((serde_json::to_string_pretty(&body)? + "\n").into())?), - Err(e) => Ok(Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body( - format!("{}\n", serde_json::to_string_pretty(&json!({ - "error": "internal_error", - "error_message": format!("{}", e), - }))?).into() - )?), - } - ) -} - // ------------------------------------------------------------------------------------------------- -struct MemoryKey { - database_file: String, - instance_rsa_key_pair: RsaKeyPair, - master_key: Option, -} - -lazy_static::lazy_static! { - static ref STATUP_RW_LOCK: Mutex> = Mutex::new(None); -} - fn init_instance(db: &str) -> XResult { let conn = db::open_db(db)?; db::init_db(&conn)?; - let mut startup_rw_lock = STATUP_RW_LOCK.lock().expect("Lock write startup rw lock error"); + let mut startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().expect("Lock write startup rw lock error"); match &*startup_rw_lock { Some(_) => Ok(false), None => { @@ -140,7 +113,7 @@ fn init_instance(db: &str) -> XResult { } fn update_instance_rsa_key_pair() -> XResult { - let mut startup_rw_lock = STATUP_RW_LOCK.lock().expect("Lock write startup rw lock error"); + let mut startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().expect("Lock write startup rw lock error"); match &mut *startup_rw_lock { Some(k) => { k.instance_rsa_key_pair = jose::generate_rsa_key(4096)?; @@ -150,97 +123,6 @@ fn update_instance_rsa_key_pair() -> XResult { } } -#[derive(Serialize, Deserialize)] -struct MultipleViewValue { - value: Option, - value_hex: Option, - value_base64: Option, -} - -impl MultipleViewValue { - fn from(v: &[u8]) -> Self { - Self { - value: Some(String::from_utf8_lossy(v).to_string()), - value_hex: Some(hex::encode(v)), - value_base64: Some(STANDARD.encode(v)), - } - } - - fn to_bytes(&self) -> XResult> { - if let Some(v) = &self.value { - Ok(v.as_bytes().to_vec()) - } else if let Some(v) = &self.value_hex { - let v = opt_result!(hex::decode(v), "Decode hex failed: {}"); - Ok(v) - } else if let Some(v) = &self.value_base64 { - let v = opt_result!(STANDARD.decode(v), "Decode base64 failed: {}"); - Ok(v) - } else { - simple_error!("Multiple view value is all empty") - } - } -} - - -#[derive(Serialize, Deserialize)] -struct DecryptRequest { - encrypted_value: String, -} - -async fn decrypt(req: Request) -> Result> { - do_response!(inner_decrypt(req).await) -} - -async fn inner_decrypt(req: Request) -> XResult<(StatusCode, Value)> { - let whole_body = hyper::body::aggregate(req).await?; - let data: DecryptRequest = serde_json::from_reader(whole_body.reader())?; - - debugging!("To be decrypted value: {}", &data.encrypted_value); - let key = match get_master_key() { - None => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "status_not_ready" }))), - Some(key) => key, - }; - let decrypted_value = jose::deserialize_jwe_aes(&data.encrypted_value, &*key.read()); - drop(key); - - decrypted_value.map(|v| { - let v = MultipleViewValue::from(&v.0); - let mut map = Map::new(); - if let Some(v) = &v.value { - map.insert("value".to_string(), Value::String(v.to_string())); - } - if let Some(v) = &v.value_hex { - map.insert("value_hex".to_string(), Value::String(v.to_string())); - } - if let Some(v) = &v.value_base64 { - map.insert("value_base64".to_string(), Value::String(v.to_string())); - } - (StatusCode::OK, Value::Object(map)) - }) -} - -async fn encrypt(req: Request) -> Result> { - do_response!(inner_encrypt(req).await) -} - -async fn inner_encrypt(req: Request) -> XResult<(StatusCode, Value)> { - let whole_body = hyper::body::aggregate(req).await?; - let data: MultipleViewValue = serde_json::from_reader(whole_body.reader())?; - let value = data.to_bytes()?; - let key = match get_master_key() { - None => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "status_not_ready" }))), - Some(key) => key, - }; - let encrypt_result = jose::serialize_jwe_aes(&value, &*key.read()); - drop(key); - - encrypt_result.map(|e| { - (StatusCode::OK, json!({ - "encrypted_value": e, - })) - }) -} - async fn update() -> Result> { do_response!(inner_update().await) } @@ -252,98 +134,6 @@ async fn inner_update() -> XResult<(StatusCode, Value)> { }))) } -async fn init(req: Request) -> Result> { - do_response!(inner_init(req).await) -} - -#[derive(Serialize, Deserialize)] -struct InitRequest { - clear_master_key_hex: Option, - clear_master_key_base64: Option, - encrypted_master_key: Option, -} - -async fn inner_init(req: Request) -> XResult<(StatusCode, Value)> { - 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.lock().expect("Lock read startup rw lock error"); - match &*startup_rw_lock { - None => return Ok((StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " }))), - Some(memory_key) => match memory_key.master_key { - Some(_) => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "already init " }))), - None => {} - }, - } - - let clear_master_key = if let Some(clear_master_key_base64) = &init_request.clear_master_key_base64 { - STANDARD.decode(clear_master_key_base64)? - } else if let Some(clear_master_key_hex) = init_request.clear_master_key_hex { - hex::decode(clear_master_key_hex)? - } else if let Some(encrypted_master_key) = init_request.encrypted_master_key { - debugging!("Received encrypted master key: {}", encrypted_master_key); - if let Some(k) = &*startup_rw_lock { - let (clear_master_key, _) = jose::deserialize_jwe_rsa(&encrypted_master_key, &k.instance_rsa_key_pair.to_jwk_private_key())?; - clear_master_key - } else { - return Ok((StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " }))); - } - } else { - return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "master key is not assigned" }))); - }; - - if clear_master_key.len() != 32 { - return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "bad clear_master_key length" }))); - } - - if let Some(k) = &mut *startup_rw_lock { - let conn = opt_result!(db::open_db(&k.database_file), "Open db failed: {}"); - let default_master_key_verification_key = db::find_key(&conn, db::DEFAULT_MASTER_KEY_VERIFICATION_KEY)?; - match default_master_key_verification_key { - None => { - let key = Key { - name: db::DEFAULT_MASTER_KEY_VERIFICATION_KEY.to_string(), - encrypted_key: jose::serialize_jwe_aes("LOCAL-MINI-KMS:MAGIC-VERIFICATION-KEY".as_bytes(), &clear_master_key)?, - }; - db::insert_key(&conn, &key)?; - } - Some(key) => { - debugging!("Found jwe: {}", &key.encrypted_key); - let _ = opt_result!(jose::deserialize_jwe_aes(&key.encrypted_key, &clear_master_key), "Deserialize master key verification key failed: {}"); - } - } - information!("Set master key success"); - let sec_bytes = SecBytes::with(clear_master_key.len(), |buf| buf.copy_from_slice(&clear_master_key.as_slice()[..])); - let mut clear_master_key = clear_master_key; - clear_master_key.zeroize(); - k.master_key = Some(sec_bytes); - k.instance_rsa_key_pair = jose::generate_rsa_key(4096)?; - } - Ok((StatusCode::OK, json!({}))) -} - -async fn status() -> Result> { - do_response!(inner_status().await) -} - -async fn inner_status() -> XResult<(StatusCode, Value)> { - let startup_rw_lock = STATUP_RW_LOCK.lock().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()?, - "instance_public_key_pem": String::from_utf8_lossy(&memory_key.instance_rsa_key_pair.to_pem_public_key()).to_string(), - }), - Some(_) => json!({ - "status": "ready", - }), - } - }; - Ok((StatusCode::OK, body)) -} - async fn get_version() -> Result> { Ok(Response::builder().body(format!( "{} - {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") @@ -367,17 +157,3 @@ Supports commands: Ok(Response::builder().body("Root Not Found\n".into())?) } } - -fn get_master_key() -> Option { - let startup_rw_lock = STATUP_RW_LOCK.lock().expect("Lock read startup rw lock error"); - match &*startup_rw_lock { - None => None, - Some(k) => match &k.master_key { - None => None, - Some(k) => { - let k = &*k.read(); - Some(SecBytes::with(k.len(), |buf| buf.copy_from_slice(k))) - } - }, - } -} diff --git a/src/serve_common.rs b/src/serve_common.rs new file mode 100644 index 0000000..5928aa1 --- /dev/null +++ b/src/serve_common.rs @@ -0,0 +1,110 @@ +use std::sync::Mutex; + +use base64::Engine; +use base64::engine::general_purpose::STANDARD; +use josekit::jwk::alg::rsa::RsaKeyPair; +use rusqlite::Connection; +use rust_util::{opt_result, simple_error, XResult}; +use seckey::SecBytes; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; + +use crate::db; + +pub type GenericError = Box; +pub type Result = std::result::Result; + +#[macro_export] +macro_rules! do_response { + ($ex: expr) => ( + match $ex { + Ok((status_code, body)) => Ok(Response::builder().status(status_code).body((serde_json::to_string_pretty(&body)? + "\n").into())?), + Err(e) => Ok(Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body( + format!("{}\n", serde_json::to_string_pretty(&json!({ + "error": "internal_error", + "error_message": format!("{}", e), + }))?).into() + )?), + } + ) +} + +pub struct MemoryKey { + pub database_file: String, + pub instance_rsa_key_pair: RsaKeyPair, + pub master_key: Option, +} + +lazy_static::lazy_static! { + pub static ref STATUP_RW_LOCK: Mutex> = Mutex::new(None); +} + +#[derive(Serialize, Deserialize)] +pub struct MultipleViewValue { + pub value: Option, + pub value_hex: Option, + pub value_base64: Option, +} + +impl MultipleViewValue { + pub fn from(v: &[u8]) -> Self { + Self { + value: Some(String::from_utf8_lossy(v).to_string()), + value_hex: Some(hex::encode(v)), + value_base64: Some(STANDARD.encode(v)), + } + } + + pub fn to_bytes(&self) -> XResult> { + if let Some(v) = &self.value { + Ok(v.as_bytes().to_vec()) + } else if let Some(v) = &self.value_hex { + let v = opt_result!(hex::decode(v), "Decode hex failed: {}"); + Ok(v) + } else if let Some(v) = &self.value_base64 { + let v = opt_result!(STANDARD.decode(v), "Decode base64 failed: {}"); + Ok(v) + } else { + simple_error!("Multiple view value is all empty") + } + } +} + +pub fn get_master_key() -> Option { + let startup_rw_lock = STATUP_RW_LOCK.lock().expect("Lock read startup rw lock error"); + match &*startup_rw_lock { + None => None, + Some(k) => match &k.master_key { + None => None, + Some(k) => { + let k = &*k.read(); + Some(SecBytes::with(k.len(), |buf| buf.copy_from_slice(k))) + } + }, + } +} + +pub fn byte_to_multi_view_map(bytes: &[u8]) -> Map { + let v = MultipleViewValue::from(bytes); + let mut map = Map::new(); + if let Some(v) = &v.value { + map.insert("value".to_string(), Value::String(v.to_string())); + } + if let Some(v) = &v.value_hex { + map.insert("value_hex".to_string(), Value::String(v.to_string())); + } + if let Some(v) = &v.value_base64 { + map.insert("value_base64".to_string(), Value::String(v.to_string())); + } + map +} + +pub fn open_local_db() -> XResult { + let startup_rw_lock = STATUP_RW_LOCK.lock().expect("Lock read startup rw lock error"); + match &*startup_rw_lock { + None => simple_error!("Db is not initted!"), + Some(k) => { + Ok(opt_result!(db::open_db(& k.database_file), "Open db failed: {}")) + } + } +} diff --git a/src/serve_encrypt_decrypt.rs b/src/serve_encrypt_decrypt.rs new file mode 100644 index 0000000..6668c01 --- /dev/null +++ b/src/serve_encrypt_decrypt.rs @@ -0,0 +1,59 @@ +use hyper::{Body, Request, Response, StatusCode}; +use hyper::body::Buf; +use rust_util::{debugging, XResult}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +use crate::do_response; +use crate::jose; +use crate::serve_common::{byte_to_multi_view_map, get_master_key, MultipleViewValue}; +use crate::serve_common::Result; + +#[derive(Serialize, Deserialize)] +struct DecryptRequest { + encrypted_value: String, +} + +pub async fn decrypt(req: Request) -> Result> { + do_response!(inner_decrypt(req).await) +} + +async fn inner_decrypt(req: Request) -> XResult<(StatusCode, Value)> { + let whole_body = hyper::body::aggregate(req).await?; + let data: DecryptRequest = serde_json::from_reader(whole_body.reader())?; + + debugging!("To be decrypted value: {}", &data.encrypted_value); + let key = match get_master_key() { + None => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "status_not_ready" }))), + Some(key) => key, + }; + let decrypted_value = jose::deserialize_jwe_aes(&data.encrypted_value, &*key.read()); + drop(key); + + decrypted_value.map(|v| { + let map = byte_to_multi_view_map(&v.0); + (StatusCode::OK, Value::Object(map)) + }) +} + +pub async fn encrypt(req: Request) -> Result> { + do_response!(inner_encrypt(req).await) +} + +async fn inner_encrypt(req: Request) -> XResult<(StatusCode, Value)> { + let whole_body = hyper::body::aggregate(req).await?; + let data: MultipleViewValue = serde_json::from_reader(whole_body.reader())?; + let value = data.to_bytes()?; + let key = match get_master_key() { + None => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "status_not_ready" }))), + Some(key) => key, + }; + let encrypt_result = jose::serialize_jwe_aes(&value, &*key.read()); + drop(key); + + encrypt_result.map(|e| { + (StatusCode::OK, json!({ + "encrypted_value": e, + })) + }) +} diff --git a/src/serve_init.rs b/src/serve_init.rs new file mode 100644 index 0000000..b32f377 --- /dev/null +++ b/src/serve_init.rs @@ -0,0 +1,85 @@ +use base64::Engine; +use base64::engine::general_purpose::STANDARD; +use hyper::{Body, Request, Response, StatusCode}; +use hyper::body::Buf; +use rust_util::{debugging, information, opt_result, XResult}; +use seckey::SecBytes; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +use zeroize::Zeroize; + +use crate::{db, jose}; +use crate::db::Key; +use crate::do_response; +use crate::serve_common; +use crate::serve_common::Result; + +pub async fn init(req: Request) -> Result> { + do_response!(inner_init(req).await) +} + +#[derive(Serialize, Deserialize)] +struct InitRequest { + clear_master_key_hex: Option, + clear_master_key_base64: Option, + encrypted_master_key: Option, +} + +async fn inner_init(req: Request) -> XResult<(StatusCode, Value)> { + let whole_body = hyper::body::aggregate(req).await?; + let init_request: InitRequest = serde_json::from_reader(whole_body.reader())?; + + let mut startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().expect("Lock read startup rw lock error"); + match &*startup_rw_lock { + None => return Ok((StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " }))), + Some(memory_key) => match memory_key.master_key { + Some(_) => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "already init " }))), + None => {} + }, + } + + let clear_master_key = if let Some(clear_master_key_base64) = &init_request.clear_master_key_base64 { + STANDARD.decode(clear_master_key_base64)? + } else if let Some(clear_master_key_hex) = init_request.clear_master_key_hex { + hex::decode(clear_master_key_hex)? + } else if let Some(encrypted_master_key) = init_request.encrypted_master_key { + debugging!("Received encrypted master key: {}", encrypted_master_key); + if let Some(k) = &*startup_rw_lock { + let (clear_master_key, _) = jose::deserialize_jwe_rsa(&encrypted_master_key, &k.instance_rsa_key_pair.to_jwk_private_key())?; + clear_master_key + } else { + return Ok((StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " }))); + } + } else { + return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "master key is not assigned" }))); + }; + + if clear_master_key.len() != 32 { + return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "bad clear_master_key length" }))); + } + + if let Some(k) = &mut *startup_rw_lock { + let conn = opt_result!(db::open_db(&k.database_file), "Open db failed: {}"); + let default_master_key_verification_key = db::find_key(&conn, db::DEFAULT_MASTER_KEY_VERIFICATION_KEY)?; + match default_master_key_verification_key { + None => { + let key = Key { + name: db::DEFAULT_MASTER_KEY_VERIFICATION_KEY.to_string(), + encrypted_key: jose::serialize_jwe_aes("LOCAL-MINI-KMS:MAGIC-VERIFICATION-KEY".as_bytes(), &clear_master_key)?, + }; + db::insert_key(&conn, &key)?; + } + Some(key) => { + debugging!("Found jwe: {}", &key.encrypted_key); + let _ = opt_result!(jose::deserialize_jwe_aes(&key.encrypted_key, &clear_master_key), "Deserialize master key verification key failed: {}"); + } + } + information!("Set master key success"); + let sec_bytes = SecBytes::with(clear_master_key.len(), |buf| buf.copy_from_slice(&clear_master_key.as_slice()[..])); + let mut clear_master_key = clear_master_key; + clear_master_key.zeroize(); + k.master_key = Some(sec_bytes); + k.instance_rsa_key_pair = jose::generate_rsa_key(4096)?; + } + Ok((StatusCode::OK, json!({}))) +} \ No newline at end of file diff --git a/src/serve_read_write.rs b/src/serve_read_write.rs new file mode 100644 index 0000000..988c1f5 --- /dev/null +++ b/src/serve_read_write.rs @@ -0,0 +1,102 @@ +use hyper::{Body, Request, Response, StatusCode}; +use hyper::body::Buf; +use rust_util::{simple_error, XResult}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +use crate::{db, jose}; +use crate::db::Key; +use crate::do_response; +use crate::serve_common::{byte_to_multi_view_map, get_master_key, MultipleViewValue, open_local_db}; +use crate::serve_common::Result; + +#[derive(Serialize, Deserialize)] +struct Named { + name: String, +} + +#[derive(Serialize, Deserialize)] +struct NamedValue { + name: String, + force_write: Option, + value: MultipleViewValue, +} + +pub async fn read(req: Request) -> Result> { + do_response!(inner_read(req).await) +} + +async fn inner_read(req: Request) -> XResult<(StatusCode, Value)> { + let whole_body = hyper::body::aggregate(req).await?; + let named: Named = serde_json::from_reader(whole_body.reader())?; + let name = &named.name; + if name.is_empty() { + return simple_error!("Key name cannot be empty"); + } + let db_key_name = db::make_db_key_name(name); + + let conn = open_local_db()?; + let db_key = db::find_key(&conn, &db_key_name)?; + + let db_key_value = match db_key { + None => return simple_error!("Key '{}' already exists", name), + Some(k) => k, + }; + + let key = match get_master_key() { + None => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "status_not_ready" }))), + Some(key) => key, + }; + let data = jose::deserialize_jwe_aes(&db_key_value.encrypted_key, &*key.read())?; + drop(key); + + let mut map = byte_to_multi_view_map(&data.0); + map.insert("name".to_string(), Value::String(name.to_string())); + Ok((StatusCode::OK, Value::Object(map))) +} + +pub async fn write(req: Request) -> Result> { + do_response!(inner_write(req).await) +} + +async fn inner_write(req: Request) -> XResult<(StatusCode, Value)> { + let whole_body = hyper::body::aggregate(req).await?; + let named_value: NamedValue = serde_json::from_reader(whole_body.reader())?; + let name = &named_value.name; + if name.is_empty() { + return simple_error!("Key name cannot be empty"); + } + let db_key_name = db::make_db_key_name(name); + let force_write = named_value.force_write.unwrap_or(false); + + let conn = open_local_db()?; + let db_key = db::find_key(&conn, &db_key_name)?; + + if db_key.is_some() && !force_write { + return simple_error!("Key '{}' already exists", name); + } + + let value = named_value.value.to_bytes()?; + let key = match get_master_key() { + None => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "status_not_ready" }))), + Some(key) => key, + }; + let encrypt_value = jose::serialize_jwe_aes(&value, &*key.read())?; + drop(key); + + let new_db_key = Key { + name: db_key_name, + encrypted_key: encrypt_value.clone(), + }; + if db_key.is_some() { + db::update_key(&conn, &new_db_key)?; + } else { + db::insert_key(&conn, &new_db_key)?; + } + + Ok((StatusCode::OK, json!({ + "name": name.to_string(), + "override": db_key.is_some(), + "encrypted_value": encrypt_value, + }))) +} diff --git a/src/serve_status.rs b/src/serve_status.rs new file mode 100644 index 0000000..fc33191 --- /dev/null +++ b/src/serve_status.rs @@ -0,0 +1,29 @@ +use hyper::{Body, Response, StatusCode}; +use rust_util::XResult; +use serde_json::{json, Value}; + +use crate::do_response; +use crate::serve_common; +use crate::serve_common::Result; + +pub async fn status() -> Result> { + do_response!(inner_status().await) +} + +async fn inner_status() -> XResult<(StatusCode, Value)> { + let startup_rw_lock = serve_common::STATUP_RW_LOCK.lock().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()?, + "instance_public_key_pem": String::from_utf8_lossy(&memory_key.instance_rsa_key_pair.to_pem_public_key()).to_string(), + }), + Some(_) => json!({ + "status": "ready", + }), + } + }; + Ok((StatusCode::OK, body)) +} \ No newline at end of file