todo: fix UserData decryption

This commit is contained in:
mokurin000
2025-07-31 01:13:00 +08:00
parent c4860b812b
commit 1943b5b1f6
10 changed files with 216 additions and 23 deletions

View File

@@ -17,6 +17,7 @@ serde_json = { workspace = true }
strum = { workspace = true }
tokio = { workspace = true, optional = true }
compio = { workspace = true, optional = true }
spdlog-rs = { workspace = true }
# hashing
digest = "0.10.7"
@@ -34,7 +35,7 @@ serde = { version = "1.0.219", features = ["derive"] }
# compression / encryption
flate2 = "1.1.2"
cbc = "0.1.2"
cbc = { version = "0.1.2", features = ["alloc"] }
aes = "0.8.4"
cipher = { version = "0.4.4", features = ["block-padding"] }
bincode = { version = "2.0.1", optional = true }

View File

@@ -1,19 +1,18 @@
use std::io::{Read, Write as _};
use std::io::Write as _;
use aes::cipher::{
BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyIvInit, block_padding::Pkcs7,
};
use digest::generic_array::GenericArray;
use flate2::write::ZlibEncoder;
use flate2::{Compression, read::ZlibDecoder};
use flate2::Compression;
use flate2::write::{ZlibDecoder, ZlibEncoder};
use crate::error::ApiError;
use crate::title::{MaiVersion, MaiVersionExt, Sdgb1_40, Sdgb1_50};
impl MaiVersionExt for Sdgb1_40 {
fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> {
let mut decompressed = decompress(data.as_mut());
fn decode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
let mut decompressed = decompress(data.as_ref());
if decompressed.is_empty() {
return Err(ApiError::EmptyResponse);
}
@@ -41,14 +40,19 @@ impl MaiVersionExt for Sdgb1_40 {
}
impl MaiVersionExt for Sdgb1_50 {
fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> {
if data.as_mut().is_empty() {
fn decode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
let mut data = data.as_ref().to_vec();
if data.is_empty() {
return Err(ApiError::EmptyResponse);
}
let decrypted = decrypt(&mut data, Self::AES_KEY, Self::AES_IV)?;
let decompressed = decompress(decrypted);
Ok(decompressed)
if data.len() % 16 != 0 {
let pad = 16 - (data.len() % 16);
data.resize(data.len() + pad, pad as _);
}
let decrypted = decrypt_vec(&data, Self::AES_KEY, Self::AES_IV)?;
Ok(decompress(decrypted))
}
fn encode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
@@ -72,8 +76,9 @@ fn compress(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
fn decompress(data: impl AsRef<[u8]>) -> Vec<u8> {
let mut buf = Vec::with_capacity(data.as_ref().len() * 2);
let mut decode = ZlibDecoder::new(data.as_ref());
_ = decode.read_to_end(&mut buf);
let mut decode = ZlibDecoder::new(&mut buf);
_ = decode.write_all(data.as_ref());
_ = decode.finish();
buf
}
@@ -104,6 +109,13 @@ fn decrypt<'ct>(
Ok(result)
}
fn decrypt_vec(data: impl AsRef<[u8]>, key: &[u8; 32], iv: &[u8; 16]) -> Result<Vec<u8>, ApiError> {
let key = GenericArray::from_slice(key);
let iv = GenericArray::from_slice(iv);
let decryptor = Aes256CbcDec::new(key, iv);
Ok(decryptor.decrypt_padded_vec_mut::<Pkcs7>(data.as_ref())?)
}
#[cfg(test)]
mod _tests {
@@ -148,4 +160,69 @@ mod _tests {
assert_eq!(enc, data);
Ok(())
}
// FIXME: user data decryption
#[test]
fn test_user_data_dec() -> Result<(), ApiError> {
let data = [
60, 33, 100, 111, 99, 116, 121, 112, 101, 32, 104, 116, 109, 108, 62, 60, 104, 116,
109, 108, 32, 108, 97, 110, 103, 61, 34, 101, 110, 34, 62, 60, 104, 101, 97, 100, 62,
60, 116, 105, 116, 108, 101, 62, 72, 84, 84, 80, 32, 83, 116, 97, 116, 117, 115, 32,
53, 48, 48, 32, 226, 128, 147, 32, 73, 110, 116, 101, 114, 110, 97, 108, 32, 83, 101,
114, 118, 101, 114, 32, 69, 114, 114, 111, 114, 60, 47, 116, 105, 116, 108, 101, 62,
60, 115, 116, 121, 108, 101, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47,
99, 115, 115, 34, 62, 104, 49, 32, 123, 102, 111, 110, 116, 45, 102, 97, 109, 105, 108,
121, 58, 84, 97, 104, 111, 109, 97, 44, 65, 114, 105, 97, 108, 44, 115, 97, 110, 115,
45, 115, 101, 114, 105, 102, 59, 99, 111, 108, 111, 114, 58, 119, 104, 105, 116, 101,
59, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 45, 99, 111, 108, 111, 114, 58, 35,
53, 50, 53, 68, 55, 54, 59, 102, 111, 110, 116, 45, 115, 105, 122, 101, 58, 50, 50,
112, 120, 59, 125, 32, 104, 50, 32, 123, 102, 111, 110, 116, 45, 102, 97, 109, 105,
108, 121, 58, 84, 97, 104, 111, 109, 97, 44, 65, 114, 105, 97, 108, 44, 115, 97, 110,
115, 45, 115, 101, 114, 105, 102, 59, 99, 111, 108, 111, 114, 58, 119, 104, 105, 116,
101, 59, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 45, 99, 111, 108, 111, 114, 58,
35, 53, 50, 53, 68, 55, 54, 59, 102, 111, 110, 116, 45, 115, 105, 122, 101, 58, 49, 54,
112, 120, 59, 125, 32, 104, 51, 32, 123, 102, 111, 110, 116, 45, 102, 97, 109, 105,
108, 121, 58, 84, 97, 104, 111, 109, 97, 44, 65, 114, 105, 97, 108, 44, 115, 97, 110,
115, 45, 115, 101, 114, 105, 102, 59, 99, 111, 108, 111, 114, 58, 119, 104, 105, 116,
101, 59, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 45, 99, 111, 108, 111, 114, 58,
35, 53, 50, 53, 68, 55, 54, 59, 102, 111, 110, 116, 45, 115, 105, 122, 101, 58, 49, 52,
112, 120, 59, 125, 32, 98, 111, 100, 121, 32, 123, 102, 111, 110, 116, 45, 102, 97,
109, 105, 108, 121, 58, 84, 97, 104, 111, 109, 97, 44, 65, 114, 105, 97, 108, 44, 115,
97, 110, 115, 45, 115, 101, 114, 105, 102, 59, 99, 111, 108, 111, 114, 58, 98, 108, 97,
99, 107, 59, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 45, 99, 111, 108, 111, 114,
58, 119, 104, 105, 116, 101, 59, 125, 32, 98, 32, 123, 102, 111, 110, 116, 45, 102, 97,
109, 105, 108, 121, 58, 84, 97, 104, 111, 109, 97, 44, 65, 114, 105, 97, 108, 44, 115,
97, 110, 115, 45, 115, 101, 114, 105, 102, 59, 99, 111, 108, 111, 114, 58, 119, 104,
105, 116, 101, 59, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 45, 99, 111, 108,
111, 114, 58, 35, 53, 50, 53, 68, 55, 54, 59, 125, 32, 112, 32, 123, 102, 111, 110,
116, 45, 102, 97, 109, 105, 108, 121, 58, 84, 97, 104, 111, 109, 97, 44, 65, 114, 105,
97, 108, 44, 115, 97, 110, 115, 45, 115, 101, 114, 105, 102, 59, 98, 97, 99, 107, 103,
114, 111, 117, 110, 100, 58, 119, 104, 105, 116, 101, 59, 99, 111, 108, 111, 114, 58,
98, 108, 97, 99, 107, 59, 102, 111, 110, 116, 45, 115, 105, 122, 101, 58, 49, 50, 112,
120, 59, 125, 32, 97, 32, 123, 99, 111, 108, 111, 114, 58, 98, 108, 97, 99, 107, 59,
125, 32, 97, 46, 110, 97, 109, 101, 32, 123, 99, 111, 108, 111, 114, 58, 98, 108, 97,
99, 107, 59, 125, 32, 46, 108, 105, 110, 101, 32, 123, 104, 101, 105, 103, 104, 116,
58, 49, 112, 120, 59, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 45, 99, 111, 108,
111, 114, 58, 35, 53, 50, 53, 68, 55, 54, 59, 98, 111, 114, 100, 101, 114, 58, 110,
111, 110, 101, 59, 125, 60, 47, 115, 116, 121, 108, 101, 62, 60, 47, 104, 101, 97, 100,
62, 60, 98, 111, 100, 121, 62, 60, 104, 49, 62, 72, 84, 84, 80, 32, 83, 116, 97, 116,
117, 115, 32, 53, 48, 48, 32, 226, 128, 147, 32, 73, 110, 116, 101, 114, 110, 97, 108,
32, 83, 101, 114, 118, 101, 114, 32, 69, 114, 114, 111, 114, 60, 47, 104, 49, 62, 60,
104, 114, 32, 99, 108, 97, 115, 115, 61, 34, 108, 105, 110, 101, 34, 32, 47, 62, 60,
112, 62, 60, 98, 62, 84, 121, 112, 101, 60, 47, 98, 62, 32, 83, 116, 97, 116, 117, 115,
32, 82, 101, 112, 111, 114, 116, 60, 47, 112, 62, 60, 112, 62, 60, 98, 62, 68, 101,
115, 99, 114, 105, 112, 116, 105, 111, 110, 60, 47, 98, 62, 32, 84, 104, 101, 32, 115,
101, 114, 118, 101, 114, 32, 101, 110, 99, 111, 117, 110, 116, 101, 114, 101, 100, 32,
97, 110, 32, 117, 110, 101, 120, 112, 101, 99, 116, 101, 100, 32, 99, 111, 110, 100,
105, 116, 105, 111, 110, 32, 116, 104, 97, 116, 32, 112, 114, 101, 118, 101, 110, 116,
101, 100, 32, 105, 116, 32, 102, 114, 111, 109, 32, 102, 117, 108, 102, 105, 108, 108,
105, 110, 103, 32, 116, 104, 101, 32, 114, 101, 113, 117, 101, 115, 116, 46, 60, 47,
112, 62, 60, 104, 114, 32, 99, 108, 97, 115, 115, 61, 34, 108, 105, 110, 101, 34, 32,
47, 62, 60, 104, 51, 62, 65, 112, 97, 99, 104, 101, 32, 84, 111, 109, 99, 97, 116, 47,
56, 46, 53, 46, 51, 57, 60, 47, 104, 51, 62, 60, 47, 98, 111, 100, 121, 62, 60, 47,
104, 116, 109, 108, 62,
];
let _ = Sdgb1_50::decode(data)?;
Ok(())
}
}

View File

@@ -14,6 +14,7 @@ use nyquest::{
header::{ACCEPT_ENCODING, CONTENT_ENCODING, EXPECT, USER_AGENT},
};
use serde::{Deserialize, Serialize};
use spdlog::debug;
pub trait MaiVersion {
const AES_KEY: &[u8; 32];
@@ -24,7 +25,7 @@ pub trait MaiVersion {
pub trait MaiVersionExt: MaiVersion {
fn encode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError>;
fn decode(data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError>;
fn decode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError>;
fn api_hash(api: APIMethod) -> String {
let api_name: &str = api.into();
@@ -78,6 +79,9 @@ pub trait MaiVersionExt: MaiVersion {
.await
.map_err(|_| ApiError::JoinError)??;
let data = client.request(req).await?.bytes().await?;
debug!("received: {data:?}");
let decoded = spawn_blocking(move || Self::decode(data))
.await
.map_err(|_| ApiError::JoinError)??;

View File

@@ -1,7 +1,10 @@
use serde::Deserialize;
use serde::Serialize;
pub struct GetUserDataApi {}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GetUserDataApi {
pub user_id: u32,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -135,7 +138,7 @@ pub struct UserData {
pub friend_regist_skip: i64,
pub cm_last_emoney_credit: i64,
pub cm_last_emoney_brand: i64,
/// 访问密码(国区无)
pub access_code: Option<String>,
/// 好友代码(国区无)

View File

@@ -1,3 +1,5 @@
use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -29,3 +31,24 @@ pub struct UserLoginApiResp {
/// needed for some operation
pub login_id: Option<u64>,
}
impl UserLoginApi {
pub fn new(user_id: u32) -> Self {
let date_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|t| t.as_secs())
.unwrap_or_default();
// 爱玩星球焦作解放店
UserLoginApi {
user_id,
date_time,
region_id: 13,
acsess_code: "".to_owned(),
place_id: 3223.to_string(),
is_continue: false,
generic_flag: 0,
client_id: "A63E01E6170".into(),
}
}
}