feat: authlite command

This commit is contained in:
mokurin000
2025-07-30 13:14:06 +08:00
parent 82e30c020d
commit d3c3592e67
7 changed files with 78 additions and 22 deletions

1
Cargo.lock generated
View File

@@ -1124,6 +1124,7 @@ dependencies = [
"serde_json", "serde_json",
"snafu", "snafu",
"spdlog-rs", "spdlog-rs",
"strum 0.27.2",
] ]
[[package]] [[package]]

View File

@@ -7,6 +7,7 @@ sdgb-api = { path = "./sdgb-api" }
snafu = { version = "0.8.6", features = ["backtrace", "rust_1_81"] } snafu = { version = "0.8.6", features = ["backtrace", "rust_1_81"] }
serde_json = "1.0.141" serde_json = "1.0.141"
strum = { version = "0.27.2", features = ["derive"] }
[profile.release] [profile.release]
lto = true lto = true

View File

@@ -8,6 +8,7 @@ license = "GPL-3.0"
[dependencies] [dependencies]
snafu = { workspace = true } snafu = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
strum = {workspace = true}
# hashing # hashing
digest = "0.10.7" digest = "0.10.7"
@@ -16,7 +17,6 @@ md5 = "0.8.0"
# other utils # other utils
chrono = "0.4.41" chrono = "0.4.41"
strum = { version = "0.27.2", features = ["derive"] }
# network request # network request
nyquest = { version = "0.2.0", features = ["async", "json"] } nyquest = { version = "0.2.0", features = ["async", "json"] }

View File

@@ -10,42 +10,71 @@ use nyquest::{
use crate::error::ApiError; use crate::error::ApiError;
const AES_KEY: &[u8; 16] = br#"/?jo+"L&\Cr9(=kG"#;
const AES_IV: &[u8; 16] = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; const AES_IV: &[u8; 16] = b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
pub async fn delivery_raw( pub trait Delivery {
const AES_KEY: &[u8; 16];
const TITLE_ID: &str;
const USER_AGENT: &str;
}
/// only for fun
pub struct SDHJ;
pub struct SDGB;
impl Delivery for SDHJ {
const AES_KEY: &[u8; 16] = b"-a57UX4y9/h(ImAQ";
const TITLE_ID: &str = "SDHJ";
const USER_AGENT: &str = "SDHJ;Windows/Lite";
}
impl Delivery for SDGB {
const AES_KEY: &[u8; 16] = br#"/?jo+"L&\Cr9(=kG"#;
const TITLE_ID: &str = "SDGB";
const USER_AGENT: &str = "SDGB;Windows/Lite";
}
pub async fn delivery_raw<D>(
client: &AsyncClient, client: &AsyncClient,
title_ver: impl AsRef<str>, title_ver: impl AsRef<str>,
) -> Result<Vec<u8>, ApiError> { ) -> Result<Vec<u8>, ApiError>
where
D: Delivery,
{
let title_ver = title_ver.as_ref(); let title_ver = title_ver.as_ref();
let params = format!("title_id=SDGB&title_ver={title_ver}&client_id=A63E01C2805"); let params = format!(
let enc_data = encrypt(params, AES_KEY, AES_IV)?; "title_id={}&title_ver={title_ver}&client_id=A63E01C2805",
D::TITLE_ID
);
let enc_data = encrypt(params, D::AES_KEY, AES_IV)?;
let req = Request::post("http://at.sys-allnet.cn/net/delivery/instruction") let req = Request::post("http://at.sys-allnet.cn/net/delivery/instruction")
.with_body(Body::bytes(enc_data, "application/xxx-form-urlencoded")) .with_body(Body::bytes(enc_data, "application/xxx-form-urlencoded"))
.with_header(USER_AGENT, "SDGB;Windows/Lite") .with_header(USER_AGENT, D::USER_AGENT)
.with_header("Pragma", "DFI"); .with_header("Pragma", "DFI");
let mut resp = client.request(req).await?.bytes().await?; let mut resp = client.request(req).await?.bytes().await?;
let dec_len = decrypt(&mut resp, AES_KEY, AES_IV)?.len(); let dec_len = decrypt(&mut resp, D::AES_KEY, AES_IV)?.len();
resp.truncate(dec_len); Ok(resp[16..dec_len].to_vec())
Ok(resp)
} }
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>; type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>; type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
fn encrypt(data: impl AsRef<[u8]>, key: &[u8; 16], iv: &[u8; 16]) -> Result<Vec<u8>, ApiError> { fn encrypt(data: impl AsRef<[u8]>, key: &[u8; 16], iv: &[u8; 16]) -> Result<Vec<u8>, ApiError> {
let mut headed_data = vec![0u8; 16];
headed_data.extend_from_slice(data.as_ref());
let key = GenericArray::from_slice(key); let key = GenericArray::from_slice(key);
let iv = GenericArray::from_slice(iv); let iv = GenericArray::from_slice(iv);
let encryptor = Aes128CbcEnc::new(key, iv); let encryptor = Aes128CbcEnc::new(key, iv);
let data = data.as_ref();
let bs = aes::Aes256::block_size();
let pad_len = bs - data.len() % bs;
let mut buf = vec![0; pad_len + data.len()]; let bs = aes::Aes128::block_size();
buf[..data.len()].copy_from_slice(&data); let pad_len = bs - headed_data.len() % bs;
encryptor.encrypt_padded_mut::<Pkcs7>(&mut buf, data.len())?;
let mut buf = vec![0; pad_len + headed_data.len()];
buf[..headed_data.len()].copy_from_slice(&headed_data);
encryptor.encrypt_padded_mut::<Pkcs7>(&mut buf, headed_data.len())?;
Ok(buf) Ok(buf)
} }

View File

@@ -9,6 +9,7 @@ description = "CLI tool for SDGB protocol"
snafu = { workspace = true } snafu = { workspace = true }
sdgb-api = { workspace = true } sdgb-api = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
strum = { workspace = true }
nyquest-preset = { version = "0.2.0", features = ["async"] } nyquest-preset = { version = "0.2.0", features = ["async"] }
compio = { version = "0.15.0", default-features = false, features = [ compio = { version = "0.15.0", default-features = false, features = [

View File

@@ -1,5 +1,6 @@
use palc::Parser; use palc::Parser;
use palc::Subcommand; use palc::Subcommand;
use strum::EnumString;
#[derive(Parser)] #[derive(Parser)]
#[command(about = "SDGB api tool", long_about = env!("CARGO_PKG_DESCRIPTION"))] #[command(about = "SDGB api tool", long_about = env!("CARGO_PKG_DESCRIPTION"))]
@@ -8,8 +9,28 @@ pub struct Cli {
pub command: Commands, pub command: Commands,
} }
#[derive(EnumString)]
pub enum AuthLiteVariant {
SDGB,
SDHJ,
}
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum Commands { pub enum Commands {
/// Login with QRCode from wechat
QRLogin {
/// content of the qrcode, only the last 64 characters were used
#[arg(short, long)]
qrcode_content: String,
},
AuthLite {
#[arg(short, long, default_value = "1.50")]
title_ver: String,
#[arg(long, default_value = "SDGB")]
variant: AuthLiteVariant,
},
Ping, Ping,
Preview { Preview {
#[arg(short, long)] #[arg(short, long)]
@@ -19,10 +40,4 @@ pub enum Commands {
#[arg(short, long)] #[arg(short, long)]
user_id: u32, user_id: u32,
}, },
/// Login with QRCode from wechat
QRLogin {
/// content of the qrcode, only the last 64 characters were used
#[arg(short, long)]
qrcode_content: String,
},
} }

View File

@@ -2,6 +2,7 @@ use nyquest_preset::nyquest::ClientBuilder;
use palc::Parser; use palc::Parser;
use sdgb_api::{ use sdgb_api::{
all_net::QRCode, all_net::QRCode,
auth_lite::{SDGB, SDHJ, delivery_raw},
title::{ title::{
MaiVersionExt, Sdgb1_40, Sdgb1_50, MaiVersionExt, Sdgb1_40, Sdgb1_50,
methods::APIMethod, methods::APIMethod,
@@ -69,6 +70,14 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
Err(e) => error!("login failed: {e}"), Err(e) => error!("login failed: {e}"),
} }
} }
commands::Commands::AuthLite { title_ver, variant } => {
let resp = match variant {
commands::AuthLiteVariant::SDGB => delivery_raw::<SDGB>(&client, title_ver).await?,
commands::AuthLiteVariant::SDHJ => delivery_raw::<SDHJ>(&client, title_ver).await?,
};
println!("{}", String::from_utf8_lossy(&resp));
}
} }
Ok(()) Ok(())