From ee0c59bbcfb128231ddf337452d2ea086a72916b Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 24 Jul 2022 17:55:08 +0800 Subject: [PATCH] feat: encrypt works --- .gitignore | 1 + Cargo.lock | 7 +++ Cargo.toml | 1 + src/db.rs | 4 +- src/jose.rs | 40 +++--------- src/serve.rs | 172 +++++++++++++++++++++++++++++++++++++++------------ 6 files changed, 151 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 3a4c601..ea846e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +local-mini-kms.db .idea/ # ---> Rust # Generated by Cargo diff --git a/Cargo.lock b/Cargo.lock index ccc05b4..77a55c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,6 +410,7 @@ dependencies = [ "serde_derive", "serde_json", "tokio", + "zeroize", ] [[package]] @@ -993,3 +994,9 @@ name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/Cargo.toml b/Cargo.toml index c0da46f..b03e4f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +zeroize = "1.5.7" clap = "2.33" hex = "0.4" base64 = "0.13.0" diff --git a/src/db.rs b/src/db.rs index 992c6c9..adda78a 100644 --- a/src/db.rs +++ b/src/db.rs @@ -4,8 +4,8 @@ use rust_util::{debugging, information, opt_result, simple_error, success, XResu pub const DEFAULT_MASTER_KEY_VERIFICATION_KEY: &'static str = "__master_verification_key"; pub struct Key { - name: String, - encrypted_key: String, + pub name: String, + pub encrypted_key: String, } pub fn open_db(db: &str) -> XResult { diff --git a/src/jose.rs b/src/jose.rs index 326e63f..0688a1d 100644 --- a/src/jose.rs +++ b/src/jose.rs @@ -2,20 +2,20 @@ 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 josekit::jwk::Jwk; use rust_util::XResult; pub fn generate_rsa_key(bits: u32) -> XResult { Ok(RsaKeyPair::generate(bits)?) } -pub fn serialize_jwe_rsa(payload: &[u8], jwk: &Jwk) -> XResult { - 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 serialize_jwe_rsa(payload: &[u8], jwk: &Jwk) -> XResult { +// 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, JweHeader)> { let decrypter = RsaesJweAlgorithm::RsaOaep.decrypter_from_jwk(jwk)?; @@ -30,28 +30,6 @@ pub fn serialize_jwe_aes(payload: &[u8], key: &[u8]) -> XResult { } pub fn deserialize_jwe_aes(jwe: &str, key: &[u8]) -> XResult<(Vec, 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); + let decrypter = AeskwJweAlgorithm::A256kw.decrypter_from_bytes(key)?; + Ok(jwe::deserialize_compact(jwe, &decrypter)?) } diff --git a/src/serve.rs b/src/serve.rs index cbf8891..57b61af 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -1,22 +1,24 @@ use std::sync::RwLock; use clap::{App, Arg, ArgMatches, SubCommand}; -use hyper::{Body, Client, header, Method, Request, Response, Server, StatusCode}; +use hyper::{Body, Client, Method, Request, Response, Server, StatusCode}; use hyper::body::Buf; use hyper::client::HttpConnector; use hyper::service::{make_service_fn, service_fn}; use josekit::jwk::alg::rsa::RsaKeyPair; -use rust_util::{failure_and_exit, iff, information, success, XResult}; +use josekit::jwk::KeyPair; +use rust_util::{debugging, failure_and_exit, information, opt_result, opt_value_result, simple_error, success, XResult}; use rust_util::util_clap::{Command, CommandError}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use zeroize::Zeroize; -use crate::jose; +use crate::{db, jose}; +use crate::db::Key; type GenericError = Box; type Result = std::result::Result; -static NOTFOUND: &[u8] = b"Not Found\n"; pub struct CommandImpl; @@ -26,10 +28,12 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Local mini KMS serve") .arg(Arg::with_name("listen").long("listen").takes_value(true).default_value("127.0.0.1:5567").help("Listen")) + .arg(Arg::with_name("local-db").long("local-db").takes_value(true).default_value("local-mini-kms.db").help("Local db file")) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - match init_instance() { + let local_mini_kms_db = sub_arg_matches.value_of("local-db").expect("Get local mini kms db error"); + match init_instance(local_mini_kms_db) { 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), @@ -71,13 +75,26 @@ async fn response_requests( (&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())?), + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND).body(serde_json::to_string_pretty(&json!({ "error": "not_found" }))?.into())?), } } +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)?.into())?), + Err(e) => Ok(Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(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>, } @@ -86,12 +103,16 @@ lazy_static::lazy_static! { static ref STATUP_RW_LOCK: RwLock> = RwLock::new(None); } -fn init_instance() -> XResult { +fn init_instance(db: &str) -> XResult { + let conn = db::open_db(db)?; + db::init_db(&conn)?; + 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 { + database_file: db.to_string(), instance_rsa_key_pair: jose::generate_rsa_key(4096)?, master_key: None, }; @@ -101,6 +122,37 @@ fn init_instance() -> 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(base64::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!(base64::decode(v), "Decode base64 failed: {}"); + Ok(v) + } else { + simple_error!("Multiple view value is all empty") + } + } +} + async fn decrypt(req: Request) -> Result> { let whole_body = hyper::body::aggregate(req).await?; let data: serde_json::Value = serde_json::from_reader(whole_body.reader())?; @@ -108,9 +160,26 @@ async fn decrypt(req: Request) -> Result> { } 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: serde_json::Value = serde_json::from_reader(whole_body.reader())?; - Ok(Response::builder().body(format!("{}", data).into())?) + let data: MultipleViewValue = serde_json::from_reader(whole_body.reader())?; + let value = data.to_bytes()?; + let mut key = opt_value_result!( get_master_key(), "Server is not init"); + let encrypt_result = jose::serialize_jwe_aes(&value, &key); + key.zeroize(); + + encrypt_result.map(|e| { + (StatusCode::OK, json!({ + "encrypted_value": e, + })) + }) +} + +async fn init(req: Request) -> Result> { + do_response!(inner_init(req).await) } #[derive(Serialize, Deserialize)] @@ -120,52 +189,65 @@ struct InitRequest { encrypted_master_key: Option, } -async fn init(req: Request) -> Result> { +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.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 " })), + 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(_) => (StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "already init " })), - None => (StatusCode::OK, Value::Null), + Some(_) => return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "already init " }))), + None => {} }, - }; - 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!({})) - } + let clear_master_key = if let Some(clear_master_key_base64) = &init_request.clear_master_key_base64 { + base64::decode(clear_master_key_base64)? } 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!({})) - } + hex::decode(clear_master_key_hex)? } else if let Some(encrypted_master_key) = init_request.encrypted_master_key { - // TODO ... - (StatusCode::BAD_REQUEST, json!({ "error": "not_implement", "error_message": "not_implement" })) + 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 { - (StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "master key is not provided" })) + return Ok((StatusCode::BAD_REQUEST, json!({ "error": "bad_request", "error_message": "master key is not assigned" }))); }; - Ok(Response::builder().status(status_code).body(serde_json::to_string_pretty(&body)?.into())?) + + 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"); + k.master_key = Some(clear_master_key); + } + 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.read().expect("Lock read startup rw lock error"); let body = match &*startup_rw_lock { None => json!({ "status": "n/a" }), @@ -180,7 +262,7 @@ async fn status() -> Result> { }), } }; - Ok(Response::builder().body(serde_json::to_string_pretty(&body)?.into())?) + Ok((StatusCode::OK, body)) } async fn get_version() -> Result> { @@ -188,3 +270,11 @@ async fn get_version() -> Result> { "{} - {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") ).into())?) } + +fn get_master_key() -> Option> { + let startup_rw_lock = STATUP_RW_LOCK.read().expect("Lock read startup rw lock error"); + match &*startup_rw_lock { + None => None, + Some(k) => k.master_key.clone(), + } +}