feat: v0.3.6, add generate aes(128,192,256 bit) datakey

This commit is contained in:
2024-09-04 00:14:59 +08:00
parent b188a2bc1e
commit b1121ffeeb
10 changed files with 113 additions and 18 deletions

2
Cargo.lock generated
View File

@@ -742,7 +742,7 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "local-mini-kms"
version = "0.3.5"
version = "0.3.6"
dependencies = [
"base64 0.21.7",
"clap",

View File

@@ -1,6 +1,6 @@
[package]
name = "local-mini-kms"
version = "0.3.5"
version = "0.3.6"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -67,6 +67,13 @@ curl -X POST http://127.0.0.1:5567/read \
-d '{"name":"test"}'
```
Generate data key:
```shell
curl -X POST http://127.0.0.1:5567/datakey \
-H "Content-Type: application/json" \
-d '{"key_type":"aes", "key_spec":"256", "return_plaintext": true}'
```
Upgrade to v3.2
```sql
ALTER TABLE keys ADD COLUMN comment TEXT;

View File

@@ -14,6 +14,7 @@ mod serve_init;
mod serve_encrypt_decrypt;
mod serve_read_write;
mod yubikey_init_master_key;
mod serve_datakey;
pub struct DefaultCommandImpl;

View File

@@ -10,7 +10,6 @@ use rust_util::{failure_and_exit, information, success, warning, XResult};
use serde_json::{json, Value};
use tokio::runtime::Runtime;
use crate::do_response;
use crate::serve_common::{self, GenericError, MemoryKey, Result};
use crate::serve_encrypt_decrypt;
use crate::serve_init;
@@ -19,6 +18,7 @@ use crate::serve_read_write;
use crate::serve_status;
use crate::yubikey_hmac;
use crate::{db, jose, proc};
use crate::{do_response, serve_datakey};
pub struct CommandImpl;
@@ -94,6 +94,7 @@ async fn response_requests(
(&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::POST, "/datakey") => serve_datakey::generate(req).await,
(&Method::GET, "/status") => serve_status::status().await,
(&Method::GET, "/version") => get_version().await,
(&Method::GET, "/") => get_root().await,

View File

@@ -1,7 +1,7 @@
use std::sync::Mutex;
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use hyper::StatusCode;
use josekit::jwk::alg::rsa::RsaKeyPair;
use rusqlite::Connection;
@@ -74,6 +74,14 @@ impl MultipleViewValue {
}
}
pub fn from_without_value(v: &[u8]) -> Self {
Self {
value: None,
value_hex: Some(hex::encode(v)),
value_base64: Some(STANDARD.encode(v)),
}
}
pub fn to_bytes(&self) -> XResult<Vec<u8>> {
if let Some(v) = &self.value {
Ok(v.as_bytes().to_vec())
@@ -103,8 +111,12 @@ pub fn get_master_key() -> Option<SecBytes> {
}
}
pub fn byte_to_multi_view_map(bytes: &[u8]) -> Map<String, Value> {
let v = MultipleViewValue::from(bytes);
pub fn byte_to_multi_view_map(bytes: &[u8], with_value: bool) -> Map<String, Value> {
let v = if with_value {
MultipleViewValue::from(bytes)
} else {
MultipleViewValue::from_without_value(bytes)
};
let mut map = Map::new();
if let Some(v) = &v.value {
map.insert("value".to_string(), Value::String(v.to_string()));

77
src/serve_datakey.rs Normal file
View File

@@ -0,0 +1,77 @@
use crate::serve_common::{get_master_key, Result};
use crate::{do_response, jose, serve_common};
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use hyper::body::Buf;
use hyper::{Body, Request, Response, StatusCode};
use rand::random;
use rust_util::{debugging, iff, XResult};
use seckey::SecBytes;
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use serde_json::{Map, Value};
// type:aes, spec:128,192,256
// type:rsa, spec:2048,3072,4096
// type:ec, spec:p256,p384,p521,ed25519,cv25519
#[derive(Serialize, Deserialize)]
struct DataKeyRequest {
key_type: String,
key_spec: String,
return_plaintext: Option<bool>,
}
pub async fn generate(req: Request<Body>) -> Result<Response<Body>> {
do_response!(inner_generate(req).await)
}
async fn inner_generate(req: Request<Body>) -> XResult<(StatusCode, Value)> {
let whole_body = hyper::body::aggregate(req).await?;
let request: DataKeyRequest = serde_json::from_reader(whole_body.reader())?;
debugging!("Generate data key: {} {}", &request.key_type, &request.key_spec);
let key = match get_master_key() {
None => return serve_common::error("status_not_ready"),
Some(key) => key,
};
let ret_key_plaintext = request.return_plaintext.unwrap_or(false);
let response_result = match (request.key_type.as_str(), request.key_spec.as_str()) {
("aes", "128") => generate_aes("datakey.aes_128:", key, 16, ret_key_plaintext),
("aes", "192") => generate_aes("datakey.aes_192:", key, 24, ret_key_plaintext),
("aes", "256") => generate_aes("datakey.aes_256:", key, 32, ret_key_plaintext),
// TODO rsa 2048, rsa 3072, rsa 4096
// TODO ec p256, p384, p521, ed25519, cv25519
_ => return serve_common::error("invalid key_type or key_spec"),
};
match response_result {
Err(e) => serve_common::error(&format!("internal error: {}", e)),
Ok((key_plaintext, key_ciphertext)) => {
let mut map = Map::new();
map.insert("key_type".to_string(), Value::String(request.key_type));
map.insert("key_spec".to_string(), Value::String(request.key_spec));
if let Some(key_plaintext) = key_plaintext {
map.insert("key_plaintext".to_string(), Value::String(STANDARD.encode(&key_plaintext)));
}
map.insert("key_ciphertext".to_string(), Value::String(key_ciphertext));
Ok((StatusCode::OK, Value::Object(map)))
}
}
}
fn generate_aes(prefix: &str, key: SecBytes, len: i32, ret_key_plaintext: bool) -> XResult<(Option<Vec<u8>>, String)> {
let bytes: [u8; 32] = random();
let value = &bytes[0..len as usize];
let key_plaintext = iff!(ret_key_plaintext, Some(value.to_vec()), None);
let key_ciphertext = jose::serialize_jwe_aes(&join_prefix_value(prefix, value), &key.read())?;
Ok((key_plaintext, key_ciphertext))
}
fn join_prefix_value(prefix: &str, value: &[u8]) -> Vec<u8> {
let mut ret = Vec::with_capacity(prefix.len() + value.len());
ret.extend_from_slice(prefix.as_bytes());
ret.extend_from_slice(value);
ret
}

View File

@@ -13,9 +13,7 @@ struct DecryptRequest {
encrypted_value: String,
}
pub
async fn decrypt(req: Request<Body>) -> Result<Response<Body>> {
pub async fn decrypt(req: Request<Body>) -> Result<Response<Body>> {
do_response!(inner_decrypt(req).await)
}
@@ -32,7 +30,7 @@ async fn inner_decrypt(req: Request<Body>) -> XResult<(StatusCode, Value)> {
drop(key);
decrypted_value.map(|v| {
let map = byte_to_multi_view_map(&v.0);
let map = byte_to_multi_view_map(&v.0, true);
(StatusCode::OK, Value::Object(map))
})
}

View File

@@ -1,14 +1,13 @@
use hyper::{Body, Request, Response, StatusCode};
use crate::db::Key;
use crate::do_response;
use crate::serve_common::{self, byte_to_multi_view_map, get_master_key, open_local_db, MultipleViewValue, Result};
use crate::{db, jose};
use hyper::body::Buf;
use hyper::{Body, Request, Response, StatusCode};
use rust_util::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::{self, byte_to_multi_view_map, get_master_key, MultipleViewValue, open_local_db, Result};
#[derive(Serialize, Deserialize)]
struct Named {
name: String,
@@ -50,7 +49,7 @@ async fn inner_read(req: Request<Body>) -> XResult<(StatusCode, Value)> {
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);
let mut map = byte_to_multi_view_map(&data.0, true);
map.insert("name".to_string(), name.as_str().into());
map.insert("comment".to_string(), db_key_value.comment.into());
serve_common::ok(Value::Object(map))

View File

@@ -34,7 +34,7 @@ impl Command for CommandImpl {
opt_result!(STANDARD.decode(base64_value), "Decode key-base64 failed: {}")
} else {
let clear_master_key: [u8; 32] = random();
success!("Clear master key generated: {}", hex::encode(&clear_master_key));
success!("Clear master key generated: {}", hex::encode(clear_master_key));
clear_master_key.to_vec()
};