Files
local-mini-kms/src/serve_datakey.rs

91 lines
3.7 KiB
Rust

use crate::db::Key;
use crate::serve_common::{open_local_db, Result};
use crate::{db, do_response, jose, require_master_key, 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::{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 {
r#type: String,
spec: String,
name: Option<String>,
comment: Option<String>,
exportable: Option<bool>,
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())?;
log::debug!("Generate data key: {} {}", &request.r#type, &request.spec);
let key = require_master_key!();
let exportable = request.exportable.unwrap_or(true);
let ret_key_plaintext = iff!(!exportable, false, request.return_plaintext.unwrap_or(false));
let response_result = match (request.r#type.as_str(), request.spec.as_str()) {
// ("aes", "128") => generate_aes("datakey:aes-128", exportable, key, 16, ret_key_plaintext),
// ("aes", "192") => generate_aes("datakey:aes-192", exportable, key, 24, ret_key_plaintext),
("aes", "256") => generate_aes("datakey:aes-256", exportable, key, 32, ret_key_plaintext),
// TODO rsa 2048, rsa 3072, rsa 4096
// TODO ec p256, p384, p521, ed25519, cv25519
_ => return serve_common::client_error("invalid key_type or key_spec"),
};
match response_result {
Err(e) => serve_common::server_error(&format!("internal error: {}", e)),
Ok((key_plaintext, key_ciphertext)) => {
let mut map = Map::new();
map.insert("key_type".to_string(), Value::String(request.r#type));
map.insert("key_spec".to_string(), Value::String(request.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.clone()));
if let Some(name) = &request.name {
if name.is_empty() {
return serve_common::client_error("name_is_empty");
}
let conn = open_local_db()?;
let db_key_name = db::make_data_key_name(name);
let db_key = db::find_key(&conn, &db_key_name)?;
if db_key.is_some() {
return serve_common::client_error("name_exists");
}
let key = Key {
name: db_key_name,
encrypted_key: key_ciphertext,
comment: request.comment,
};
db::insert_key(&conn, &key)?;
}
Ok((StatusCode::OK, Value::Object(map)))
}
}
}
fn generate_aes(data_key_type: &str, exportable: bool, 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_with_data_type(data_key_type, exportable, value, &key.read())?;
Ok((key_plaintext, key_ciphertext))
}