feat: v0.3.6, add generate aes(128,192,256 bit) datakey
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
77
src/serve_datakey.rs
Normal 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
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user