diff --git a/__crypto/jose-test/src/jose.rs b/__crypto/jose-test/src/jose.rs index 6c7795e..5760c6e 100644 --- a/__crypto/jose-test/src/jose.rs +++ b/__crypto/jose-test/src/jose.rs @@ -9,9 +9,9 @@ use josekit::jwe::JweHeader; use josekit::jwk::alg::rsa::RsaKeyPair; use josekit::jwk::Jwk; use rand::random; -use rand::rngs::OsRng; -use rsa::{Oaep, RsaPrivateKey}; -use rust_util::XResult; +use rand::rngs::{OsRng, ThreadRng}; +use rsa::{Oaep, RsaPrivateKey, RsaPublicKey}; +use rust_util::{opt_result, simple_error, XResult}; use serde::{Deserialize, Serialize}; use serde_json::Value; use sha1::Sha1; @@ -48,59 +48,6 @@ const LOCAL_KMS_PREFIX: &str = "LKMS:"; // A256GCM Advanced Encryption Standard (AES) using 256 bit keys in Galois/Counter Mode, as defined in [FIPS‑197] and [NIST‑800‑38D] // A256KW Advanced Encryption Standard (AES) Key Wrap Algorithm using 256 bit keys, as defined in RFC 3394 [RFC3394] -pub fn generate_rsa_key_2(bits: u32) -> XResult { - let mut rng = OsRng::default(); - Ok(RsaPrivateKey::new(&mut rng, bits as usize)?) -} - -pub fn generate_rsa_key(bits: u32) -> XResult { - Ok(RsaKeyPair::generate(bits)?) -} - -pub fn serialize_jwe_rsa_2(payload: &[u8], jwk: &Jwk) { - // TODO ... -} - -pub fn serialize_jwe_rsa(payload: &[u8], jwk: &Jwk) -> XResult { - 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 deserialize_jwe_rsa_2(jwe: &str, rsa: &RsaPrivateKey) -> XResult<(Vec, JweHeader2)> { - let jwe_parts = jwe.split(".").collect::>(); - if jwe_parts.len() != 5 { - panic!("Invalid jwe"); - } - let header_bytes = URL_SAFE_NO_PAD.decode(jwe_parts[0].as_bytes()).unwrap(); - let header: JweHeader2 = serde_json::from_slice(&header_bytes).unwrap(); - println!("{:?}", jwe_parts); - println!("{:?}", header); - let key_wrap = URL_SAFE_NO_PAD.decode(jwe_parts[1].as_bytes()).unwrap(); - let nonce = URL_SAFE_NO_PAD.decode(jwe_parts[2].as_bytes()).unwrap(); - let ciphertext = URL_SAFE_NO_PAD.decode(jwe_parts[3].as_bytes()).unwrap(); - let tag = URL_SAFE_NO_PAD.decode(jwe_parts[4].as_bytes()).unwrap(); - - let data_key = rsa.decrypt(Oaep::new::(), &key_wrap).unwrap(); - let data_key_b32 = bytes_to_32(&data_key); - - let mut decryptor = Aes256GcmStreamDecryptor::new(data_key_b32, &nonce); - decryptor.init_adata(jwe_parts[0].as_bytes()); - let mut p1 = decryptor.update(&ciphertext); - let p2 = decryptor.update(&tag); - let pf = decryptor.finalize().unwrap(); - p1.extend_from_slice(&p2); - p1.extend_from_slice(&pf); - Ok((p1, header)) -} - -pub fn deserialize_jwe_rsa(jwe: &str, jwk: &Jwk) -> XResult<(Vec, JweHeader)> { - let decrypter = RsaesJweAlgorithm::RsaOaep.decrypter_from_jwk(jwk)?; - Ok(jwe::deserialize_compact(&get_jwe(jwe), &decrypter)?) -} - // JWE Header: {"alg":"dir","enc":"A256GCM"} // Encrypted key (CEK): (blank) // IV: Vlf_WdLm-spHbfJe @@ -115,6 +62,46 @@ pub fn deserialize_jwe_rsa(jwe: &str, jwk: &Jwk) -> XResult<(Vec, JweHeader) // // https://security.stackexchange.com/questions/80966/what-is-the-point-of-aes-key-wrap-with-json-web-encryption +pub fn generate_rsa_key_2(bits: u32) -> XResult { + let mut rng = OsRng::default(); + Ok(RsaPrivateKey::new(&mut rng, bits as usize)?) +} + +pub fn generate_rsa_key(bits: u32) -> XResult { + Ok(RsaKeyPair::generate(bits)?) +} + +pub fn serialize_jwe_rsa_2(payload: &[u8], rsa_public_key: &RsaPublicKey) -> XResult { + let header = JweHeader2 { + enc: "A256GCM".to_string(), + alg: "RSA-OAEP".to_string(), + vendor: "local-mini-kms".to_string(), + }; + serialize_jwe_fn(&header, payload, |data_key| -> XResult> { + let mut r = ThreadRng::default(); + Ok(opt_result!(rsa_public_key.encrypt(&mut r, Oaep::new::(), data_key), "Wrap key failed: {}")) + }) +} + +pub fn serialize_jwe_rsa(payload: &[u8], jwk: &Jwk) -> XResult { + 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 deserialize_jwe_rsa_2(jwe: &str, rsa: &RsaPrivateKey) -> XResult<(Vec, JweHeader2)> { + deserialize_jwe_fn(jwe, |key_wrap| -> XResult<(Vec)> { + Ok(opt_result!(rsa.decrypt(Oaep::new::(), &key_wrap), "Unwrap key failed: {}")) + }) +} + +pub fn deserialize_jwe_rsa(jwe: &str, jwk: &Jwk) -> XResult<(Vec, JweHeader)> { + let decrypter = RsaesJweAlgorithm::RsaOaep.decrypter_from_jwk(jwk)?; + Ok(jwe::deserialize_compact(&get_jwe(jwe), &decrypter)?) +} + #[derive(Debug, Serialize, Deserialize)] pub struct JweHeader2 { pub enc: String, @@ -128,9 +115,39 @@ pub fn serialize_jwe_aes_2(payload: &[u8], key: [u8; 32]) -> XResult { alg: "A256KW".to_string(), vendor: "local-mini-kms".to_string(), }; + serialize_jwe_fn(&header, payload, |data_key| -> XResult> { + let kek = Kek::from(key); + Ok(opt_result!(kek.wrap_vec(&data_key[..]), "Wrap key failed: {}")) + }) +} + +pub fn serialize_jwe_aes(payload: &[u8], key: &[u8]) -> XResult { + 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)?)) +} + +pub fn deserialize_jwe_aes_2(jwe: &str, key: [u8; 32]) -> XResult<(Vec, JweHeader2)> { + deserialize_jwe_fn(jwe, |key_wrap| -> XResult<(Vec)> { + let kek = Kek::from(key); + Ok(opt_result!(kek.unwrap_vec(&key_wrap), "Unwrap key failed: {}")) + }) +} + +pub fn deserialize_jwe_aes(jwe: &str, key: &[u8]) -> XResult<(Vec, JweHeader)> { + let decrypter = AeskwJweAlgorithm::A256kw.decrypter_from_bytes(key)?; + Ok(jwe::deserialize_compact(&get_jwe(jwe), &decrypter)?) +} + +fn serialize_jwe_fn(header: &JweHeader2, payload: &[u8], key_wrap_fn: F) -> XResult +where + F: Fn(&[u8]) -> XResult>, +{ let header_str = serde_json::to_string(&header).unwrap(); let header_b64 = URL_SAFE_NO_PAD.encode(header_str.as_bytes()); - // let key: [u8; 32] = key.into(); let data_key: [u8; 32] = random(); let nonce: [u8; 12] = random(); @@ -140,8 +157,7 @@ pub fn serialize_jwe_aes_2(payload: &[u8], key: [u8; 32]) -> XResult { let (f, t) = encryptor.finalize(); e.extend_from_slice(&f); - let kek = Kek::from(key); - let wrap_key = kek.wrap_vec(&data_key[..]).unwrap(); + let wrap_key = key_wrap_fn(&data_key)?; let mut jwe = String::new(); jwe.push_str(&header_b64); @@ -157,59 +173,52 @@ pub fn serialize_jwe_aes_2(payload: &[u8], key: [u8; 32]) -> XResult { Ok(jwe) } -pub fn serialize_jwe_aes(payload: &[u8], key: &[u8]) -> XResult { - 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)?)) -} - -pub fn deserialize_jwe_aes_2(jwe: &str, key: [u8; 32]) -> XResult<(Vec, JweHeader2)> { +fn deserialize_jwe_fn(jwe: &str, key_unwrap_fn: F) -> XResult<(Vec, JweHeader2)> +where + F: Fn(&[u8]) -> XResult>, +{ let jwe_parts = jwe.split(".").collect::>(); if jwe_parts.len() != 5 { - panic!("Invalid jwe"); + return simple_error!("Invalid JWE: {}", jwe); } - let header_bytes = URL_SAFE_NO_PAD.decode(jwe_parts[0].as_bytes()).unwrap(); - let header: JweHeader2 = serde_json::from_slice(&header_bytes).unwrap(); - println!("{:?}", jwe_parts); - println!("{:?}", header); - let key_wrap = URL_SAFE_NO_PAD.decode(jwe_parts[1].as_bytes()).unwrap(); - let nonce = URL_SAFE_NO_PAD.decode(jwe_parts[2].as_bytes()).unwrap(); - let ciphertext = URL_SAFE_NO_PAD.decode(jwe_parts[3].as_bytes()).unwrap(); - let tag = URL_SAFE_NO_PAD.decode(jwe_parts[4].as_bytes()).unwrap(); + let header_bytes = opt_result!(decode_url_safe_no_pad(jwe_parts[0]), "Invalid JWE header: {}, JWE: {}", jwe); + let header: JweHeader2 = opt_result!(serde_json::from_slice(&header_bytes), "Invalid JWE header: {}, JWE: {}", jwe); + 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 kek = Kek::from(key); - let data_key = kek.unwrap_vec(&key_wrap).unwrap(); - let data_key_b32 = bytes_to_32(&data_key); + let data_key = key_unwrap_fn(&cek)?; + let data_key_b32 = opt_result!(to_bytes32(&data_key), "Invalid JWE CEK: {}, JWE: {}", jwe); - let mut decryptor = Aes256GcmStreamDecryptor::new(data_key_b32, &nonce); + let mut decryptor = Aes256GcmStreamDecryptor::new(data_key_b32, &iv); decryptor.init_adata(jwe_parts[0].as_bytes()); - let mut p1 = decryptor.update(&ciphertext); - let p2 = decryptor.update(&tag); - let pf = decryptor.finalize().unwrap(); - p1.extend_from_slice(&p2); - p1.extend_from_slice(&pf); - Ok((p1, header)) + 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)) } -pub fn deserialize_jwe_aes(jwe: &str, key: &[u8]) -> XResult<(Vec, JweHeader)> { - let decrypter = AeskwJweAlgorithm::A256kw.decrypter_from_bytes(key)?; - Ok(jwe::deserialize_compact(&get_jwe(jwe), &decrypter)?) +#[inline] +fn decode_url_safe_no_pad(s: &str) -> XResult> { + Ok(URL_SAFE_NO_PAD.decode(s.as_bytes())?) } -fn bytes_to_32(bytes: &[u8]) -> [u8; 32] { +#[inline] +fn to_bytes32(bytes: &[u8]) -> XResult<[u8; 32]> { if bytes.len() != 32 { - panic!("Invalid bytes"); + return simple_error!("Not valid 32 bytes"); } let mut ret = [0; 32]; for i in 0..32 { ret[i] = bytes[i]; } - ret + Ok(ret) } +#[inline] fn get_jwe(jwe: &str) -> String { if jwe.starts_with(LOCAL_KMS_PREFIX) { jwe.chars().skip(LOCAL_KMS_PREFIX.len()).collect() diff --git a/__crypto/jose-test/src/main.rs b/__crypto/jose-test/src/main.rs index cb7840b..086ebc9 100644 --- a/__crypto/jose-test/src/main.rs +++ b/__crypto/jose-test/src/main.rs @@ -1,6 +1,6 @@ mod jose; -use crate::jose::{deserialize_jwe_aes, deserialize_jwe_aes_2, deserialize_jwe_rsa_2, serialize_jwe_aes_2, serialize_jwe_rsa}; +use crate::jose::{deserialize_jwe_aes, deserialize_jwe_aes_2, deserialize_jwe_rsa_2, serialize_jwe_aes_2, serialize_jwe_rsa, serialize_jwe_rsa_2}; use base64::engine::general_purpose::STANDARD; use base64::Engine; use jose_jwk::{Jwk, Key, Rsa}; @@ -48,6 +48,15 @@ fn main() { println!("DD: {}", String::from_utf8_lossy(&dd)); println!("HH: {:?}", hh); + println!("-------------------------------------------------------------------------------------"); + let rsa_jwe2 = serialize_jwe_rsa_2(b"hello world 003", rsa_public_key).unwrap(); + println!(">>> {}", rsa_jwe2); + // let rsa_jwe2 = rsa_jwe2.chars().skip(5).collect::(); + let (ddd, hhh) = deserialize_jwe_rsa_2(&rsa_jwe2, &rsa_key).unwrap(); + println!("DDD: {}", String::from_utf8_lossy(&ddd)); + println!("HHH: {:?}", hhh); + + println!("-------------------------------------------------------------------------------------"); main2(); }