feat: jose
This commit is contained in:
153
__crypto/jose-test/src/jose.rs
Normal file
153
__crypto/jose-test/src/jose.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
use aes_gcm_stream::Aes256GcmStreamEncryptor;
|
||||
use aes_kw::Kek;
|
||||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||
use base64::Engine;
|
||||
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 rand::random;
|
||||
use rand::rngs::OsRng;
|
||||
use rsa::RsaPrivateKey;
|
||||
use rust_util::XResult;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
const LOCAL_KMS_PREFIX: &str = "LKMS:";
|
||||
|
||||
|
||||
// JWE format:
|
||||
// BASE64URL(UTF8(JWE Protected Header)) || '.' ||
|
||||
// BASE64URL(JWE Encrypted Key) || '.' || BASE64URL(JWE Initialization Vector)
|
||||
// || '.' || BASE64URL(JWE Ciphertext) || '.' || BASE64URL(JWE Authentication Tag).
|
||||
//
|
||||
// RSA JWE Header:
|
||||
// {"enc":"A256GCM","vendor":"local-mini-kms","alg":"RSA-OAEP"}
|
||||
// eyJlbmMiOiJBMjU2R0NNIiwidmVuZG9yIjoibG9jYWwtbWluaS1rbXMiLCJhbGciOiJSU0EtT0FFUCJ9.VQ_R
|
||||
// yGjXqQlUIbRIMgaYRSaX5FMRBzZ6ApfdZ2yAwiG70hjNfR3ss7x4PYqMm6QtITm1O4_fp7I3bY8iUz5Njyth_
|
||||
// Min7Xm2-WsQ6gq9yN58btkUBFm60c7SC5XLaqE1pEtBAz7786bJk6M4NeOtDAOFAmIb2j1EwnS5vweBtmNv7N
|
||||
// UFIgvx806T3WkCFDOkMSJ10_6LSa0z-lIac-s68svsU5WW8CXVKxHAbxaHyX_otu2HxXzDZlF5Goamh5ZJtr0
|
||||
// 0yW_bzDCx3hZ2nMK3Ve7IJ2ZLAMmvhj9LKWkPtoqH0dGHaPHWff5P3rZ4vtKywt_h5b6SYII_mEoJcpByMyGw
|
||||
// TXCtZymDt82Tyv_FesW2721JgyGxnukuOxQRTw4MfGYIO5bldL3uGGI_H4HXlXhM_kp3wuPAZ0vH4Jj2KD6MV
|
||||
// DDTJQaEBQIEF07i7WiNynr57kbahYwextRXYP7LgoUHfFwA5GGGpN-UkuWLlKkYLTmXGrPYnL6Cf9D3euP7nF
|
||||
// ml2oA2hjig-UuYf9A_QSEqNsMxYDuG-rggn3H_iXNl4ooYcxSVOXhTKfoV578MkNwG75BdHN5FeRYIKq0HCTM
|
||||
// lGqqBWmDibPtMd7Uq1JrDd8774lnA8JcZcCMSia4m6WJSbG0kOuJ4NJPOUrYtNEJXgWKU3FQzDB-apLMQdac.
|
||||
// WYJgsdZRLk310KWd.P333-S2VYg.PCfruTdk8vh3a8wcjJCe-g
|
||||
//
|
||||
// RSA-OAEP RSA using Optimal Asymmetric Encryption Padding (OAEP), as defined in RFC 3447 [RFC3447]
|
||||
// A256GCM Advanced Encryption Standard (AES) using 256 bit keys in Galois/Counter Mode, as defined in [FIPS‑197] and [NIST‑800‑38D]
|
||||
//
|
||||
// AES JWE Header:
|
||||
// {"enc":"A256GCM","vendor":"local-mini-kms","version":"5b90f66a1c6a918d","alg":"A256KW"}
|
||||
// eyJlbmMiOiJBMjU2R0NNIiwidmVuZG9yIjoibG9jYWwtbWluaS1rbXMiLCJ2ZXJzaW9uIjoiNWI5MGY2NmExYz
|
||||
// ZhOTE4ZCIsImFsZyI6IkEyNTZLVyJ9.K2_P-b_Gq9wbrssbcS5AmiUwcnNTnnZSe7rBI1SixVrC7TfFK0fruw.
|
||||
// ez3OKjOHAIIYnfM0.wSO3aXo.-vGJwk8JQKhi3voIlAA9gQ
|
||||
//
|
||||
// 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<RsaPrivateKey> {
|
||||
let mut rng = OsRng::default();
|
||||
Ok(RsaPrivateKey::new(&mut rng, bits as usize)?)
|
||||
}
|
||||
|
||||
pub fn generate_rsa_key(bits: u32) -> XResult<RsaKeyPair> {
|
||||
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<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 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)?)
|
||||
}
|
||||
|
||||
// JWE Header: {"alg":"dir","enc":"A256GCM"}
|
||||
// Encrypted key (CEK): (blank)
|
||||
// IV: Vlf_WdLm-spHbfJe
|
||||
// Ciphertext: RxMPrw
|
||||
// Authentication Tag: 5VC8Y_qSPdSubbGNGyfn6A
|
||||
//
|
||||
// JWE Header: {"alg":"A256KW","enc":"A256GCM"}
|
||||
// Encrypted key (CEK): 66xZoxFI18zfvLMO6WU1zzqqX1tT8xu_qZzMQyPcfVuajPNkOJUXQA
|
||||
// IV: X5ZL8yaOektXmfny
|
||||
// Ciphertext: brz-Lg
|
||||
// Authentication Tag: xG-EvM-9hrw0XRiuRW7HrA
|
||||
//
|
||||
// https://security.stackexchange.com/questions/80966/what-is-the-point-of-aes-key-wrap-with-json-web-encryption
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct JweHeader2 {
|
||||
pub enc: String,
|
||||
pub alg: String,
|
||||
pub vendor: String,
|
||||
}
|
||||
|
||||
pub fn serialize_jwe_aes_2(payload: &[u8], key: [u8; 32]) -> XResult<String> {
|
||||
let header = JweHeader2 {
|
||||
enc: "A256GCM".to_string(),
|
||||
alg: "A256KW".to_string(),
|
||||
vendor: "local-mini-kms".to_string(),
|
||||
};
|
||||
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();
|
||||
let mut encryptor = Aes256GcmStreamEncryptor::new(data_key, &nonce);
|
||||
encryptor.init_adata(header_b64.as_bytes());
|
||||
let mut e = encryptor.update(payload);
|
||||
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 mut jwe = String::new();
|
||||
jwe.push_str(&header_b64);
|
||||
jwe.push_str(".");
|
||||
jwe.push_str(&URL_SAFE_NO_PAD.encode(&wrap_key));
|
||||
jwe.push_str(".");
|
||||
jwe.push_str(&URL_SAFE_NO_PAD.encode(&nonce));
|
||||
jwe.push_str(".");
|
||||
jwe.push_str(&URL_SAFE_NO_PAD.encode(&e));
|
||||
jwe.push_str(".");
|
||||
jwe.push_str(&URL_SAFE_NO_PAD.encode(&t));
|
||||
|
||||
Ok(jwe)
|
||||
}
|
||||
|
||||
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)?))
|
||||
}
|
||||
|
||||
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)?)
|
||||
}
|
||||
|
||||
fn get_jwe(jwe: &str) -> String {
|
||||
if jwe.starts_with(LOCAL_KMS_PREFIX) {
|
||||
jwe.chars().skip(LOCAL_KMS_PREFIX.len()).collect()
|
||||
} else {
|
||||
jwe.to_string()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user