You can use serde_json
to parse the JSON, base64
to decode the Base64 strings, and aes
/ccm
to perform the actual decryption.
/*
[dependencies]
aes = "0.8.2"
base64 = "0.21.0"
ccm = "0.5.0"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.93"
*/
use aes::Aes256;
use base64::{engine::general_purpose::STANDARD, Engine};
use ccm::{
aead::{generic_array::typenum::Unsigned, Aead},
consts::{U11, U12, U13, U8},
Ccm, KeyInit,
};
use serde::Deserialize;
use std::str;
fn decrypt(input: &str, key: &str) -> Vec<u8> {
#[derive(Deserialize)]
struct Input {
iv: String,
ct: String,
}
let input: Input = serde_json::from_str(input).unwrap();
let iv = STANDARD.decode(input.iv).unwrap();
let ct = STANDARD.decode(input.ct).unwrap();
let key: Vec<u8> = key
.split(" ")
.flat_map(|chunk| u32::from_str_radix(chunk, 16).unwrap().to_be_bytes())
.collect();
fn decrypt_with<Alg: KeyInit + Aead>(key: &[u8], iv: &[u8], ct: &[u8]) -> Vec<u8> {
Alg::new_from_slice(key)
.unwrap()
.decrypt(iv[..Alg::NonceSize::USIZE].try_into().unwrap(), &ct[..])
.unwrap()
}
// SJCL automatically adjusts the nonce size, we must do so manually
if ct.len() < 0x1_0008 {
decrypt_with::<Ccm<Aes256, U8, U13>>(&key, &iv, &ct)
} else if ct.len() < 0x100_0008 {
decrypt_with::<Ccm<Aes256, U8, U12>>(&key, &iv, &ct)
} else {
decrypt_with::<Ccm<Aes256, U8, U11>>(&key, &iv, &ct)
}
}
fn main() {
let input = r#"{
"iv":"7M+yeOE9TaZpW711d8YffA==",
"v":1,
"iter":1000,
"ks":256,
"ts":64,
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"O4ifalzj/Es=",
"ct":"Lsbc3V06Fruulq83CRSiD2dohA=="
}"#;
let key = "7DED638F DD30ACDB 4A34D153 48F12D95 BB388A7E 8CB9C35B 8D9C9009 89454E1E";
let output = decrypt(input, key);
println!("{:?}", str::from_utf8(&output).unwrap());
}