feat: works
This commit is contained in:
93
src/lib.rs
93
src/lib.rs
@@ -1,8 +1,10 @@
|
|||||||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use rust_util::{opt_result, simple_error, SimpleError, XResult};
|
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||||
|
use rust_util::{SimpleError, XResult, opt_result, simple_error};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sm4_gcm::{sm4_gcm_encrypt, Sm4GcmStreamEncryptor, Sm4Key};
|
use sm4_gcm::{
|
||||||
|
Sm4GcmStreamDecryptor, Sm4GcmStreamEncryptor, Sm4Key, sm4_gcm_decrypt, sm4_gcm_encrypt,
|
||||||
|
};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
const SM4GCM: &str = "SM4GCM";
|
const SM4GCM: &str = "SM4GCM";
|
||||||
@@ -70,6 +72,14 @@ fn test() {
|
|||||||
.key_id("k001");
|
.key_id("k001");
|
||||||
println!("{}", k.encrypt(JweAlg::Dir, b"hello world").unwrap());
|
println!("{}", k.encrypt(JweAlg::Dir, b"hello world").unwrap());
|
||||||
println!("{}", k.encrypt(JweAlg::Sm4Skw, b"hello world").unwrap());
|
println!("{}", k.encrypt(JweAlg::Sm4Skw, b"hello world").unwrap());
|
||||||
|
|
||||||
|
let key_finder = |_key_id| -> XResult<Sm4Key> { Ok(Sm4Key(k.sm4key.0)) };
|
||||||
|
let plaintext = Sm4Jwk::decrypt(key_finder,
|
||||||
|
"eyJraWQiOiJrMDAxIiwiZW5jIjoiU000R0NNIiwiYWxnIjoiZGlyIn0..R0vzs8r5gTzz72ve.K6iaSJpyel_IZUw.iLPlxGV-xpBe9O5vZ8oz9A").unwrap();
|
||||||
|
println!("1:{}", String::from_utf8_lossy(&plaintext));
|
||||||
|
let plaintext = Sm4Jwk::decrypt(key_finder,
|
||||||
|
"eyJraWQiOiJrMDAxIiwiZW5jIjoiU000R0NNIiwiYWxnIjoiU000U0tXIn0.Yf2NSJSr-coYZP2IhzjtcgYfRq1Nalt7MyrRMgUm8hqD4vZ8QPMyp2brhGY.tIhwFoCSnzWcycVY.KaMzkCzGSmylUdY.du5GLF6lMz3vGJuSO614aw").unwrap();
|
||||||
|
println!("2:{}", String::from_utf8_lossy(&plaintext));
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sm4Jwk {
|
impl Sm4Jwk {
|
||||||
@@ -78,6 +88,65 @@ impl Sm4Jwk {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decrypt(
|
||||||
|
key_finder: impl Fn(Option<String>) -> XResult<Sm4Key>,
|
||||||
|
jwe: &str,
|
||||||
|
) -> XResult<Vec<u8>> {
|
||||||
|
let jwe_parts: Vec<&str> = jwe.split(".").collect();
|
||||||
|
if jwe_parts.len() != 5 {
|
||||||
|
return simple_error!(
|
||||||
|
"invalid JWE format, expect 5 parts, actual {} part(s)",
|
||||||
|
jwe_parts.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let header_base64 = jwe_parts[0];
|
||||||
|
let header_bytes = decode_url_safe_no_pad("header", header_base64)?;
|
||||||
|
let header: JweHeader = opt_result!(
|
||||||
|
serde_json::from_slice(&header_bytes),
|
||||||
|
"invalid JWE header: {}"
|
||||||
|
);
|
||||||
|
let alg = match header.alg.as_str() {
|
||||||
|
"dir" => JweAlg::Dir,
|
||||||
|
"SM4SKW" => JweAlg::Sm4Skw,
|
||||||
|
_ => return simple_error!("invalid JWE alg: {}", header.alg),
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_id = header.kid.as_deref();
|
||||||
|
let sm4_key = opt_result!(
|
||||||
|
key_finder(key_id.map(|s| s.to_string())),
|
||||||
|
"find key: {:?} failed: {}",
|
||||||
|
key_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut temp_key = vec![];
|
||||||
|
if alg == JweAlg::Sm4Skw {
|
||||||
|
let encrypted_key_bytes = decode_url_safe_no_pad("encrypted wrap key", jwe_parts[1])?;
|
||||||
|
temp_key = decrypt_sm4ske(&sm4_key, &encrypted_key_bytes)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonce_bytes = decode_url_safe_no_pad("nonce", jwe_parts[2])?;
|
||||||
|
|
||||||
|
let mut decryptor;
|
||||||
|
if alg == JweAlg::Sm4Skw {
|
||||||
|
let temp_sm4_key = Sm4Key::from_slice(&temp_key)?;
|
||||||
|
decryptor = Sm4GcmStreamDecryptor::new(&temp_sm4_key, &nonce_bytes);
|
||||||
|
} else {
|
||||||
|
decryptor = Sm4GcmStreamDecryptor::new(&sm4_key, &nonce_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ciphertext = decode_url_safe_no_pad("ciphertext", jwe_parts[3])?;
|
||||||
|
let tag = decode_url_safe_no_pad("tag", jwe_parts[4])?;
|
||||||
|
|
||||||
|
decryptor.init_adata(header_base64.as_bytes());
|
||||||
|
let mut plaintext = decryptor.update(&ciphertext);
|
||||||
|
let plaintext2 = decryptor.update(&tag);
|
||||||
|
let plaintext3 = opt_result!(decryptor.finalize(), "decrypt JWE failed: {}");
|
||||||
|
plaintext.extend_from_slice(&plaintext2);
|
||||||
|
plaintext.extend_from_slice(&plaintext3);
|
||||||
|
|
||||||
|
Ok(plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn encrypt(&self, alg: JweAlg, message: &[u8]) -> XResult<String> {
|
pub fn encrypt(&self, alg: JweAlg, message: &[u8]) -> XResult<String> {
|
||||||
if alg == JweAlg::Sm2Pke {
|
if alg == JweAlg::Sm2Pke {
|
||||||
return simple_error!("SM2PKE is not supported");
|
return simple_error!("SM2PKE is not supported");
|
||||||
@@ -131,6 +200,16 @@ impl Sm4Jwk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decrypt_sm4ske(sm4jwk: &Sm4Key, wrap_key: &[u8]) -> XResult<Vec<u8>> {
|
||||||
|
let nonce = &wrap_key[0..12];
|
||||||
|
let ciphertext = &wrap_key[12..];
|
||||||
|
let temp_key = opt_result!(
|
||||||
|
sm4_gcm_decrypt(sm4jwk, nonce, ciphertext),
|
||||||
|
"unwrap temp key failed: {}"
|
||||||
|
);
|
||||||
|
Ok(temp_key)
|
||||||
|
}
|
||||||
|
|
||||||
fn encrypt_sm4ske(sm4key: &Sm4Key, key: &[u8]) -> XResult<Vec<u8>> {
|
fn encrypt_sm4ske(sm4key: &Sm4Key, key: &[u8]) -> XResult<Vec<u8>> {
|
||||||
let nonce: [u8; 12] = rand::random();
|
let nonce: [u8; 12] = rand::random();
|
||||||
let encrypted_key = sm4_gcm_encrypt(sm4key, &nonce, key);
|
let encrypted_key = sm4_gcm_encrypt(sm4key, &nonce, key);
|
||||||
@@ -139,6 +218,14 @@ fn encrypt_sm4ske(sm4key: &Sm4Key, key: &[u8]) -> XResult<Vec<u8>> {
|
|||||||
Ok(ske)
|
Ok(ske)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_url_safe_no_pad(tag: &str, s: &str) -> XResult<Vec<u8>> {
|
||||||
|
Ok(opt_result!(
|
||||||
|
URL_SAFE_NO_PAD.decode(s),
|
||||||
|
"decode {} failed: {}",
|
||||||
|
tag
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_url_safe_no_pad(bytes: &[u8]) -> String {
|
fn encode_url_safe_no_pad(bytes: &[u8]) -> String {
|
||||||
URL_SAFE_NO_PAD.encode(bytes)
|
URL_SAFE_NO_PAD.encode(bytes)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user