1use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit};
8use md5::{Digest, Md5};
9use rand::Rng;
10use rsa::traits::PublicKeyParts;
11use rsa::{BigUint, RsaPublicKey};
12use std::collections::HashMap;
13
14type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
15type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;
16type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;
17
18const IV: &[u8] = b"0102030405060708";
19const PRESET_KEY: &[u8] = b"0CoJUm6Qyw8W8jud";
20const LINUXAPI_KEY: &[u8] = b"rFgB&h#%2?^eDg:Q";
21const EAPI_KEY: &[u8] = b"e82ckenh8dichen8";
22const BASE62: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
23
24const PUBLIC_KEY_DER_B64: &str = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB";
26
27fn aes_cbc_encrypt_base64(plaintext: &[u8], key: &[u8], iv: &[u8]) -> String {
29 let cipher = Aes128CbcEnc::new(key.into(), iv.into());
30 let ciphertext = cipher.encrypt_padded_vec_mut::<Pkcs7>(plaintext);
31 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &ciphertext)
32}
33
34fn aes_ecb_encrypt_hex(plaintext: &[u8], key: &[u8]) -> String {
36 let cipher = Aes128EcbEnc::new(key.into());
37 let ciphertext = cipher.encrypt_padded_vec_mut::<Pkcs7>(plaintext);
38 hex::encode_upper(&ciphertext)
39}
40
41fn aes_ecb_decrypt_hex(ciphertext_hex: &str, key: &[u8]) -> Result<Vec<u8>, String> {
43 let ciphertext = hex::decode(ciphertext_hex).map_err(|e| e.to_string())?;
44 let cipher = Aes128EcbDec::new(key.into());
45 cipher
46 .decrypt_padded_vec_mut::<Pkcs7>(&ciphertext)
47 .map_err(|e| e.to_string())
48}
49
50fn rsa_encrypt_no_padding(plaintext: &[u8]) -> String {
53 use base64::Engine;
54 use rsa::pkcs8::DecodePublicKey;
55
56 let der_bytes = base64::engine::general_purpose::STANDARD
57 .decode(PUBLIC_KEY_DER_B64)
58 .expect("Failed to decode RSA public key base64");
59
60 let public_key =
61 RsaPublicKey::from_public_key_der(&der_bytes).expect("Failed to parse RSA public key DER");
62
63 let n = public_key.n().clone();
64 let e = public_key.e().clone();
65
66 let m = BigUint::from_bytes_be(plaintext);
68 let c = m.modpow(&e, &n);
69
70 let n_bytes = n.bits() / 8;
72 let c_bytes = c.to_bytes_be();
73
74 let mut padded = vec![0u8; n_bytes - c_bytes.len()];
76 padded.extend_from_slice(&c_bytes);
77 hex::encode(&padded)
78}
79
80pub fn weapi(object: &serde_json::Value) -> HashMap<String, String> {
83 let text = serde_json::to_string(object).unwrap();
84 let mut rng = rand::thread_rng();
85
86 let secret_key: String = (0..16)
88 .map(|_| BASE62[rng.gen_range(0..62)] as char)
89 .collect();
90
91 let first_encrypt = aes_cbc_encrypt_base64(text.as_bytes(), PRESET_KEY, IV);
93 let params = aes_cbc_encrypt_base64(first_encrypt.as_bytes(), secret_key.as_bytes(), IV);
95
96 let reversed_key: String = secret_key.chars().rev().collect();
98 let enc_sec_key = rsa_encrypt_no_padding(reversed_key.as_bytes());
99
100 let mut result = HashMap::new();
101 result.insert("params".to_string(), params);
102 result.insert("encSecKey".to_string(), enc_sec_key);
103 result
104}
105
106pub fn linuxapi(object: &serde_json::Value) -> HashMap<String, String> {
109 let text = serde_json::to_string(object).unwrap();
110 let mut result = HashMap::new();
111 result.insert(
112 "eparams".to_string(),
113 aes_ecb_encrypt_hex(text.as_bytes(), LINUXAPI_KEY),
114 );
115 result
116}
117
118pub fn eapi(url: &str, object: &serde_json::Value) -> HashMap<String, String> {
121 let text = serde_json::to_string(object).unwrap();
122 let message = format!("nobody{}use{}md5forencrypt", url, text);
123 let digest = format!("{:x}", Md5::digest(message.as_bytes()));
124 let data = format!("{}-36cd479b6b5-{}-36cd479b6b5-{}", url, text, digest);
125
126 let mut result = HashMap::new();
127 result.insert(
128 "params".to_string(),
129 aes_ecb_encrypt_hex(data.as_bytes(), EAPI_KEY),
130 );
131 result
132}
133
134pub fn eapi_res_decrypt(encrypted_hex: &str) -> Option<serde_json::Value> {
136 let decrypted = aes_ecb_decrypt_hex(encrypted_hex, EAPI_KEY).ok()?;
137 let text = String::from_utf8(decrypted).ok()?;
138 serde_json::from_str(&text).ok()
139}
140
141pub fn eapi_req_decrypt(encrypted_hex: &str) -> Option<(String, serde_json::Value)> {
143 let decrypted = aes_ecb_decrypt_hex(encrypted_hex, EAPI_KEY).ok()?;
144 let text = String::from_utf8(decrypted).ok()?;
145
146 let parts: Vec<&str> = text.splitn(3, "-36cd479b6b5-").collect();
148 if parts.len() >= 2 {
149 let url = parts[0].to_string();
150 let data: serde_json::Value = serde_json::from_str(parts[1]).ok()?;
151 Some((url, data))
152 } else {
153 None
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_aes_ecb_roundtrip() {
163 let plaintext = b"hello world test";
164 let encrypted = aes_ecb_encrypt_hex(plaintext, EAPI_KEY);
165 let decrypted = aes_ecb_decrypt_hex(&encrypted, EAPI_KEY).unwrap();
166 assert_eq!(decrypted, plaintext);
167 }
168
169 #[test]
170 fn test_weapi_produces_params_and_encseckey() {
171 let obj = serde_json::json!({"id": 123});
172 let result = weapi(&obj);
173 assert!(result.contains_key("params"));
174 assert!(result.contains_key("encSecKey"));
175 assert!(!result["params"].is_empty());
176 assert_eq!(result["encSecKey"].len(), 256);
178 }
179
180 #[test]
181 fn test_linuxapi_produces_eparams() {
182 let obj = serde_json::json!({"method": "POST", "url": "https://music.163.com/api/test"});
183 let result = linuxapi(&obj);
184 assert!(result.contains_key("eparams"));
185 assert!(!result["eparams"].is_empty());
186 }
187
188 #[test]
189 fn test_eapi_encrypt_decrypt() {
190 let url = "/api/song/detail";
191 let obj = serde_json::json!({"id": 123});
192 let encrypted = eapi(url, &obj);
193 let params = &encrypted["params"];
194
195 let (dec_url, dec_data) = eapi_req_decrypt(params).unwrap();
197 assert_eq!(dec_url, url);
198 assert_eq!(dec_data, obj);
199 }
200}