feat: authlite command
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1124,6 +1124,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu",
|
"snafu",
|
||||||
"spdlog-rs",
|
"spdlog-rs",
|
||||||
|
"strum 0.27.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"] }
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 = [
|
||||||
|
|||||||
@@ -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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
|||||||
Reference in New Issue
Block a user