feat: v0.3.8, remove openssl dependency
This commit is contained in:
12
src/cli.rs
12
src/cli.rs
@@ -1,16 +1,16 @@
|
||||
use std::io::Write;
|
||||
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use hyper::{Body, Client, Method, Request, Response, StatusCode};
|
||||
use hyper::body::Buf;
|
||||
use josekit::jwk::Jwk;
|
||||
use rust_util::{debugging, opt_value_result, simple_error, success, XResult};
|
||||
use hyper::{Body, Client, Method, Request, Response, StatusCode};
|
||||
use rust_util::util_clap::{Command, CommandError};
|
||||
use rust_util::{debugging, opt_value_result, simple_error, success, XResult};
|
||||
use serde_json::{json, Map, Value};
|
||||
|
||||
use crate::jose;
|
||||
use crate::jose::jwk_to_rsa_pubic_key;
|
||||
|
||||
pub struct CommandImpl;
|
||||
|
||||
@@ -209,8 +209,8 @@ fn do_offline_init(_arg_matches: &ArgMatches<'_>, _sub_arg_matches: &ArgMatches<
|
||||
line.as_bytes().to_vec()
|
||||
};
|
||||
let jwk = read_line("Input JWK: ")?;
|
||||
let jwk = Jwk::from_bytes(jwk.as_bytes())?;
|
||||
let encrypted_master_key = jose::serialize_jwe_rsa(&master_key, &jwk)?;
|
||||
let rsa_public_key = jwk_to_rsa_pubic_key(&jwk)?;
|
||||
let encrypted_master_key = jose::serialize_jwe_rsa(&master_key, &rsa_public_key)?;
|
||||
|
||||
success!("Encrypted master key: {}", encrypted_master_key);
|
||||
Ok(Some(0))
|
||||
|
||||
194
src/jose.rs
194
src/jose.rs
@@ -1,44 +1,184 @@
|
||||
use josekit::jwe;
|
||||
use josekit::jwe::alg::aeskw::AeskwJweAlgorithm;
|
||||
use josekit::jwe::alg::rsaes::RsaesJweAlgorithm;
|
||||
use josekit::jwe::JweHeader;
|
||||
use josekit::jwk::alg::rsa::RsaKeyPair;
|
||||
use josekit::jwk::Jwk;
|
||||
use rust_util::XResult;
|
||||
use serde_json::Value;
|
||||
use aes_gcm_stream::{Aes256GcmStreamDecryptor, Aes256GcmStreamEncryptor};
|
||||
use aes_kw::Kek;
|
||||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||
use base64::Engine;
|
||||
use jose_jwk::{Key, Rsa};
|
||||
use rand::{random, thread_rng};
|
||||
use rsa::pkcs1::LineEnding;
|
||||
use rsa::pkcs8::EncodePublicKey;
|
||||
use rsa::{Oaep, RsaPrivateKey, RsaPublicKey};
|
||||
use rust_util::{opt_result, simple_error, XResult};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
const LOCAL_KMS_PREFIX: &str = "LKMS:";
|
||||
const JWE_ENC_A256GCM: &str = "A256GCM";
|
||||
const JWE_ALG_A256KW: &str = "A256KW";
|
||||
const JWE_ALG_RSA_OAEP: &str = "RSA-OAEP";
|
||||
const JWE_DOT: &str = ".";
|
||||
|
||||
pub fn generate_rsa_key(bits: u32) -> XResult<RsaKeyPair> {
|
||||
Ok(RsaKeyPair::generate(bits)?)
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct JweHeader {
|
||||
pub enc: String,
|
||||
pub alg: String,
|
||||
pub vendor: String,
|
||||
pub version: Option<String>,
|
||||
}
|
||||
|
||||
pub fn serialize_jwe_rsa(payload: &[u8], jwk: &Jwk) -> XResult<String> {
|
||||
let mut header = JweHeader::new();
|
||||
header.set_content_encryption("A256GCM");
|
||||
header.set_claim("vendor", Some(Value::String("local-mini-kms".to_string())))?;
|
||||
let encrypter = RsaesJweAlgorithm::RsaOaep.encrypter_from_jwk(jwk)?;
|
||||
Ok(format!("{}{}", LOCAL_KMS_PREFIX, jwe::serialize_compact(payload, &header, &encrypter)?))
|
||||
pub fn generate_rsa_key(bits: u32) -> XResult<RsaPrivateKey> {
|
||||
let mut rng = thread_rng();
|
||||
Ok(RsaPrivateKey::new(&mut rng, bits as usize)?)
|
||||
}
|
||||
|
||||
pub fn deserialize_jwe_rsa(jwe: &str, jwk: &Jwk) -> XResult<(Vec<u8>, JweHeader)> {
|
||||
let decrypter = RsaesJweAlgorithm::RsaOaep.decrypter_from_jwk(jwk)?;
|
||||
Ok(jwe::deserialize_compact(&get_jwe(jwe), &decrypter)?)
|
||||
pub fn rsa_key_to_jwk(rsa_private_key: &RsaPrivateKey) -> XResult<jose_jwk::Jwk> {
|
||||
let rsa_public_key = rsa_private_key.as_ref();
|
||||
let public_rsa: Rsa = rsa_public_key.into();
|
||||
Ok(jose_jwk::Jwk {
|
||||
key: Key::Rsa(public_rsa),
|
||||
prm: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn rsa_key_to_pem(rsa_private_key: &RsaPrivateKey) -> XResult<String> {
|
||||
Ok(rsa_private_key.to_public_key().to_public_key_pem(LineEnding::LF)?)
|
||||
}
|
||||
|
||||
pub fn jwk_to_rsa_pubic_key(rsa_jwk: &str) -> XResult<RsaPublicKey> {
|
||||
let rsa: Rsa = opt_result!(serde_json::from_str(&rsa_jwk), "Bad RSA JWK: {}, error: {}", rsa_jwk);
|
||||
let rsa_public_key = opt_result!(RsaPublicKey::try_from(rsa), "Bad RSA JWK: {}, error: {:?}", rsa_jwk);
|
||||
Ok(rsa_public_key)
|
||||
}
|
||||
|
||||
pub fn serialize_jwe_rsa(payload: &[u8], rsa_public_key: &RsaPublicKey) -> XResult<String> {
|
||||
let header = JweHeader {
|
||||
enc: JWE_ENC_A256GCM.to_string(),
|
||||
alg: JWE_ALG_RSA_OAEP.to_string(),
|
||||
vendor: "local-mini-kms".to_string(),
|
||||
version: None,
|
||||
};
|
||||
serialize_jwe_fn(&header, payload, |data_key| -> XResult<Vec<u8>> {
|
||||
let mut r = thread_rng();
|
||||
Ok(opt_result!(rsa_public_key.encrypt(&mut r, Oaep::new::<Sha1>(), data_key), "Wrap key failed: {}"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deserialize_jwe_rsa(jwe: &str, rsa: &RsaPrivateKey) -> XResult<(Vec<u8>, JweHeader)> {
|
||||
deserialize_jwe_fn(jwe, |alg, key_wrap| -> XResult<Vec<u8>> {
|
||||
if alg != JWE_ALG_RSA_OAEP {
|
||||
return simple_error!("Invalid JWE header alg: {}", alg);
|
||||
}
|
||||
Ok(opt_result!(rsa.decrypt(Oaep::new::<Sha1>(), &key_wrap), "Unwrap key failed: {}"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn serialize_jwe_aes(payload: &[u8], key: &[u8]) -> XResult<String> {
|
||||
let mut header = JweHeader::new();
|
||||
header.set_content_encryption("A256GCM");
|
||||
header.set_claim("vendor", Some(Value::String("local-mini-kms".to_string())))?;
|
||||
header.set_claim("version", Some(Value::String(get_master_key_checksum(key))))?;
|
||||
let encrypter = AeskwJweAlgorithm::A256kw.encrypter_from_bytes(key)?;
|
||||
Ok(format!("{}{}", LOCAL_KMS_PREFIX, jwe::serialize_compact(payload, &header, &encrypter)?))
|
||||
serialize_jwe_aes_32(payload, to_bytes32(key)?)
|
||||
}
|
||||
|
||||
pub fn serialize_jwe_aes_32(payload: &[u8], key: [u8; 32]) -> XResult<String> {
|
||||
let header = JweHeader {
|
||||
enc: JWE_ENC_A256GCM.to_string(),
|
||||
alg: JWE_ALG_A256KW.to_string(),
|
||||
vendor: "local-mini-kms".to_string(),
|
||||
version: Some(get_master_key_checksum(&key)),
|
||||
};
|
||||
serialize_jwe_fn(&header, payload, |data_key| -> XResult<Vec<u8>> {
|
||||
let kek = Kek::from(key);
|
||||
Ok(opt_result!(kek.wrap_vec(&data_key[..]), "Wrap key failed: {}"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn deserialize_jwe_aes(jwe: &str, key: &[u8]) -> XResult<(Vec<u8>, JweHeader)> {
|
||||
let decrypter = AeskwJweAlgorithm::A256kw.decrypter_from_bytes(key)?;
|
||||
Ok(jwe::deserialize_compact(&get_jwe(jwe), &decrypter)?)
|
||||
deserialize_jwe_aes_32(jwe, to_bytes32(key)?)
|
||||
}
|
||||
|
||||
pub fn deserialize_jwe_aes_32(jwe: &str, key: [u8; 32]) -> XResult<(Vec<u8>, JweHeader)> {
|
||||
deserialize_jwe_fn(jwe, |alg, key_wrap| -> XResult<Vec<u8>> {
|
||||
if alg != JWE_ALG_A256KW {
|
||||
return simple_error!("Invalid JWE header alg: {}", alg);
|
||||
}
|
||||
let kek = Kek::from(key);
|
||||
Ok(opt_result!(kek.unwrap_vec(&key_wrap), "Unwrap key failed: {}"))
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_jwe_fn<F>(header: &JweHeader, payload: &[u8], key_wrap_fn: F) -> XResult<String>
|
||||
where
|
||||
F: Fn(&[u8]) -> XResult<Vec<u8>>,
|
||||
{
|
||||
let header_str = opt_result!(serde_json::to_string(&header), "Invalid JWE header: {}");
|
||||
let header_b64 = URL_SAFE_NO_PAD.encode(header_str.as_bytes());
|
||||
|
||||
let data_key: [u8; 32] = random();
|
||||
let iv: [u8; 12] = random();
|
||||
let mut encryptor = Aes256GcmStreamEncryptor::new(data_key, &iv);
|
||||
encryptor.init_adata(header_b64.as_bytes());
|
||||
let mut ciphertext = encryptor.update(payload);
|
||||
let (ciphertext_final, tag) = encryptor.finalize();
|
||||
ciphertext.extend_from_slice(&ciphertext_final);
|
||||
|
||||
let cek = key_wrap_fn(&data_key)?;
|
||||
|
||||
Ok(format!(
|
||||
"{}{}.{}.{}.{}.{}",
|
||||
LOCAL_KMS_PREFIX,
|
||||
header_b64,
|
||||
URL_SAFE_NO_PAD.encode(&cek),
|
||||
URL_SAFE_NO_PAD.encode(&iv),
|
||||
URL_SAFE_NO_PAD.encode(&ciphertext),
|
||||
URL_SAFE_NO_PAD.encode(&tag)
|
||||
))
|
||||
}
|
||||
|
||||
fn deserialize_jwe_fn<F>(jwe: &str, key_unwrap_fn: F) -> XResult<(Vec<u8>, JweHeader)>
|
||||
where
|
||||
F: Fn(&str, &[u8]) -> XResult<Vec<u8>>,
|
||||
{
|
||||
let jwe = get_jwe(jwe);
|
||||
let jwe_parts = jwe.split(JWE_DOT).collect::<Vec<&str>>();
|
||||
if jwe_parts.len() != 5 {
|
||||
return simple_error!("Invalid JWE: {}", jwe);
|
||||
}
|
||||
let header_bytes = opt_result!(decode_url_safe_no_pad(jwe_parts[0]), "Invalid JWE header: {}, JWE: {}", jwe);
|
||||
let header: JweHeader = opt_result!(serde_json::from_slice(&header_bytes), "Invalid JWE header: {}, JWE: {}", jwe);
|
||||
if header.enc != JWE_ENC_A256GCM {
|
||||
return simple_error!("Invalid JWE header enc: {}", header.enc);
|
||||
}
|
||||
|
||||
let cek = opt_result!(decode_url_safe_no_pad(jwe_parts[1]), "Invalid JWE CEK: {}, JWE: {}", jwe);
|
||||
let iv = opt_result!(decode_url_safe_no_pad(jwe_parts[2]), "Invalid JWE IV: {}, JWE: {}", jwe);
|
||||
let ciphertext = opt_result!(decode_url_safe_no_pad(jwe_parts[3]), "Invalid JWE ciphertext: {}, JWE: {}", jwe);
|
||||
let tag = opt_result!(decode_url_safe_no_pad(jwe_parts[4]), "Invalid JWE tag: {}, JWE: {}", jwe);
|
||||
|
||||
let data_key = key_unwrap_fn(&header.alg, &cek)?;
|
||||
let data_key_b32 = opt_result!(to_bytes32(&data_key), "Invalid JWE CEK: {}, JWE: {}", jwe);
|
||||
|
||||
let mut decryptor = Aes256GcmStreamDecryptor::new(data_key_b32, &iv);
|
||||
decryptor.init_adata(jwe_parts[0].as_bytes());
|
||||
let mut plaintext = decryptor.update(&ciphertext);
|
||||
let plaintext_2 = decryptor.update(&tag);
|
||||
let plaintext_final = opt_result!(decryptor.finalize(), "Invalid JWE: {}, JWE: {}", jwe);
|
||||
plaintext.extend_from_slice(&plaintext_2);
|
||||
plaintext.extend_from_slice(&plaintext_final);
|
||||
Ok((plaintext, header))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn decode_url_safe_no_pad(s: &str) -> XResult<Vec<u8>> {
|
||||
Ok(URL_SAFE_NO_PAD.decode(s.as_bytes())?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_bytes32(bytes: &[u8]) -> XResult<[u8; 32]> {
|
||||
if bytes.len() != 32 {
|
||||
return simple_error!("Not valid 32 bytes");
|
||||
}
|
||||
let mut ret = [0; 32];
|
||||
for i in 0..32 {
|
||||
ret[i] = bytes[i];
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn get_master_key_checksum(key: &[u8]) -> String {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Mutex;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
use hyper::StatusCode;
|
||||
use josekit::jwk::alg::rsa::RsaKeyPair;
|
||||
use rsa::RsaPrivateKey;
|
||||
use rusqlite::Connection;
|
||||
use rust_util::{opt_result, simple_error, XResult};
|
||||
use seckey::SecBytes;
|
||||
@@ -50,7 +50,7 @@ pub fn error(error: &str) -> XResult<(StatusCode, Value)> {
|
||||
|
||||
pub struct MemoryKey {
|
||||
pub database_file: String,
|
||||
pub instance_rsa_key_pair: RsaKeyPair,
|
||||
pub instance_rsa_key_pair: RsaPrivateKey,
|
||||
pub master_key: Option<SecBytes>,
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ pub async fn inner_init_request(init_request: InitRequest) -> XResult<(StatusCod
|
||||
} else if let Some(encrypted_master_key) = init_request.encrypted_master_key {
|
||||
debugging!("Received encrypted master key: {}", encrypted_master_key);
|
||||
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())?;
|
||||
let (clear_master_key, _) = jose::deserialize_jwe_rsa(&encrypted_master_key, &k.instance_rsa_key_pair)?;
|
||||
clear_master_key
|
||||
} else {
|
||||
return Ok((StatusCode::INTERNAL_SERVER_ERROR, json!({ "error": "internal_error", "error_message": "not init " })));
|
||||
|
||||
@@ -3,6 +3,7 @@ use rust_util::XResult;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::do_response;
|
||||
use crate::jose::{rsa_key_to_jwk, rsa_key_to_pem};
|
||||
use crate::serve_common::{self, Result};
|
||||
|
||||
pub async fn status() -> Result<Response<Body>> {
|
||||
@@ -16,8 +17,8 @@ async fn inner_status() -> XResult<(StatusCode, Value)> {
|
||||
Some(memory_key) => match memory_key.master_key {
|
||||
None => json!({
|
||||
"status": "not-ready",
|
||||
"instance_public_key_jwk": memory_key.instance_rsa_key_pair.to_jwk_key_pair().to_public_key()?,
|
||||
"instance_public_key_pem": String::from_utf8_lossy(&memory_key.instance_rsa_key_pair.to_pem_public_key()).to_string(),
|
||||
"instance_public_key_jwk": rsa_key_to_jwk(&memory_key.instance_rsa_key_pair)?,
|
||||
"instance_public_key_pem": rsa_key_to_pem(&memory_key.instance_rsa_key_pair)?,
|
||||
}),
|
||||
Some(_) => json!({
|
||||
"status": "ready",
|
||||
|
||||
Reference in New Issue
Block a user