diff --git a/Cargo.lock b/Cargo.lock index e3c1884..d67943c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "aligned-array" version = "1.0.1" @@ -101,6 +112,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.6.1" @@ -122,6 +142,15 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.2.30" @@ -157,6 +186,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "compio" version = "0.15.0" @@ -316,6 +355,24 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -426,6 +483,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -574,6 +641,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -1015,8 +1092,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" name = "sdgb-api" version = "0.1.0" dependencies = [ + "aes", + "cbc", "chrono", "digest", + "flate2", "hmac-sha256", "nyquest", "serde", diff --git a/sdgb-api/Cargo.toml b/sdgb-api/Cargo.toml index b198d85..2fbac9a 100644 --- a/sdgb-api/Cargo.toml +++ b/sdgb-api/Cargo.toml @@ -17,3 +17,6 @@ nyquest = { version = "0.2.0", features = ["async", "json"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.141" +flate2 = "1.1.2" +cbc = "0.1.2" +aes = "0.8.4" diff --git a/sdgb-api/src/title/encryption/constants/mod.rs b/sdgb-api/src/title/encryption/constants/mod.rs new file mode 100644 index 0000000..c08fa49 --- /dev/null +++ b/sdgb-api/src/title/encryption/constants/mod.rs @@ -0,0 +1,4 @@ +pub const SDGB_1_50_KEY: &[u8; 32] = b"a>32bVP7v<63BVLkY[xM>daZ1s9MBP; +type Aes256CbcDec = cbc::Decryptor; + +pub fn decompress(data: impl AsRef<[u8]>) -> Vec { + 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>( + data: &'ct mut impl AsMut<[u8]>, + key: &[u8; 32], + iv: &[u8; 16], +) -> Result<&'ct [u8], ApiError> { + let key = GenericArray::from_slice(key); + let iv = GenericArray::from_slice(iv); + let decryptor = Aes256CbcDec::new(key, iv); + let result = decryptor.decrypt_padded_mut::(data.as_mut())?; + Ok(result) +} + +#[cfg(test)] +mod _tests { + + use crate::title::encryption::{constants::*, *}; + + #[test] + fn test_sdgb_140_dec() { + 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"}"#); + } + + #[test] + fn test_sdgb_150_dec() { + let mut 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, + 118, 80, 21, 159, 37, 248, 54, 85, 94, 61, 48, 59, 117, 163, 161, 165, 206, 36, 23, 71, + 73, 231, 214, 81, 82, 117, 115, 32, 122, 8, 161, 213, 252, 125, 35, 131, 144, 147, 74, + 27, 138, 26, 133, 240, 73, 197, 25, 173, 213, 237, 216, 76, 101, 210, 202, 172, 216, + 91, 83, 87, 243, 79, 143, 42, 149, 130, 210, 13, 63, 98, 198, 165, 122, 58, 254, 39, + 150, 71, 155, 231, 55, 142, 5, 102, 253, 148, 191, 9, 212, 188, 69, 236, 60, 152, 13, + 40, 111, 219, 162, 160, 34, 150, 211, 85, 190, 176, 137, 60, 25, 228, 218, 163, 240, + 143, 44, 238, 77, 92, 12, 166, 209, 238, 100, 92, 98, 142, 10, 104, 213, 12, 89, 236, + 114, 212, 222, 0, 237, 1, 208, 216, 114, 114, 71, 135, 21, 213, 61, 6, 162, 155, 119, + 143, 70, 83, 136, 136, 136, 251, 94, 137, 244, 26, 125, 15, 132, 207, 60, 57, 105, 78, + 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()) + } +} diff --git a/sdgb-api/src/title/error/mod.rs b/sdgb-api/src/title/error/mod.rs new file mode 100644 index 0000000..44898f6 --- /dev/null +++ b/sdgb-api/src/title/error/mod.rs @@ -0,0 +1,14 @@ +use aes::cipher::block_padding::UnpadError; +use snafu::Snafu; + +#[derive(Debug, Snafu)] +pub enum ApiError { + #[snafu(display("unpad data: {error}"))] + UnpadError { error: UnpadError }, +} + +impl From for ApiError { + fn from(error: UnpadError) -> Self { + Self::UnpadError { error } + } +} diff --git a/sdgb-api/src/title/mod.rs b/sdgb-api/src/title/mod.rs index 8b13789..ec203f8 100644 --- a/sdgb-api/src/title/mod.rs +++ b/sdgb-api/src/title/mod.rs @@ -1 +1,2 @@ - +pub mod encryption; +mod error;