mod key;
use base64::{Engine as _, engine::general_purpose::STANDARD};
use openssl::{
error::ErrorStack,
hash::{MessageDigest, hash},
rsa::{Padding, Rsa},
symm::{Cipher, decrypt, encrypt},
};
use rand::RngExt;
use serde::Serialize;
use key::{BASE62, EAPI_KEY, IV, LINUX_API_KEY, PRESET_KEY, PUBLIC_KEY};
#[derive(Serialize, Debug, PartialEq, Eq, Clone, Copy)]
pub enum Crypto {
Weapi,
Eapi,
#[allow(unused)]
Linuxapi,
}
pub struct WeapiForm {
params: String,
enc_sec_key: String,
}
pub struct EapiForm {
params: String,
}
pub struct LinuxapiForm {
eparams: String,
}
impl WeapiForm {
pub fn into_vec(self) -> Vec<(String, String)> {
vec![
("params".to_owned(), self.params),
("encSecKey".to_owned(), self.enc_sec_key),
]
}
}
impl EapiForm {
pub fn into_vec(self) -> Vec<(String, String)> {
vec![("params".to_owned(), self.params)]
}
}
impl LinuxapiForm {
pub fn into_vec(self) -> Vec<(String, String)> {
vec![("eparams".to_owned(), self.eparams)]
}
}
pub fn weapi(text: &[u8]) -> Result<WeapiForm, ErrorStack> {
let mut rng = rand::rng();
let mut rand_buf = [0u8; 16];
rng.fill(&mut rand_buf);
let sk = rand_buf
.iter()
.map(|i| BASE62.as_bytes()[(i % 62) as usize])
.collect::<Vec<u8>>();
let params = {
let p = STANDARD.encode(aes_128_cbc(
text,
PRESET_KEY.as_bytes(),
Some(IV.as_bytes()),
)?);
STANDARD.encode(aes_128_cbc(p.as_bytes(), &sk, Some(IV.as_bytes()))?)
};
let enc_sec_key = {
let reversed_sk = sk.iter().rev().copied().collect::<Vec<u8>>();
hex::encode(rsa(&reversed_sk, PUBLIC_KEY.as_bytes())?)
};
Ok(WeapiForm {
params,
enc_sec_key,
})
}
pub fn eapi(url: &[u8], data: &[u8]) -> Result<EapiForm, ErrorStack> {
let msg = format!(
"nobody{}use{}md5forencrypt",
String::from_utf8_lossy(url),
String::from_utf8_lossy(data)
);
let digest = hex::encode(hash(MessageDigest::md5(), msg.as_bytes())?);
let text = {
let d = "-36cd479b6b5-";
[url, d.as_bytes(), data, d.as_bytes(), digest.as_bytes()].concat()
};
let params = {
let p = aes_128_ecb(&text, EAPI_KEY.as_bytes(), None)?;
hex::encode_upper(p)
};
Ok(EapiForm { params })
}
#[allow(unused)]
pub fn eapi_decrypt(ct: &[u8]) -> Result<Vec<u8>, ErrorStack> {
aes_128_ecb_decrypt(ct, EAPI_KEY.as_bytes(), None)
}
pub fn linuxapi(text: &[u8]) -> Result<LinuxapiForm, ErrorStack> {
let ct = aes_128_ecb(text, LINUX_API_KEY.as_bytes(), None)?;
let eparams = hex::encode_upper(ct);
Ok(LinuxapiForm { eparams })
}
fn aes_128_ecb(pt: &[u8], key: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, ErrorStack> {
let cipher = Cipher::aes_128_ecb();
encrypt(cipher, key, iv, pt)
}
fn aes_128_ecb_decrypt(ct: &[u8], key: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, ErrorStack> {
let cipher = Cipher::aes_128_ecb();
decrypt(cipher, key, iv, ct)
}
fn aes_128_cbc(pt: &[u8], key: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, ErrorStack> {
let cipher = Cipher::aes_128_cbc();
encrypt(cipher, key, iv, pt)
}
fn rsa(pt: &[u8], key: &[u8]) -> Result<Vec<u8>, ErrorStack> {
let rsa = Rsa::public_key_from_pem(key)?;
let prefix = vec![0u8; 128 - pt.len()];
let pt = [&prefix[..], pt].concat();
let mut ct = vec![0; rsa.size() as usize];
rsa.public_encrypt(&pt, &mut ct, Padding::NONE)?;
Ok(ct)
}
#[cfg(test)]
mod tests {
use super::key::{EAPI_KEY, IV, PRESET_KEY, PUBLIC_KEY};
use super::{aes_128_cbc, aes_128_ecb, aes_128_ecb_decrypt, rsa, weapi};
use crate::crypto::{eapi, eapi_decrypt, linuxapi};
#[test]
fn test_aes_128_ecb() {
let pt = "plain text";
let ct = aes_128_ecb(pt.as_bytes(), EAPI_KEY.as_bytes(), None).expect("valid test cipher");
let _pt = aes_128_ecb_decrypt(&ct, EAPI_KEY.as_bytes(), None);
assert!(_pt.is_ok());
if let Ok(decrypted) = _pt {
assert_eq!(&decrypted, pt.as_bytes());
}
}
#[test]
fn test_aes_cbc() {
let pt = "plain text";
let ct = aes_128_cbc(pt.as_bytes(), PRESET_KEY.as_bytes(), Some(IV.as_bytes()))
.expect("valid test cipher");
assert!(hex::encode(ct).ends_with("baf0"))
}
#[test]
fn test_rsa() {
let ct = rsa(PRESET_KEY.as_bytes(), PUBLIC_KEY.as_bytes()).expect("valid public key");
assert!(hex::encode(ct).ends_with("4413"));
}
#[test]
fn test_weapi() {
weapi(r#"{"username": "alex"}"#.as_bytes()).expect("valid weapi payload");
}
#[test]
fn test_eapi() {
let ct = eapi("/url".as_bytes(), "plain text".as_bytes()).expect("valid eapi payload");
assert!(ct.params.ends_with("C3F3"));
}
#[test]
fn test_eapi_decrypt() {
let pt = "plain text";
let ct = aes_128_ecb(pt.as_bytes(), EAPI_KEY.as_bytes(), None).expect("valid test cipher");
assert_eq!(pt.as_bytes(), &eapi_decrypt(&ct).unwrap())
}
#[test]
fn test_linuxapi() {
let ct = linuxapi(r#""plain text""#.as_bytes()).expect("valid linuxapi payload");
assert!(ct.eparams.ends_with("2250"));
}
}