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, comment: Option, exportable: Option, return_plaintext: Option, } pub async fn generate(req: Request) -> Result> { do_response!(inner_generate(req).await) } async fn inner_generate(req: Request) -> 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>, 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)) }