mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 14:36:16 +01:00
Impliment getting app token
This commit is contained in:
87
Cargo.lock
generated
87
Cargo.lock
generated
@@ -8,6 +8,16 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aead"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"generic-array 0.14.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aes"
|
name = "aes"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@@ -19,6 +29,20 @@ dependencies = [
|
|||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"ghash",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_system_properties"
|
name = "android_system_properties"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -276,9 +300,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
|
"rand_core 0.6.4",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctr"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "data-encoding"
|
name = "data-encoding"
|
||||||
version = "2.10.0"
|
version = "2.10.0"
|
||||||
@@ -474,6 +508,16 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ghash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||||
|
dependencies = [
|
||||||
|
"opaque-debug",
|
||||||
|
"polyval",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@@ -801,6 +845,7 @@ name = "isideload"
|
|||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
|
"aes-gcm",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64",
|
"base64",
|
||||||
"cbc",
|
"cbc",
|
||||||
@@ -920,6 +965,7 @@ name = "minimal"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"isideload",
|
"isideload",
|
||||||
|
"plist-macro",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@@ -1009,6 +1055,12 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1065,6 +1117,18 @@ dependencies = [
|
|||||||
"plist",
|
"plist",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polyval"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -1185,7 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core 0.9.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1195,7 +1259,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1903,6 +1976,16 @@ version = "1.0.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
isideload = { path = "../../isideload" }
|
isideload = { path = "../../isideload" }
|
||||||
|
plist-macro = "0.1.3"
|
||||||
tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] }
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::env;
|
|||||||
use isideload::{
|
use isideload::{
|
||||||
anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder,
|
anisette::remote_v3::RemoteV3AnisetteProvider, auth::apple_account::AppleAccountBuilder,
|
||||||
};
|
};
|
||||||
|
use plist_macro::pretty_print_dictionary;
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use tracing_subscriber::FmtSubscriber;
|
use tracing_subscriber::FmtSubscriber;
|
||||||
|
|
||||||
@@ -36,8 +37,15 @@ async fn main() {
|
|||||||
.login(apple_password, get_2fa_code)
|
.login(apple_password, get_2fa_code)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match account {
|
match &account {
|
||||||
Ok(a) => println!("Logged in. {}", a),
|
Ok(a) => println!("Logged in. {}", a),
|
||||||
Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e),
|
Err(e) => eprintln!("Failed to log in to Apple ID: {:?}", e),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let app_token = account.unwrap().get_app_token("xcode.auth").await;
|
||||||
|
|
||||||
|
match app_token {
|
||||||
|
Ok(t) => println!("App token: {}", pretty_print_dictionary(&t)),
|
||||||
|
Err(e) => eprintln!("Failed to get app token: {:?}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,3 +38,4 @@ pbkdf2 = "0.12.2"
|
|||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
cbc = { version = "0.1.2", features = ["std"] }
|
cbc = { version = "0.1.2", features = ["std"] }
|
||||||
aes = "0.8.4"
|
aes = "0.8.4"
|
||||||
|
aes-gcm = "0.10.3"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::fs;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use base64::prelude::*;
|
use base64::prelude::*;
|
||||||
use chrono::{SubsecRound, Utc};
|
// use chrono::{SubsecRound, Utc};
|
||||||
use plist_macro::plist;
|
use plist_macro::plist;
|
||||||
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ use crate::{
|
|||||||
auth::grandslam::{GrandSlam, GrandSlamErrorChecker},
|
auth::grandslam::{GrandSlam, GrandSlamErrorChecker},
|
||||||
util::plist::PlistDataExtract,
|
util::plist::PlistDataExtract,
|
||||||
};
|
};
|
||||||
use aes::cipher::block_padding::Pkcs7;
|
use aes::{
|
||||||
|
Aes256,
|
||||||
|
cipher::{block_padding::Pkcs7, consts::U16},
|
||||||
|
};
|
||||||
|
use aes_gcm::{AeadInPlace, AesGcm, KeyInit, Nonce};
|
||||||
use base64::{Engine, prelude::BASE64_STANDARD};
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||||
use cbc::cipher::{BlockDecryptMut, KeyIvInit};
|
use cbc::cipher::{BlockDecryptMut, KeyIvInit};
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::Mac;
|
||||||
use plist::Dictionary;
|
use plist::Dictionary;
|
||||||
use plist_macro::plist;
|
use plist_macro::plist;
|
||||||
use reqwest::header::{HeaderMap, HeaderValue};
|
use reqwest::header::{HeaderMap, HeaderValue};
|
||||||
@@ -563,12 +567,93 @@ impl AppleAccount {
|
|||||||
Ok(LoginState::LoggedIn)
|
Ok(LoginState::LoggedIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_session_key(usr: &SrpClientVerifier<Sha256>, name: &str) -> Result<Vec<u8>, Report> {
|
pub async fn get_app_token(&mut self, app: &str) -> Result<Dictionary, Report> {
|
||||||
Ok(Hmac::<Sha256>::new_from_slice(&usr.key())?
|
let app = if app.contains("com.apple.gs.") {
|
||||||
.chain_update(name.as_bytes())
|
app.to_string()
|
||||||
|
} else {
|
||||||
|
format!("com.apple.gs.{}", app)
|
||||||
|
};
|
||||||
|
|
||||||
|
let spd = self
|
||||||
|
.spd
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| report!("SPD data not available, cannot get app token"))?;
|
||||||
|
|
||||||
|
let dsid = spd.get_str("adsid").context("Failed to get app token")?;
|
||||||
|
let auth_token = spd
|
||||||
|
.get_str("GsIdmsToken")
|
||||||
|
.context("Failed to get app token")?;
|
||||||
|
let session_key = spd.get_data("sk").context("Failed to get app token")?;
|
||||||
|
let c = spd.get_data("c").context("Failed to get app token")?;
|
||||||
|
|
||||||
|
let checksum = <hmac::Hmac<Sha256> as hmac::Mac>::new_from_slice(session_key)
|
||||||
|
.unwrap()
|
||||||
|
.chain_update("apptokens".as_bytes())
|
||||||
|
.chain_update(dsid.as_bytes())
|
||||||
|
.chain_update(app.as_bytes())
|
||||||
.finalize()
|
.finalize()
|
||||||
.into_bytes()
|
.into_bytes()
|
||||||
.to_vec())
|
.to_vec();
|
||||||
|
|
||||||
|
let gs_service_url = self.grandslam_client.get_url("gsService").await?;
|
||||||
|
let cpd = self
|
||||||
|
.anisette_data
|
||||||
|
.get_client_provided_data(SERIAL_NUMBER.to_string());
|
||||||
|
|
||||||
|
let request = plist!(dict {
|
||||||
|
"Header": {
|
||||||
|
"Version": "1.0.1"
|
||||||
|
},
|
||||||
|
"Request": {
|
||||||
|
"app": [app],
|
||||||
|
"c": c,
|
||||||
|
"checksum": checksum,
|
||||||
|
"cpd": cpd,
|
||||||
|
"o": "apptokens",
|
||||||
|
"u": dsid,
|
||||||
|
"t": auth_token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let resp = self
|
||||||
|
.grandslam_client
|
||||||
|
.plist_request(&gs_service_url, &request, None)
|
||||||
|
.await
|
||||||
|
.context("Failed to send app token request")?
|
||||||
|
.check_grandslam_error()
|
||||||
|
.context("GrandSlam error during app token request")?;
|
||||||
|
|
||||||
|
let encrypted_token = resp
|
||||||
|
.get_data("et")
|
||||||
|
.context("Failed to get encrypted token")?;
|
||||||
|
|
||||||
|
let decrypted_token = Self::decrypt_gcm(&encrypted_token, &session_key)
|
||||||
|
.context("Failed to decrypt app token")?;
|
||||||
|
|
||||||
|
let token: Dictionary = plist::from_bytes(&decrypted_token)
|
||||||
|
.context("Failed to parse decrypted app token plist")?;
|
||||||
|
|
||||||
|
let status = token
|
||||||
|
.get_signed_integer("status-code")
|
||||||
|
.context("Failed to get status code from app token")?;
|
||||||
|
if status != 200 {
|
||||||
|
bail!("App token request failed with status code {}", status);
|
||||||
|
}
|
||||||
|
let token_dict = token
|
||||||
|
.get_dict("t")
|
||||||
|
.context("Failed to get token dictionary from app token")?;
|
||||||
|
|
||||||
|
Ok(token_dict.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_session_key(usr: &SrpClientVerifier<Sha256>, name: &str) -> Result<Vec<u8>, Report> {
|
||||||
|
Ok(
|
||||||
|
<hmac::Hmac<Sha256> as hmac::Mac>::new_from_slice(&usr.key())?
|
||||||
|
.chain_update(name.as_bytes())
|
||||||
|
.finalize()
|
||||||
|
.into_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_cbc(usr: &SrpClientVerifier<Sha256>, data: &[u8]) -> Result<Vec<u8>, Report> {
|
fn decrypt_cbc(usr: &SrpClientVerifier<Sha256>, data: &[u8]) -> Result<Vec<u8>, Report> {
|
||||||
@@ -581,6 +666,43 @@ impl AppleAccount {
|
|||||||
.decrypt_padded_vec_mut::<Pkcs7>(&data)?,
|
.decrypt_padded_vec_mut::<Pkcs7>(&data)?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decrypt_gcm(data: &[u8], key: &[u8]) -> Result<Vec<u8>, Report> {
|
||||||
|
if data.len() < 3 + 16 + 16 {
|
||||||
|
bail!(
|
||||||
|
"Encrypted token is too short to be valid (only {} bytes)",
|
||||||
|
data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let header = &data[0..3];
|
||||||
|
if header != b"XYZ" {
|
||||||
|
bail!(
|
||||||
|
"Encrypted token is in an unknown format: {}",
|
||||||
|
String::from_utf8_lossy(header)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let iv = &data[3..19];
|
||||||
|
let ciphertext_and_tag = &data[19..];
|
||||||
|
|
||||||
|
if key.len() != 32 {
|
||||||
|
bail!("Session key is not the correct length: {} bytes", key.len());
|
||||||
|
}
|
||||||
|
if iv.len() != 16 {
|
||||||
|
bail!("IV is not the correct length: {} bytes", iv.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = aes_gcm::Key::<AesGcm<Aes256, U16>>::from_slice(key);
|
||||||
|
let cipher = AesGcm::<Aes256, U16>::new(key);
|
||||||
|
let nonce = Nonce::<U16>::from_slice(iv);
|
||||||
|
|
||||||
|
let mut buf = ciphertext_and_tag.to_vec();
|
||||||
|
|
||||||
|
cipher
|
||||||
|
.decrypt_in_place(nonce, header, &mut buf)
|
||||||
|
.map_err(|e| report!("Failed to decrypt gcm: {}", e))?;
|
||||||
|
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for AppleAccount {
|
impl std::fmt::Display for AppleAccount {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use rootcause::prelude::*;
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
SideloadError,
|
||||||
anisette::AnisetteClientInfo,
|
anisette::AnisetteClientInfo,
|
||||||
util::plist::{PlistDataExtract, plist_to_xml_string},
|
util::plist::{PlistDataExtract, plist_to_xml_string},
|
||||||
};
|
};
|
||||||
@@ -166,24 +167,18 @@ impl GrandSlam {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait GrandSlamErrorChecker {
|
pub trait GrandSlamErrorChecker {
|
||||||
fn check_grandslam_error(self) -> Result<Dictionary, Report<GrandSlamError>>;
|
fn check_grandslam_error(self) -> Result<Dictionary, Report<SideloadError>>;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum GrandSlamError {
|
|
||||||
#[error("Auth error {0}: {1}")]
|
|
||||||
AuthWithMessage(i64, String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GrandSlamErrorChecker for Dictionary {
|
impl GrandSlamErrorChecker for Dictionary {
|
||||||
fn check_grandslam_error(self) -> Result<Self, Report<GrandSlamError>> {
|
fn check_grandslam_error(self) -> Result<Self, Report<SideloadError>> {
|
||||||
let result = match self.get("Status") {
|
let result = match self.get("Status") {
|
||||||
Some(plist::Value::Dictionary(d)) => d,
|
Some(plist::Value::Dictionary(d)) => d,
|
||||||
_ => &self,
|
_ => &self,
|
||||||
};
|
};
|
||||||
|
|
||||||
if result.get_signed_integer("ec").unwrap_or(0) != 0 {
|
if result.get_signed_integer("ec").unwrap_or(0) != 0 {
|
||||||
bail!(GrandSlamError::AuthWithMessage(
|
bail!(SideloadError::AuthWithMessage(
|
||||||
result.get_signed_integer("ec").unwrap_or(-1),
|
result.get_signed_integer("ec").unwrap_or(-1),
|
||||||
result.get_str("em").unwrap_or("Unknown error").to_string(),
|
result.get_str("em").unwrap_or("Unknown error").to_string(),
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ pub mod anisette;
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum SideloadError {
|
||||||
|
#[error("Auth error {0}: {1}")]
|
||||||
|
AuthWithMessage(i64, String),
|
||||||
|
|
||||||
|
#[error("Plist parse error: {0}")]
|
||||||
|
PlistParseError(String),
|
||||||
|
}
|
||||||
|
|
||||||
struct ReqwestErrorFormatter;
|
struct ReqwestErrorFormatter;
|
||||||
|
|
||||||
impl ContextFormatterHook<reqwest::Error> for ReqwestErrorFormatter {
|
impl ContextFormatterHook<reqwest::Error> for ReqwestErrorFormatter {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
use plist::Dictionary;
|
||||||
use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes, pretty_print_dictionary};
|
use plist_macro::{plist_to_xml_bytes, plist_value_to_xml_bytes, pretty_print_dictionary};
|
||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
|
|
||||||
pub fn plist_to_xml_string(p: &plist::Dictionary) -> String {
|
pub fn plist_to_xml_string(p: &Dictionary) -> String {
|
||||||
String::from_utf8(plist_to_xml_bytes(p)).unwrap()
|
String::from_utf8(plist_to_xml_bytes(p)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,24 +10,59 @@ pub fn plist_value_to_xml_string(p: &plist::Value) -> String {
|
|||||||
String::from_utf8(plist_value_to_xml_bytes(p)).unwrap()
|
String::from_utf8(plist_value_to_xml_bytes(p)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SensitivePlistAttachment {
|
||||||
|
pub plist: Dictionary,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SensitivePlistAttachment {
|
||||||
|
pub fn new(plist: Dictionary) -> Self {
|
||||||
|
SensitivePlistAttachment { plist }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
// if env variable DEBUG_SENSITIVE is set, print full plist
|
||||||
|
if std::env::var("DEBUG_SENSITIVE").is_ok() {
|
||||||
|
return writeln!(f, "{}", pretty_print_dictionary(&self.plist));
|
||||||
|
}
|
||||||
|
writeln!(
|
||||||
|
f,
|
||||||
|
"<Potentially sensitive data - set DEBUG_SENSITIVE env variable to see contents>"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SensitivePlistAttachment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for SensitivePlistAttachment {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait PlistDataExtract {
|
pub trait PlistDataExtract {
|
||||||
fn get_data(&self, key: &str) -> Result<&[u8], Report>;
|
fn get_data(&self, key: &str) -> Result<&[u8], Report>;
|
||||||
fn get_str(&self, key: &str) -> Result<&str, Report>;
|
fn get_str(&self, key: &str) -> Result<&str, Report>;
|
||||||
fn get_string(&self, key: &str) -> Result<String, Report>;
|
fn get_string(&self, key: &str) -> Result<String, Report>;
|
||||||
fn get_signed_integer(&self, key: &str) -> Result<i64, Report>;
|
fn get_signed_integer(&self, key: &str) -> Result<i64, Report>;
|
||||||
fn get_dict(&self, key: &str) -> Result<&plist::Dictionary, Report>;
|
fn get_dict(&self, key: &str) -> Result<&Dictionary, Report>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlistDataExtract for plist::Dictionary {
|
impl PlistDataExtract for Dictionary {
|
||||||
fn get_data(&self, key: &str) -> Result<&[u8], Report> {
|
fn get_data(&self, key: &str) -> Result<&[u8], Report> {
|
||||||
self.get(key).and_then(|v| v.as_data()).ok_or_else(|| {
|
self.get(key).and_then(|v| v.as_data()).ok_or_else(|| {
|
||||||
report!("Plist missing data for key '{}'", key).attach(pretty_print_dictionary(self))
|
report!("Plist missing data for key '{}'", key)
|
||||||
|
.attach(SensitivePlistAttachment::new(self.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_str(&self, key: &str) -> Result<&str, Report> {
|
fn get_str(&self, key: &str) -> Result<&str, Report> {
|
||||||
self.get(key).and_then(|v| v.as_string()).ok_or_else(|| {
|
self.get(key).and_then(|v| v.as_string()).ok_or_else(|| {
|
||||||
report!("Plist missing string for key '{}'", key).attach(pretty_print_dictionary(self))
|
report!("Plist missing string for key '{}'", key)
|
||||||
|
.attach(SensitivePlistAttachment::new(self.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +72,7 @@ impl PlistDataExtract for plist::Dictionary {
|
|||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
report!("Plist missing string for key '{}'", key)
|
report!("Plist missing string for key '{}'", key)
|
||||||
.attach(pretty_print_dictionary(self))
|
.attach(SensitivePlistAttachment::new(self.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,16 +81,16 @@ impl PlistDataExtract for plist::Dictionary {
|
|||||||
.and_then(|v| v.as_signed_integer())
|
.and_then(|v| v.as_signed_integer())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
report!("Plist missing signed integer for key '{}'", key)
|
report!("Plist missing signed integer for key '{}'", key)
|
||||||
.attach(pretty_print_dictionary(self))
|
.attach(SensitivePlistAttachment::new(self.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_dict(&self, key: &str) -> Result<&plist::Dictionary, Report> {
|
fn get_dict(&self, key: &str) -> Result<&Dictionary, Report> {
|
||||||
self.get(key)
|
self.get(key)
|
||||||
.and_then(|v| v.as_dictionary())
|
.and_then(|v| v.as_dictionary())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
report!("Plist missing dictionary for key '{}'", key)
|
report!("Plist missing dictionary for key '{}'", key)
|
||||||
.attach(pretty_print_dictionary(self))
|
.attach(SensitivePlistAttachment::new(self.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user