feat: encrypt works
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
local-mini-kms.db
|
||||
.idea/
|
||||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
|
||||
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<Connection> {
|
||||
|
||||
40
src/jose.rs
40
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<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 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)?;
|
||||
@@ -30,28 +30,6 @@ pub fn serialize_jwe_aes(payload: &[u8], key: &[u8]) -> XResult<String> {
|
||||
}
|
||||
|
||||
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);
|
||||
let decrypter = AeskwJweAlgorithm::A256kw.decrypter_from_bytes(key)?;
|
||||
Ok(jwe::deserialize_compact(jwe, &decrypter)?)
|
||||
}
|
||||
|
||||
172
src/serve.rs
172
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<dyn std::error::Error + Send + Sync>;
|
||||
type Result<T> = std::result::Result<T, GenericError>;
|
||||
|
||||
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<Vec<u8>>,
|
||||
}
|
||||
@@ -86,12 +103,16 @@ lazy_static::lazy_static! {
|
||||
static ref STATUP_RW_LOCK: RwLock<Option<MemoryKey>> = RwLock::new(None);
|
||||
}
|
||||
|
||||
fn init_instance() -> XResult<bool> {
|
||||
fn init_instance(db: &str) -> XResult<bool> {
|
||||
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<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct MultipleViewValue {
|
||||
value: Option<String>,
|
||||
value_hex: Option<String>,
|
||||
value_base64: Option<String>,
|
||||
}
|
||||
|
||||
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<Vec<u8>> {
|
||||
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<Body>) -> Result<Response<Body>> {
|
||||
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<Body>) -> Result<Response<Body>> {
|
||||
}
|
||||
|
||||
async fn encrypt(req: Request<Body>) -> Result<Response<Body>> {
|
||||
do_response!(inner_encrypt(req).await)
|
||||
}
|
||||
|
||||
async fn inner_encrypt(req: Request<Body>) -> 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<Body>) -> Result<Response<Body>> {
|
||||
do_response!(inner_init(req).await)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -120,52 +189,65 @@ struct InitRequest {
|
||||
encrypted_master_key: Option<String>,
|
||||
}
|
||||
|
||||
async fn init(req: Request<Body>) -> Result<Response<Body>> {
|
||||
async fn inner_init(req: Request<Body>) -> 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<Response<Body>> {
|
||||
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<Response<Body>> {
|
||||
}),
|
||||
}
|
||||
};
|
||||
Ok(Response::builder().body(serde_json::to_string_pretty(&body)?.into())?)
|
||||
Ok((StatusCode::OK, body))
|
||||
}
|
||||
|
||||
async fn get_version() -> Result<Response<Body>> {
|
||||
@@ -188,3 +270,11 @@ async fn get_version() -> Result<Response<Body>> {
|
||||
"{} - {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")
|
||||
).into())?)
|
||||
}
|
||||
|
||||
fn get_master_key() -> Option<Vec<u8>> {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user