|
|
|
|
@@ -1,24 +1,78 @@
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
use std::io::{Read, Write as _};
|
|
|
|
|
|
|
|
|
|
use aes::cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7};
|
|
|
|
|
use aes::cipher::{
|
|
|
|
|
BlockDecryptMut, BlockEncryptMut, BlockSizeUser, KeyIvInit, block_padding::Pkcs7,
|
|
|
|
|
};
|
|
|
|
|
use digest::generic_array::GenericArray;
|
|
|
|
|
use flate2::read::ZlibDecoder;
|
|
|
|
|
use flate2::write::ZlibEncoder;
|
|
|
|
|
use flate2::{Compression, read::ZlibDecoder};
|
|
|
|
|
|
|
|
|
|
use crate::title::error::ApiError;
|
|
|
|
|
use crate::title::{MaiVersion, MaiVersionExt, Sdgb1_40, Sdgb1_50, error::ApiError};
|
|
|
|
|
|
|
|
|
|
pub mod constants;
|
|
|
|
|
impl MaiVersionExt for Sdgb1_40 {
|
|
|
|
|
fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> {
|
|
|
|
|
let mut decompressed = decompress(data.as_mut());
|
|
|
|
|
let unpad_size = decrypt(&mut decompressed, Self::AES_KEY, Self::AES_IV)?.len();
|
|
|
|
|
decompressed.truncate(unpad_size);
|
|
|
|
|
Ok(decompressed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
|
|
|
|
fn encode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
|
|
|
|
|
let enc = encrypt(data, Self::AES_KEY, Self::AES_IV)?;
|
|
|
|
|
let compressed = compress(enc)?;
|
|
|
|
|
Ok(compressed)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MaiVersionExt for Sdgb1_50 {
|
|
|
|
|
fn decode(mut data: impl AsMut<[u8]>) -> Result<Vec<u8>, ApiError> {
|
|
|
|
|
let decrypted = decrypt(&mut data, Self::AES_KEY, Self::AES_IV)?;
|
|
|
|
|
let decompressed = decompress(decrypted);
|
|
|
|
|
Ok(decompressed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn encode(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
|
|
|
|
|
let compressed = compress(data)?;
|
|
|
|
|
let enc = encrypt(compressed, Self::AES_KEY, Self::AES_IV)?;
|
|
|
|
|
Ok(enc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
|
|
|
|
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
|
|
|
|
|
|
|
|
|
pub fn decompress(data: impl AsRef<[u8]>) -> Vec<u8> {
|
|
|
|
|
fn compress(data: impl AsRef<[u8]>) -> Result<Vec<u8>, ApiError> {
|
|
|
|
|
let mut buf = Vec::with_capacity(data.as_ref().len());
|
|
|
|
|
// 6 is the default compression level of zlib
|
|
|
|
|
let mut encoder = ZlibEncoder::new(&mut buf, Compression::new(6));
|
|
|
|
|
encoder.write_all(data.as_ref())?;
|
|
|
|
|
encoder.finish()?;
|
|
|
|
|
Ok(buf)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
buf
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decrypt<'ct>(
|
|
|
|
|
fn encrypt(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 encryptor = Aes256CbcEnc::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()];
|
|
|
|
|
buf[..data.len()].copy_from_slice(&data);
|
|
|
|
|
|
|
|
|
|
encryptor.encrypt_padded_mut::<Pkcs7>(&mut buf, data.len())?;
|
|
|
|
|
Ok(buf)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn decrypt<'ct>(
|
|
|
|
|
data: &'ct mut impl AsMut<[u8]>,
|
|
|
|
|
key: &[u8; 32],
|
|
|
|
|
iv: &[u8; 16],
|
|
|
|
|
@@ -33,24 +87,26 @@ pub fn decrypt<'ct>(
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod _tests {
|
|
|
|
|
|
|
|
|
|
use crate::title::encryption::{constants::*, *};
|
|
|
|
|
use crate::title::{Sdgb1_50, encryption::*};
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_sdgb_140_dec() {
|
|
|
|
|
fn test_sdgb_140_dec_enc() -> Result<(), ApiError> {
|
|
|
|
|
let data = [
|
|
|
|
|
120_u8, 156, 171, 77, 91, 233, 184, 108, 2, 71, 125, 142, 118, 135, 112, 181, 85, 217,
|
|
|
|
|
239, 243, 159, 153, 248, 98, 159, 185, 63, 43, 173, 106, 221, 115, 104, 105, 221, 107,
|
|
|
|
|
0, 241, 176, 16, 37,
|
|
|
|
|
];
|
|
|
|
|
let mut decompressed = decompress(data);
|
|
|
|
|
let data =
|
|
|
|
|
decrypt(&mut decompressed, SDGB_1_40_KEY, SDGB_1_40_IV).expect("decryption failed!");
|
|
|
|
|
assert_eq!(&data, br#"{"result":"Pong"}"#);
|
|
|
|
|
|
|
|
|
|
let dec = Sdgb1_40::decode(data)?;
|
|
|
|
|
assert_eq!(dec, br#"{"result":"Pong"}"#);
|
|
|
|
|
let enc = Sdgb1_40::encode(dec)?;
|
|
|
|
|
assert_eq!(enc, data);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_sdgb_150_dec() {
|
|
|
|
|
let mut data = [
|
|
|
|
|
fn test_sdgb_150_dec_enc() -> Result<(), ApiError> {
|
|
|
|
|
let data = [
|
|
|
|
|
161, 166, 3, 157, 202, 233, 151, 73, 40, 113, 186, 162, 177, 46, 118, 113, 98, 231, 67,
|
|
|
|
|
185, 246, 180, 109, 253, 1, 152, 0, 31, 81, 211, 28, 137, 95, 12, 110, 105, 181, 246,
|
|
|
|
|
177, 1, 45, 59, 182, 113, 56, 97, 56, 100, 34, 168, 27, 51, 228, 77, 192, 194, 248, 45,
|
|
|
|
|
@@ -66,8 +122,10 @@ mod _tests {
|
|
|
|
|
177, 84, 85, 152, 183, 77, 67, 163, 61, 165, 144, 125, 255, 89, 108, 58, 137, 142, 9,
|
|
|
|
|
8, 54, 228, 34, 55, 124, 158, 83, 36,
|
|
|
|
|
];
|
|
|
|
|
let decrypted = decrypt(&mut data, SDGB_1_50_KEY, SDGB_1_50_IV).expect("decryption failed");
|
|
|
|
|
let decompressed = decompress(decrypted);
|
|
|
|
|
assert_eq!(decompressed, r#"{"userId":10103750,"userName":"舞萌","isLogin":false,"lastGameId":null,"lastRomVersion":"1.01.00","lastDataVersion":"1.05.03","lastLoginDate":"1970-01-01 00:00:00","lastPlayDate":"1970-01-01 00:00:00","playerRating":1024,"nameplateId":0,"iconId":11,"trophyId":0,"isNetMember":1,"isInherit":false,"totalAwake":5,"dispRate":0,"dailyBonusDate":"1970-01-01 09:00:00","headPhoneVolume":null,"banState":0}"#.as_bytes())
|
|
|
|
|
let dec = Sdgb1_50::decode(data)?;
|
|
|
|
|
assert_eq!(dec, r#"{"userId":10103750,"userName":"舞萌","isLogin":false,"lastGameId":null,"lastRomVersion":"1.01.00","lastDataVersion":"1.05.03","lastLoginDate":"1970-01-01 00:00:00","lastPlayDate":"1970-01-01 00:00:00","playerRating":1024,"nameplateId":0,"iconId":11,"trophyId":0,"isNetMember":1,"isInherit":false,"totalAwake":5,"dispRate":0,"dailyBonusDate":"1970-01-01 09:00:00","headPhoneVolume":null,"banState":0}"#.as_bytes());
|
|
|
|
|
let enc = Sdgb1_50::encode(dec)?;
|
|
|
|
|
assert_eq!(enc, data);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|