From 223e77ef0aba0eaf790a8e4e22db094a70102ded Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 8 Aug 2025 15:04:32 -0400 Subject: [PATCH 01/30] Apply YCode changes --- icloud-auth/Cargo.toml | 27 +-- icloud-auth/rustcrypto-srp/Cargo.toml | 8 +- icloud-auth/src/client.rs | 295 +++++++++++++++++++------- icloud-auth/src/lib.rs | 7 +- icloud-auth/tests/auth_debug.rs | 31 ++- icloud-auth/tests/gsa_auth.rs | 13 +- omnisette/Cargo.toml | 12 +- omnisette/src/adi_proxy.rs | 10 +- omnisette/src/remote_anisette_v3.rs | 294 ++++++++++++++++--------- omnisette/src/store_services_core.rs | 26 ++- 10 files changed, 491 insertions(+), 232 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 24bc631..8b40bbe 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -3,28 +3,31 @@ name = "icloud_auth" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +vendored-botan = ["botan/vendored"] [dependencies] -serde = { version = "1.0.147", features = ["derive"] } -serde_json = { version = "1.0.87" } -base64 = "0.13.1" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = { version = "1.0.142" } +base64 = "0.22.1" srp = { version = "0.6.0", path = "./rustcrypto-srp" } -pbkdf2 = { version = "0.11.0" } +pbkdf2 = "0.11.0" sha2 = { version = "0.10.6" } -rand = { version = "0.8.5" } -rustls = { version = "0.20.7" } -rustls-pemfile = { version = "1.0.1" } -plist = { version = "1.3.1" } +rand = "0.9.2" +rustls = "0.23.31" +rustls-pemfile = "2.2.0" +plist = { version = "1.7.2" } hmac = "0.12.1" num-bigint = "0.4.3" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" -pkcs7 = "0.3.0" -reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } +pkcs7 = "0.4.1" +reqwest = { version = "0.12.22", features = ["blocking", "json", "default-tls"] } omnisette = {path = "../omnisette", features = ["remote-anisette-v3"]} -thiserror = "1.0.58" +thiserror = "2.0.12" tokio = "1" +botan = "0.12.0" [dev-dependencies] tokio = { version = "1", features = ["rt", "macros"] } diff --git a/icloud-auth/rustcrypto-srp/Cargo.toml b/icloud-auth/rustcrypto-srp/Cargo.toml index fc898af..253457a 100644 --- a/icloud-auth/rustcrypto-srp/Cargo.toml +++ b/icloud-auth/rustcrypto-srp/Cargo.toml @@ -14,15 +14,15 @@ rust-version = "1.56" [dependencies] num-bigint = "0.4" -generic-array = "0.14" +generic-array = "1.2" digest = "0.10" lazy_static = "1.2" subtle = "2.4" -base64 = "0.21.0" +base64 = "0.22.1" [dev-dependencies] -hex-literal = "0.3" +hex-literal = "1.0" num-traits = "0.2" -rand = "0.8" +rand = "0.9" sha1 = "0.10" sha2 = "0.10" diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index 52135fd..a69141f 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -1,13 +1,13 @@ -use std::str::FromStr; - -// use crate::anisette::AnisetteData; use crate::{anisette::AnisetteData, Error}; use aes::cipher::block_padding::Pkcs7; +use base64::{engine::general_purpose, Engine}; +use botan::Cipher; use cbc::cipher::{BlockDecryptMut, KeyIvInit}; use hmac::{Hmac, Mac}; use omnisette::AnisetteConfiguration; use reqwest::{ - header::{HeaderMap, HeaderName, HeaderValue}, Certificate, Client, ClientBuilder, Proxy, Response + header::{HeaderMap, HeaderName, HeaderValue}, + Certificate, Client, ClientBuilder, Response, }; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -15,6 +15,7 @@ use srp::{ client::{SrpClient, SrpClientVerifier}, groups::G_2048, }; +use std::str::FromStr; use tokio::sync::Mutex; const GSA_ENDPOINT: &str = "https://gsa.apple.com/grandslam/GsService2"; @@ -94,7 +95,7 @@ pub struct AppleAccount { client: Client, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct AppToken { pub app_tokens: plist::Dictionary, pub auth_token: String, @@ -121,7 +122,7 @@ struct VerifyCode { #[derive(Serialize, Debug, Clone)] struct PhoneNumber { - id: u32 + id: u32, } #[derive(Serialize, Debug, Clone)] @@ -129,7 +130,7 @@ struct PhoneNumber { pub struct VerifyBody { phone_number: PhoneNumber, mode: String, - security_code: Option + security_code: Option, } #[repr(C)] @@ -139,7 +140,7 @@ pub struct TrustedPhoneNumber { pub number_with_dial_code: String, pub last_two_digits: String, pub push_mode: String, - pub id: u32 + pub id: u32, } #[derive(Deserialize)] @@ -156,19 +157,9 @@ pub struct AuthenticationExtras { pub new_state: Option, } -// impl Send2FAToDevices { -// pub fn send_2fa_to_devices(&self) -> LoginResponse { -// self.account.send_2fa_to_devices().unwrap() -// } -// } - -// impl Verify2FA { -// pub fn verify_2fa(&self, tfa_code: &str) -> LoginResponse { -// self.account.verify_2fa(&tfa_code).unwrap() -// } -// } - -async fn parse_response(res: Result) -> Result { +async fn parse_response( + res: Result, +) -> Result { let res = res?.text().await?; let res: plist::Dictionary = plist::from_bytes(res.as_bytes())?; let res: plist::Value = res.get("Response").unwrap().to_owned(); @@ -187,6 +178,8 @@ impl AppleAccount { pub fn new_with_anisette(anisette: AnisetteData) -> Result { let client = ClientBuilder::new() .add_root_certificate(Certificate::from_der(APPLE_ROOT)?) + // uncomment when debugging w/ charles proxy + // .danger_accept_invalid_certs(true) .http1_title_case_headers() .connection_verbose(true) .build()?; @@ -199,8 +192,8 @@ impl AppleAccount { } pub async fn login( - appleid_closure: impl Fn() -> (String, String), - tfa_closure: impl Fn() -> String, + appleid_closure: impl Fn() -> Result<(String, String), String>, + tfa_closure: impl Fn() -> Result, config: AnisetteConfiguration, ) -> Result { let anisette = AnisetteData::new(config).await?; @@ -217,7 +210,6 @@ impl AppleAccount { pub async fn get_app_token(&self, app_name: &str) -> Result { let spd = self.spd.as_ref().unwrap(); - // println!("spd: {:#?}", spd); let dsid = spd.get("adsid").unwrap().as_string().unwrap(); let auth_token = spd.get("GsIdmsToken").unwrap().as_string().unwrap(); @@ -225,11 +217,6 @@ impl AppleAccount { let sk = spd.get("sk").unwrap().as_data().unwrap(); let c = spd.get("c").unwrap().as_data().unwrap(); - println!("adsid: {}", dsid); - println!("GsIdmsToken: {}", auth_token); - // println!("spd: {:#?}", spd); - println!("sk: {:#?}", sk); - println!("c: {:#?}", c); let checksum = Self::create_checksum(&sk.to_vec(), dsid, app_name); @@ -270,22 +257,78 @@ impl AppleAccount { plist::to_writer_xml(&mut buffer, &packet)?; let buffer = String::from_utf8(buffer).unwrap(); - println!("{:?}", gsa_headers.clone()); - println!("{:?}", buffer); - let res = self .client .post(GSA_ENDPOINT) .headers(gsa_headers.clone()) .body(buffer) - .send().await; + .send() + .await; let res = parse_response(res).await?; let err_check = Self::check_error(&res); if err_check.is_err() { return Err(err_check.err().unwrap()); } - println!("{:?}", res); - todo!() + + let encrypted_token = res + .get("et") + .ok_or(Error::Parse)? + .as_data() + .ok_or(Error::Parse)?; + + if encrypted_token.len() < 3 + 16 + 16 { + return Err(Error::Parse); + } + let header = &encrypted_token[0..3]; + if header != b"XYZ" { + return Err(Error::AuthSrpWithMessage( + 0, + "Encrypted token is in an unknown format.".to_string(), + )); + } + let iv = &encrypted_token[3..19]; + let ciphertext_and_tag = &encrypted_token[19..]; + + if sk.len() != 32 { + return Err(Error::Parse); + } + if iv.len() != 16 { + return Err(Error::Parse); + } + + let mut cipher = Cipher::new("AES-256/GCM", botan::CipherDirection::Decrypt) + .map_err(|_| Error::Parse)?; + cipher.set_key(sk).map_err(|_| Error::Parse)?; + cipher + .set_associated_data(header) + .map_err(|_| Error::Parse)?; + cipher.start(iv).map_err(|_| Error::Parse)?; + + let mut buf = ciphertext_and_tag.to_vec(); + buf = cipher.finish(&mut buf).map_err(|_| { + Error::AuthSrpWithMessage( + 0, + "Failed to decrypt app token (Botan AES-256/GCM).".to_string(), + ) + })?; + + let decrypted_token: plist::Dictionary = + plist::from_bytes(&buf).map_err(|_| Error::Parse)?; + + let t_val = decrypted_token.get("t").ok_or(Error::Parse)?; + let app_tokens = t_val.as_dictionary().ok_or(Error::Parse)?; + let app_token_dict = app_tokens.get(app_name).ok_or(Error::Parse)?; + let app_token = app_token_dict.as_dictionary().ok_or(Error::Parse)?; + let token = app_token + .get("token") + .and_then(|v| v.as_string()) + .ok_or(Error::Parse)?; + + Ok(AppToken { + app_tokens: app_tokens.clone(), + auth_token: token.to_string(), + app: app_name.to_string(), + }) } fn create_checksum(session_key: &Vec, dsid: &str, app_name: &str) -> Vec { @@ -312,32 +355,49 @@ impl AppleAccount { /// /// let anisette = AnisetteData::new(); /// let account = AppleAccount::login( - /// || ("test@waffle.me", "password") - /// || "123123", + /// || Ok(("test@waffle.me", "password")) + /// || Ok("123123"), /// anisette /// ); /// ``` /// Note: You would not provide the 2FA code like this, you would have to actually ask input for it. //TODO: add login_with_anisette and login, where login autodetcts anisette - pub async fn login_with_anisette (String, String), G: Fn() -> String>( + pub async fn login_with_anisette< + F: Fn() -> Result<(String, String), String>, + G: Fn() -> Result, + >( appleid_closure: F, tfa_closure: G, anisette: AnisetteData, ) -> Result { let mut _self = AppleAccount::new_with_anisette(anisette)?; - let (username, password) = appleid_closure(); + let (username, password) = appleid_closure().map_err(|e| { + Error::AuthSrpWithMessage(0, format!("Failed to get Apple ID credentials: {}", e)) + })?; let mut response = _self.login_email_pass(&username, &password).await?; loop { match response { LoginState::NeedsDevice2FA => response = _self.send_2fa_to_devices().await?, LoginState::Needs2FAVerification => { - response = _self.verify_2fa(tfa_closure()).await? - } - LoginState::NeedsSMS2FA => { - response = _self.send_sms_2fa_to_devices(1).await? + response = _self + .verify_2fa(tfa_closure().map_err(|e| { + Error::AuthSrpWithMessage(0, format!("Failed to get 2FA code: {}", e)) + })?) + .await? } + LoginState::NeedsSMS2FA => response = _self.send_sms_2fa_to_devices(1).await?, LoginState::NeedsSMS2FAVerification(body) => { - response = _self.verify_sms_2fa(tfa_closure(), body).await? + response = _self + .verify_sms_2fa( + tfa_closure().map_err(|e| { + Error::AuthSrpWithMessage( + 0, + format!("Failed to get SMS 2FA code: {}", e), + ) + })?, + body, + ) + .await? } LoginState::NeedsLogin => { response = _self.login_email_pass(&username, &password).await? @@ -345,9 +405,9 @@ impl AppleAccount { LoginState::LoggedIn => return Ok(_self), LoginState::NeedsExtraStep(step) => { if _self.get_pet().is_some() { - return Ok(_self) + return Ok(_self); } else { - return Err(Error::ExtraStep(step)) + return Err(Error::ExtraStep(step)); } } } @@ -356,16 +416,42 @@ impl AppleAccount { pub fn get_pet(&self) -> Option { let Some(token) = self.spd.as_ref().unwrap().get("t") else { - return None + return None; }; - Some(token.as_dictionary().unwrap().get("com.apple.gs.idms.pet") - .unwrap().as_dictionary().unwrap().get("token").unwrap().as_string().unwrap().to_string()) + Some( + token + .as_dictionary() + .unwrap() + .get("com.apple.gs.idms.pet") + .unwrap() + .as_dictionary() + .unwrap() + .get("token") + .unwrap() + .as_string() + .unwrap() + .to_string(), + ) } pub fn get_name(&self) -> (String, String) { ( - self.spd.as_ref().unwrap().get("fn").unwrap().as_string().unwrap().to_string(), - self.spd.as_ref().unwrap().get("ln").unwrap().as_string().unwrap().to_string() + self.spd + .as_ref() + .unwrap() + .get("fn") + .unwrap() + .as_string() + .unwrap() + .to_string(), + self.spd + .as_ref() + .unwrap() + .get("ln") + .unwrap() + .as_string() + .unwrap() + .to_string(), ) } @@ -423,7 +509,8 @@ impl AppleAccount { .post(GSA_ENDPOINT) .headers(gsa_headers.clone()) .body(buffer) - .send().await; + .send() + .await; let res = parse_response(res).await?; let err_check = Self::check_error(&res); @@ -436,9 +523,7 @@ impl AppleAccount { let iters = res.get("i").unwrap().as_signed_integer().unwrap(); let c = res.get("c").unwrap().as_string().unwrap(); - let mut password_hasher = sha2::Sha256::new(); - password_hasher.update(&password.as_bytes()); - let hashed_password = password_hasher.finalize(); + let hashed_password = Sha256::digest(password.as_bytes()); let mut password_buf = [0u8; 32]; pbkdf2::pbkdf2::>( @@ -476,7 +561,8 @@ impl AppleAccount { .post(GSA_ENDPOINT) .headers(gsa_headers.clone()) .body(buffer) - .send().await; + .send() + .await; let res = parse_response(res).await?; let err_check = Self::check_error(&res); @@ -499,8 +585,8 @@ impl AppleAccount { return match s.as_str() { "trustedDeviceSecondaryAuth" => Ok(LoginState::NeedsDevice2FA), "secondaryAuth" => Ok(LoginState::NeedsSMS2FA), - _unk => Ok(LoginState::NeedsExtraStep(_unk.to_string())) - } + _unk => Ok(LoginState::NeedsExtraStep(_unk.to_string())), + }; } Ok(LoginState::LoggedIn) @@ -533,7 +619,8 @@ impl AppleAccount { .client .get("https://gsa.apple.com/auth/verify/trusteddevice") .headers(headers.await) - .send().await?; + .send() + .await?; if !res.status().is_success() { return Err(Error::AuthSrp); @@ -545,13 +632,10 @@ impl AppleAccount { pub async fn send_sms_2fa_to_devices(&self, phone_id: u32) -> Result { let headers = self.build_2fa_headers(true); - let body = VerifyBody { - phone_number: PhoneNumber { - id: phone_id - }, + phone_number: PhoneNumber { id: phone_id }, mode: "sms".to_string(), - security_code: None + security_code: None, }; let res = self @@ -559,7 +643,8 @@ impl AppleAccount { .put("https://gsa.apple.com/auth/verify/phone/") .headers(headers.await) .json(&body) - .send().await?; + .send() + .await?; if !res.status().is_success() { return Err(Error::AuthSrp); @@ -571,20 +656,22 @@ impl AppleAccount { pub async fn get_auth_extras(&self) -> Result { let headers = self.build_2fa_headers(true); - let req = self.client + let req = self + .client .get("https://gsa.apple.com/auth") .headers(headers.await) .header("Accept", "application/json") - .send().await?; + .send() + .await?; let status = req.status().as_u16(); let mut new_state = req.json::().await?; if status == 201 { new_state.new_state = Some(LoginState::NeedsSMS2FAVerification(VerifyBody { phone_number: PhoneNumber { - id: new_state.trusted_phone_numbers.first().unwrap().id + id: new_state.trusted_phone_numbers.first().unwrap().id, }, mode: "sms".to_string(), - security_code: None + security_code: None, })); } @@ -602,17 +689,21 @@ impl AppleAccount { HeaderName::from_str("security-code").unwrap(), HeaderValue::from_str(&code).unwrap(), ) - .send().await?; + .send() + .await?; - let res: plist::Dictionary = - plist::from_bytes(res.text().await?.as_bytes())?; + let res: plist::Dictionary = plist::from_bytes(res.text().await?.as_bytes())?; Self::check_error(&res)?; Ok(LoginState::NeedsLogin) } - pub async fn verify_sms_2fa(&self, code: String, mut body: VerifyBody) -> Result { + pub async fn verify_sms_2fa( + &self, + code: String, + mut body: VerifyBody, + ) -> Result { let headers = self.build_2fa_headers(true).await; // println!("Recieved code: {}", code); @@ -624,7 +715,8 @@ impl AppleAccount { .headers(headers) .header("accept", "application/json") .json(&body) - .send().await?; + .send() + .await?; if res.status() != 200 { return Err(Error::Bad2faCode); @@ -654,7 +746,7 @@ impl AppleAccount { let dsid = spd.get("adsid").unwrap().as_string().unwrap(); let token = spd.get("GsIdmsToken").unwrap().as_string().unwrap(); - let identity_token = base64::encode(format!("{}:{}", dsid, token)); + let identity_token = general_purpose::STANDARD.encode(format!("{}:{}", dsid, token)); let valid_anisette = self.get_anisette().await; @@ -690,4 +782,57 @@ impl AppleAccount { headers } + + pub async fn send_request( + &self, + url: &str, + body: Option, + ) -> Result { + let spd = self.spd.as_ref().unwrap(); + let app_token = self.get_app_token("com.apple.gs.xcode.auth").await?; + let valid_anisette = self.get_anisette().await; + + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("text/x-xml-plist")); + headers.insert("Accept", HeaderValue::from_static("text/x-xml-plist")); + headers.insert("Accept-Language", HeaderValue::from_static("en-us")); + headers.insert("User-Agent", HeaderValue::from_static("Xcode")); + headers.insert( + "X-Apple-I-Identity-Id", + HeaderValue::from_str(spd.get("adsid").unwrap().as_string().unwrap()).unwrap(), + ); + headers.insert( + "X-Apple-GS-Token", + HeaderValue::from_str(&app_token.auth_token).unwrap(), + ); + + for (k, v) in valid_anisette.generate_headers(false, true, true) { + headers.insert( + HeaderName::from_bytes(k.as_bytes()).unwrap(), + HeaderValue::from_str(&v).unwrap(), + ); + } + + if let Ok(locale) = valid_anisette.get_header("x-apple-locale") { + headers.insert("X-Apple-Locale", HeaderValue::from_str(&locale).unwrap()); + } + + let response = if let Some(body) = body { + let mut buf = Vec::new(); + plist::to_writer_xml(&mut buf, &body)?; + self.client + .post(url) + .headers(headers) + .body(buf) + .send() + .await? + } else { + self.client.get(url).headers(headers).send().await? + }; + + let response = response.text().await?; + + let response: plist::Dictionary = plist::from_bytes(response.as_bytes())?; + Ok(response) + } } diff --git a/icloud-auth/src/lib.rs b/icloud-auth/src/lib.rs index fc79925..1ec3205 100644 --- a/icloud-auth/src/lib.rs +++ b/icloud-auth/src/lib.rs @@ -1,8 +1,7 @@ pub mod anisette; mod client; -use std::fmt::Display; -pub use client::{AppleAccount, LoginState, TrustedPhoneNumber, AuthenticationExtras, VerifyBody}; +pub use client::{AppleAccount, AuthenticationExtras, LoginState, TrustedPhoneNumber, VerifyBody}; pub use omnisette::AnisetteConfiguration; use thiserror::Error; @@ -23,5 +22,5 @@ pub enum Error { #[error("Request failed {0}")] ReqwestError(#[from] reqwest::Error), #[error("Failed getting anisette data {0}")] - ErrorGettingAnisette(#[from] omnisette::AnisetteError) -} \ No newline at end of file + ErrorGettingAnisette(#[from] omnisette::AnisetteError), +} diff --git a/icloud-auth/tests/auth_debug.rs b/icloud-auth/tests/auth_debug.rs index c68c392..aeb3b1c 100644 --- a/icloud-auth/tests/auth_debug.rs +++ b/icloud-auth/tests/auth_debug.rs @@ -1,8 +1,5 @@ -// use icloud_auth::ani -use std::sync::Arc; - +use base64::engine::{general_purpose, Engine}; use num_bigint::BigUint; -use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use srp::{ client::{SrpClient, SrpClientVerifier}, @@ -11,15 +8,18 @@ use srp::{ #[cfg(test)] mod tests { + use super::*; #[test] fn auth_debug() { // not a real account - let bytes_a = base64::decode("XChHXELsQ+ljxTFbvRMUsGJxiDIlOh9f8e+JzoegmVcOdAXXtPNzkHpAbAgSjyA+vXrTA93+BUu8EJ9+4xZu9g==").unwrap(); + let bytes_a = general_purpose::STANDARD.decode("XChHXELsQ+ljxTFbvRMUsGJxiDIlOh9f8e+JzoegmVcOdAXXtPNzkHpAbAgSjyA+vXrTA93+BUu8EJ9+4xZu9g==").unwrap(); let username = "apple3@f1sh.me"; let password = "WaffleTest123"; - let salt = base64::decode("6fK6ailLUcp2kJswJVrKjQ==").unwrap(); + let salt = general_purpose::STANDARD + .decode("6fK6ailLUcp2kJswJVrKjQ==") + .unwrap(); let iters = 20832; let mut password_hasher = sha2::Sha256::new(); @@ -41,7 +41,9 @@ mod tests { // apub: N2XHuh/4P1urPoBvDocF0RCRIl2pliZYqg9p6wGH0nnJdckJPn3M00jEqoM4teqH03HjG1murdcZiNHb5YayufW//+asW01XB7nYIIVvGiUFLRypYITEKYWBQ6h2q02GaZspYJKy98V8Fwcvr0ri+al7zJo1X1aoRKINyjV5TywhhwmTleI1qJkf+JBRYKKqO1XFtOTpQsysWD3ZJdK3K78kSgT3q0kXE3oDRMiHPAO77GFJZErYTuvI6QPRbOgcrn+RKV6AsjR5tUQAoSGRdtibdZTAQijJg788qVg+OFVCNZoY9GYVxa+Ze1bPGdkkgCYicTE8iNFG9KlJ+QpKgQ== - let a_random = base64::decode("ywN1O32vmBogb5Fyt9M7Tn8bbzLtDDbcYgPFpSy8n9E=").unwrap(); + let a_random = general_purpose::STANDARD + .decode("ywN1O32vmBogb5Fyt9M7Tn8bbzLtDDbcYgPFpSy8n9E=") + .unwrap(); let client = SrpClient::::new(&G_2048); let a_pub_compute = @@ -49,14 +51,21 @@ mod tests { // expect it to be same to a_pub println!( "compute a_pub: {:?}", - base64::encode(&a_pub_compute.to_bytes_be()) + general_purpose::STANDARD.encode(&a_pub_compute.to_bytes_be()) ); - let b_pub = base64::decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap(); - println!("fixed b_pub: {:?}", base64::encode(&b_pub)); + let b_pub = general_purpose::STANDARD.decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap(); + println!( + "fixed b_pub: {:?}", + general_purpose::STANDARD.encode(&b_pub) + ); println!(""); - println!("salt: {:?} iterations: {:?}", base64::encode(&salt), iters); + println!( + "salt: {:?} iterations: {:?}", + general_purpose::STANDARD.encode(&salt), + iters + ); let verifier: SrpClientVerifier = SrpClient::::process_reply( &client, diff --git a/icloud-auth/tests/gsa_auth.rs b/icloud-auth/tests/gsa_auth.rs index 6770245..ae4336d 100644 --- a/icloud-auth/tests/gsa_auth.rs +++ b/icloud-auth/tests/gsa_auth.rs @@ -22,16 +22,21 @@ mod tests { input.trim().to_string() }); - let appleid_closure = move || (email.clone(), password.clone()); + let appleid_closure = move || Ok((email.clone(), password.clone())); // ask console for 2fa code, make sure it is only 6 digits, no extra characters let tfa_closure = || { println!("Enter 2FA code: "); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); - input.trim().to_string() + Ok(input.trim().to_string()) }; - let acc = AppleAccount::login(appleid_closure, tfa_closure, AnisetteConfiguration::new() - .set_configuration_path(PathBuf::from_str("anisette_test").unwrap())).await; + let acc = AppleAccount::login( + appleid_closure, + tfa_closure, + AnisetteConfiguration::new() + .set_configuration_path(PathBuf::from_str("anisette_test").unwrap()), + ) + .await; let account = acc.unwrap(); println!("data {:?}", account.get_name()); diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 1f54873..b7c22ff 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -10,11 +10,11 @@ default = ["remote-anisette", "dep:remove-async-await"] remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungstenite", "dep:futures-util", "dep:chrono"] [dependencies] -base64 = "0.21" +base64 = "0.22" hex = "0.4" plist = "1.4" -reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls"] } -rand = "0.8" +reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } +rand = "0.9" sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } android-loader = { git = "https://github.com/Dadoum/android-loader", branch = "bigger_pages" } @@ -24,14 +24,14 @@ async-trait = { version = "0.1", optional = true } remove-async-await = { version = "1.0", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0.115", optional = true } -tokio-tungstenite = { version = "0.20.1", optional = true, features = ["rustls-tls-webpki-roots"] } +tokio-tungstenite = { version = "0.27.0", optional = true, features = ["rustls-tls-webpki-roots"] } futures-util = { version = "0.3.28", optional = true } chrono = { version = "0.4.37", optional = true } -thiserror = "1.0.58" +thiserror = "2.0.12" anyhow = "1.0.81" [target.'cfg(target_os = "macos")'.dependencies] -dlopen2 = "0.4" +dlopen2 = "0.8" objc = "0.2" objc-foundation = "0.1" diff --git a/omnisette/src/adi_proxy.rs b/omnisette/src/adi_proxy.rs index 617af58..ccaa3b9 100644 --- a/omnisette/src/adi_proxy.rs +++ b/omnisette/src/adi_proxy.rs @@ -46,7 +46,7 @@ pub enum ADIError { ReqwestError(#[from] reqwest::Error), Base64Error(#[from] base64::DecodeError), InvalidHeaderValue(#[from] InvalidHeaderValue), - IOError(#[from] io::Error) + IOError(#[from] io::Error), } impl ADIError { @@ -95,7 +95,7 @@ pub struct RequestOTPData { } #[cfg_attr(feature = "async", async_trait::async_trait(?Send))] -pub trait ADIProxy: Send + Sync { +pub trait ADIProxy: Send + Sync { fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError>; fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result; fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError>; @@ -291,7 +291,9 @@ pub struct ADIProxyAnisetteProvider { impl ADIProxyAnisetteProvider { /// If you use this method, you are expected to set the identifier yourself. - pub fn without_identifier(adi_proxy: ProxyType) -> Result, ADIError> { + pub fn without_identifier( + adi_proxy: ProxyType, + ) -> Result, ADIError> { Ok(ADIProxyAnisetteProvider { adi_proxy }) } @@ -309,7 +311,7 @@ impl ADIProxyAnisetteProvider { if identifier_file.metadata()?.len() == IDENTIFIER_LENGTH as u64 { identifier_file.read_exact(&mut identifier)?; } else { - rand::thread_rng().fill_bytes(&mut identifier); + rand::rng().fill_bytes(&mut identifier); identifier_file.write_all(&identifier)?; } diff --git a/omnisette/src/remote_anisette_v3.rs b/omnisette/src/remote_anisette_v3.rs index d5f28a9..5f30bfc 100644 --- a/omnisette/src/remote_anisette_v3.rs +++ b/omnisette/src/remote_anisette_v3.rs @@ -1,26 +1,24 @@ - // Implementing the SideStore Anisette v3 protocol use std::{collections::HashMap, fs, io::Cursor, path::PathBuf}; +use async_trait::async_trait; use base64::engine::general_purpose; +use base64::Engine; use chrono::{DateTime, SubsecRound, Utc}; +use futures_util::{stream::StreamExt, SinkExt}; use log::debug; use plist::{Data, Dictionary}; +use rand::Rng; use reqwest::{Client, ClientBuilder, RequestBuilder}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use rand::Rng; -use sha2::{Sha256, Digest}; +use sha2::{Digest, Sha256}; +use std::fmt::Write; use tokio_tungstenite::{connect_async, tungstenite::Message}; use uuid::Uuid; -use futures_util::{stream::StreamExt, SinkExt}; -use std::fmt::Write; -use base64::Engine; -use async_trait::async_trait; use crate::{anisette_headers_provider::AnisetteHeadersProvider, AnisetteError}; - fn plist_to_string(value: &T) -> Result { plist_to_buf(value).map(|val| String::from_utf8(val).unwrap()) } @@ -78,8 +76,6 @@ fn base64_decode(data: &str) -> Vec { general_purpose::STANDARD.decode(data.trim()).unwrap() } - - #[derive(Deserialize)] struct AnisetteClientInfo { client_info: String, @@ -88,17 +84,23 @@ struct AnisetteClientInfo { #[derive(Serialize, Deserialize)] pub struct AnisetteState { - #[serde(serialize_with = "bin_serialize", deserialize_with = "bin_deserialize_16")] + #[serde( + serialize_with = "bin_serialize", + deserialize_with = "bin_deserialize_16" + )] keychain_identifier: [u8; 16], - #[serde(serialize_with = "bin_serialize_opt", deserialize_with = "bin_deserialize_opt")] + #[serde( + serialize_with = "bin_serialize_opt", + deserialize_with = "bin_deserialize_opt" + )] adi_pb: Option>, } impl Default for AnisetteState { fn default() -> Self { AnisetteState { - keychain_identifier: rand::thread_rng().gen::<[u8; 16]>(), - adi_pb: None + keychain_identifier: rand::rng().random::<[u8; 16]>(), + adi_pb: None, } } } @@ -124,7 +126,7 @@ impl AnisetteState { } pub struct AnisetteClient { client_info: AnisetteClientInfo, - url: String + url: String, } #[derive(Serialize)] @@ -141,25 +143,37 @@ pub struct AnisetteData { routing_info: String, device_description: String, local_user_id: String, - device_unique_identifier: String + device_unique_identifier: String, } impl AnisetteData { pub fn get_headers(&self, serial: String) -> HashMap { let dt: DateTime = Utc::now().round_subsecs(0); - - HashMap::from_iter([ - ("X-Apple-I-Client-Time".to_string(), dt.format("%+").to_string().replace("+00:00", "Z")), - ("X-Apple-I-SRL-NO".to_string(), serial), - ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), - ("X-Apple-Locale".to_string(), "en_US".to_string()), - ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), - ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), - ("X-Mme-Device-Id".to_string(), self.device_unique_identifier.clone()), - ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), - ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), - ("X-Mme-Client-Info".to_string(), self.device_description.clone()), - ].into_iter()) + + HashMap::from_iter( + [ + ( + "X-Apple-I-Client-Time".to_string(), + dt.format("%+").to_string().replace("+00:00", "Z"), + ), + ("X-Apple-I-SRL-NO".to_string(), serial), + ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), + ("X-Apple-Locale".to_string(), "en_US".to_string()), + ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), + ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), + ( + "X-Mme-Device-Id".to_string(), + self.device_unique_identifier.clone(), + ), + ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), + ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), + ( + "X-Mme-Client-Info".to_string(), + self.device_description.clone(), + ), + ] + .into_iter(), + ) } } @@ -174,19 +188,24 @@ impl AnisetteClient { pub async fn new(url: String) -> Result { let path = format!("{}/v3/client_info", url); let http_client = make_reqwest()?; - let client_info = http_client.get(path) - .send().await? - .json::().await?; - Ok(AnisetteClient { - client_info, - url - }) + let client_info = http_client + .get(path) + .send() + .await? + .json::() + .await?; + Ok(AnisetteClient { client_info, url }) } - fn build_apple_request(&self, state: &AnisetteState, builder: RequestBuilder) -> RequestBuilder { + fn build_apple_request( + &self, + state: &AnisetteState, + builder: RequestBuilder, + ) -> RequestBuilder { let dt: DateTime = Utc::now().round_subsecs(0); - builder.header("X-Mme-Client-Info", &self.client_info.client_info) + builder + .header("X-Mme-Client-Info", &self.client_info.client_info) .header("User-Agent", &self.client_info.user_agent) .header("Content-Type", "text/x-xml-plist") .header("X-Apple-I-MD-LU", encode_hex(&state.md_lu())) @@ -207,14 +226,19 @@ impl AnisetteClient { } let body = GetHeadersBody { identifier: base64_encode(&state.keychain_identifier), - adi_pb: base64_encode(state.adi_pb.as_ref().ok_or(AnisetteError::AnisetteNotProvisioned)?), + adi_pb: base64_encode( + state + .adi_pb + .as_ref() + .ok_or(AnisetteError::AnisetteNotProvisioned)?, + ), }; #[derive(Deserialize)] #[serde(tag = "result")] enum AnisetteHeaders { GetHeadersError { - message: String + message: String, }, Headers { #[serde(rename = "X-Apple-I-MD-M")] @@ -223,13 +247,16 @@ impl AnisetteClient { one_time_password: String, #[serde(rename = "X-Apple-I-MD-RINFO")] routing_info: String, - } + }, } - let headers = http_client.post(path) + let headers = http_client + .post(path) .json(&body) - .send().await? - .json::().await?; + .send() + .await? + .json::() + .await?; match headers { AnisetteHeaders::GetHeadersError { message } => { if message.contains("-45061") { @@ -237,38 +264,62 @@ impl AnisetteClient { } else { panic!("Unknown error {}", message) } - }, - AnisetteHeaders::Headers { machine_id, one_time_password, routing_info } => { - Ok(AnisetteData { - machine_id, - one_time_password, - routing_info, - device_description: self.client_info.client_info.clone(), - local_user_id: encode_hex(&state.md_lu()), - device_unique_identifier: state.device_id() - }) } + AnisetteHeaders::Headers { + machine_id, + one_time_password, + routing_info, + } => Ok(AnisetteData { + machine_id, + one_time_password, + routing_info, + device_description: self.client_info.client_info.clone(), + local_user_id: encode_hex(&state.md_lu()), + device_unique_identifier: state.device_id(), + }), } } pub async fn provision(&self, state: &mut AnisetteState) -> Result<(), AnisetteError> { debug!("Provisioning Anisette"); let http_client = make_reqwest()?; - let resp = self.build_apple_request(&state, http_client.get("https://gsa.apple.com/grandslam/GsService2/lookup")) - .send().await?; + let resp = self + .build_apple_request( + &state, + http_client.get("https://gsa.apple.com/grandslam/GsService2/lookup"), + ) + .send() + .await?; let text = resp.text().await?; let protocol_val = plist::Value::from_reader(Cursor::new(text.as_str()))?; - let urls = protocol_val.as_dictionary().unwrap().get("urls").unwrap().as_dictionary().unwrap(); - - let start_provisioning_url = urls.get("midStartProvisioning").unwrap().as_string().unwrap(); - let end_provisioning_url = urls.get("midFinishProvisioning").unwrap().as_string().unwrap(); - debug!("Got provisioning urls: {} and {}", start_provisioning_url, end_provisioning_url); + let urls = protocol_val + .as_dictionary() + .unwrap() + .get("urls") + .unwrap() + .as_dictionary() + .unwrap(); + + let start_provisioning_url = urls + .get("midStartProvisioning") + .unwrap() + .as_string() + .unwrap(); + let end_provisioning_url = urls + .get("midFinishProvisioning") + .unwrap() + .as_string() + .unwrap(); + debug!( + "Got provisioning urls: {} and {}", + start_provisioning_url, end_provisioning_url + ); - let provision_ws_url = format!("{}/v3/provisioning_session", self.url).replace("https://", "wss://"); + let provision_ws_url = + format!("{}/v3/provisioning_session", self.url).replace("https://", "wss://"); let (mut connection, _) = connect_async(&provision_ws_url).await?; - #[derive(Deserialize)] #[serde(tag = "result")] enum ProvisionInput { @@ -276,17 +327,17 @@ impl AnisetteClient { GiveStartProvisioningData, GiveEndProvisioningData { #[allow(dead_code)] // it's not even dead, rust just has problems - cpim: String + cpim: String, }, ProvisioningSuccess { #[allow(dead_code)] // it's not even dead, rust just has problems - adi_pb: String - } + adi_pb: String, + }, } loop { let Some(Ok(data)) = connection.next().await else { - continue + continue; }; if data.is_text() { let txt = data.to_text().unwrap(); @@ -295,44 +346,77 @@ impl AnisetteClient { ProvisionInput::GiveIdentifier => { #[derive(Serialize)] struct Identifier { - identifier: String // base64 + identifier: String, // base64 } - let identifier = Identifier { identifier: base64_encode(&state.keychain_identifier) }; - connection.send(Message::Text(serde_json::to_string(&identifier)?)).await?; - }, + let identifier = Identifier { + identifier: base64_encode(&state.keychain_identifier), + }; + connection + .send(Message::Text(serde_json::to_string(&identifier)?.into())) + .await?; + } ProvisionInput::GiveStartProvisioningData => { let http_client = make_reqwest()?; - let body_data = ProvisionBodyData { header: Dictionary::new(), request: Dictionary::new() }; - let resp = self.build_apple_request(state, http_client.post(start_provisioning_url)) + let body_data = ProvisionBodyData { + header: Dictionary::new(), + request: Dictionary::new(), + }; + let resp = self + .build_apple_request(state, http_client.post(start_provisioning_url)) .body(plist_to_string(&body_data)?) - .send().await?; + .send() + .await?; let text = resp.text().await?; let protocol_val = plist::Value::from_reader(Cursor::new(text.as_str()))?; - let spim = protocol_val.as_dictionary().unwrap().get("Response").unwrap().as_dictionary().unwrap() - .get("spim").unwrap().as_string().unwrap(); - + let spim = protocol_val + .as_dictionary() + .unwrap() + .get("Response") + .unwrap() + .as_dictionary() + .unwrap() + .get("spim") + .unwrap() + .as_string() + .unwrap(); + debug!("GiveStartProvisioningData"); #[derive(Serialize)] struct Spim { - spim: String // base64 + spim: String, // base64 } - let spim = Spim { spim: spim.to_string() }; - connection.send(Message::Text(serde_json::to_string(&spim)?)).await?; - }, + let spim = Spim { + spim: spim.to_string(), + }; + connection + .send(Message::Text(serde_json::to_string(&spim)?.into())) + .await?; + } ProvisionInput::GiveEndProvisioningData { cpim } => { let http_client = make_reqwest()?; - let body_data = ProvisionBodyData { header: Dictionary::new(), request: Dictionary::from_iter([("cpim", cpim)].into_iter()) }; - let resp = self.build_apple_request(state, http_client.post(end_provisioning_url)) + let body_data = ProvisionBodyData { + header: Dictionary::new(), + request: Dictionary::from_iter([("cpim", cpim)].into_iter()), + }; + let resp = self + .build_apple_request(state, http_client.post(end_provisioning_url)) .body(plist_to_string(&body_data)?) - .send().await?; + .send() + .await?; let text = resp.text().await?; let protocol_val = plist::Value::from_reader(Cursor::new(text.as_str()))?; - let response = protocol_val.as_dictionary().unwrap().get("Response").unwrap().as_dictionary().unwrap(); + let response = protocol_val + .as_dictionary() + .unwrap() + .get("Response") + .unwrap() + .as_dictionary() + .unwrap(); debug!("GiveEndProvisioningData"); - + #[derive(Serialize)] struct EndProvisioning<'t> { ptm: &'t str, @@ -342,8 +426,12 @@ impl AnisetteClient { ptm: response.get("ptm").unwrap().as_string().unwrap(), tk: response.get("tk").unwrap().as_string().unwrap(), }; - connection.send(Message::Text(serde_json::to_string(&end_provisioning)?)).await?; - }, + connection + .send(Message::Text( + serde_json::to_string(&end_provisioning)?.into(), + )) + .await?; + } ProvisionInput::ProvisioningSuccess { adi_pb } => { debug!("ProvisioningSuccess"); state.adi_pb = Some(base64_decode(&adi_pb)); @@ -360,23 +448,26 @@ impl AnisetteClient { } } - pub struct RemoteAnisetteProviderV3 { client_url: String, client: Option, pub state: Option, configuration_path: PathBuf, - serial: String + serial: String, } impl RemoteAnisetteProviderV3 { - pub fn new(url: String, configuration_path: PathBuf, serial: String) -> RemoteAnisetteProviderV3 { + pub fn new( + url: String, + configuration_path: PathBuf, + serial: String, + ) -> RemoteAnisetteProviderV3 { RemoteAnisetteProviderV3 { client_url: url, client: None, state: None, configuration_path, - serial + serial, } } } @@ -393,7 +484,7 @@ impl AnisetteHeadersProvider for RemoteAnisetteProviderV3 { let client = self.client.as_ref().unwrap(); fs::create_dir_all(&self.configuration_path)?; - + let config_path = self.configuration_path.join("state.plist"); if self.state.is_none() { self.state = Some(if let Ok(text) = plist::from_file(&config_path) { @@ -416,8 +507,10 @@ impl AnisetteHeadersProvider for RemoteAnisetteProviderV3 { client.provision(state).await?; plist::to_file_xml(config_path, state)?; client.get_headers(&state).await? - } else { panic!() } - }, + } else { + panic!() + } + } }; Ok(data.get_headers(self.serial.clone())) } @@ -434,12 +527,17 @@ mod tests { async fn fetch_anisette_remote_v3() -> Result<(), AnisetteError> { crate::tests::init_logger(); - let mut provider = RemoteAnisetteProviderV3::new(DEFAULT_ANISETTE_URL_V3.to_string(), "anisette_test".into(), "0".to_string()); + let mut provider = RemoteAnisetteProviderV3::new( + DEFAULT_ANISETTE_URL_V3.to_string(), + "anisette_test".into(), + "0".to_string(), + ); info!( "Remote headers: {:?}", - (&mut provider as &mut dyn AnisetteHeadersProvider).get_authentication_headers().await? + (&mut provider as &mut dyn AnisetteHeadersProvider) + .get_authentication_headers() + .await? ); Ok(()) } } - diff --git a/omnisette/src/store_services_core.rs b/omnisette/src/store_services_core.rs index 5782c03..b81d223 100644 --- a/omnisette/src/store_services_core.rs +++ b/omnisette/src/store_services_core.rs @@ -66,11 +66,16 @@ pub struct StoreServicesCoreADIProxy<'lt> { } impl StoreServicesCoreADIProxy<'_> { - pub fn new<'lt>(library_path: &PathBuf) -> Result, AnisetteError> { + pub fn new<'lt>( + library_path: &PathBuf, + ) -> Result, AnisetteError> { Self::with_custom_provisioning_path(library_path, library_path) } - pub fn with_custom_provisioning_path<'lt>(library_path: &PathBuf, provisioning_path: &PathBuf) -> Result, AnisetteError> { + pub fn with_custom_provisioning_path<'lt>( + library_path: &PathBuf, + provisioning_path: &PathBuf, + ) -> Result, AnisetteError> { // Should be safe if the library is correct. unsafe { LoaderHelpers::setup_hooks(); @@ -104,12 +109,8 @@ impl StoreServicesCoreADIProxy<'_> { .ok_or(AnisetteError::InvalidLibraryFormat)?, ); - let path = CString::new( - native_library_path - .to_str() - .ok_or(AnisetteError::Misc)?, - ) - .unwrap(); + let path = + CString::new(native_library_path.to_str().ok_or(AnisetteError::Misc)?).unwrap(); assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0); let adi_set_android_id = store_services_core @@ -163,9 +164,7 @@ impl StoreServicesCoreADIProxy<'_> { adi_otp_request: std::mem::transmute(adi_otp_request), }; - proxy.set_provisioning_path( - provisioning_path.to_str().ok_or(AnisetteError::Misc)?, - )?; + proxy.set_provisioning_path(provisioning_path.to_str().ok_or(AnisetteError::Misc)?)?; Ok(proxy) } @@ -370,7 +369,7 @@ unsafe fn __errno_location() -> *mut i32 { #[sysv64] fn arc4random() -> u32 { - rand::thread_rng().gen() + rand::rng().random() } #[sysv64] @@ -412,10 +411,10 @@ impl LoaderHelpers { #[cfg(test)] mod tests { + use crate::AnisetteError; use crate::{AnisetteConfiguration, AnisetteHeaders}; use log::info; use std::path::PathBuf; - use crate::AnisetteError; #[cfg(not(feature = "async"))] #[test] @@ -436,7 +435,6 @@ mod tests { #[cfg(feature = "async")] #[tokio::test] async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> { - crate::tests::init_logger(); let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( From 666a6fb6d0278a9556da2cdc6e368910018fd961 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 8 Aug 2025 19:44:11 -0400 Subject: [PATCH 02/30] Update dependencies --- icloud-auth/Cargo.toml | 22 +++++++++++----------- icloud-auth/rustcrypto-srp/Cargo.toml | 6 +++--- omnisette/Cargo.toml | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 8b40bbe..1c78eb8 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -8,24 +8,24 @@ default = [] vendored-botan = ["botan/vendored"] [dependencies] -serde = { version = "1.0.219", features = ["derive"] } -serde_json = { version = "1.0.142" } -base64 = "0.22.1" +serde = { version = "1", features = ["derive"] } +serde_json = { version = "1" } +base64 = "0.22" srp = { version = "0.6.0", path = "./rustcrypto-srp" } -pbkdf2 = "0.11.0" -sha2 = { version = "0.10.6" } -rand = "0.9.2" -rustls = "0.23.31" -rustls-pemfile = "2.2.0" -plist = { version = "1.7.2" } +pbkdf2 = "0.11" +sha2 = "0.10" +rand = "0.9" +rustls = "0.23" +rustls-pemfile = "2.2" +plist = "1.7.2" hmac = "0.12.1" num-bigint = "0.4.3" cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" -reqwest = { version = "0.12.22", features = ["blocking", "json", "default-tls"] } +reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } omnisette = {path = "../omnisette", features = ["remote-anisette-v3"]} -thiserror = "2.0.12" +thiserror = "2" tokio = "1" botan = "0.12.0" diff --git a/icloud-auth/rustcrypto-srp/Cargo.toml b/icloud-auth/rustcrypto-srp/Cargo.toml index 253457a..63f7808 100644 --- a/icloud-auth/rustcrypto-srp/Cargo.toml +++ b/icloud-auth/rustcrypto-srp/Cargo.toml @@ -14,14 +14,14 @@ rust-version = "1.56" [dependencies] num-bigint = "0.4" -generic-array = "1.2" +generic-array = "1" digest = "0.10" lazy_static = "1.2" subtle = "2.4" -base64 = "0.22.1" +base64 = "0.22" [dev-dependencies] -hex-literal = "1.0" +hex-literal = "1" num-traits = "0.2" rand = "0.9" sha1 = "0.10" diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index b7c22ff..d0fe13f 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -13,7 +13,7 @@ remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungste base64 = "0.22" hex = "0.4" plist = "1.4" -reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } +reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls", "gzip"] } rand = "0.9" sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } @@ -27,11 +27,11 @@ serde_json = { version = "1.0.115", optional = true } tokio-tungstenite = { version = "0.27.0", optional = true, features = ["rustls-tls-webpki-roots"] } futures-util = { version = "0.3.28", optional = true } chrono = { version = "0.4.37", optional = true } -thiserror = "2.0.12" +thiserror = "2" anyhow = "1.0.81" [target.'cfg(target_os = "macos")'.dependencies] -dlopen2 = "0.8" +dlopen2 = "0.4" objc = "0.2" objc-foundation = "0.1" From 7a88f90f36afe6c0ce2470abe11fd8bc088f41be Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 9 Aug 2025 20:51:56 -0400 Subject: [PATCH 03/30] prepare for temporary publishing --- Cargo.toml | 2 - icloud-auth/Cargo.toml | 4 +- icloud-auth/src/anisette.rs | 8 +- icloud-auth/src/client.rs | 4 +- icloud-auth/src/lib.rs | 4 +- icloud-auth/tests/gsa_auth.rs | 2 +- omnisette/Cargo.toml | 3 +- omnisette/src/lib.rs | 35 +- omnisette/src/store_services_core.rs | 450 ------------------ .../src/store_services_core/posix_macos.rs | 102 ---- .../src/store_services_core/posix_windows.rs | 268 ----------- 11 files changed, 22 insertions(+), 860 deletions(-) delete mode 100644 omnisette/src/store_services_core.rs delete mode 100644 omnisette/src/store_services_core/posix_macos.rs delete mode 100644 omnisette/src/store_services_core/posix_windows.rs diff --git a/Cargo.toml b/Cargo.toml index 6fa595d..98b3491 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,4 @@ members = [ "omnisette", "icloud-auth", - "apple-dev-apis", - "apple-codesign-wrapper" ] diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 1c78eb8..a801820 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "icloud_auth" +name = "icloud_auth_fork" version = "0.1.0" edition = "2021" @@ -24,7 +24,7 @@ cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } -omnisette = {path = "../omnisette", features = ["remote-anisette-v3"]} +omnisette_fork = {path = "../omnisette", features = ["remote-anisette-v3"]} thiserror = "2" tokio = "1" botan = "0.12.0" diff --git a/icloud-auth/src/anisette.rs b/icloud-auth/src/anisette.rs index d11601a..b7ffd2b 100644 --- a/icloud-auth/src/anisette.rs +++ b/icloud-auth/src/anisette.rs @@ -1,5 +1,5 @@ use crate::Error; -use omnisette::{AnisetteConfiguration, AnisetteHeaders}; +use omnisette_fork::{AnisetteConfiguration, AnisetteHeaders}; use std::{collections::HashMap, time::SystemTime}; #[derive(Debug, Clone)] @@ -15,7 +15,11 @@ impl AnisetteData { let mut b = AnisetteHeaders::get_anisette_headers_provider(config.clone())?; let base_headers = b.provider.get_authentication_headers().await?; - Ok(AnisetteData { base_headers, generated_at: SystemTime::now(), config }) + Ok(AnisetteData { + base_headers, + generated_at: SystemTime::now(), + config, + }) } pub fn needs_refresh(&self) -> bool { diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index a69141f..3fb4f96 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -4,7 +4,7 @@ use base64::{engine::general_purpose, Engine}; use botan::Cipher; use cbc::cipher::{BlockDecryptMut, KeyIvInit}; use hmac::{Hmac, Mac}; -use omnisette::AnisetteConfiguration; +use omnisette_fork::AnisetteConfiguration; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, Certificate, Client, ClientBuilder, Response, @@ -351,7 +351,7 @@ impl AppleAccount { /// /// ``` /// use icloud_auth::AppleAccount; - /// use omnisette::AnisetteData; + /// use omnisette_fork::AnisetteData; /// /// let anisette = AnisetteData::new(); /// let account = AppleAccount::login( diff --git a/icloud-auth/src/lib.rs b/icloud-auth/src/lib.rs index 1ec3205..3ee4ca6 100644 --- a/icloud-auth/src/lib.rs +++ b/icloud-auth/src/lib.rs @@ -2,7 +2,7 @@ pub mod anisette; mod client; pub use client::{AppleAccount, AuthenticationExtras, LoginState, TrustedPhoneNumber, VerifyBody}; -pub use omnisette::AnisetteConfiguration; +pub use omnisette_fork::AnisetteConfiguration; use thiserror::Error; #[derive(Debug, Error)] @@ -22,5 +22,5 @@ pub enum Error { #[error("Request failed {0}")] ReqwestError(#[from] reqwest::Error), #[error("Failed getting anisette data {0}")] - ErrorGettingAnisette(#[from] omnisette::AnisetteError), + ErrorGettingAnisette(#[from] omnisette_fork::AnisetteError), } diff --git a/icloud-auth/tests/gsa_auth.rs b/icloud-auth/tests/gsa_auth.rs index ae4336d..9903ed9 100644 --- a/icloud-auth/tests/gsa_auth.rs +++ b/icloud-auth/tests/gsa_auth.rs @@ -3,7 +3,7 @@ mod tests { use std::{path::PathBuf, str::FromStr}; use icloud_auth::*; - use omnisette::AnisetteConfiguration; + use omnisette_fork::AnisetteConfiguration; #[tokio::test] async fn gsa_auth() { diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index d0fe13f..051dee3 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "omnisette" +name = "omnisette_fork" version = "0.1.0" edition = "2021" @@ -17,7 +17,6 @@ reqwest = { version = "0.11", default-features = false, features = ["blocking", rand = "0.9" sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } -android-loader = { git = "https://github.com/Dadoum/android-loader", branch = "bigger_pages" } libc = "0.2" log = "0.4" async-trait = { version = "0.1", optional = true } diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index bfd2681..b7de8ad 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -6,14 +6,13 @@ use crate::adi_proxy::{ADIProxyAnisetteProvider, ConfigurableADIProxy}; use crate::anisette_headers_provider::AnisetteHeadersProvider; +use adi_proxy::ADIError; use std::io; use std::path::PathBuf; -use adi_proxy::ADIError; use thiserror::Error; pub mod adi_proxy; pub mod anisette_headers_provider; -pub mod store_services_core; #[cfg(feature = "remote-anisette-v3")] pub mod remote_anisette_v3; @@ -58,7 +57,7 @@ pub enum AnisetteError { #[error("Missing Libraries")] MissingLibraries, #[error("{0}")] - Anyhow(#[from] anyhow::Error) + Anyhow(#[from] anyhow::Error), } pub const DEFAULT_ANISETTE_URL: &str = "https://ani.f1sh.me/"; @@ -85,7 +84,7 @@ impl AnisetteConfiguration { anisette_url: DEFAULT_ANISETTE_URL.to_string(), anisette_url_v3: DEFAULT_ANISETTE_URL_V3.to_string(), configuration_path: PathBuf::new(), - macos_serial: "0".to_string() + macos_serial: "0".to_string(), } } @@ -148,16 +147,13 @@ impl AnisetteHeaders { return Ok(AnisetteHeadersProviderRes::local(Box::new(prov))); } - // TODO: handle Err because it will just go to remote anisette and not tell the user anything - if let Ok(ssc_anisette_headers_provider) = - AnisetteHeaders::get_ssc_anisette_headers_provider(configuration.clone()) - { - return Ok(ssc_anisette_headers_provider); - } - #[cfg(feature = "remote-anisette-v3")] return Ok(AnisetteHeadersProviderRes::remote(Box::new( - remote_anisette_v3::RemoteAnisetteProviderV3::new(configuration.anisette_url_v3, configuration.configuration_path.clone(), configuration.macos_serial.clone()), + remote_anisette_v3::RemoteAnisetteProviderV3::new( + configuration.anisette_url_v3, + configuration.configuration_path.clone(), + configuration.macos_serial.clone(), + ), ))); #[cfg(feature = "remote-anisette")] @@ -168,21 +164,6 @@ impl AnisetteHeaders { #[cfg(not(feature = "remote-anisette"))] bail!(AnisetteMetaError::UnsupportedDevice) } - - pub fn get_ssc_anisette_headers_provider( - configuration: AnisetteConfiguration, - ) -> Result { - let mut ssc_adi_proxy = store_services_core::StoreServicesCoreADIProxy::new( - configuration.configuration_path(), - )?; - let config_path = configuration.configuration_path(); - ssc_adi_proxy.set_provisioning_path(config_path.to_str().ok_or( - AnisetteError::InvalidArgument("configuration.configuration_path".to_string()), - )?)?; - Ok(AnisetteHeadersProviderRes::local(Box::new( - ADIProxyAnisetteProvider::new(ssc_adi_proxy, config_path.to_path_buf())?, - ))) - } } #[cfg(test)] diff --git a/omnisette/src/store_services_core.rs b/omnisette/src/store_services_core.rs deleted file mode 100644 index b81d223..0000000 --- a/omnisette/src/store_services_core.rs +++ /dev/null @@ -1,450 +0,0 @@ -#[cfg(target_os = "macos")] -mod posix_macos; -#[cfg(target_family = "windows")] -mod posix_windows; - -use crate::adi_proxy::{ - ADIError, ADIProxy, ConfigurableADIProxy, RequestOTPData, StartProvisioningData, - SynchronizeData, -}; -use crate::AnisetteError; - -use android_loader::android_library::AndroidLibrary; -use android_loader::sysv64_type; -use android_loader::{hook_manager, sysv64}; -use std::collections::HashMap; -use std::ffi::{c_char, CString}; -use std::path::PathBuf; - -pub struct StoreServicesCoreADIProxy<'lt> { - #[allow(dead_code)] - store_services_core: AndroidLibrary<'lt>, - - local_user_uuid: String, - device_identifier: String, - - adi_set_android_id: sysv64_type!(fn(id: *const u8, length: u32) -> i32), - adi_set_provisioning_path: sysv64_type!(fn(path: *const u8) -> i32), - - adi_provisioning_erase: sysv64_type!(fn(ds_id: i64) -> i32), - adi_synchronize: sysv64_type!( - fn( - ds_id: i64, - sim: *const u8, - sim_length: u32, - out_mid: *mut *const u8, - out_mid_length: *mut u32, - out_srm: *mut *const u8, - out_srm_length: *mut u32, - ) -> i32 - ), - adi_provisioning_destroy: sysv64_type!(fn(session: u32) -> i32), - adi_provisioning_end: sysv64_type!( - fn(session: u32, ptm: *const u8, ptm_length: u32, tk: *const u8, tk_length: u32) -> i32 - ), - adi_provisioning_start: sysv64_type!( - fn( - ds_id: i64, - spim: *const u8, - spim_length: u32, - out_cpim: *mut *const u8, - out_cpim_length: *mut u32, - out_session: *mut u32, - ) -> i32 - ), - adi_get_login_code: sysv64_type!(fn(ds_id: i64) -> i32), - adi_dispose: sysv64_type!(fn(ptr: *const u8) -> i32), - adi_otp_request: sysv64_type!( - fn( - ds_id: i64, - out_mid: *mut *const u8, - out_mid_size: *mut u32, - out_otp: *mut *const u8, - out_otp_size: *mut u32, - ) -> i32 - ), -} - -impl StoreServicesCoreADIProxy<'_> { - pub fn new<'lt>( - library_path: &PathBuf, - ) -> Result, AnisetteError> { - Self::with_custom_provisioning_path(library_path, library_path) - } - - pub fn with_custom_provisioning_path<'lt>( - library_path: &PathBuf, - provisioning_path: &PathBuf, - ) -> Result, AnisetteError> { - // Should be safe if the library is correct. - unsafe { - LoaderHelpers::setup_hooks(); - - if !library_path.exists() { - std::fs::create_dir(library_path)?; - return Err(AnisetteError::MissingLibraries.into()); - } - - let library_path = library_path.canonicalize()?; - - #[cfg(target_arch = "x86_64")] - const ARCH: &str = "x86_64"; - #[cfg(target_arch = "x86")] - const ARCH: &str = "x86"; - #[cfg(target_arch = "arm")] - const ARCH: &str = "armeabi-v7a"; - #[cfg(target_arch = "aarch64")] - const ARCH: &str = "arm64-v8a"; - - let native_library_path = library_path.join("lib").join(ARCH); - - let path = native_library_path.join("libstoreservicescore.so"); - let path = path.to_str().ok_or(AnisetteError::Misc)?; - let store_services_core = AndroidLibrary::load(path)?; - - let adi_load_library_with_path: sysv64_type!(fn(path: *const u8) -> i32) = - std::mem::transmute( - store_services_core - .get_symbol("kq56gsgHG6") - .ok_or(AnisetteError::InvalidLibraryFormat)?, - ); - - let path = - CString::new(native_library_path.to_str().ok_or(AnisetteError::Misc)?).unwrap(); - assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0); - - let adi_set_android_id = store_services_core - .get_symbol("Sph98paBcz") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_set_provisioning_path = store_services_core - .get_symbol("nf92ngaK92") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - - let adi_provisioning_erase = store_services_core - .get_symbol("p435tmhbla") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_synchronize = store_services_core - .get_symbol("tn46gtiuhw") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_provisioning_destroy = store_services_core - .get_symbol("fy34trz2st") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_provisioning_end = store_services_core - .get_symbol("uv5t6nhkui") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_provisioning_start = store_services_core - .get_symbol("rsegvyrt87") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_get_login_code = store_services_core - .get_symbol("aslgmuibau") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_dispose = store_services_core - .get_symbol("jk24uiwqrg") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - let adi_otp_request = store_services_core - .get_symbol("qi864985u0") - .ok_or(AnisetteError::InvalidLibraryFormat)?; - - let mut proxy = StoreServicesCoreADIProxy { - store_services_core, - - local_user_uuid: String::new(), - device_identifier: String::new(), - - adi_set_android_id: std::mem::transmute(adi_set_android_id), - adi_set_provisioning_path: std::mem::transmute(adi_set_provisioning_path), - - adi_provisioning_erase: std::mem::transmute(adi_provisioning_erase), - adi_synchronize: std::mem::transmute(adi_synchronize), - adi_provisioning_destroy: std::mem::transmute(adi_provisioning_destroy), - adi_provisioning_end: std::mem::transmute(adi_provisioning_end), - adi_provisioning_start: std::mem::transmute(adi_provisioning_start), - adi_get_login_code: std::mem::transmute(adi_get_login_code), - adi_dispose: std::mem::transmute(adi_dispose), - adi_otp_request: std::mem::transmute(adi_otp_request), - }; - - proxy.set_provisioning_path(provisioning_path.to_str().ok_or(AnisetteError::Misc)?)?; - - Ok(proxy) - } - } -} - -impl ADIProxy for StoreServicesCoreADIProxy<'_> { - fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError> { - match (self.adi_provisioning_erase)(ds_id) { - 0 => Ok(()), - err => Err(ADIError::resolve(err)), - } - } - - fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result { - unsafe { - let sim_size = sim.len() as u32; - let sim_ptr = sim.as_ptr(); - - let mut mid_size: u32 = 0; - let mut mid_ptr: *const u8 = std::ptr::null(); - let mut srm_size: u32 = 0; - let mut srm_ptr: *const u8 = std::ptr::null(); - - match (self.adi_synchronize)( - ds_id, - sim_ptr, - sim_size, - &mut mid_ptr, - &mut mid_size, - &mut srm_ptr, - &mut srm_size, - ) { - 0 => { - let mut mid = vec![0; mid_size as usize]; - let mut srm = vec![0; srm_size as usize]; - - mid.copy_from_slice(std::slice::from_raw_parts(mid_ptr, mid_size as usize)); - srm.copy_from_slice(std::slice::from_raw_parts(srm_ptr, srm_size as usize)); - - (self.adi_dispose)(mid_ptr); - (self.adi_dispose)(srm_ptr); - - Ok(SynchronizeData { mid, srm }) - } - err => Err(ADIError::resolve(err)), - } - } - } - - fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError> { - match (self.adi_provisioning_destroy)(session) { - 0 => Ok(()), - err => Err(ADIError::resolve(err)), - } - } - - fn end_provisioning(&mut self, session: u32, ptm: &[u8], tk: &[u8]) -> Result<(), ADIError> { - let ptm_size = ptm.len() as u32; - let ptm_ptr = ptm.as_ptr(); - - let tk_size = tk.len() as u32; - let tk_ptr = tk.as_ptr(); - - match (self.adi_provisioning_end)(session, ptm_ptr, ptm_size, tk_ptr, tk_size) { - 0 => Ok(()), - err => Err(ADIError::resolve(err)), - } - } - - fn start_provisioning( - &mut self, - ds_id: i64, - spim: &[u8], - ) -> Result { - unsafe { - let spim_size = spim.len() as u32; - let spim_ptr = spim.as_ptr(); - - let mut cpim_size: u32 = 0; - let mut cpim_ptr: *const u8 = std::ptr::null(); - - let mut session: u32 = 0; - - match (self.adi_provisioning_start)( - ds_id, - spim_ptr, - spim_size, - &mut cpim_ptr, - &mut cpim_size, - &mut session, - ) { - 0 => { - let mut cpim = vec![0; cpim_size as usize]; - - cpim.copy_from_slice(std::slice::from_raw_parts(cpim_ptr, cpim_size as usize)); - - (self.adi_dispose)(cpim_ptr); - - Ok(StartProvisioningData { cpim, session }) - } - err => Err(ADIError::resolve(err)), - } - } - } - - fn is_machine_provisioned(&self, ds_id: i64) -> bool { - (self.adi_get_login_code)(ds_id) == 0 - } - - fn request_otp(&self, ds_id: i64) -> Result { - unsafe { - let mut mid_size: u32 = 0; - let mut mid_ptr: *const u8 = std::ptr::null(); - let mut otp_size: u32 = 0; - let mut otp_ptr: *const u8 = std::ptr::null(); - - match (self.adi_otp_request)( - ds_id, - &mut mid_ptr, - &mut mid_size, - &mut otp_ptr, - &mut otp_size, - ) { - 0 => { - let mut mid = vec![0; mid_size as usize]; - let mut otp = vec![0; otp_size as usize]; - - mid.copy_from_slice(std::slice::from_raw_parts(mid_ptr, mid_size as usize)); - otp.copy_from_slice(std::slice::from_raw_parts(otp_ptr, otp_size as usize)); - - (self.adi_dispose)(mid_ptr); - (self.adi_dispose)(otp_ptr); - - Ok(RequestOTPData { mid, otp }) - } - err => Err(ADIError::resolve(err)), - } - } - } - - fn set_local_user_uuid(&mut self, local_user_uuid: String) { - self.local_user_uuid = local_user_uuid; - } - - fn set_device_identifier(&mut self, device_identifier: String) -> Result<(), ADIError> { - self.set_identifier(&device_identifier[0..16])?; - self.device_identifier = device_identifier; - Ok(()) - } - - fn get_local_user_uuid(&self) -> String { - self.local_user_uuid.clone() - } - - fn get_device_identifier(&self) -> String { - self.device_identifier.clone() - } - - fn get_serial_number(&self) -> String { - "0".to_string() - } -} - -impl ConfigurableADIProxy for StoreServicesCoreADIProxy<'_> { - fn set_identifier(&mut self, identifier: &str) -> Result<(), ADIError> { - match (self.adi_set_android_id)(identifier.as_ptr(), identifier.len() as u32) { - 0 => Ok(()), - err => Err(ADIError::resolve(err)), - } - } - - fn set_provisioning_path(&mut self, path: &str) -> Result<(), ADIError> { - let path = CString::new(path).unwrap(); - match (self.adi_set_provisioning_path)(path.as_ptr() as *const u8) { - 0 => Ok(()), - err => Err(ADIError::resolve(err)), - } - } -} - -struct LoaderHelpers; - -use rand::Rng; - -#[cfg(all(target_family = "unix", not(target_os = "macos")))] -use libc::{ - chmod, close, free, fstat, ftruncate, gettimeofday, lstat, malloc, mkdir, open, read, strncpy, - umask, write, -}; -#[cfg(target_os = "macos")] -use posix_macos::*; - -static mut ERRNO: i32 = 0; - -#[allow(unreachable_code)] -#[sysv64] -unsafe fn __errno_location() -> *mut i32 { - ERRNO = std::io::Error::last_os_error().raw_os_error().unwrap_or(0); - &mut ERRNO -} - -#[sysv64] -fn arc4random() -> u32 { - rand::rng().random() -} - -#[sysv64] -unsafe fn __system_property_get(_name: *const c_char, value: *mut c_char) -> i32 { - *value = '0' as c_char; - return 1; -} - -#[cfg(target_family = "windows")] -use posix_windows::*; - -impl LoaderHelpers { - pub fn setup_hooks() { - let mut hooks = HashMap::new(); - hooks.insert("arc4random".to_owned(), arc4random as usize); - hooks.insert("chmod".to_owned(), chmod as usize); - hooks.insert( - "__system_property_get".to_owned(), - __system_property_get as usize, - ); - hooks.insert("__errno".to_owned(), __errno_location as usize); - hooks.insert("close".to_owned(), close as usize); - hooks.insert("free".to_owned(), free as usize); - hooks.insert("fstat".to_owned(), fstat as usize); - hooks.insert("ftruncate".to_owned(), ftruncate as usize); - hooks.insert("gettimeofday".to_owned(), gettimeofday as usize); - hooks.insert("lstat".to_owned(), lstat as usize); - hooks.insert("malloc".to_owned(), malloc as usize); - hooks.insert("mkdir".to_owned(), mkdir as usize); - hooks.insert("open".to_owned(), open as usize); - hooks.insert("read".to_owned(), read as usize); - hooks.insert("strncpy".to_owned(), strncpy as usize); - hooks.insert("umask".to_owned(), umask as usize); - hooks.insert("write".to_owned(), write as usize); - - hook_manager::add_hooks(hooks); - } -} - -#[cfg(test)] -mod tests { - use crate::AnisetteError; - use crate::{AnisetteConfiguration, AnisetteHeaders}; - use log::info; - use std::path::PathBuf; - - #[cfg(not(feature = "async"))] - #[test] - fn fetch_anisette_ssc() -> Result<(), AnisetteError> { - crate::tests::init_logger(); - - let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( - AnisetteConfiguration::new() - .set_configuration_path(PathBuf::new().join("anisette_test")), - )?; - info!( - "Headers: {:?}", - provider.provider.get_authentication_headers()? - ); - Ok(()) - } - - #[cfg(feature = "async")] - #[tokio::test] - async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> { - crate::tests::init_logger(); - - let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( - AnisetteConfiguration::new() - .set_configuration_path(PathBuf::new().join("anisette_test")), - )?; - info!( - "Headers: {:?}", - provider.provider.get_authentication_headers().await? - ); - Ok(()) - } -} diff --git a/omnisette/src/store_services_core/posix_macos.rs b/omnisette/src/store_services_core/posix_macos.rs deleted file mode 100644 index 840a039..0000000 --- a/omnisette/src/store_services_core/posix_macos.rs +++ /dev/null @@ -1,102 +0,0 @@ -pub use libc::{chmod, close, free, ftruncate, gettimeofday, malloc, mkdir, read, strncpy, umask, write}; - -use libc::{lstat as lstat_macos, fstat as fstat_macos, stat as stat_macos, open as open_macos, O_CREAT, O_WRONLY, O_RDWR, O_RDONLY}; - -use android_loader::sysv64; - -#[repr(C)] -pub struct StatLinux { - pub st_dev: u64, - pub st_ino: u64, - pub st_nlink: u64, - pub st_mode: u32, - pub st_uid: u32, - pub st_gid: u32, - __pad0: libc::c_int, - pub st_rdev: u64, - pub st_size: i64, - pub st_blksize: i64, - pub st_blocks: i64, - pub st_atime: i64, - pub st_atime_nsec: i64, - pub st_mtime: i64, - pub st_mtime_nsec: i64, - pub st_ctime: i64, - pub st_ctime_nsec: i64, - __unused: [i64; 3], -} - -#[sysv64] -pub unsafe fn lstat(path: *const libc::c_char, buf: *mut StatLinux) -> libc::c_int { - let mut st: stat_macos = std::mem::zeroed(); - lstat_macos(path, &mut st); - *buf = StatLinux { - st_dev: st.st_dev as _, - st_ino: st.st_ino as _, - st_nlink: st.st_nlink as _, - st_mode: st.st_mode as _, - st_uid: st.st_uid as _, - st_gid: st.st_gid as _, - __pad0: 0 as _, - st_rdev: st.st_rdev as _, - st_size: st.st_size as _, - st_blksize: st.st_blksize as _, - st_blocks: st.st_blocks as _, - st_atime: st.st_atime as _, - st_atime_nsec: st.st_atime_nsec as _, - st_mtime: st.st_mtime as _, - st_mtime_nsec: st.st_mtime_nsec as _, - st_ctime: st.st_ctime as _, - st_ctime_nsec: st.st_ctime_nsec as _, - __unused: [0, 0, 0], - }; - 0 -} - -#[sysv64] -pub unsafe fn fstat(fildes: libc::c_int, buf: *mut StatLinux) -> libc::c_int { - let mut st: stat_macos = std::mem::zeroed(); - fstat_macos(fildes, &mut st); - *buf = StatLinux { - st_dev: st.st_dev as _, - st_ino: st.st_ino as _, - st_nlink: st.st_nlink as _, - st_mode: st.st_mode as _, - st_uid: st.st_uid as _, - st_gid: st.st_gid as _, - __pad0: 0 as _, - st_rdev: st.st_rdev as _, - st_size: st.st_size as _, - st_blksize: st.st_blksize as _, - st_blocks: st.st_blocks as _, - st_atime: st.st_atime as _, - st_atime_nsec: st.st_atime_nsec as _, - st_mtime: st.st_mtime as _, - st_mtime_nsec: st.st_mtime_nsec as _, - st_ctime: st.st_ctime as _, - st_ctime_nsec: st.st_ctime_nsec as _, - __unused: [0, 0, 0], - }; - 0 -} - -#[sysv64] -pub unsafe fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int { - let mut win_flag = 0; // binary mode - - if oflag & 0o100 != 0 { - win_flag |= O_CREAT; - } - - if oflag & 0o1 == 1 { - win_flag |= O_WRONLY; - } else if oflag & 0o2 != 0 { - win_flag |= O_RDWR; - } else { - win_flag |= O_RDONLY; - } - - let val = open_macos(path, win_flag); - - val -} diff --git a/omnisette/src/store_services_core/posix_windows.rs b/omnisette/src/store_services_core/posix_windows.rs deleted file mode 100644 index 486e2b5..0000000 --- a/omnisette/src/store_services_core/posix_windows.rs +++ /dev/null @@ -1,268 +0,0 @@ -use android_loader::sysv64; -use libc::{O_CREAT, O_RDONLY, O_RDWR, O_WRONLY}; -use log::debug; -use std::ffi::{CStr, CString}; -use std::mem::MaybeUninit; - -#[link(name = "ucrt")] -extern "C" { - fn _errno() -> *mut libc::c_int; - fn _timespec64_get(__ts: *mut libc::timespec, __base: libc::c_int) -> libc::c_int; - fn _chsize(handle: i64, length: u64) -> usize; -} - -// took from cosmopolitan libc -#[sysv64] -pub unsafe fn umask(mask: usize) -> usize { - debug!("umask: Windows specific implementation called!"); - mask -} - -#[sysv64] -pub unsafe fn ftruncate(handle: i64, length: u64) -> usize { - debug!( - "ftruncate: Windows translate-call. handle: {}, length: {}", - handle, length - ); - let ftr = _chsize(handle, length); - - ftr -} - -#[repr(C)] -pub struct PosixTimeval { - tv_sec: u64, - tv_usec: u64, /* microseconds */ -} - -#[repr(C)] -pub struct PosixTimespec { - tv_sec: i64, - tv_nsec: i64, /* microseconds */ -} - -#[repr(C)] -pub struct PosixTimezone { - tz_minuteswest: u32, - tz_dsttime: u32, /* microseconds */ -} - -static HECTONANOSECONDS: u64 = 10000000; - -impl PosixTimespec { - pub fn from_windows_time(time: u64) -> PosixTimespec { - PosixTimespec { - tv_sec: (time / HECTONANOSECONDS) as i64, - tv_nsec: (time % HECTONANOSECONDS) as i64 * 100, - } - } -} - -#[sysv64] -pub unsafe fn gettimeofday(timeval: *mut PosixTimeval, _tz: *mut PosixTimezone) -> isize { - debug!("gettimeofday: Windows specific implementation called!"); - let mut ts = MaybeUninit::::zeroed(); - - let ret = _timespec64_get(ts.as_mut_ptr(), 1); - let ts = ts.assume_init(); - - *timeval = PosixTimeval { - tv_sec: ts.tv_sec as _, - tv_usec: (ts.tv_nsec / 1000) as _, - }; - - ret as _ -} - -#[repr(C)] -pub struct StatLinux { - pub st_dev: u64, - pub st_ino: u64, - pub st_nlink: u64, - pub st_mode: u32, - pub st_uid: u32, - pub st_gid: u32, - __pad0: libc::c_int, - pub st_rdev: u64, - pub st_size: i64, - pub st_blksize: i64, - pub st_blocks: i64, - pub st_atime: i64, - pub st_atime_nsec: i64, - pub st_mtime: i64, - pub st_mtime_nsec: i64, - pub st_ctime: i64, - pub st_ctime_nsec: i64, - __unused: [i64; 3], -} - -trait ToWindows { - unsafe fn to_windows(&self) -> T; -} - -impl ToWindows for CStr { - unsafe fn to_windows(&self) -> CString { - let path = self - .to_str() - .unwrap() - .to_string() - .chars() - .map(|x| match x { - '/' => '\\', - c => c, - }) - .collect::(); - - let path = path.trim_start_matches("\\\\?\\").to_string(); - - CString::new(path).unwrap() - } -} - -#[sysv64] -pub unsafe fn lstat(path: *const libc::c_char, buf: *mut StatLinux) -> libc::c_int { - debug!( - "lstat: Windows translate-call, path: {:?}", - CStr::from_ptr(path) - ); - let mut stat_win = MaybeUninit::::zeroed(); - let path = CStr::from_ptr(path).to_windows(); - - let ret = libc::stat(path.as_ptr(), stat_win.as_mut_ptr()); - let stat_win = stat_win.assume_init(); - - *buf = stat_win.to_windows(); - - ret -} - -impl ToWindows for libc::stat { - unsafe fn to_windows(&self) -> StatLinux { - let atime = PosixTimespec::from_windows_time(self.st_atime as u64); - let mtime = PosixTimespec::from_windows_time(self.st_mtime as u64); - let ctime = PosixTimespec::from_windows_time(self.st_ctime as u64); - - let mut mode = 0o555; - let win_mode = self.st_mode; - - if win_mode & 0b11 != 0 { - mode |= 0o200; - } - - if win_mode & 0x4000 != 0 { - mode |= 0o40000; - } - - StatLinux { - st_dev: self.st_dev as _, - st_ino: self.st_ino as _, - st_nlink: self.st_nlink as _, - st_mode: mode as _, - st_uid: self.st_uid as _, - st_gid: self.st_gid as _, - __pad0: 0, - st_rdev: self.st_rdev as _, - st_size: self.st_size as _, - st_blksize: 0, - st_blocks: 0, - st_atime: atime.tv_sec, - st_atime_nsec: 0, - st_mtime: mtime.tv_sec, - st_mtime_nsec: 0, - st_ctime: ctime.tv_sec, - st_ctime_nsec: 0, - __unused: [0, 0, 0], - } - } -} - -#[sysv64] -pub unsafe fn fstat(fildes: libc::c_int, buf: *mut StatLinux) -> libc::c_int { - debug!("fstat: Windows translate-call"); - let mut stat_win = MaybeUninit::::zeroed(); - let ret = libc::fstat(fildes, stat_win.as_mut_ptr()); - let stat_win = stat_win.assume_init(); - - *buf = stat_win.to_windows(); - - ret -} - -#[sysv64] -pub unsafe fn malloc(size: libc::size_t) -> *mut libc::c_void { - // debug!("malloc: Windows translate-call"); - libc::malloc(size) -} - -#[sysv64] -pub unsafe fn free(p: *mut libc::c_void) { - // debug!("free: Windows translate-call"); - libc::free(p) -} - -#[sysv64] -pub unsafe fn strncpy( - dst: *mut libc::c_char, - src: *const libc::c_char, - n: libc::size_t, -) -> *mut libc::c_char { - debug!("strncpy: Windows translate-call"); - libc::strncpy(dst, src, n) -} - -#[sysv64] -pub unsafe fn chmod(path: *const libc::c_char, mode: libc::c_int) -> libc::c_int { - debug!("chmod: Windows translate-call"); - libc::chmod(path, mode) -} - -#[sysv64] -pub unsafe fn mkdir(path: *const libc::c_char) -> libc::c_int { - debug!("mkdir: Windows translate-call"); - libc::mkdir(path) -} - -#[sysv64] -pub unsafe fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int { - debug!("open: Windows translate-call oflag 0o{:o}", oflag); - - let path = CStr::from_ptr(path).to_windows(); - - let mut win_flag = 0x8000; // binary mode - - if oflag & 0o100 != 0 { - win_flag |= O_CREAT; - } - - if oflag & 0o1 == 1 { - win_flag |= O_WRONLY; - } else if oflag & 0o2 != 0 { - win_flag |= O_RDWR; - } else { - win_flag |= O_RDONLY; - } - - let val = libc::open(path.as_ptr(), win_flag); - - val -} - -#[sysv64] -pub unsafe fn close(fd: libc::c_int) -> libc::c_int { - debug!("close: Windows translate-call"); - libc::close(fd) -} - -#[sysv64] -pub unsafe fn read(fd: libc::c_int, buf: *mut libc::c_void, count: libc::c_uint) -> libc::c_int { - debug!("read: Windows translate-call"); - - let r = libc::read(fd, buf, count); - r -} - -#[sysv64] -pub unsafe fn write(fd: libc::c_int, buf: *const libc::c_void, count: libc::c_uint) -> libc::c_int { - debug!("write: Windows translate-call"); - libc::write(fd, buf, count) -} From bb25d8dbe634928b903b261cbc5dc1ea378de1e3 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 9 Aug 2025 20:58:22 -0400 Subject: [PATCH 04/30] add description, repo, and library --- omnisette/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 051dee3..023de1b 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -2,6 +2,9 @@ name = "omnisette_fork" version = "0.1.0" edition = "2021" +description = "A library to generate \"anisette\" data. Modified from Sidestore/apple-private-apis" +repository = "https://github.com/nab138/apple-private-apis" +license = "MPL-2.0" [features] remote-anisette = [] From 8fea84423cf0a5d7499d0b1978efcad4ca69a7f4 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 9 Aug 2025 21:06:51 -0400 Subject: [PATCH 05/30] Update readmes and names --- README.md | 13 ++++++++----- icloud-auth/Cargo.toml | 7 +++++-- icloud-auth/rustcrypto-srp/Cargo.toml | 4 ++-- icloud-auth/rustcrypto-srp/README.md | 15 +++++++-------- icloud-auth/src/anisette.rs | 2 +- icloud-auth/src/client.rs | 4 ++-- icloud-auth/src/lib.rs | 4 ++-- icloud-auth/tests/gsa_auth.rs | 2 +- omnisette/Cargo.toml | 2 +- 9 files changed, 29 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 0692ba9..bc6c3b6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # `apple-private-apis` +This version has been published to crates.io temporarily (with permission from original developers) while they work on getting it ready for a proper publish. Find the original at https://github.com/SideStore/apple-private-apis + _A set of Rust libraries to interact with apple's private APIs and servers, made for use in [**SideInstaller**](https://sidestore.io)._ [![Rust](https://github.com/SideStore/apple-private-apis/actions/workflows/rust.yml/badge.svg)](https://github.com/SideStore/apple-private-apis/actions/workflows/rust.yml) @@ -7,11 +9,12 @@ _A set of Rust libraries to interact with apple's private APIs and servers, made ![Alt](https://repobeats.axiom.co/api/embed/4d96ea07d261281763b31ead2354ad6dcd6afed1.svg "Repobeats analytics image") -| Library | Description | -| --- | --- | -| [`omnisette`](./omnisette) | A library to generate "anisette" data | -| [`icloud-auth`](./icloud-auth/) | A library to authenticate with Apple's GSA servers | -| [`apple-dev-apis`](./apple-dev-apis/) | An implementation of Apple's Xcode signing/developer APIs | + +| Library | Description | +| ----------------------------------------------------- | ---------------------------------------------------------------------- | +| [`omnisette`](./omnisette) | A library to generate "anisette" data | +| [`icloud-auth`](./icloud-auth/) | A library to authenticate with Apple's GSA servers | +| [`apple-dev-apis`](./apple-dev-apis/) | An implementation of Apple's Xcode signing/developer APIs | | [`apple-codesign-wrapper`](./apple-codesign-wrapper/) | A wrapper for the `apple-codesign` crate. See the README for more info | diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index a801820..a965907 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,7 +1,10 @@ [package] -name = "icloud_auth_fork" +name = "nab138_icloud_auth" version = "0.1.0" edition = "2021" +description = "A library to authenticate with Apple's GSA servers" +repository = "https://github.com/nab138/apple-private-apis" +license = "MPL-2.0" [features] default = [] @@ -24,7 +27,7 @@ cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } -omnisette_fork = {path = "../omnisette", features = ["remote-anisette-v3"]} +omnisette = { version = "0.1", features = ["remote-anisette-v3"], package = "nab138_omnisette" } thiserror = "2" tokio = "1" botan = "0.12.0" diff --git a/icloud-auth/rustcrypto-srp/Cargo.toml b/icloud-auth/rustcrypto-srp/Cargo.toml index 63f7808..44d31fa 100644 --- a/icloud-auth/rustcrypto-srp/Cargo.toml +++ b/icloud-auth/rustcrypto-srp/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "srp" +name = "nab138_srp" version = "0.6.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" description = "Secure Remote Password (SRP) protocol implementation" documentation = "https://docs.rs/srp" -repository = "https://github.com/RustCrypto/PAKEs" +repository = "https://github.com/nab138/apple-private-apis" keywords = ["crypto", "pake", "authentication"] categories = ["cryptography", "authentication"] readme = "README.md" diff --git a/icloud-auth/rustcrypto-srp/README.md b/icloud-auth/rustcrypto-srp/README.md index 3aa7583..f8fbeb8 100644 --- a/icloud-auth/rustcrypto-srp/README.md +++ b/icloud-auth/rustcrypto-srp/README.md @@ -1,5 +1,7 @@ # [RustCrypto]: SRP +Forked to support an apple SRP session for icloud authentication. + [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] ![Apache2/MIT licensed][license-image] @@ -15,7 +17,7 @@ key-exchange algorithm. ## About This implementation is generic over hash functions using the [`Digest`] trait, -so you will need to choose a hash function, e.g. `Sha256` from [`sha2`] crate. +so you will need to choose a hash function, e.g. `Sha256` from [`sha2`] crate. Additionally this crate allows to use a specialized password hashing algorithm for private key computation instead of method described in the @@ -41,8 +43,8 @@ done with a minor version bump. Licensed under either of: - * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - * [MIT license](http://opensource.org/licenses/MIT) +- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +- [MIT license](http://opensource.org/licenses/MIT) at your option. @@ -52,8 +54,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -[//]: # (badges) - +[//]: # "badges" [crate-image]: https://img.shields.io/crates/v/srp.svg [crate-link]: https://crates.io/crates/srp [docs-image]: https://docs.rs/srp/badge.svg @@ -64,9 +65,7 @@ dual licensed as above, without any additional terms or conditions. [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260045-PAKEs [build-image]: https://github.com/RustCrypto/PAKEs/actions/workflows/srp.yml/badge.svg [build-link]: https://github.com/RustCrypto/PAKEs/actions/workflows/srp.yml - -[//]: # (general links) - +[//]: # "general links" [RustCrypto]: https://github.com/RustCrypto [Secure Remote Password]: https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol [`Digest`]: https://docs.rs/digest diff --git a/icloud-auth/src/anisette.rs b/icloud-auth/src/anisette.rs index b7ffd2b..a5311ae 100644 --- a/icloud-auth/src/anisette.rs +++ b/icloud-auth/src/anisette.rs @@ -1,5 +1,5 @@ use crate::Error; -use omnisette_fork::{AnisetteConfiguration, AnisetteHeaders}; +use nab138_omnisette::{AnisetteConfiguration, AnisetteHeaders}; use std::{collections::HashMap, time::SystemTime}; #[derive(Debug, Clone)] diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index 3fb4f96..e14d990 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -4,7 +4,7 @@ use base64::{engine::general_purpose, Engine}; use botan::Cipher; use cbc::cipher::{BlockDecryptMut, KeyIvInit}; use hmac::{Hmac, Mac}; -use omnisette_fork::AnisetteConfiguration; +use nab138_omnisette::AnisetteConfiguration; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, Certificate, Client, ClientBuilder, Response, @@ -351,7 +351,7 @@ impl AppleAccount { /// /// ``` /// use icloud_auth::AppleAccount; - /// use omnisette_fork::AnisetteData; + /// use nab138_omnisette::AnisetteData; /// /// let anisette = AnisetteData::new(); /// let account = AppleAccount::login( diff --git a/icloud-auth/src/lib.rs b/icloud-auth/src/lib.rs index 3ee4ca6..2ff9303 100644 --- a/icloud-auth/src/lib.rs +++ b/icloud-auth/src/lib.rs @@ -2,7 +2,7 @@ pub mod anisette; mod client; pub use client::{AppleAccount, AuthenticationExtras, LoginState, TrustedPhoneNumber, VerifyBody}; -pub use omnisette_fork::AnisetteConfiguration; +pub use nab138_omnisette::AnisetteConfiguration; use thiserror::Error; #[derive(Debug, Error)] @@ -22,5 +22,5 @@ pub enum Error { #[error("Request failed {0}")] ReqwestError(#[from] reqwest::Error), #[error("Failed getting anisette data {0}")] - ErrorGettingAnisette(#[from] omnisette_fork::AnisetteError), + ErrorGettingAnisette(#[from] nab138_omnisette::AnisetteError), } diff --git a/icloud-auth/tests/gsa_auth.rs b/icloud-auth/tests/gsa_auth.rs index 9903ed9..59ac80d 100644 --- a/icloud-auth/tests/gsa_auth.rs +++ b/icloud-auth/tests/gsa_auth.rs @@ -3,7 +3,7 @@ mod tests { use std::{path::PathBuf, str::FromStr}; use icloud_auth::*; - use omnisette_fork::AnisetteConfiguration; + use nab138_omnisette::AnisetteConfiguration; #[tokio::test] async fn gsa_auth() { diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 023de1b..78b7925 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "omnisette_fork" +name = "nab138_omnisette" version = "0.1.0" edition = "2021" description = "A library to generate \"anisette\" data. Modified from Sidestore/apple-private-apis" From 097e5da86af900a1d94b95cbab492fde937dcf7d Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 9 Aug 2025 21:09:53 -0400 Subject: [PATCH 06/30] update srp dep --- icloud-auth/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index a965907..13cfba6 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -14,7 +14,7 @@ vendored-botan = ["botan/vendored"] serde = { version = "1", features = ["derive"] } serde_json = { version = "1" } base64 = "0.22" -srp = { version = "0.6.0", path = "./rustcrypto-srp" } +srp = { version = "0.6.0", package = "nab138_srp" } pbkdf2 = "0.11" sha2 = "0.10" rand = "0.9" From 3d2284389c7a1710f3b967c4338742cfb41d7686 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 9 Aug 2025 21:11:24 -0400 Subject: [PATCH 07/30] update imports --- icloud-auth/src/anisette.rs | 2 +- icloud-auth/src/client.rs | 4 ++-- icloud-auth/src/lib.rs | 4 ++-- icloud-auth/tests/gsa_auth.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/icloud-auth/src/anisette.rs b/icloud-auth/src/anisette.rs index a5311ae..c58b767 100644 --- a/icloud-auth/src/anisette.rs +++ b/icloud-auth/src/anisette.rs @@ -1,5 +1,5 @@ use crate::Error; -use nab138_omnisette::{AnisetteConfiguration, AnisetteHeaders}; +use omnisette::{AnisetteConfiguration, AnisetteHeaders}; use std::{collections::HashMap, time::SystemTime}; #[derive(Debug, Clone)] diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index e14d990..a69141f 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -4,7 +4,7 @@ use base64::{engine::general_purpose, Engine}; use botan::Cipher; use cbc::cipher::{BlockDecryptMut, KeyIvInit}; use hmac::{Hmac, Mac}; -use nab138_omnisette::AnisetteConfiguration; +use omnisette::AnisetteConfiguration; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, Certificate, Client, ClientBuilder, Response, @@ -351,7 +351,7 @@ impl AppleAccount { /// /// ``` /// use icloud_auth::AppleAccount; - /// use nab138_omnisette::AnisetteData; + /// use omnisette::AnisetteData; /// /// let anisette = AnisetteData::new(); /// let account = AppleAccount::login( diff --git a/icloud-auth/src/lib.rs b/icloud-auth/src/lib.rs index 2ff9303..1ec3205 100644 --- a/icloud-auth/src/lib.rs +++ b/icloud-auth/src/lib.rs @@ -2,7 +2,7 @@ pub mod anisette; mod client; pub use client::{AppleAccount, AuthenticationExtras, LoginState, TrustedPhoneNumber, VerifyBody}; -pub use nab138_omnisette::AnisetteConfiguration; +pub use omnisette::AnisetteConfiguration; use thiserror::Error; #[derive(Debug, Error)] @@ -22,5 +22,5 @@ pub enum Error { #[error("Request failed {0}")] ReqwestError(#[from] reqwest::Error), #[error("Failed getting anisette data {0}")] - ErrorGettingAnisette(#[from] nab138_omnisette::AnisetteError), + ErrorGettingAnisette(#[from] omnisette::AnisetteError), } diff --git a/icloud-auth/tests/gsa_auth.rs b/icloud-auth/tests/gsa_auth.rs index 59ac80d..ae4336d 100644 --- a/icloud-auth/tests/gsa_auth.rs +++ b/icloud-auth/tests/gsa_auth.rs @@ -3,7 +3,7 @@ mod tests { use std::{path::PathBuf, str::FromStr}; use icloud_auth::*; - use nab138_omnisette::AnisetteConfiguration; + use omnisette::AnisetteConfiguration; #[tokio::test] async fn gsa_auth() { From a5b81671e5dd643d5a6238bb2dec5cfa76b76aee Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 9 Aug 2025 21:26:43 -0400 Subject: [PATCH 08/30] add apple_id to apple account --- icloud-auth/Cargo.toml | 2 +- icloud-auth/src/client.rs | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 13cfba6..d04befd 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_icloud_auth" -version = "0.1.0" +version = "0.1.1" edition = "2021" description = "A library to authenticate with Apple's GSA servers" repository = "https://github.com/nab138/apple-private-apis" diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index a69141f..531690c 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -92,6 +92,7 @@ pub struct AppleAccount { // pub spd: Option, //mutable spd pub spd: Option, + pub apple_id: String, client: Client, } @@ -170,12 +171,18 @@ async fn parse_response( } impl AppleAccount { - pub async fn new(config: AnisetteConfiguration) -> Result { + pub async fn new( + config: AnisetteConfiguration, + apple_id: String, + ) -> Result { let anisette = AnisetteData::new(config).await?; - Ok(Self::new_with_anisette(anisette)?) + Ok(Self::new_with_anisette(anisette, apple_id)?) } - pub fn new_with_anisette(anisette: AnisetteData) -> Result { + pub fn new_with_anisette( + anisette: AnisetteData, + apple_id: String, + ) -> Result { let client = ClientBuilder::new() .add_root_certificate(Certificate::from_der(APPLE_ROOT)?) // uncomment when debugging w/ charles proxy @@ -187,6 +194,7 @@ impl AppleAccount { Ok(AppleAccount { client, anisette: Mutex::new(anisette), + apple_id, spd: None, }) } @@ -370,10 +378,11 @@ impl AppleAccount { tfa_closure: G, anisette: AnisetteData, ) -> Result { - let mut _self = AppleAccount::new_with_anisette(anisette)?; let (username, password) = appleid_closure().map_err(|e| { Error::AuthSrpWithMessage(0, format!("Failed to get Apple ID credentials: {}", e)) })?; + let mut _self = AppleAccount::new_with_anisette(anisette, username.clone())?; + let mut response = _self.login_email_pass(&username, &password).await?; loop { match response { From c5e44fa255f50b316ba13eb170f55e537213d44e Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 20 Sep 2025 13:04:11 -0400 Subject: [PATCH 09/30] Hopefully fix mac builds --- icloud-auth/src/client.rs | 2 + omnisette/Cargo.toml | 27 +- omnisette/src/adi_proxy.rs | 383 --------------------- omnisette/src/anisette_headers_provider.rs | 9 +- omnisette/src/aos_kit.rs | 124 ------- omnisette/src/lib.rs | 52 +-- omnisette/src/remote_anisette.rs | 47 --- 7 files changed, 16 insertions(+), 628 deletions(-) delete mode 100644 omnisette/src/adi_proxy.rs delete mode 100644 omnisette/src/aos_kit.rs delete mode 100644 omnisette/src/remote_anisette.rs diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index 531690c..b0034d4 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -688,6 +688,7 @@ impl AppleAccount { } pub async fn verify_2fa(&self, code: String) -> Result { + println!("Verifying 2fa Code {}", code.clone()); let headers = self.build_2fa_headers(false); // println!("Recieved code: {}", code); let res = self @@ -700,6 +701,7 @@ impl AppleAccount { ) .send() .await?; + println!("Response: {:?}", res); let res: plist::Dictionary = plist::from_bytes(res.text().await?.as_bytes())?; diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 78b7925..ef26104 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -1,17 +1,11 @@ [package] name = "nab138_omnisette" -version = "0.1.0" +version = "0.1.1" edition = "2021" description = "A library to generate \"anisette\" data. Modified from Sidestore/apple-private-apis" repository = "https://github.com/nab138/apple-private-apis" license = "MPL-2.0" -[features] -remote-anisette = [] -async = ["dep:async-trait"] -default = ["remote-anisette", "dep:remove-async-await"] -remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungstenite", "dep:futures-util", "dep:chrono"] - [dependencies] base64 = "0.22" hex = "0.4" @@ -22,21 +16,16 @@ sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } libc = "0.2" log = "0.4" -async-trait = { version = "0.1", optional = true } -remove-async-await = { version = "1.0", optional = true } -serde = { version = "1.0", features = ["derive"], optional = true } -serde_json = { version = "1.0.115", optional = true } -tokio-tungstenite = { version = "0.27.0", optional = true, features = ["rustls-tls-webpki-roots"] } -futures-util = { version = "0.3.28", optional = true } -chrono = { version = "0.4.37", optional = true } +async-trait = { version = "0.1" } +remove-async-await = { version = "1.0" } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0.115" } +tokio-tungstenite = { version = "0.27.0", features = ["rustls-tls-webpki-roots"] } +futures-util = { version = "0.3.28" } +chrono = { version = "0.4.37" } thiserror = "2" anyhow = "1.0.81" -[target.'cfg(target_os = "macos")'.dependencies] -dlopen2 = "0.4" -objc = "0.2" -objc-foundation = "0.1" - [dev-dependencies] tokio = { version = "1", features = ["rt", "macros"] } simplelog = "0.12" diff --git a/omnisette/src/adi_proxy.rs b/omnisette/src/adi_proxy.rs deleted file mode 100644 index ccaa3b9..0000000 --- a/omnisette/src/adi_proxy.rs +++ /dev/null @@ -1,383 +0,0 @@ -use crate::adi_proxy::ProvisioningError::InvalidResponse; -use crate::anisette_headers_provider::AnisetteHeadersProvider; -use crate::AnisetteError; -use base64::engine::general_purpose::STANDARD as base64_engine; -use base64::Engine; -use log::debug; -use plist::{Dictionary, Value}; -use rand::RngCore; -#[cfg(not(feature = "async"))] -use reqwest::blocking::{Client, ClientBuilder, Response}; -use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; -#[cfg(feature = "async")] -use reqwest::{Client, ClientBuilder, Response}; -use sha2::{Digest, Sha256}; -use std::collections::HashMap; -use std::fmt::{Display, Formatter}; -use std::io::{self, Read, Write}; -use std::path::PathBuf; -use thiserror::Error; - -#[derive(Debug)] -pub struct ServerError { - pub code: i64, - pub description: String, -} - -#[derive(Debug)] -pub enum ProvisioningError { - InvalidResponse, - ServerError(ServerError), -} - -impl std::fmt::Display for ProvisioningError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -impl std::error::Error for ProvisioningError {} - -#[derive(Debug, Error)] -pub enum ADIError { - Unknown(i32), - ProvisioningError(#[from] ProvisioningError), - PlistError(#[from] plist::Error), - ReqwestError(#[from] reqwest::Error), - Base64Error(#[from] base64::DecodeError), - InvalidHeaderValue(#[from] InvalidHeaderValue), - IOError(#[from] io::Error), -} - -impl ADIError { - pub fn resolve(error_number: i32) -> ADIError { - ADIError::Unknown(error_number) - } -} - -#[cfg_attr(feature = "async", async_trait::async_trait)] -trait ToPlist { - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn plist(self) -> Result; -} - -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl ToPlist for Response { - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn plist(self) -> Result { - if let Ok(property_list) = Value::from_reader_xml(&*self.bytes().await?) { - Ok(property_list.as_dictionary().unwrap().to_owned()) - } else { - Err(ProvisioningError::InvalidResponse.into()) - } - } -} - -impl Display for ADIError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -pub struct SynchronizeData { - pub mid: Vec, - pub srm: Vec, -} - -pub struct StartProvisioningData { - pub cpim: Vec, - pub session: u32, -} - -pub struct RequestOTPData { - pub otp: Vec, - pub mid: Vec, -} - -#[cfg_attr(feature = "async", async_trait::async_trait(?Send))] -pub trait ADIProxy: Send + Sync { - fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError>; - fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result; - fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError>; - fn end_provisioning(&mut self, session: u32, ptm: &[u8], tk: &[u8]) -> Result<(), ADIError>; - fn start_provisioning( - &mut self, - ds_id: i64, - spim: &[u8], - ) -> Result; - fn is_machine_provisioned(&self, ds_id: i64) -> bool; - fn request_otp(&self, ds_id: i64) -> Result; - - fn set_local_user_uuid(&mut self, local_user_uuid: String); - fn set_device_identifier(&mut self, device_identifier: String) -> Result<(), ADIError>; - - fn get_local_user_uuid(&self) -> String; - fn get_device_identifier(&self) -> String; - fn get_serial_number(&self) -> String; -} - -pub trait ConfigurableADIProxy: ADIProxy { - fn set_identifier(&mut self, identifier: &str) -> Result<(), ADIError>; - fn set_provisioning_path(&mut self, path: &str) -> Result<(), ADIError>; -} - -pub const AKD_USER_AGENT: &str = "akd/1.0 CFNetwork/808.1.4"; -pub const CLIENT_INFO_HEADER: &str = - " "; -pub const DS_ID: i64 = -2; -pub const IDENTIFIER_LENGTH: usize = 16; -pub type Identifier = [u8; IDENTIFIER_LENGTH]; - -trait AppleRequestResult { - fn check_status(&self) -> Result<(), ADIError>; - fn get_response(&self) -> Result<&Dictionary, ADIError>; -} - -impl AppleRequestResult for Dictionary { - fn check_status(&self) -> Result<(), ADIError> { - let status = self - .get("Status") - .ok_or(InvalidResponse)? - .as_dictionary() - .unwrap(); - let code = status.get("ec").unwrap().as_signed_integer().unwrap(); - if code != 0 { - let description = status.get("em").unwrap().as_string().unwrap().to_string(); - Err(ProvisioningError::ServerError(ServerError { code, description }).into()) - } else { - Ok(()) - } - } - - fn get_response(&self) -> Result<&Dictionary, ADIError> { - if let Some(response) = self.get("Response") { - let response = response.as_dictionary().unwrap(); - response.check_status()?; - Ok(response) - } else { - Err(InvalidResponse.into()) - } - } -} - -impl dyn ADIProxy { - fn make_http_client(&mut self) -> Result { - let mut headers = HeaderMap::new(); - headers.insert("Content-Type", HeaderValue::from_str("text/x-xml-plist")?); - - headers.insert( - "X-Mme-Client-Info", - HeaderValue::from_str(CLIENT_INFO_HEADER)?, - ); - headers.insert( - "X-Mme-Device-Id", - HeaderValue::from_str(self.get_device_identifier().as_str())?, - ); - headers.insert( - "X-Apple-I-MD-LU", - HeaderValue::from_str(self.get_local_user_uuid().as_str())?, - ); - headers.insert( - "X-Apple-I-SRL-NO", - HeaderValue::from_str(self.get_serial_number().as_str())?, - ); - - debug!("Headers sent: {headers:?}"); - - let http_client = ClientBuilder::new() - .http1_title_case_headers() - .danger_accept_invalid_certs(true) // TODO: pin the apple certificate - .user_agent(AKD_USER_AGENT) - .default_headers(headers) - .build()?; - - Ok(http_client) - } - - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn provision_device(&mut self) -> Result<(), ADIError> { - let client = self.make_http_client()?; - - let url_bag_res = client - .get("https://gsa.apple.com/grandslam/GsService2/lookup") - .send() - .await? - .plist() - .await?; - - let urls = url_bag_res.get("urls").unwrap().as_dictionary().unwrap(); - - let start_provisioning_url = urls - .get("midStartProvisioning") - .unwrap() - .as_string() - .unwrap(); - let finish_provisioning_url = urls - .get("midFinishProvisioning") - .unwrap() - .as_string() - .unwrap(); - - let mut body = plist::Dictionary::new(); - body.insert( - "Header".to_string(), - plist::Value::Dictionary(plist::Dictionary::new()), - ); - body.insert( - "Request".to_string(), - plist::Value::Dictionary(plist::Dictionary::new()), - ); - - let mut sp_request = Vec::new(); - plist::Value::Dictionary(body).to_writer_xml(&mut sp_request)?; - - debug!("First provisioning request..."); - let response = client - .post(start_provisioning_url) - .body(sp_request) - .send() - .await? - .plist() - .await?; - - let response = response.get_response()?; - - let spim = response - .get("spim") - .unwrap() - .as_string() - .unwrap() - .to_owned(); - - let spim = base64_engine.decode(spim)?; - let first_step = self.start_provisioning(DS_ID, spim.as_slice())?; - - let mut body = Dictionary::new(); - let mut request = Dictionary::new(); - request.insert( - "cpim".to_owned(), - Value::String(base64_engine.encode(first_step.cpim)), - ); - body.insert("Header".to_owned(), Value::Dictionary(Dictionary::new())); - body.insert("Request".to_owned(), Value::Dictionary(request)); - - let mut fp_request = Vec::new(); - Value::Dictionary(body).to_writer_xml(&mut fp_request)?; - - debug!("Second provisioning request..."); - let response = client - .post(finish_provisioning_url) - .body(fp_request) - .send() - .await? - .plist() - .await?; - - let response = response.get_response()?; - - let ptm = base64_engine.decode(response.get("ptm").unwrap().as_string().unwrap())?; - let tk = base64_engine.decode(response.get("tk").unwrap().as_string().unwrap())?; - - self.end_provisioning(first_step.session, ptm.as_slice(), tk.as_slice())?; - debug!("Done."); - - Ok(()) - } -} - -pub struct ADIProxyAnisetteProvider { - adi_proxy: ProxyType, -} - -impl ADIProxyAnisetteProvider { - /// If you use this method, you are expected to set the identifier yourself. - pub fn without_identifier( - adi_proxy: ProxyType, - ) -> Result, ADIError> { - Ok(ADIProxyAnisetteProvider { adi_proxy }) - } - - pub fn new( - mut adi_proxy: ProxyType, - configuration_path: PathBuf, - ) -> Result, ADIError> { - let identifier_file_path = configuration_path.join("identifier"); - let mut identifier_file = std::fs::OpenOptions::new() - .create(true) - .read(true) - .write(true) - .open(identifier_file_path)?; - let mut identifier = [0u8; IDENTIFIER_LENGTH]; - if identifier_file.metadata()?.len() == IDENTIFIER_LENGTH as u64 { - identifier_file.read_exact(&mut identifier)?; - } else { - rand::rng().fill_bytes(&mut identifier); - identifier_file.write_all(&identifier)?; - } - - let mut local_user_uuid_hasher = Sha256::new(); - local_user_uuid_hasher.update(identifier); - - adi_proxy.set_device_identifier( - uuid::Uuid::from_bytes(identifier) - .to_string() - .to_uppercase(), - )?; // UUID, uppercase - adi_proxy - .set_local_user_uuid(hex::encode(local_user_uuid_hasher.finalize()).to_uppercase()); // 64 uppercase character hex - - Ok(ADIProxyAnisetteProvider { adi_proxy }) - } - - pub fn adi_proxy(&mut self) -> &mut ProxyType { - &mut self.adi_proxy - } -} - -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl AnisetteHeadersProvider - for ADIProxyAnisetteProvider -{ - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn get_anisette_headers( - &mut self, - skip_provisioning: bool, - ) -> Result, AnisetteError> { - let adi_proxy = &mut self.adi_proxy as &mut dyn ADIProxy; - - if !adi_proxy.is_machine_provisioned(DS_ID) && !skip_provisioning { - adi_proxy.provision_device().await?; - } - - let machine_data = adi_proxy.request_otp(DS_ID)?; - - let mut headers = HashMap::new(); - headers.insert( - "X-Apple-I-MD".to_string(), - base64_engine.encode(machine_data.otp), - ); - headers.insert( - "X-Apple-I-MD-M".to_string(), - base64_engine.encode(machine_data.mid), - ); - headers.insert("X-Apple-I-MD-RINFO".to_string(), "17106176".to_string()); - headers.insert( - "X-Apple-I-MD-LU".to_string(), - adi_proxy.get_local_user_uuid(), - ); - headers.insert( - "X-Apple-I-SRL-NO".to_string(), - adi_proxy.get_serial_number(), - ); - headers.insert( - "X-Mme-Client-Info".to_string(), - CLIENT_INFO_HEADER.to_string(), - ); - headers.insert( - "X-Mme-Device-Id".to_string(), - adi_proxy.get_device_identifier(), - ); - - Ok(headers) - } -} diff --git a/omnisette/src/anisette_headers_provider.rs b/omnisette/src/anisette_headers_provider.rs index 903203e..aab500b 100644 --- a/omnisette/src/anisette_headers_provider.rs +++ b/omnisette/src/anisette_headers_provider.rs @@ -1,18 +1,17 @@ - use std::collections::HashMap; use crate::AnisetteError; -#[cfg_attr(feature = "async", async_trait::async_trait)] +#[async_trait::async_trait] pub trait AnisetteHeadersProvider: Send + Sync { - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] async fn get_anisette_headers( &mut self, skip_provisioning: bool, ) -> Result, AnisetteError>; - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn get_authentication_headers(&mut self) -> Result, AnisetteError> { + async fn get_authentication_headers( + &mut self, + ) -> Result, AnisetteError> { let headers = self.get_anisette_headers(false).await?; Ok(self.normalize_headers(headers)) } diff --git a/omnisette/src/aos_kit.rs b/omnisette/src/aos_kit.rs deleted file mode 100644 index 5e53787..0000000 --- a/omnisette/src/aos_kit.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::anisette_headers_provider::AnisetteHeadersProvider; -use anyhow::Result; - -use dlopen2::symbor::Library; -use objc::{msg_send, runtime::Class, sel, sel_impl}; -use objc_foundation::{INSString, NSObject, NSString}; -use std::collections::HashMap; -use std::error::Error; -use std::fmt::{Display, Formatter}; -pub struct AOSKitAnisetteProvider<'lt> { - aos_utilities: &'lt Class, - ak_device: &'lt Class, -} - -impl<'lt> AOSKitAnisetteProvider<'lt> { - pub fn new() -> Result> { - Library::open("/System/Library/PrivateFrameworks/AOSKit.framework/AOSKit")?; - Library::open("/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit")?; - Ok(AOSKitAnisetteProvider { - aos_utilities: Class::get("AOSUtilities").ok_or(AOSKitError::ClassLoadFailed)?, - ak_device: Class::get("AKDevice").ok_or(AOSKitError::ClassLoadFailed)?, - }) - } -} - -#[cfg_attr(feature = "async", async_trait::async_trait(?Send))] -impl<'lt> AnisetteHeadersProvider for AOSKitAnisetteProvider<'lt> { - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn get_anisette_headers( - &mut self, - _skip_provisioning: bool, - ) -> Result> { - let mut headers_map = HashMap::new(); - - let headers: *const NSObject = unsafe { - msg_send![self.aos_utilities, retrieveOTPHeadersForDSID: NSString::from_str("-2")] - }; - - let otp: *const NSString = - unsafe { msg_send![headers, valueForKey: NSString::from_str("X-Apple-MD")] }; - headers_map.insert( - "X-Apple-I-MD".to_string(), - unsafe { (*otp).as_str() }.to_string(), - ); - - let mid: *const NSString = - unsafe { msg_send![headers, valueForKey: NSString::from_str("X-Apple-MD-M")] }; - headers_map.insert( - "X-Apple-I-MD-M".to_string(), - unsafe { (*mid).as_str() }.to_string(), - ); - - let machine_serial_number: *const NSString = - unsafe { msg_send![self.aos_utilities, machineSerialNumber] }; - headers_map.insert( - "X-Apple-SRL-NO".to_string(), - unsafe { (*machine_serial_number).as_str() }.to_string(), - ); - - let current_device: *const NSObject = unsafe { msg_send![self.ak_device, currentDevice] }; - - let local_user_uuid: *const NSString = unsafe { msg_send![current_device, localUserUUID] }; - headers_map.insert( - "X-Apple-I-MD-LU".to_string(), - unsafe { (*local_user_uuid).as_str() }.to_string(), - ); - - let locale: *const NSObject = unsafe { msg_send![current_device, locale] }; - let locale: *const NSString = unsafe { msg_send![locale, localeIdentifier] }; - headers_map.insert( - "X-Apple-Locale".to_string(), - unsafe { (*locale).as_str() }.to_string(), - ); // FIXME maybe not the right header name - - let server_friendly_description: *const NSString = - unsafe { msg_send![current_device, serverFriendlyDescription] }; - headers_map.insert( - "X-Mme-Client-Info".to_string(), - unsafe { (*server_friendly_description).as_str() }.to_string(), - ); - - let unique_device_identifier: *const NSString = - unsafe { msg_send![current_device, uniqueDeviceIdentifier] }; - headers_map.insert( - "X-Mme-Device-Id".to_string(), - unsafe { (*unique_device_identifier).as_str() }.to_string(), - ); - - Ok(headers_map) - } -} - -#[derive(Debug)] -enum AOSKitError { - ClassLoadFailed, -} - -impl Display for AOSKitError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -impl Error for AOSKitError {} - -#[cfg(all(test, not(feature = "async")))] -mod tests { - use crate::anisette_headers_provider::AnisetteHeadersProvider; - use crate::aos_kit::AOSKitAnisetteProvider; - use anyhow::Result; - use log::info; - - #[test] - fn fetch_anisette_aoskit() -> Result<()> { - crate::tests::init_logger(); - - let mut provider = AOSKitAnisetteProvider::new()?; - info!( - "AOSKit headers: {:?}", - (&mut provider as &mut dyn AnisetteHeadersProvider).get_authentication_headers()? - ); - Ok(()) - } -} diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index b7de8ad..880e0ef 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -4,25 +4,15 @@ //! //! If you want remote anisette, make sure the `remote-anisette` feature is enabled. (it's currently on by default) -use crate::adi_proxy::{ADIProxyAnisetteProvider, ConfigurableADIProxy}; use crate::anisette_headers_provider::AnisetteHeadersProvider; -use adi_proxy::ADIError; use std::io; use std::path::PathBuf; use thiserror::Error; -pub mod adi_proxy; pub mod anisette_headers_provider; -#[cfg(feature = "remote-anisette-v3")] pub mod remote_anisette_v3; -#[cfg(target_os = "macos")] -pub mod aos_kit; - -#[cfg(feature = "remote-anisette")] -pub mod remote_anisette; - #[allow(dead_code)] pub struct AnisetteHeaders; @@ -40,16 +30,12 @@ pub enum AnisetteError { PlistError(#[from] plist::Error), #[error("Request Error {0}")] ReqwestError(#[from] reqwest::Error), - #[cfg(feature = "remote-anisette-v3")] #[error("Provisioning socket error {0}")] WsError(#[from] tokio_tungstenite::tungstenite::error::Error), - #[cfg(feature = "remote-anisette-v3")] #[error("JSON error {0}")] SerdeError(#[from] serde_json::Error), #[error("IO error {0}")] IOError(#[from] io::Error), - #[error("ADI error {0}")] - ADIError(#[from] ADIError), #[error("Invalid library format")] InvalidLibraryFormat, #[error("Misc")] @@ -142,27 +128,13 @@ impl AnisetteHeaders { pub fn get_anisette_headers_provider( configuration: AnisetteConfiguration, ) -> Result { - #[cfg(target_os = "macos")] - if let Ok(prov) = aos_kit::AOSKitAnisetteProvider::new() { - return Ok(AnisetteHeadersProviderRes::local(Box::new(prov))); - } - - #[cfg(feature = "remote-anisette-v3")] - return Ok(AnisetteHeadersProviderRes::remote(Box::new( + Ok(AnisetteHeadersProviderRes::remote(Box::new( remote_anisette_v3::RemoteAnisetteProviderV3::new( configuration.anisette_url_v3, configuration.configuration_path.clone(), configuration.macos_serial.clone(), ), - ))); - - #[cfg(feature = "remote-anisette")] - return Ok(AnisetteHeadersProviderRes::remote(Box::new( - remote_anisette::RemoteAnisetteProvider::new(configuration.anisette_url), - ))); - - #[cfg(not(feature = "remote-anisette"))] - bail!(AnisetteMetaError::UnsupportedDevice) + ))) } } @@ -184,24 +156,4 @@ mod tests { .is_ok() {} } - - #[cfg(not(feature = "async"))] - #[test] - fn fetch_anisette_auto() -> Result<()> { - use crate::{AnisetteConfiguration, AnisetteHeaders}; - use log::info; - use std::path::PathBuf; - - crate::tests::init_logger(); - - let mut provider = AnisetteHeaders::get_anisette_headers_provider( - AnisetteConfiguration::new() - .set_configuration_path(PathBuf::new().join("anisette_test")), - )?; - info!( - "Headers: {:?}", - provider.provider.get_authentication_headers()? - ); - Ok(()) - } } diff --git a/omnisette/src/remote_anisette.rs b/omnisette/src/remote_anisette.rs deleted file mode 100644 index b061bd9..0000000 --- a/omnisette/src/remote_anisette.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::{anisette_headers_provider::AnisetteHeadersProvider, AnisetteError}; -#[cfg(not(feature = "async"))] -use reqwest::blocking::get; -#[cfg(feature = "async")] -use reqwest::get; -use std::collections::HashMap; - -pub struct RemoteAnisetteProvider { - url: String, -} - -impl RemoteAnisetteProvider { - pub fn new(url: String) -> RemoteAnisetteProvider { - RemoteAnisetteProvider { url } - } -} - -#[cfg_attr(feature = "async", async_trait::async_trait)] -impl AnisetteHeadersProvider for RemoteAnisetteProvider { - #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] - async fn get_anisette_headers( - &mut self, - _skip_provisioning: bool, - ) -> Result, AnisetteError> { - Ok(get(&self.url).await?.json().await?) - } -} - -#[cfg(all(test, not(feature = "async")))] -mod tests { - use crate::anisette_headers_provider::AnisetteHeadersProvider; - use crate::remote_anisette::RemoteAnisetteProvider; - use crate::DEFAULT_ANISETTE_URL; - use log::info; - - #[test] - fn fetch_anisette_remote() -> Result<(), AnisetteError> { - crate::tests::init_logger(); - - let mut provider = RemoteAnisetteProvider::new(DEFAULT_ANISETTE_URL.to_string()); - info!( - "Remote headers: {:?}", - (&mut provider as &mut dyn AnisetteHeadersProvider).get_authentication_headers()? - ); - Ok(()) - } -} From 679688b38ad7ae70f241f0cf9f5f53a630ee0c44 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 20 Sep 2025 13:19:59 -0400 Subject: [PATCH 10/30] Attempt to fix sms 2fa --- icloud-auth/Cargo.toml | 2 +- icloud-auth/src/client.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index d04befd..6f7676a 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_icloud_auth" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "A library to authenticate with Apple's GSA servers" repository = "https://github.com/nab138/apple-private-apis" diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index b0034d4..7047669 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -649,9 +649,8 @@ impl AppleAccount { let res = self .client - .put("https://gsa.apple.com/auth/verify/phone/") + .get("https://gsa.apple.com/auth") .headers(headers.await) - .json(&body) .send() .await?; From 740d58fded7b31586d74195f2f913085d4c53f4c Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 20 Sep 2025 13:23:06 -0400 Subject: [PATCH 11/30] Update omnisette version --- icloud-auth/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 6f7676a..dc02a09 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -27,7 +27,7 @@ cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } -omnisette = { version = "0.1", features = ["remote-anisette-v3"], package = "nab138_omnisette" } +omnisette = { version = "0.1.1", package = "nab138_omnisette" } thiserror = "2" tokio = "1" botan = "0.12.0" From d21975bf833b7a715a3276fe326fc95bbcf24a13 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sat, 20 Sep 2025 13:41:12 -0400 Subject: [PATCH 12/30] Remove unused log --- icloud-auth/Cargo.toml | 2 +- icloud-auth/src/client.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index dc02a09..4845831 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_icloud_auth" -version = "0.1.2" +version = "0.1.3" edition = "2021" description = "A library to authenticate with Apple's GSA servers" repository = "https://github.com/nab138/apple-private-apis" diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index 7047669..af94964 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -687,7 +687,7 @@ impl AppleAccount { } pub async fn verify_2fa(&self, code: String) -> Result { - println!("Verifying 2fa Code {}", code.clone()); + // println!("Verifying 2fa Code {}", code.clone()); let headers = self.build_2fa_headers(false); // println!("Recieved code: {}", code); let res = self @@ -700,7 +700,6 @@ impl AppleAccount { ) .send() .await?; - println!("Response: {:?}", res); let res: plist::Dictionary = plist::from_bytes(res.text().await?.as_bytes())?; From 403b41c6c0a439718884cbff4d38c4a143aa8854 Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 5 Nov 2025 20:05:44 -0500 Subject: [PATCH 13/30] Allow changing anisette url v3 --- omnisette/Cargo.toml | 2 +- omnisette/src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index ef26104..1137181 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_omnisette" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "A library to generate \"anisette\" data. Modified from Sidestore/apple-private-apis" repository = "https://github.com/nab138/apple-private-apis" diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index 880e0ef..aeb66f4 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -87,6 +87,11 @@ impl AnisetteConfiguration { self } + pub fn set_anisette_url_v3(mut self, anisette_url_v3: String) -> AnisetteConfiguration { + self.anisette_url_v3 = anisette_url_v3; + self + } + pub fn set_macos_serial(mut self, macos_serial: String) -> AnisetteConfiguration { self.macos_serial = macos_serial; self From 6c211e41ce64a3f8b05e8b81afa0383e1cfddd07 Mon Sep 17 00:00:00 2001 From: nab138 Date: Wed, 5 Nov 2025 20:09:01 -0500 Subject: [PATCH 14/30] Bump icloud_auth version --- icloud-auth/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 4845831..5ab90d0 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_icloud_auth" -version = "0.1.3" +version = "0.1.4" edition = "2021" description = "A library to authenticate with Apple's GSA servers" repository = "https://github.com/nab138/apple-private-apis" @@ -27,7 +27,7 @@ cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } -omnisette = { version = "0.1.1", package = "nab138_omnisette" } +omnisette = { version = "0.1.2", package = "nab138_omnisette" } thiserror = "2" tokio = "1" botan = "0.12.0" From 6615ecdaed9550ac9afd878a3c5276d2bd485cef Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:03:23 -0500 Subject: [PATCH 15/30] Remove dependency on aws-lc-rs --- icloud-auth/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 5ab90d0..f7407c5 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -18,7 +18,7 @@ srp = { version = "0.6.0", package = "nab138_srp" } pbkdf2 = "0.11" sha2 = "0.10" rand = "0.9" -rustls = "0.23" +rustls = { version = "0.23", default-features = false, features = ["ring"] } rustls-pemfile = "2.2" plist = "1.7.2" hmac = "0.12.1" From acc8ea56aacc46505578a96db5d4f70915b1516e Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:03:41 -0500 Subject: [PATCH 16/30] Bump version --- icloud-auth/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index f7407c5..fc3ee7d 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_icloud_auth" -version = "0.1.4" +version = "0.1.5" edition = "2021" description = "A library to authenticate with Apple's GSA servers" repository = "https://github.com/nab138/apple-private-apis" From 76e6ba2bd7b7029d34308454fee44918fcf1a248 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:39:15 -0500 Subject: [PATCH 17/30] Remove dependency on botan!!! --- icloud-auth/Cargo.toml | 3 +-- icloud-auth/src/client.rs | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index fc3ee7d..14f8cef 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -8,7 +8,6 @@ license = "MPL-2.0" [features] default = [] -vendored-botan = ["botan/vendored"] [dependencies] serde = { version = "1", features = ["derive"] } @@ -30,7 +29,7 @@ reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] omnisette = { version = "0.1.2", package = "nab138_omnisette" } thiserror = "2" tokio = "1" -botan = "0.12.0" +aes-gcm = "0.10.3" [dev-dependencies] tokio = { version = "1", features = ["rt", "macros"] } diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index af94964..59ef371 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -1,9 +1,12 @@ use crate::{anisette::AnisetteData, Error}; -use aes::cipher::block_padding::Pkcs7; +use aes::{ + cipher::{block_padding::Pkcs7, consts::U16}, + Aes256, +}; +use aes_gcm::{aead::KeyInit, AeadInPlace, AesGcm, Nonce}; use base64::{engine::general_purpose, Engine}; -use botan::Cipher; use cbc::cipher::{BlockDecryptMut, KeyIvInit}; -use hmac::{Hmac, Mac}; +use hmac::Mac; use omnisette::AnisetteConfiguration; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, @@ -304,21 +307,20 @@ impl AppleAccount { return Err(Error::Parse); } - let mut cipher = Cipher::new("AES-256/GCM", botan::CipherDirection::Decrypt) - .map_err(|_| Error::Parse)?; - cipher.set_key(sk).map_err(|_| Error::Parse)?; - cipher - .set_associated_data(header) - .map_err(|_| Error::Parse)?; - cipher.start(iv).map_err(|_| Error::Parse)?; + let key = aes_gcm::Key::>::from_slice(sk); + let cipher = AesGcm::::new(key); + let nonce = Nonce::::from_slice(iv); let mut buf = ciphertext_and_tag.to_vec(); - buf = cipher.finish(&mut buf).map_err(|_| { - Error::AuthSrpWithMessage( - 0, - "Failed to decrypt app token (Botan AES-256/GCM).".to_string(), - ) - })?; + + cipher + .decrypt_in_place(nonce, header, &mut buf) + .map_err(|_| { + Error::AuthSrpWithMessage( + 0, + "Failed to decrypt app token (AES-256/GCM aes-gcm).".to_string(), + ) + })?; let decrypted_token: plist::Dictionary = plist::from_bytes(&buf).map_err(|_| Error::Parse)?; @@ -340,7 +342,7 @@ impl AppleAccount { } fn create_checksum(session_key: &Vec, dsid: &str, app_name: &str) -> Vec { - Hmac::::new_from_slice(&session_key) + as hmac::Mac>::new_from_slice(session_key.as_slice()) .unwrap() .chain_update("apptokens".as_bytes()) .chain_update(dsid.as_bytes()) @@ -602,7 +604,7 @@ impl AppleAccount { } fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Vec { - Hmac::::new_from_slice(&usr.key()) + as hmac::Mac>::new_from_slice(&usr.key()) .unwrap() .chain_update(name.as_bytes()) .finalize() From 0c3605d01664a4446b8639bfb3ec76a89bb61920 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:41:33 -0500 Subject: [PATCH 18/30] Fix clippy warnings --- icloud-auth/src/client.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/icloud-auth/src/client.rs b/icloud-auth/src/client.rs index 59ef371..e5b431f 100644 --- a/icloud-auth/src/client.rs +++ b/icloud-auth/src/client.rs @@ -179,7 +179,7 @@ impl AppleAccount { apple_id: String, ) -> Result { let anisette = AnisetteData::new(config).await?; - Ok(Self::new_with_anisette(anisette, apple_id)?) + Self::new_with_anisette(anisette, apple_id) } pub fn new_with_anisette( @@ -426,11 +426,8 @@ impl AppleAccount { } pub fn get_pet(&self) -> Option { - let Some(token) = self.spd.as_ref().unwrap().get("t") else { - return None; - }; Some( - token + self.spd.as_ref().unwrap().get("t")? .as_dictionary() .unwrap() .get("com.apple.gs.idms.pet") @@ -545,7 +542,7 @@ impl AppleAccount { ); let verifier: SrpClientVerifier = srp_client - .process_reply(&a, &username.as_bytes(), &password_buf, salt, b_pub) + .process_reply(&a, username.as_bytes(), &password_buf, salt, b_pub) .unwrap(); let m = verifier.proof(); @@ -582,7 +579,7 @@ impl AppleAccount { } // println!("{:?}", res); let m2 = res.get("M2").unwrap().as_data().unwrap(); - verifier.verify_server(&m2).unwrap(); + verifier.verify_server(m2).unwrap(); let spd = res.get("spd").unwrap().as_data().unwrap(); let decrypted_spd = Self::decrypt_cbc(&verifier, spd); @@ -604,7 +601,7 @@ impl AppleAccount { } fn create_session_key(usr: &SrpClientVerifier, name: &str) -> Vec { - as hmac::Mac>::new_from_slice(&usr.key()) + as hmac::Mac>::new_from_slice(usr.key()) .unwrap() .chain_update(name.as_bytes()) .finalize() @@ -619,7 +616,7 @@ impl AppleAccount { cbc::Decryptor::::new_from_slices(&extra_data_key, extra_data_iv) .unwrap() - .decrypt_padded_vec_mut::(&data) + .decrypt_padded_vec_mut::(data) .unwrap() } @@ -637,7 +634,7 @@ impl AppleAccount { return Err(Error::AuthSrp); } - return Ok(LoginState::Needs2FAVerification); + Ok(LoginState::Needs2FAVerification) } pub async fn send_sms_2fa_to_devices(&self, phone_id: u32) -> Result { @@ -660,7 +657,7 @@ impl AppleAccount { return Err(Error::AuthSrp); } - return Ok(LoginState::NeedsSMS2FAVerification(body)); + Ok(LoginState::NeedsSMS2FAVerification(body)) } pub async fn get_auth_extras(&self) -> Result { @@ -739,7 +736,7 @@ impl AppleAccount { fn check_error(res: &plist::Dictionary) -> Result<(), Error> { let res = match res.get("Status") { Some(plist::Value::Dictionary(d)) => d, - _ => &res, + _ => res, }; if res.get("ec").unwrap().as_signed_integer().unwrap() != 0 { From cd051e7cbd9da848d11decfba0598ef7385955a3 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:42:35 -0500 Subject: [PATCH 19/30] (omnisette) Fix clippy warnings --- omnisette/src/remote_anisette_v3.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/omnisette/src/remote_anisette_v3.rs b/omnisette/src/remote_anisette_v3.rs index 5f30bfc..b7c7aa9 100644 --- a/omnisette/src/remote_anisette_v3.rs +++ b/omnisette/src/remote_anisette_v3.rs @@ -41,7 +41,7 @@ fn bin_serialize_opt(x: &Option>, s: S) -> Result where S: Serializer, { - x.clone().map(|i| Data::new(i)).serialize(s) + x.clone().map(Data::new).serialize(s) } fn bin_deserialize_opt<'de, D>(d: D) -> Result>, D::Error> @@ -116,7 +116,7 @@ impl AnisetteState { fn md_lu(&self) -> [u8; 32] { let mut hasher = Sha256::new(); - hasher.update(&self.keychain_identifier); + hasher.update(self.keychain_identifier); hasher.finalize().into() } @@ -171,8 +171,7 @@ impl AnisetteData { "X-Mme-Client-Info".to_string(), self.device_description.clone(), ), - ] - .into_iter(), + ], ) } } @@ -285,7 +284,7 @@ impl AnisetteClient { let http_client = make_reqwest()?; let resp = self .build_apple_request( - &state, + state, http_client.get("https://gsa.apple.com/grandslam/GsService2/lookup"), ) .send() @@ -499,14 +498,14 @@ impl AnisetteHeadersProvider for RemoteAnisetteProviderV3 { client.provision(state).await?; plist::to_file_xml(&config_path, state)?; } - let data = match client.get_headers(&state).await { + let data = match client.get_headers(state).await { Ok(data) => data, Err(err) => { if matches!(err, AnisetteError::AnisetteNotProvisioned) { state.adi_pb = None; client.provision(state).await?; plist::to_file_xml(config_path, state)?; - client.get_headers(&state).await? + client.get_headers(state).await? } else { panic!() } From 93a97030104797922f100186f2a8f53ecf10eecb Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:46:47 -0500 Subject: [PATCH 20/30] (omnisette) remove unneccesary dependencies and box tungstenite errors --- omnisette/Cargo.toml | 3 --- omnisette/src/lib.rs | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 1137181..6bef20b 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -8,16 +8,13 @@ license = "MPL-2.0" [dependencies] base64 = "0.22" -hex = "0.4" plist = "1.4" reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls", "gzip"] } rand = "0.9" sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } -libc = "0.2" log = "0.4" async-trait = { version = "0.1" } -remove-async-await = { version = "1.0" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0.115" } tokio-tungstenite = { version = "0.27.0", features = ["rustls-tls-webpki-roots"] } diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index aeb66f4..7a213b5 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -31,7 +31,7 @@ pub enum AnisetteError { #[error("Request Error {0}")] ReqwestError(#[from] reqwest::Error), #[error("Provisioning socket error {0}")] - WsError(#[from] tokio_tungstenite::tungstenite::error::Error), + WsError(Box), #[error("JSON error {0}")] SerdeError(#[from] serde_json::Error), #[error("IO error {0}")] @@ -46,6 +46,12 @@ pub enum AnisetteError { Anyhow(#[from] anyhow::Error), } +impl From for AnisetteError { + fn from(err: tokio_tungstenite::tungstenite::error::Error) -> Self { + AnisetteError::WsError(Box::new(err)) + } +} + pub const DEFAULT_ANISETTE_URL: &str = "https://ani.f1sh.me/"; pub const DEFAULT_ANISETTE_URL_V3: &str = "https://ani.sidestore.io"; From 45bd1fbbbd1427836e110cebc880ba4c2b8dbd27 Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:47:01 -0500 Subject: [PATCH 21/30] (omnisette): bump version --- omnisette/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 6bef20b..5c424be 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nab138_omnisette" -version = "0.1.2" +version = "0.1.3" edition = "2021" description = "A library to generate \"anisette\" data. Modified from Sidestore/apple-private-apis" repository = "https://github.com/nab138/apple-private-apis" From 279ca8bf6cc5f56d028437c30ccf859969e8c80c Mon Sep 17 00:00:00 2001 From: nab138 Date: Fri, 7 Nov 2025 18:49:44 -0500 Subject: [PATCH 22/30] Update omnisette --- icloud-auth/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index 14f8cef..f00511c 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -26,7 +26,7 @@ cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } -omnisette = { version = "0.1.2", package = "nab138_omnisette" } +omnisette = { version = "0.1.3", package = "nab138_omnisette" } thiserror = "2" tokio = "1" aes-gcm = "0.10.3" From 5f8545d4ab26d5c082490392909b0b73b251717b Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:34:55 -0500 Subject: [PATCH 23/30] Fixup changes for PR --- Cargo.toml | 2 ++ README.md | 13 +++++-------- icloud-auth/Cargo.toml | 14 +++++--------- icloud-auth/rustcrypto-srp/Cargo.toml | 4 ++-- omnisette/Cargo.toml | 2 +- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 98b3491..6fa595d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,6 @@ members = [ "omnisette", "icloud-auth", + "apple-dev-apis", + "apple-codesign-wrapper" ] diff --git a/README.md b/README.md index bc6c3b6..2ded424 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # `apple-private-apis` -This version has been published to crates.io temporarily (with permission from original developers) while they work on getting it ready for a proper publish. Find the original at https://github.com/SideStore/apple-private-apis - _A set of Rust libraries to interact with apple's private APIs and servers, made for use in [**SideInstaller**](https://sidestore.io)._ [![Rust](https://github.com/SideStore/apple-private-apis/actions/workflows/rust.yml/badge.svg)](https://github.com/SideStore/apple-private-apis/actions/workflows/rust.yml) @@ -10,12 +8,11 @@ _A set of Rust libraries to interact with apple's private APIs and servers, made -| Library | Description | -| ----------------------------------------------------- | ---------------------------------------------------------------------- | -| [`omnisette`](./omnisette) | A library to generate "anisette" data | -| [`icloud-auth`](./icloud-auth/) | A library to authenticate with Apple's GSA servers | -| [`apple-dev-apis`](./apple-dev-apis/) | An implementation of Apple's Xcode signing/developer APIs | -| [`apple-codesign-wrapper`](./apple-codesign-wrapper/) | A wrapper for the `apple-codesign` crate. See the README for more info | +| Library | Description | +| ------------------------------------- | --------------------------------------------------------- | +| [`omnisette`](./omnisette) | A library to generate "anisette" data | +| [`icloud-auth`](./icloud-auth/) | A library to authenticate with Apple's GSA servers | +| [`apple-dev-apis`](./apple-dev-apis/) | An implementation of Apple's Xcode signing/developer APIs | diff --git a/icloud-auth/Cargo.toml b/icloud-auth/Cargo.toml index f00511c..097a05d 100644 --- a/icloud-auth/Cargo.toml +++ b/icloud-auth/Cargo.toml @@ -1,19 +1,15 @@ [package] -name = "nab138_icloud_auth" -version = "0.1.5" +name = "icloud_auth" +version = "0.1.0" edition = "2021" -description = "A library to authenticate with Apple's GSA servers" -repository = "https://github.com/nab138/apple-private-apis" -license = "MPL-2.0" -[features] -default = [] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] serde = { version = "1", features = ["derive"] } serde_json = { version = "1" } base64 = "0.22" -srp = { version = "0.6.0", package = "nab138_srp" } +srp = { version = "0.6.0", path = "./rustcrypto-srp" } pbkdf2 = "0.11" sha2 = "0.10" rand = "0.9" @@ -26,7 +22,7 @@ cbc = { version = "0.1.2", features = ["std"] } aes = "0.8.2" pkcs7 = "0.4.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] } -omnisette = { version = "0.1.3", package = "nab138_omnisette" } +omnisette = { path = "../omnisette", version = "0.1.3", features = ["remote-anisette-v3"] } thiserror = "2" tokio = "1" aes-gcm = "0.10.3" diff --git a/icloud-auth/rustcrypto-srp/Cargo.toml b/icloud-auth/rustcrypto-srp/Cargo.toml index 44d31fa..63f7808 100644 --- a/icloud-auth/rustcrypto-srp/Cargo.toml +++ b/icloud-auth/rustcrypto-srp/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "nab138_srp" +name = "srp" version = "0.6.0" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" description = "Secure Remote Password (SRP) protocol implementation" documentation = "https://docs.rs/srp" -repository = "https://github.com/nab138/apple-private-apis" +repository = "https://github.com/RustCrypto/PAKEs" keywords = ["crypto", "pake", "authentication"] categories = ["cryptography", "authentication"] readme = "README.md" diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 5c424be..18b560f 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nab138_omnisette" +name = "omnisette" version = "0.1.3" edition = "2021" description = "A library to generate \"anisette\" data. Modified from Sidestore/apple-private-apis" From 80b73a2164b6531f77fcd9d016e8f1b88b2dcdc8 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:36:23 -0500 Subject: [PATCH 24/30] Undo README changes --- README.md | 10 +++++----- icloud-auth/rustcrypto-srp/README.md | 15 ++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2ded424..0692ba9 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ _A set of Rust libraries to interact with apple's private APIs and servers, made ![Alt](https://repobeats.axiom.co/api/embed/4d96ea07d261281763b31ead2354ad6dcd6afed1.svg "Repobeats analytics image") - -| Library | Description | -| ------------------------------------- | --------------------------------------------------------- | -| [`omnisette`](./omnisette) | A library to generate "anisette" data | -| [`icloud-auth`](./icloud-auth/) | A library to authenticate with Apple's GSA servers | +| Library | Description | +| --- | --- | +| [`omnisette`](./omnisette) | A library to generate "anisette" data | +| [`icloud-auth`](./icloud-auth/) | A library to authenticate with Apple's GSA servers | | [`apple-dev-apis`](./apple-dev-apis/) | An implementation of Apple's Xcode signing/developer APIs | +| [`apple-codesign-wrapper`](./apple-codesign-wrapper/) | A wrapper for the `apple-codesign` crate. See the README for more info | diff --git a/icloud-auth/rustcrypto-srp/README.md b/icloud-auth/rustcrypto-srp/README.md index f8fbeb8..3aa7583 100644 --- a/icloud-auth/rustcrypto-srp/README.md +++ b/icloud-auth/rustcrypto-srp/README.md @@ -1,7 +1,5 @@ # [RustCrypto]: SRP -Forked to support an apple SRP session for icloud authentication. - [![crate][crate-image]][crate-link] [![Docs][docs-image]][docs-link] ![Apache2/MIT licensed][license-image] @@ -17,7 +15,7 @@ key-exchange algorithm. ## About This implementation is generic over hash functions using the [`Digest`] trait, -so you will need to choose a hash function, e.g. `Sha256` from [`sha2`] crate. +so you will need to choose a hash function, e.g. `Sha256` from [`sha2`] crate. Additionally this crate allows to use a specialized password hashing algorithm for private key computation instead of method described in the @@ -43,8 +41,8 @@ done with a minor version bump. Licensed under either of: -- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) -- [MIT license](http://opensource.org/licenses/MIT) + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) at your option. @@ -54,7 +52,8 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -[//]: # "badges" +[//]: # (badges) + [crate-image]: https://img.shields.io/crates/v/srp.svg [crate-link]: https://crates.io/crates/srp [docs-image]: https://docs.rs/srp/badge.svg @@ -65,7 +64,9 @@ dual licensed as above, without any additional terms or conditions. [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260045-PAKEs [build-image]: https://github.com/RustCrypto/PAKEs/actions/workflows/srp.yml/badge.svg [build-link]: https://github.com/RustCrypto/PAKEs/actions/workflows/srp.yml -[//]: # "general links" + +[//]: # (general links) + [RustCrypto]: https://github.com/RustCrypto [Secure Remote Password]: https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol [`Digest`]: https://docs.rs/digest From 93b9e03a709c37bd2c6b12a6581dd8a1600acd7d Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:43:00 -0500 Subject: [PATCH 25/30] Add deleted files --- omnisette/Cargo.toml | 24 +- omnisette/src/adi_proxy.rs | 383 +++++++++++++++ omnisette/src/aos_kit.rs | 124 +++++ omnisette/src/lib.rs | 9 + omnisette/src/remote_anisette.rs | 47 ++ omnisette/src/store_services_core.rs | 450 ++++++++++++++++++ .../src/store_services_core/posix_macos.rs | 107 +++++ .../src/store_services_core/posix_windows.rs | 268 +++++++++++ 8 files changed, 1406 insertions(+), 6 deletions(-) create mode 100644 omnisette/src/adi_proxy.rs create mode 100644 omnisette/src/aos_kit.rs create mode 100644 omnisette/src/remote_anisette.rs create mode 100644 omnisette/src/store_services_core.rs create mode 100644 omnisette/src/store_services_core/posix_macos.rs create mode 100644 omnisette/src/store_services_core/posix_windows.rs diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 18b560f..7b22a64 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -6,6 +6,12 @@ description = "A library to generate \"anisette\" data. Modified from Sidestore/ repository = "https://github.com/nab138/apple-private-apis" license = "MPL-2.0" +[features] +remote-anisette = [] +async = ["dep:async-trait"] +default = ["remote-anisette", "dep:remove-async-await"] +remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungstenite", "dep:futures-util", "dep:chrono"] + [dependencies] base64 = "0.22" plist = "1.4" @@ -14,15 +20,21 @@ rand = "0.9" sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } log = "0.4" -async-trait = { version = "0.1" } -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0.115" } -tokio-tungstenite = { version = "0.27.0", features = ["rustls-tls-webpki-roots"] } -futures-util = { version = "0.3.28" } -chrono = { version = "0.4.37" } +async-trait = { version = "0.1", optional = true } +remove-async-await = { version = "1.0", optional = true } +serde = { version = "1.0", features = ["derive"], optional = true } +serde_json = { version = "1.0.115", optional = true } +tokio-tungstenite = { version = "0.27.0", features = ["rustls-tls-webpki-roots"], optional = true } +futures-util = { version = "0.3.28", optional = true } +chrono = { version = "0.4.37", optional = true } thiserror = "2" anyhow = "1.0.81" +[target.'cfg(target_os = "macos")'.dependencies] +dlopen2 = "0.4" +objc = "0.2" +objc-foundation = "0.1" + [dev-dependencies] tokio = { version = "1", features = ["rt", "macros"] } simplelog = "0.12" diff --git a/omnisette/src/adi_proxy.rs b/omnisette/src/adi_proxy.rs new file mode 100644 index 0000000..74f6a6f --- /dev/null +++ b/omnisette/src/adi_proxy.rs @@ -0,0 +1,383 @@ +use crate::adi_proxy::ProvisioningError::InvalidResponse; +use crate::anisette_headers_provider::AnisetteHeadersProvider; +use crate::AnisetteError; +use base64::engine::general_purpose::STANDARD as base64_engine; +use base64::Engine; +use log::debug; +use plist::{Dictionary, Value}; +use rand::RngCore; +#[cfg(not(feature = "async"))] +use reqwest::blocking::{Client, ClientBuilder, Response}; +use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; +#[cfg(feature = "async")] +use reqwest::{Client, ClientBuilder, Response}; +use sha2::{Digest, Sha256}; +use std::collections::HashMap; +use std::fmt::{Display, Formatter}; +use std::io::{self, Read, Write}; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Debug)] +pub struct ServerError { + pub code: i64, + pub description: String, +} + +#[derive(Debug)] +pub enum ProvisioningError { + InvalidResponse, + ServerError(ServerError), +} + +impl std::fmt::Display for ProvisioningError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +impl std::error::Error for ProvisioningError {} + +#[derive(Debug, Error)] +pub enum ADIError { + Unknown(i32), + ProvisioningError(#[from] ProvisioningError), + PlistError(#[from] plist::Error), + ReqwestError(#[from] reqwest::Error), + Base64Error(#[from] base64::DecodeError), + InvalidHeaderValue(#[from] InvalidHeaderValue), + IOError(#[from] io::Error), +} + +impl ADIError { + pub fn resolve(error_number: i32) -> ADIError { + ADIError::Unknown(error_number) + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +trait ToPlist { + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn plist(self) -> Result; +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl ToPlist for Response { + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn plist(self) -> Result { + if let Ok(property_list) = Value::from_reader_xml(&*self.bytes().await?) { + Ok(property_list.as_dictionary().unwrap().to_owned()) + } else { + Err(ProvisioningError::InvalidResponse.into()) + } + } +} + +impl Display for ADIError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +pub struct SynchronizeData { + pub mid: Vec, + pub srm: Vec, +} + +pub struct StartProvisioningData { + pub cpim: Vec, + pub session: u32, +} + +pub struct RequestOTPData { + pub otp: Vec, + pub mid: Vec, +} + +#[cfg_attr(feature = "async", async_trait::async_trait(?Send))] +pub trait ADIProxy: Send + Sync { + fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError>; + fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result; + fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError>; + fn end_provisioning(&mut self, session: u32, ptm: &[u8], tk: &[u8]) -> Result<(), ADIError>; + fn start_provisioning( + &mut self, + ds_id: i64, + spim: &[u8], + ) -> Result; + fn is_machine_provisioned(&self, ds_id: i64) -> bool; + fn request_otp(&self, ds_id: i64) -> Result; + + fn set_local_user_uuid(&mut self, local_user_uuid: String); + fn set_device_identifier(&mut self, device_identifier: String) -> Result<(), ADIError>; + + fn get_local_user_uuid(&self) -> String; + fn get_device_identifier(&self) -> String; + fn get_serial_number(&self) -> String; +} + +pub trait ConfigurableADIProxy: ADIProxy { + fn set_identifier(&mut self, identifier: &str) -> Result<(), ADIError>; + fn set_provisioning_path(&mut self, path: &str) -> Result<(), ADIError>; +} + +pub const AKD_USER_AGENT: &str = "akd/1.0 CFNetwork/808.1.4"; +pub const CLIENT_INFO_HEADER: &str = + " "; +pub const DS_ID: i64 = -2; +pub const IDENTIFIER_LENGTH: usize = 16; +pub type Identifier = [u8; IDENTIFIER_LENGTH]; + +trait AppleRequestResult { + fn check_status(&self) -> Result<(), ADIError>; + fn get_response(&self) -> Result<&Dictionary, ADIError>; +} + +impl AppleRequestResult for Dictionary { + fn check_status(&self) -> Result<(), ADIError> { + let status = self + .get("Status") + .ok_or(InvalidResponse)? + .as_dictionary() + .unwrap(); + let code = status.get("ec").unwrap().as_signed_integer().unwrap(); + if code != 0 { + let description = status.get("em").unwrap().as_string().unwrap().to_string(); + Err(ProvisioningError::ServerError(ServerError { code, description }).into()) + } else { + Ok(()) + } + } + + fn get_response(&self) -> Result<&Dictionary, ADIError> { + if let Some(response) = self.get("Response") { + let response = response.as_dictionary().unwrap(); + response.check_status()?; + Ok(response) + } else { + Err(InvalidResponse.into()) + } + } +} + +impl dyn ADIProxy { + fn make_http_client(&mut self) -> Result { + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_str("text/x-xml-plist")?); + + headers.insert( + "X-Mme-Client-Info", + HeaderValue::from_str(CLIENT_INFO_HEADER)?, + ); + headers.insert( + "X-Mme-Device-Id", + HeaderValue::from_str(self.get_device_identifier().as_str())?, + ); + headers.insert( + "X-Apple-I-MD-LU", + HeaderValue::from_str(self.get_local_user_uuid().as_str())?, + ); + headers.insert( + "X-Apple-I-SRL-NO", + HeaderValue::from_str(self.get_serial_number().as_str())?, + ); + + debug!("Headers sent: {headers:?}"); + + let http_client = ClientBuilder::new() + .http1_title_case_headers() + .danger_accept_invalid_certs(true) // TODO: pin the apple certificate + .user_agent(AKD_USER_AGENT) + .default_headers(headers) + .build()?; + + Ok(http_client) + } + + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn provision_device(&mut self) -> Result<(), ADIError> { + let client = self.make_http_client()?; + + let url_bag_res = client + .get("https://gsa.apple.com/grandslam/GsService2/lookup") + .send() + .await? + .plist() + .await?; + + let urls = url_bag_res.get("urls").unwrap().as_dictionary().unwrap(); + + let start_provisioning_url = urls + .get("midStartProvisioning") + .unwrap() + .as_string() + .unwrap(); + let finish_provisioning_url = urls + .get("midFinishProvisioning") + .unwrap() + .as_string() + .unwrap(); + + let mut body = plist::Dictionary::new(); + body.insert( + "Header".to_string(), + plist::Value::Dictionary(plist::Dictionary::new()), + ); + body.insert( + "Request".to_string(), + plist::Value::Dictionary(plist::Dictionary::new()), + ); + + let mut sp_request = Vec::new(); + plist::Value::Dictionary(body).to_writer_xml(&mut sp_request)?; + + debug!("First provisioning request..."); + let response = client + .post(start_provisioning_url) + .body(sp_request) + .send() + .await? + .plist() + .await?; + + let response = response.get_response()?; + + let spim = response + .get("spim") + .unwrap() + .as_string() + .unwrap() + .to_owned(); + + let spim = base64_engine.decode(spim)?; + let first_step = self.start_provisioning(DS_ID, spim.as_slice())?; + + let mut body = Dictionary::new(); + let mut request = Dictionary::new(); + request.insert( + "cpim".to_owned(), + Value::String(base64_engine.encode(first_step.cpim)), + ); + body.insert("Header".to_owned(), Value::Dictionary(Dictionary::new())); + body.insert("Request".to_owned(), Value::Dictionary(request)); + + let mut fp_request = Vec::new(); + Value::Dictionary(body).to_writer_xml(&mut fp_request)?; + + debug!("Second provisioning request..."); + let response = client + .post(finish_provisioning_url) + .body(fp_request) + .send() + .await? + .plist() + .await?; + + let response = response.get_response()?; + + let ptm = base64_engine.decode(response.get("ptm").unwrap().as_string().unwrap())?; + let tk = base64_engine.decode(response.get("tk").unwrap().as_string().unwrap())?; + + self.end_provisioning(first_step.session, ptm.as_slice(), tk.as_slice())?; + debug!("Done."); + + Ok(()) + } +} + +pub struct ADIProxyAnisetteProvider { + adi_proxy: ProxyType, +} + +impl ADIProxyAnisetteProvider { + /// If you use this method, you are expected to set the identifier yourself. + pub fn without_identifier( + adi_proxy: ProxyType, + ) -> Result, ADIError> { + Ok(ADIProxyAnisetteProvider { adi_proxy }) + } + + pub fn new( + mut adi_proxy: ProxyType, + configuration_path: PathBuf, + ) -> Result, ADIError> { + let identifier_file_path = configuration_path.join("identifier"); + let mut identifier_file = std::fs::OpenOptions::new() + .create(true) + .read(true) + .write(true) + .open(identifier_file_path)?; + let mut identifier = [0u8; IDENTIFIER_LENGTH]; + if identifier_file.metadata()?.len() == IDENTIFIER_LENGTH as u64 { + identifier_file.read_exact(&mut identifier)?; + } else { + rand::thread_rng().fill_bytes(&mut identifier); + identifier_file.write_all(&identifier)?; + } + + let mut local_user_uuid_hasher = Sha256::new(); + local_user_uuid_hasher.update(identifier); + + adi_proxy.set_device_identifier( + uuid::Uuid::from_bytes(identifier) + .to_string() + .to_uppercase(), + )?; // UUID, uppercase + adi_proxy + .set_local_user_uuid(hex::encode(local_user_uuid_hasher.finalize()).to_uppercase()); // 64 uppercase character hex + + Ok(ADIProxyAnisetteProvider { adi_proxy }) + } + + pub fn adi_proxy(&mut self) -> &mut ProxyType { + &mut self.adi_proxy + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl AnisetteHeadersProvider + for ADIProxyAnisetteProvider +{ + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn get_anisette_headers( + &mut self, + skip_provisioning: bool, + ) -> Result, AnisetteError> { + let adi_proxy = &mut self.adi_proxy as &mut dyn ADIProxy; + + if !adi_proxy.is_machine_provisioned(DS_ID) && !skip_provisioning { + adi_proxy.provision_device().await?; + } + + let machine_data = adi_proxy.request_otp(DS_ID)?; + + let mut headers = HashMap::new(); + headers.insert( + "X-Apple-I-MD".to_string(), + base64_engine.encode(machine_data.otp), + ); + headers.insert( + "X-Apple-I-MD-M".to_string(), + base64_engine.encode(machine_data.mid), + ); + headers.insert("X-Apple-I-MD-RINFO".to_string(), "17106176".to_string()); + headers.insert( + "X-Apple-I-MD-LU".to_string(), + adi_proxy.get_local_user_uuid(), + ); + headers.insert( + "X-Apple-I-SRL-NO".to_string(), + adi_proxy.get_serial_number(), + ); + headers.insert( + "X-Mme-Client-Info".to_string(), + CLIENT_INFO_HEADER.to_string(), + ); + headers.insert( + "X-Mme-Device-Id".to_string(), + adi_proxy.get_device_identifier(), + ); + + Ok(headers) + } +} diff --git a/omnisette/src/aos_kit.rs b/omnisette/src/aos_kit.rs new file mode 100644 index 0000000..5e53787 --- /dev/null +++ b/omnisette/src/aos_kit.rs @@ -0,0 +1,124 @@ +use crate::anisette_headers_provider::AnisetteHeadersProvider; +use anyhow::Result; + +use dlopen2::symbor::Library; +use objc::{msg_send, runtime::Class, sel, sel_impl}; +use objc_foundation::{INSString, NSObject, NSString}; +use std::collections::HashMap; +use std::error::Error; +use std::fmt::{Display, Formatter}; +pub struct AOSKitAnisetteProvider<'lt> { + aos_utilities: &'lt Class, + ak_device: &'lt Class, +} + +impl<'lt> AOSKitAnisetteProvider<'lt> { + pub fn new() -> Result> { + Library::open("/System/Library/PrivateFrameworks/AOSKit.framework/AOSKit")?; + Library::open("/System/Library/PrivateFrameworks/AuthKit.framework/AuthKit")?; + Ok(AOSKitAnisetteProvider { + aos_utilities: Class::get("AOSUtilities").ok_or(AOSKitError::ClassLoadFailed)?, + ak_device: Class::get("AKDevice").ok_or(AOSKitError::ClassLoadFailed)?, + }) + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait(?Send))] +impl<'lt> AnisetteHeadersProvider for AOSKitAnisetteProvider<'lt> { + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn get_anisette_headers( + &mut self, + _skip_provisioning: bool, + ) -> Result> { + let mut headers_map = HashMap::new(); + + let headers: *const NSObject = unsafe { + msg_send![self.aos_utilities, retrieveOTPHeadersForDSID: NSString::from_str("-2")] + }; + + let otp: *const NSString = + unsafe { msg_send![headers, valueForKey: NSString::from_str("X-Apple-MD")] }; + headers_map.insert( + "X-Apple-I-MD".to_string(), + unsafe { (*otp).as_str() }.to_string(), + ); + + let mid: *const NSString = + unsafe { msg_send![headers, valueForKey: NSString::from_str("X-Apple-MD-M")] }; + headers_map.insert( + "X-Apple-I-MD-M".to_string(), + unsafe { (*mid).as_str() }.to_string(), + ); + + let machine_serial_number: *const NSString = + unsafe { msg_send![self.aos_utilities, machineSerialNumber] }; + headers_map.insert( + "X-Apple-SRL-NO".to_string(), + unsafe { (*machine_serial_number).as_str() }.to_string(), + ); + + let current_device: *const NSObject = unsafe { msg_send![self.ak_device, currentDevice] }; + + let local_user_uuid: *const NSString = unsafe { msg_send![current_device, localUserUUID] }; + headers_map.insert( + "X-Apple-I-MD-LU".to_string(), + unsafe { (*local_user_uuid).as_str() }.to_string(), + ); + + let locale: *const NSObject = unsafe { msg_send![current_device, locale] }; + let locale: *const NSString = unsafe { msg_send![locale, localeIdentifier] }; + headers_map.insert( + "X-Apple-Locale".to_string(), + unsafe { (*locale).as_str() }.to_string(), + ); // FIXME maybe not the right header name + + let server_friendly_description: *const NSString = + unsafe { msg_send![current_device, serverFriendlyDescription] }; + headers_map.insert( + "X-Mme-Client-Info".to_string(), + unsafe { (*server_friendly_description).as_str() }.to_string(), + ); + + let unique_device_identifier: *const NSString = + unsafe { msg_send![current_device, uniqueDeviceIdentifier] }; + headers_map.insert( + "X-Mme-Device-Id".to_string(), + unsafe { (*unique_device_identifier).as_str() }.to_string(), + ); + + Ok(headers_map) + } +} + +#[derive(Debug)] +enum AOSKitError { + ClassLoadFailed, +} + +impl Display for AOSKitError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +impl Error for AOSKitError {} + +#[cfg(all(test, not(feature = "async")))] +mod tests { + use crate::anisette_headers_provider::AnisetteHeadersProvider; + use crate::aos_kit::AOSKitAnisetteProvider; + use anyhow::Result; + use log::info; + + #[test] + fn fetch_anisette_aoskit() -> Result<()> { + crate::tests::init_logger(); + + let mut provider = AOSKitAnisetteProvider::new()?; + info!( + "AOSKit headers: {:?}", + (&mut provider as &mut dyn AnisetteHeadersProvider).get_authentication_headers()? + ); + Ok(()) + } +} diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index 7a213b5..1c8aeb8 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -9,10 +9,19 @@ use std::io; use std::path::PathBuf; use thiserror::Error; +pub mod adi_proxy; pub mod anisette_headers_provider; +pub mod store_services_core; +#[cfg(feature = "remote-anisette-v3")] pub mod remote_anisette_v3; +#[cfg(target_os = "macos")] +pub mod aos_kit; + +#[cfg(feature = "remote-anisette")] +pub mod remote_anisette; + #[allow(dead_code)] pub struct AnisetteHeaders; diff --git a/omnisette/src/remote_anisette.rs b/omnisette/src/remote_anisette.rs new file mode 100644 index 0000000..b061bd9 --- /dev/null +++ b/omnisette/src/remote_anisette.rs @@ -0,0 +1,47 @@ +use crate::{anisette_headers_provider::AnisetteHeadersProvider, AnisetteError}; +#[cfg(not(feature = "async"))] +use reqwest::blocking::get; +#[cfg(feature = "async")] +use reqwest::get; +use std::collections::HashMap; + +pub struct RemoteAnisetteProvider { + url: String, +} + +impl RemoteAnisetteProvider { + pub fn new(url: String) -> RemoteAnisetteProvider { + RemoteAnisetteProvider { url } + } +} + +#[cfg_attr(feature = "async", async_trait::async_trait)] +impl AnisetteHeadersProvider for RemoteAnisetteProvider { + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn get_anisette_headers( + &mut self, + _skip_provisioning: bool, + ) -> Result, AnisetteError> { + Ok(get(&self.url).await?.json().await?) + } +} + +#[cfg(all(test, not(feature = "async")))] +mod tests { + use crate::anisette_headers_provider::AnisetteHeadersProvider; + use crate::remote_anisette::RemoteAnisetteProvider; + use crate::DEFAULT_ANISETTE_URL; + use log::info; + + #[test] + fn fetch_anisette_remote() -> Result<(), AnisetteError> { + crate::tests::init_logger(); + + let mut provider = RemoteAnisetteProvider::new(DEFAULT_ANISETTE_URL.to_string()); + info!( + "Remote headers: {:?}", + (&mut provider as &mut dyn AnisetteHeadersProvider).get_authentication_headers()? + ); + Ok(()) + } +} diff --git a/omnisette/src/store_services_core.rs b/omnisette/src/store_services_core.rs new file mode 100644 index 0000000..631bf91 --- /dev/null +++ b/omnisette/src/store_services_core.rs @@ -0,0 +1,450 @@ +#[cfg(target_os = "macos")] +mod posix_macos; +#[cfg(target_family = "windows")] +mod posix_windows; + +use crate::adi_proxy::{ + ADIError, ADIProxy, ConfigurableADIProxy, RequestOTPData, StartProvisioningData, + SynchronizeData, +}; +use crate::AnisetteError; + +use android_loader::android_library::AndroidLibrary; +use android_loader::sysv64_type; +use android_loader::{hook_manager, sysv64}; +use std::collections::HashMap; +use std::ffi::{c_char, CString}; +use std::path::PathBuf; + +pub struct StoreServicesCoreADIProxy<'lt> { + #[allow(dead_code)] + store_services_core: AndroidLibrary<'lt>, + + local_user_uuid: String, + device_identifier: String, + + adi_set_android_id: sysv64_type!(fn(id: *const u8, length: u32) -> i32), + adi_set_provisioning_path: sysv64_type!(fn(path: *const u8) -> i32), + + adi_provisioning_erase: sysv64_type!(fn(ds_id: i64) -> i32), + adi_synchronize: sysv64_type!( + fn( + ds_id: i64, + sim: *const u8, + sim_length: u32, + out_mid: *mut *const u8, + out_mid_length: *mut u32, + out_srm: *mut *const u8, + out_srm_length: *mut u32, + ) -> i32 + ), + adi_provisioning_destroy: sysv64_type!(fn(session: u32) -> i32), + adi_provisioning_end: sysv64_type!( + fn(session: u32, ptm: *const u8, ptm_length: u32, tk: *const u8, tk_length: u32) -> i32 + ), + adi_provisioning_start: sysv64_type!( + fn( + ds_id: i64, + spim: *const u8, + spim_length: u32, + out_cpim: *mut *const u8, + out_cpim_length: *mut u32, + out_session: *mut u32, + ) -> i32 + ), + adi_get_login_code: sysv64_type!(fn(ds_id: i64) -> i32), + adi_dispose: sysv64_type!(fn(ptr: *const u8) -> i32), + adi_otp_request: sysv64_type!( + fn( + ds_id: i64, + out_mid: *mut *const u8, + out_mid_size: *mut u32, + out_otp: *mut *const u8, + out_otp_size: *mut u32, + ) -> i32 + ), +} + +impl StoreServicesCoreADIProxy<'_> { + pub fn new<'lt>( + library_path: &PathBuf, + ) -> Result, AnisetteError> { + Self::with_custom_provisioning_path(library_path, library_path) + } + + pub fn with_custom_provisioning_path<'lt>( + library_path: &PathBuf, + provisioning_path: &PathBuf, + ) -> Result, AnisetteError> { + // Should be safe if the library is correct. + unsafe { + LoaderHelpers::setup_hooks(); + + if !library_path.exists() { + std::fs::create_dir(library_path)?; + return Err(AnisetteError::MissingLibraries.into()); + } + + let library_path = library_path.canonicalize()?; + + #[cfg(target_arch = "x86_64")] + const ARCH: &str = "x86_64"; + #[cfg(target_arch = "x86")] + const ARCH: &str = "x86"; + #[cfg(target_arch = "arm")] + const ARCH: &str = "armeabi-v7a"; + #[cfg(target_arch = "aarch64")] + const ARCH: &str = "arm64-v8a"; + + let native_library_path = library_path.join("lib").join(ARCH); + + let path = native_library_path.join("libstoreservicescore.so"); + let path = path.to_str().ok_or(AnisetteError::Misc)?; + let store_services_core = AndroidLibrary::load(path)?; + + let adi_load_library_with_path: sysv64_type!(fn(path: *const u8) -> i32) = + std::mem::transmute( + store_services_core + .get_symbol("kq56gsgHG6") + .ok_or(AnisetteError::InvalidLibraryFormat)?, + ); + + let path = + CString::new(native_library_path.to_str().ok_or(AnisetteError::Misc)?).unwrap(); + assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0); + + let adi_set_android_id = store_services_core + .get_symbol("Sph98paBcz") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_set_provisioning_path = store_services_core + .get_symbol("nf92ngaK92") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + + let adi_provisioning_erase = store_services_core + .get_symbol("p435tmhbla") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_synchronize = store_services_core + .get_symbol("tn46gtiuhw") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_provisioning_destroy = store_services_core + .get_symbol("fy34trz2st") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_provisioning_end = store_services_core + .get_symbol("uv5t6nhkui") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_provisioning_start = store_services_core + .get_symbol("rsegvyrt87") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_get_login_code = store_services_core + .get_symbol("aslgmuibau") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_dispose = store_services_core + .get_symbol("jk24uiwqrg") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + let adi_otp_request = store_services_core + .get_symbol("qi864985u0") + .ok_or(AnisetteError::InvalidLibraryFormat)?; + + let mut proxy = StoreServicesCoreADIProxy { + store_services_core, + + local_user_uuid: String::new(), + device_identifier: String::new(), + + adi_set_android_id: std::mem::transmute(adi_set_android_id), + adi_set_provisioning_path: std::mem::transmute(adi_set_provisioning_path), + + adi_provisioning_erase: std::mem::transmute(adi_provisioning_erase), + adi_synchronize: std::mem::transmute(adi_synchronize), + adi_provisioning_destroy: std::mem::transmute(adi_provisioning_destroy), + adi_provisioning_end: std::mem::transmute(adi_provisioning_end), + adi_provisioning_start: std::mem::transmute(adi_provisioning_start), + adi_get_login_code: std::mem::transmute(adi_get_login_code), + adi_dispose: std::mem::transmute(adi_dispose), + adi_otp_request: std::mem::transmute(adi_otp_request), + }; + + proxy.set_provisioning_path(provisioning_path.to_str().ok_or(AnisetteError::Misc)?)?; + + Ok(proxy) + } + } +} + +impl ADIProxy for StoreServicesCoreADIProxy<'_> { + fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError> { + match (self.adi_provisioning_erase)(ds_id) { + 0 => Ok(()), + err => Err(ADIError::resolve(err)), + } + } + + fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result { + unsafe { + let sim_size = sim.len() as u32; + let sim_ptr = sim.as_ptr(); + + let mut mid_size: u32 = 0; + let mut mid_ptr: *const u8 = std::ptr::null(); + let mut srm_size: u32 = 0; + let mut srm_ptr: *const u8 = std::ptr::null(); + + match (self.adi_synchronize)( + ds_id, + sim_ptr, + sim_size, + &mut mid_ptr, + &mut mid_size, + &mut srm_ptr, + &mut srm_size, + ) { + 0 => { + let mut mid = vec![0; mid_size as usize]; + let mut srm = vec![0; srm_size as usize]; + + mid.copy_from_slice(std::slice::from_raw_parts(mid_ptr, mid_size as usize)); + srm.copy_from_slice(std::slice::from_raw_parts(srm_ptr, srm_size as usize)); + + (self.adi_dispose)(mid_ptr); + (self.adi_dispose)(srm_ptr); + + Ok(SynchronizeData { mid, srm }) + } + err => Err(ADIError::resolve(err)), + } + } + } + + fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError> { + match (self.adi_provisioning_destroy)(session) { + 0 => Ok(()), + err => Err(ADIError::resolve(err)), + } + } + + fn end_provisioning(&mut self, session: u32, ptm: &[u8], tk: &[u8]) -> Result<(), ADIError> { + let ptm_size = ptm.len() as u32; + let ptm_ptr = ptm.as_ptr(); + + let tk_size = tk.len() as u32; + let tk_ptr = tk.as_ptr(); + + match (self.adi_provisioning_end)(session, ptm_ptr, ptm_size, tk_ptr, tk_size) { + 0 => Ok(()), + err => Err(ADIError::resolve(err)), + } + } + + fn start_provisioning( + &mut self, + ds_id: i64, + spim: &[u8], + ) -> Result { + unsafe { + let spim_size = spim.len() as u32; + let spim_ptr = spim.as_ptr(); + + let mut cpim_size: u32 = 0; + let mut cpim_ptr: *const u8 = std::ptr::null(); + + let mut session: u32 = 0; + + match (self.adi_provisioning_start)( + ds_id, + spim_ptr, + spim_size, + &mut cpim_ptr, + &mut cpim_size, + &mut session, + ) { + 0 => { + let mut cpim = vec![0; cpim_size as usize]; + + cpim.copy_from_slice(std::slice::from_raw_parts(cpim_ptr, cpim_size as usize)); + + (self.adi_dispose)(cpim_ptr); + + Ok(StartProvisioningData { cpim, session }) + } + err => Err(ADIError::resolve(err)), + } + } + } + + fn is_machine_provisioned(&self, ds_id: i64) -> bool { + (self.adi_get_login_code)(ds_id) == 0 + } + + fn request_otp(&self, ds_id: i64) -> Result { + unsafe { + let mut mid_size: u32 = 0; + let mut mid_ptr: *const u8 = std::ptr::null(); + let mut otp_size: u32 = 0; + let mut otp_ptr: *const u8 = std::ptr::null(); + + match (self.adi_otp_request)( + ds_id, + &mut mid_ptr, + &mut mid_size, + &mut otp_ptr, + &mut otp_size, + ) { + 0 => { + let mut mid = vec![0; mid_size as usize]; + let mut otp = vec![0; otp_size as usize]; + + mid.copy_from_slice(std::slice::from_raw_parts(mid_ptr, mid_size as usize)); + otp.copy_from_slice(std::slice::from_raw_parts(otp_ptr, otp_size as usize)); + + (self.adi_dispose)(mid_ptr); + (self.adi_dispose)(otp_ptr); + + Ok(RequestOTPData { mid, otp }) + } + err => Err(ADIError::resolve(err)), + } + } + } + + fn set_local_user_uuid(&mut self, local_user_uuid: String) { + self.local_user_uuid = local_user_uuid; + } + + fn set_device_identifier(&mut self, device_identifier: String) -> Result<(), ADIError> { + self.set_identifier(&device_identifier[0..16])?; + self.device_identifier = device_identifier; + Ok(()) + } + + fn get_local_user_uuid(&self) -> String { + self.local_user_uuid.clone() + } + + fn get_device_identifier(&self) -> String { + self.device_identifier.clone() + } + + fn get_serial_number(&self) -> String { + "0".to_string() + } +} + +impl ConfigurableADIProxy for StoreServicesCoreADIProxy<'_> { + fn set_identifier(&mut self, identifier: &str) -> Result<(), ADIError> { + match (self.adi_set_android_id)(identifier.as_ptr(), identifier.len() as u32) { + 0 => Ok(()), + err => Err(ADIError::resolve(err)), + } + } + + fn set_provisioning_path(&mut self, path: &str) -> Result<(), ADIError> { + let path = CString::new(path).unwrap(); + match (self.adi_set_provisioning_path)(path.as_ptr() as *const u8) { + 0 => Ok(()), + err => Err(ADIError::resolve(err)), + } + } +} + +struct LoaderHelpers; + +use rand::Rng; + +#[cfg(all(target_family = "unix", not(target_os = "macos")))] +use libc::{ + chmod, close, free, fstat, ftruncate, gettimeofday, lstat, malloc, mkdir, open, read, strncpy, + umask, write, +}; +#[cfg(target_os = "macos")] +use posix_macos::*; + +static mut ERRNO: i32 = 0; + +#[allow(unreachable_code)] +#[sysv64] +unsafe fn __errno_location() -> *mut i32 { + ERRNO = std::io::Error::last_os_error().raw_os_error().unwrap_or(0); + &mut ERRNO +} + +#[sysv64] +fn arc4random() -> u32 { + rand::thread_rng().gen() +} + +#[sysv64] +unsafe fn __system_property_get(_name: *const c_char, value: *mut c_char) -> i32 { + *value = '0' as c_char; + return 1; +} + +#[cfg(target_family = "windows")] +use posix_windows::*; + +impl LoaderHelpers { + pub fn setup_hooks() { + let mut hooks = HashMap::new(); + hooks.insert("arc4random".to_owned(), arc4random as usize); + hooks.insert("chmod".to_owned(), chmod as usize); + hooks.insert( + "__system_property_get".to_owned(), + __system_property_get as usize, + ); + hooks.insert("__errno".to_owned(), __errno_location as usize); + hooks.insert("close".to_owned(), close as usize); + hooks.insert("free".to_owned(), free as usize); + hooks.insert("fstat".to_owned(), fstat as usize); + hooks.insert("ftruncate".to_owned(), ftruncate as usize); + hooks.insert("gettimeofday".to_owned(), gettimeofday as usize); + hooks.insert("lstat".to_owned(), lstat as usize); + hooks.insert("malloc".to_owned(), malloc as usize); + hooks.insert("mkdir".to_owned(), mkdir as usize); + hooks.insert("open".to_owned(), open as usize); + hooks.insert("read".to_owned(), read as usize); + hooks.insert("strncpy".to_owned(), strncpy as usize); + hooks.insert("umask".to_owned(), umask as usize); + hooks.insert("write".to_owned(), write as usize); + + hook_manager::add_hooks(hooks); + } +} + +#[cfg(test)] +mod tests { + use crate::AnisetteError; + use crate::{AnisetteConfiguration, AnisetteHeaders}; + use log::info; + use std::path::PathBuf; + + #[cfg(not(feature = "async"))] + #[test] + fn fetch_anisette_ssc() -> Result<(), AnisetteError> { + crate::tests::init_logger(); + + let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( + AnisetteConfiguration::new() + .set_configuration_path(PathBuf::new().join("anisette_test")), + )?; + info!( + "Headers: {:?}", + provider.provider.get_authentication_headers()? + ); + Ok(()) + } + + #[cfg(feature = "async")] + #[tokio::test] + async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> { + crate::tests::init_logger(); + + let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( + AnisetteConfiguration::new() + .set_configuration_path(PathBuf::new().join("anisette_test")), + )?; + info!( + "Headers: {:?}", + provider.provider.get_authentication_headers().await? + ); + Ok(()) + } +} diff --git a/omnisette/src/store_services_core/posix_macos.rs b/omnisette/src/store_services_core/posix_macos.rs new file mode 100644 index 0000000..2b3fe0a --- /dev/null +++ b/omnisette/src/store_services_core/posix_macos.rs @@ -0,0 +1,107 @@ +pub use libc::{ + chmod, close, free, ftruncate, gettimeofday, malloc, mkdir, read, strncpy, umask, write, +}; + +use libc::{ + fstat as fstat_macos, lstat as lstat_macos, open as open_macos, stat as stat_macos, O_CREAT, + O_RDONLY, O_RDWR, O_WRONLY, +}; + +use android_loader::sysv64; + +#[repr(C)] +pub struct StatLinux { + pub st_dev: u64, + pub st_ino: u64, + pub st_nlink: u64, + pub st_mode: u32, + pub st_uid: u32, + pub st_gid: u32, + __pad0: libc::c_int, + pub st_rdev: u64, + pub st_size: i64, + pub st_blksize: i64, + pub st_blocks: i64, + pub st_atime: i64, + pub st_atime_nsec: i64, + pub st_mtime: i64, + pub st_mtime_nsec: i64, + pub st_ctime: i64, + pub st_ctime_nsec: i64, + __unused: [i64; 3], +} + +#[sysv64] +pub unsafe fn lstat(path: *const libc::c_char, buf: *mut StatLinux) -> libc::c_int { + let mut st: stat_macos = std::mem::zeroed(); + lstat_macos(path, &mut st); + *buf = StatLinux { + st_dev: st.st_dev as _, + st_ino: st.st_ino as _, + st_nlink: st.st_nlink as _, + st_mode: st.st_mode as _, + st_uid: st.st_uid as _, + st_gid: st.st_gid as _, + __pad0: 0 as _, + st_rdev: st.st_rdev as _, + st_size: st.st_size as _, + st_blksize: st.st_blksize as _, + st_blocks: st.st_blocks as _, + st_atime: st.st_atime as _, + st_atime_nsec: st.st_atime_nsec as _, + st_mtime: st.st_mtime as _, + st_mtime_nsec: st.st_mtime_nsec as _, + st_ctime: st.st_ctime as _, + st_ctime_nsec: st.st_ctime_nsec as _, + __unused: [0, 0, 0], + }; + 0 +} + +#[sysv64] +pub unsafe fn fstat(fildes: libc::c_int, buf: *mut StatLinux) -> libc::c_int { + let mut st: stat_macos = std::mem::zeroed(); + fstat_macos(fildes, &mut st); + *buf = StatLinux { + st_dev: st.st_dev as _, + st_ino: st.st_ino as _, + st_nlink: st.st_nlink as _, + st_mode: st.st_mode as _, + st_uid: st.st_uid as _, + st_gid: st.st_gid as _, + __pad0: 0 as _, + st_rdev: st.st_rdev as _, + st_size: st.st_size as _, + st_blksize: st.st_blksize as _, + st_blocks: st.st_blocks as _, + st_atime: st.st_atime as _, + st_atime_nsec: st.st_atime_nsec as _, + st_mtime: st.st_mtime as _, + st_mtime_nsec: st.st_mtime_nsec as _, + st_ctime: st.st_ctime as _, + st_ctime_nsec: st.st_ctime_nsec as _, + __unused: [0, 0, 0], + }; + 0 +} + +#[sysv64] +pub unsafe fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int { + let mut win_flag = 0; // binary mode + + if oflag & 0o100 != 0 { + win_flag |= O_CREAT; + } + + if oflag & 0o1 == 1 { + win_flag |= O_WRONLY; + } else if oflag & 0o2 != 0 { + win_flag |= O_RDWR; + } else { + win_flag |= O_RDONLY; + } + + let val = open_macos(path, win_flag); + + val +} diff --git a/omnisette/src/store_services_core/posix_windows.rs b/omnisette/src/store_services_core/posix_windows.rs new file mode 100644 index 0000000..486e2b5 --- /dev/null +++ b/omnisette/src/store_services_core/posix_windows.rs @@ -0,0 +1,268 @@ +use android_loader::sysv64; +use libc::{O_CREAT, O_RDONLY, O_RDWR, O_WRONLY}; +use log::debug; +use std::ffi::{CStr, CString}; +use std::mem::MaybeUninit; + +#[link(name = "ucrt")] +extern "C" { + fn _errno() -> *mut libc::c_int; + fn _timespec64_get(__ts: *mut libc::timespec, __base: libc::c_int) -> libc::c_int; + fn _chsize(handle: i64, length: u64) -> usize; +} + +// took from cosmopolitan libc +#[sysv64] +pub unsafe fn umask(mask: usize) -> usize { + debug!("umask: Windows specific implementation called!"); + mask +} + +#[sysv64] +pub unsafe fn ftruncate(handle: i64, length: u64) -> usize { + debug!( + "ftruncate: Windows translate-call. handle: {}, length: {}", + handle, length + ); + let ftr = _chsize(handle, length); + + ftr +} + +#[repr(C)] +pub struct PosixTimeval { + tv_sec: u64, + tv_usec: u64, /* microseconds */ +} + +#[repr(C)] +pub struct PosixTimespec { + tv_sec: i64, + tv_nsec: i64, /* microseconds */ +} + +#[repr(C)] +pub struct PosixTimezone { + tz_minuteswest: u32, + tz_dsttime: u32, /* microseconds */ +} + +static HECTONANOSECONDS: u64 = 10000000; + +impl PosixTimespec { + pub fn from_windows_time(time: u64) -> PosixTimespec { + PosixTimespec { + tv_sec: (time / HECTONANOSECONDS) as i64, + tv_nsec: (time % HECTONANOSECONDS) as i64 * 100, + } + } +} + +#[sysv64] +pub unsafe fn gettimeofday(timeval: *mut PosixTimeval, _tz: *mut PosixTimezone) -> isize { + debug!("gettimeofday: Windows specific implementation called!"); + let mut ts = MaybeUninit::::zeroed(); + + let ret = _timespec64_get(ts.as_mut_ptr(), 1); + let ts = ts.assume_init(); + + *timeval = PosixTimeval { + tv_sec: ts.tv_sec as _, + tv_usec: (ts.tv_nsec / 1000) as _, + }; + + ret as _ +} + +#[repr(C)] +pub struct StatLinux { + pub st_dev: u64, + pub st_ino: u64, + pub st_nlink: u64, + pub st_mode: u32, + pub st_uid: u32, + pub st_gid: u32, + __pad0: libc::c_int, + pub st_rdev: u64, + pub st_size: i64, + pub st_blksize: i64, + pub st_blocks: i64, + pub st_atime: i64, + pub st_atime_nsec: i64, + pub st_mtime: i64, + pub st_mtime_nsec: i64, + pub st_ctime: i64, + pub st_ctime_nsec: i64, + __unused: [i64; 3], +} + +trait ToWindows { + unsafe fn to_windows(&self) -> T; +} + +impl ToWindows for CStr { + unsafe fn to_windows(&self) -> CString { + let path = self + .to_str() + .unwrap() + .to_string() + .chars() + .map(|x| match x { + '/' => '\\', + c => c, + }) + .collect::(); + + let path = path.trim_start_matches("\\\\?\\").to_string(); + + CString::new(path).unwrap() + } +} + +#[sysv64] +pub unsafe fn lstat(path: *const libc::c_char, buf: *mut StatLinux) -> libc::c_int { + debug!( + "lstat: Windows translate-call, path: {:?}", + CStr::from_ptr(path) + ); + let mut stat_win = MaybeUninit::::zeroed(); + let path = CStr::from_ptr(path).to_windows(); + + let ret = libc::stat(path.as_ptr(), stat_win.as_mut_ptr()); + let stat_win = stat_win.assume_init(); + + *buf = stat_win.to_windows(); + + ret +} + +impl ToWindows for libc::stat { + unsafe fn to_windows(&self) -> StatLinux { + let atime = PosixTimespec::from_windows_time(self.st_atime as u64); + let mtime = PosixTimespec::from_windows_time(self.st_mtime as u64); + let ctime = PosixTimespec::from_windows_time(self.st_ctime as u64); + + let mut mode = 0o555; + let win_mode = self.st_mode; + + if win_mode & 0b11 != 0 { + mode |= 0o200; + } + + if win_mode & 0x4000 != 0 { + mode |= 0o40000; + } + + StatLinux { + st_dev: self.st_dev as _, + st_ino: self.st_ino as _, + st_nlink: self.st_nlink as _, + st_mode: mode as _, + st_uid: self.st_uid as _, + st_gid: self.st_gid as _, + __pad0: 0, + st_rdev: self.st_rdev as _, + st_size: self.st_size as _, + st_blksize: 0, + st_blocks: 0, + st_atime: atime.tv_sec, + st_atime_nsec: 0, + st_mtime: mtime.tv_sec, + st_mtime_nsec: 0, + st_ctime: ctime.tv_sec, + st_ctime_nsec: 0, + __unused: [0, 0, 0], + } + } +} + +#[sysv64] +pub unsafe fn fstat(fildes: libc::c_int, buf: *mut StatLinux) -> libc::c_int { + debug!("fstat: Windows translate-call"); + let mut stat_win = MaybeUninit::::zeroed(); + let ret = libc::fstat(fildes, stat_win.as_mut_ptr()); + let stat_win = stat_win.assume_init(); + + *buf = stat_win.to_windows(); + + ret +} + +#[sysv64] +pub unsafe fn malloc(size: libc::size_t) -> *mut libc::c_void { + // debug!("malloc: Windows translate-call"); + libc::malloc(size) +} + +#[sysv64] +pub unsafe fn free(p: *mut libc::c_void) { + // debug!("free: Windows translate-call"); + libc::free(p) +} + +#[sysv64] +pub unsafe fn strncpy( + dst: *mut libc::c_char, + src: *const libc::c_char, + n: libc::size_t, +) -> *mut libc::c_char { + debug!("strncpy: Windows translate-call"); + libc::strncpy(dst, src, n) +} + +#[sysv64] +pub unsafe fn chmod(path: *const libc::c_char, mode: libc::c_int) -> libc::c_int { + debug!("chmod: Windows translate-call"); + libc::chmod(path, mode) +} + +#[sysv64] +pub unsafe fn mkdir(path: *const libc::c_char) -> libc::c_int { + debug!("mkdir: Windows translate-call"); + libc::mkdir(path) +} + +#[sysv64] +pub unsafe fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int { + debug!("open: Windows translate-call oflag 0o{:o}", oflag); + + let path = CStr::from_ptr(path).to_windows(); + + let mut win_flag = 0x8000; // binary mode + + if oflag & 0o100 != 0 { + win_flag |= O_CREAT; + } + + if oflag & 0o1 == 1 { + win_flag |= O_WRONLY; + } else if oflag & 0o2 != 0 { + win_flag |= O_RDWR; + } else { + win_flag |= O_RDONLY; + } + + let val = libc::open(path.as_ptr(), win_flag); + + val +} + +#[sysv64] +pub unsafe fn close(fd: libc::c_int) -> libc::c_int { + debug!("close: Windows translate-call"); + libc::close(fd) +} + +#[sysv64] +pub unsafe fn read(fd: libc::c_int, buf: *mut libc::c_void, count: libc::c_uint) -> libc::c_int { + debug!("read: Windows translate-call"); + + let r = libc::read(fd, buf, count); + r +} + +#[sysv64] +pub unsafe fn write(fd: libc::c_int, buf: *const libc::c_void, count: libc::c_uint) -> libc::c_int { + debug!("write: Windows translate-call"); + libc::write(fd, buf, count) +} From afaa8db60c3bbc0de3d2539f7a57a9e9977ce80e Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:45:30 -0500 Subject: [PATCH 26/30] Undo formatting --- omnisette/src/adi_proxy.rs | 10 ++++------ omnisette/src/aos_kit.rs | 2 +- omnisette/src/remote_anisette.rs | 2 +- omnisette/src/store_services_core.rs | 26 ++++++++++++++------------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/omnisette/src/adi_proxy.rs b/omnisette/src/adi_proxy.rs index 74f6a6f..aa11fbf 100644 --- a/omnisette/src/adi_proxy.rs +++ b/omnisette/src/adi_proxy.rs @@ -46,7 +46,7 @@ pub enum ADIError { ReqwestError(#[from] reqwest::Error), Base64Error(#[from] base64::DecodeError), InvalidHeaderValue(#[from] InvalidHeaderValue), - IOError(#[from] io::Error), + IOError(#[from] io::Error) } impl ADIError { @@ -95,7 +95,7 @@ pub struct RequestOTPData { } #[cfg_attr(feature = "async", async_trait::async_trait(?Send))] -pub trait ADIProxy: Send + Sync { +pub trait ADIProxy: Send + Sync { fn erase_provisioning(&mut self, ds_id: i64) -> Result<(), ADIError>; fn synchronize(&mut self, ds_id: i64, sim: &[u8]) -> Result; fn destroy_provisioning_session(&mut self, session: u32) -> Result<(), ADIError>; @@ -291,9 +291,7 @@ pub struct ADIProxyAnisetteProvider { impl ADIProxyAnisetteProvider { /// If you use this method, you are expected to set the identifier yourself. - pub fn without_identifier( - adi_proxy: ProxyType, - ) -> Result, ADIError> { + pub fn without_identifier(adi_proxy: ProxyType) -> Result, ADIError> { Ok(ADIProxyAnisetteProvider { adi_proxy }) } @@ -380,4 +378,4 @@ impl AnisetteHeadersProvider Ok(headers) } -} +} \ No newline at end of file diff --git a/omnisette/src/aos_kit.rs b/omnisette/src/aos_kit.rs index 5e53787..a8c5809 100644 --- a/omnisette/src/aos_kit.rs +++ b/omnisette/src/aos_kit.rs @@ -121,4 +121,4 @@ mod tests { ); Ok(()) } -} +} \ No newline at end of file diff --git a/omnisette/src/remote_anisette.rs b/omnisette/src/remote_anisette.rs index b061bd9..bcee08e 100644 --- a/omnisette/src/remote_anisette.rs +++ b/omnisette/src/remote_anisette.rs @@ -44,4 +44,4 @@ mod tests { ); Ok(()) } -} +} \ No newline at end of file diff --git a/omnisette/src/store_services_core.rs b/omnisette/src/store_services_core.rs index 631bf91..23f0ee4 100644 --- a/omnisette/src/store_services_core.rs +++ b/omnisette/src/store_services_core.rs @@ -66,16 +66,11 @@ pub struct StoreServicesCoreADIProxy<'lt> { } impl StoreServicesCoreADIProxy<'_> { - pub fn new<'lt>( - library_path: &PathBuf, - ) -> Result, AnisetteError> { + pub fn new<'lt>(library_path: &PathBuf) -> Result, AnisetteError> { Self::with_custom_provisioning_path(library_path, library_path) } - pub fn with_custom_provisioning_path<'lt>( - library_path: &PathBuf, - provisioning_path: &PathBuf, - ) -> Result, AnisetteError> { + pub fn with_custom_provisioning_path<'lt>(library_path: &PathBuf, provisioning_path: &PathBuf) -> Result, AnisetteError> { // Should be safe if the library is correct. unsafe { LoaderHelpers::setup_hooks(); @@ -109,8 +104,12 @@ impl StoreServicesCoreADIProxy<'_> { .ok_or(AnisetteError::InvalidLibraryFormat)?, ); - let path = - CString::new(native_library_path.to_str().ok_or(AnisetteError::Misc)?).unwrap(); + let path = CString::new( + native_library_path + .to_str() + .ok_or(AnisetteError::Misc)?, + ) + .unwrap(); assert_eq!((adi_load_library_with_path)(path.as_ptr() as *const u8), 0); let adi_set_android_id = store_services_core @@ -164,7 +163,9 @@ impl StoreServicesCoreADIProxy<'_> { adi_otp_request: std::mem::transmute(adi_otp_request), }; - proxy.set_provisioning_path(provisioning_path.to_str().ok_or(AnisetteError::Misc)?)?; + proxy.set_provisioning_path( + provisioning_path.to_str().ok_or(AnisetteError::Misc)?, + )?; Ok(proxy) } @@ -411,10 +412,10 @@ impl LoaderHelpers { #[cfg(test)] mod tests { - use crate::AnisetteError; use crate::{AnisetteConfiguration, AnisetteHeaders}; use log::info; use std::path::PathBuf; + use crate::AnisetteError; #[cfg(not(feature = "async"))] #[test] @@ -435,6 +436,7 @@ mod tests { #[cfg(feature = "async")] #[tokio::test] async fn fetch_anisette_ssc_async() -> Result<(), AnisetteError> { + crate::tests::init_logger(); let mut provider = AnisetteHeaders::get_ssc_anisette_headers_provider( @@ -447,4 +449,4 @@ mod tests { ); Ok(()) } -} +} \ No newline at end of file From 40d4a1f3ff759b548633ded55857f60ede9e6022 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:50:20 -0500 Subject: [PATCH 27/30] Undo more changes --- omnisette/src/adi_proxy.rs | 2 +- omnisette/src/anisette_headers_provider.rs | 9 +++++---- omnisette/src/aos_kit.rs | 2 +- omnisette/src/lib.rs | 4 +++- omnisette/src/remote_anisette.rs | 2 +- omnisette/src/store_services_core.rs | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/omnisette/src/adi_proxy.rs b/omnisette/src/adi_proxy.rs index aa11fbf..617af58 100644 --- a/omnisette/src/adi_proxy.rs +++ b/omnisette/src/adi_proxy.rs @@ -378,4 +378,4 @@ impl AnisetteHeadersProvider Ok(headers) } -} \ No newline at end of file +} diff --git a/omnisette/src/anisette_headers_provider.rs b/omnisette/src/anisette_headers_provider.rs index aab500b..903203e 100644 --- a/omnisette/src/anisette_headers_provider.rs +++ b/omnisette/src/anisette_headers_provider.rs @@ -1,17 +1,18 @@ + use std::collections::HashMap; use crate::AnisetteError; -#[async_trait::async_trait] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AnisetteHeadersProvider: Send + Sync { + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] async fn get_anisette_headers( &mut self, skip_provisioning: bool, ) -> Result, AnisetteError>; - async fn get_authentication_headers( - &mut self, - ) -> Result, AnisetteError> { + #[cfg_attr(not(feature = "async"), remove_async_await::remove_async_await)] + async fn get_authentication_headers(&mut self) -> Result, AnisetteError> { let headers = self.get_anisette_headers(false).await?; Ok(self.normalize_headers(headers)) } diff --git a/omnisette/src/aos_kit.rs b/omnisette/src/aos_kit.rs index a8c5809..5e53787 100644 --- a/omnisette/src/aos_kit.rs +++ b/omnisette/src/aos_kit.rs @@ -121,4 +121,4 @@ mod tests { ); Ok(()) } -} \ No newline at end of file +} diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index 1c8aeb8..5259e91 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -39,8 +39,10 @@ pub enum AnisetteError { PlistError(#[from] plist::Error), #[error("Request Error {0}")] ReqwestError(#[from] reqwest::Error), + #[cfg(feature = "remote-anisette-v3")] #[error("Provisioning socket error {0}")] - WsError(Box), + WsError(#[from] tokio_tungstenite::tungstenite::error::Error), + #[cfg(feature = "remote-anisette-v3")] #[error("JSON error {0}")] SerdeError(#[from] serde_json::Error), #[error("IO error {0}")] diff --git a/omnisette/src/remote_anisette.rs b/omnisette/src/remote_anisette.rs index bcee08e..b061bd9 100644 --- a/omnisette/src/remote_anisette.rs +++ b/omnisette/src/remote_anisette.rs @@ -44,4 +44,4 @@ mod tests { ); Ok(()) } -} \ No newline at end of file +} diff --git a/omnisette/src/store_services_core.rs b/omnisette/src/store_services_core.rs index 23f0ee4..5782c03 100644 --- a/omnisette/src/store_services_core.rs +++ b/omnisette/src/store_services_core.rs @@ -449,4 +449,4 @@ mod tests { ); Ok(()) } -} \ No newline at end of file +} From 53a0e7f748fcedf5bd6c051b1ddf889b62fd7526 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:51:00 -0500 Subject: [PATCH 28/30] Undo formatting --- omnisette/src/store_services_core/posix_macos.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/omnisette/src/store_services_core/posix_macos.rs b/omnisette/src/store_services_core/posix_macos.rs index 2b3fe0a..840a039 100644 --- a/omnisette/src/store_services_core/posix_macos.rs +++ b/omnisette/src/store_services_core/posix_macos.rs @@ -1,11 +1,6 @@ -pub use libc::{ - chmod, close, free, ftruncate, gettimeofday, malloc, mkdir, read, strncpy, umask, write, -}; +pub use libc::{chmod, close, free, ftruncate, gettimeofday, malloc, mkdir, read, strncpy, umask, write}; -use libc::{ - fstat as fstat_macos, lstat as lstat_macos, open as open_macos, stat as stat_macos, O_CREAT, - O_RDONLY, O_RDWR, O_WRONLY, -}; +use libc::{lstat as lstat_macos, fstat as fstat_macos, stat as stat_macos, open as open_macos, O_CREAT, O_WRONLY, O_RDWR, O_RDONLY}; use android_loader::sysv64; From 257cdb9c87bff37c7ee9e75ba550c9699b190db7 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:55:02 -0500 Subject: [PATCH 29/30] Undo even more changes --- omnisette/Cargo.toml | 4 ++ omnisette/src/lib.rs | 48 ++++++++++++++++++---- omnisette/src/remote_anisette_v3.rs | 64 ++++++++++++++++++----------- 3 files changed, 86 insertions(+), 30 deletions(-) diff --git a/omnisette/Cargo.toml b/omnisette/Cargo.toml index 7b22a64..785c301 100644 --- a/omnisette/Cargo.toml +++ b/omnisette/Cargo.toml @@ -14,11 +14,14 @@ remote-anisette-v3 = ["async", "dep:serde", "dep:serde_json", "dep:tokio-tungste [dependencies] base64 = "0.22" +hex = "0.4" plist = "1.4" reqwest = { version = "0.11", default-features = false, features = ["blocking", "json", "rustls-tls", "gzip"] } rand = "0.9" sha2 = "0.10" uuid = { version = "1.3", features = [ "v4", "fast-rng", "macro-diagnostics" ] } +android-loader = { git = "https://github.com/Dadoum/android-loader", branch = "bigger_pages" } +libc = "0.2" log = "0.4" async-trait = { version = "0.1", optional = true } remove-async-await = { version = "1.0", optional = true } @@ -30,6 +33,7 @@ chrono = { version = "0.4.37", optional = true } thiserror = "2" anyhow = "1.0.81" + [target.'cfg(target_os = "macos")'.dependencies] dlopen2 = "0.4" objc = "0.2" diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index 5259e91..4e91752 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -4,7 +4,9 @@ //! //! If you want remote anisette, make sure the `remote-anisette` feature is enabled. (it's currently on by default) +use crate::adi_proxy::{ADIProxyAnisetteProvider, ConfigurableADIProxy}; use crate::anisette_headers_provider::AnisetteHeadersProvider; +use adi_proxy::ADIError; use std::io; use std::path::PathBuf; use thiserror::Error; @@ -47,6 +49,8 @@ pub enum AnisetteError { SerdeError(#[from] serde_json::Error), #[error("IO error {0}")] IOError(#[from] io::Error), + #[error("ADI error {0}")] + ADIError(#[from] ADIError), #[error("Invalid library format")] InvalidLibraryFormat, #[error("Misc")] @@ -57,12 +61,6 @@ pub enum AnisetteError { Anyhow(#[from] anyhow::Error), } -impl From for AnisetteError { - fn from(err: tokio_tungstenite::tungstenite::error::Error) -> Self { - AnisetteError::WsError(Box::new(err)) - } -} - pub const DEFAULT_ANISETTE_URL: &str = "https://ani.f1sh.me/"; pub const DEFAULT_ANISETTE_URL_V3: &str = "https://ani.sidestore.io"; @@ -150,12 +148,48 @@ impl AnisetteHeaders { pub fn get_anisette_headers_provider( configuration: AnisetteConfiguration, ) -> Result { - Ok(AnisetteHeadersProviderRes::remote(Box::new( + #[cfg(target_os = "macos")] + if let Ok(prov) = aos_kit::AOSKitAnisetteProvider::new() { + return Ok(AnisetteHeadersProviderRes::local(Box::new(prov))); + } + + // TODO: handle Err because it will just go to remote anisette and not tell the user anything + if let Ok(ssc_anisette_headers_provider) = + AnisetteHeaders::get_ssc_anisette_headers_provider(configuration.clone()) + { + return Ok(ssc_anisette_headers_provider); + } + + #[cfg(feature = "remote-anisette-v3")] + return Ok(AnisetteHeadersProviderRes::remote(Box::new( remote_anisette_v3::RemoteAnisetteProviderV3::new( configuration.anisette_url_v3, configuration.configuration_path.clone(), configuration.macos_serial.clone(), ), + ))); + + #[cfg(feature = "remote-anisette")] + return Ok(AnisetteHeadersProviderRes::remote(Box::new( + remote_anisette::RemoteAnisetteProvider::new(configuration.anisette_url), + ))); + + #[cfg(not(feature = "remote-anisette"))] + bail!(AnisetteMetaError::UnsupportedDevice) + } + + pub fn get_ssc_anisette_headers_provider( + configuration: AnisetteConfiguration, + ) -> Result { + let mut ssc_adi_proxy = store_services_core::StoreServicesCoreADIProxy::new( + configuration.configuration_path(), + )?; + let config_path = configuration.configuration_path(); + ssc_adi_proxy.set_provisioning_path(config_path.to_str().ok_or( + AnisetteError::InvalidArgument("configuration.configuration_path".to_string()), + )?)?; + Ok(AnisetteHeadersProviderRes::local(Box::new( + ADIProxyAnisetteProvider::new(ssc_adi_proxy, config_path.to_path_buf())?, ))) } } diff --git a/omnisette/src/remote_anisette_v3.rs b/omnisette/src/remote_anisette_v3.rs index b7c7aa9..f73fb14 100644 --- a/omnisette/src/remote_anisette_v3.rs +++ b/omnisette/src/remote_anisette_v3.rs @@ -150,29 +150,27 @@ impl AnisetteData { pub fn get_headers(&self, serial: String) -> HashMap { let dt: DateTime = Utc::now().round_subsecs(0); - HashMap::from_iter( - [ - ( - "X-Apple-I-Client-Time".to_string(), - dt.format("%+").to_string().replace("+00:00", "Z"), - ), - ("X-Apple-I-SRL-NO".to_string(), serial), - ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), - ("X-Apple-Locale".to_string(), "en_US".to_string()), - ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), - ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), - ( - "X-Mme-Device-Id".to_string(), - self.device_unique_identifier.clone(), - ), - ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), - ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), - ( - "X-Mme-Client-Info".to_string(), - self.device_description.clone(), - ), - ], - ) + HashMap::from_iter([ + ( + "X-Apple-I-Client-Time".to_string(), + dt.format("%+").to_string().replace("+00:00", "Z"), + ), + ("X-Apple-I-SRL-NO".to_string(), serial), + ("X-Apple-I-TimeZone".to_string(), "UTC".to_string()), + ("X-Apple-Locale".to_string(), "en_US".to_string()), + ("X-Apple-I-MD-RINFO".to_string(), self.routing_info.clone()), + ("X-Apple-I-MD-LU".to_string(), self.local_user_id.clone()), + ( + "X-Mme-Device-Id".to_string(), + self.device_unique_identifier.clone(), + ), + ("X-Apple-I-MD".to_string(), self.one_time_password.clone()), + ("X-Apple-I-MD-M".to_string(), self.machine_id.clone()), + ( + "X-Mme-Client-Info".to_string(), + self.device_description.clone(), + ), + ]) } } @@ -539,4 +537,24 @@ mod tests { ); Ok(()) } + + #[cfg(not(feature = "async"))] + #[test] + fn fetch_anisette_auto() -> Result<()> { + use crate::{AnisetteConfiguration, AnisetteHeaders}; + use log::info; + use std::path::PathBuf; + + crate::tests::init_logger(); + + let mut provider = AnisetteHeaders::get_anisette_headers_provider( + AnisetteConfiguration::new() + .set_configuration_path(PathBuf::new().join("anisette_test")), + )?; + info!( + "Headers: {:?}", + provider.provider.get_authentication_headers()? + ); + Ok(()) + } } From 65baf127f9702176b7488256fe5e01339668116a Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 9 Nov 2025 13:58:10 -0500 Subject: [PATCH 30/30] Undo more changes --- icloud-auth/src/anisette.rs | 6 +----- icloud-auth/src/lib.rs | 7 ++++--- omnisette/src/lib.rs | 37 ++++++++++++++++++++++++------------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/icloud-auth/src/anisette.rs b/icloud-auth/src/anisette.rs index c58b767..d11601a 100644 --- a/icloud-auth/src/anisette.rs +++ b/icloud-auth/src/anisette.rs @@ -15,11 +15,7 @@ impl AnisetteData { let mut b = AnisetteHeaders::get_anisette_headers_provider(config.clone())?; let base_headers = b.provider.get_authentication_headers().await?; - Ok(AnisetteData { - base_headers, - generated_at: SystemTime::now(), - config, - }) + Ok(AnisetteData { base_headers, generated_at: SystemTime::now(), config }) } pub fn needs_refresh(&self) -> bool { diff --git a/icloud-auth/src/lib.rs b/icloud-auth/src/lib.rs index 1ec3205..fc79925 100644 --- a/icloud-auth/src/lib.rs +++ b/icloud-auth/src/lib.rs @@ -1,7 +1,8 @@ pub mod anisette; mod client; +use std::fmt::Display; -pub use client::{AppleAccount, AuthenticationExtras, LoginState, TrustedPhoneNumber, VerifyBody}; +pub use client::{AppleAccount, LoginState, TrustedPhoneNumber, AuthenticationExtras, VerifyBody}; pub use omnisette::AnisetteConfiguration; use thiserror::Error; @@ -22,5 +23,5 @@ pub enum Error { #[error("Request failed {0}")] ReqwestError(#[from] reqwest::Error), #[error("Failed getting anisette data {0}")] - ErrorGettingAnisette(#[from] omnisette::AnisetteError), -} + ErrorGettingAnisette(#[from] omnisette::AnisetteError) +} \ No newline at end of file diff --git a/omnisette/src/lib.rs b/omnisette/src/lib.rs index 4e91752..bfd2681 100644 --- a/omnisette/src/lib.rs +++ b/omnisette/src/lib.rs @@ -6,9 +6,9 @@ use crate::adi_proxy::{ADIProxyAnisetteProvider, ConfigurableADIProxy}; use crate::anisette_headers_provider::AnisetteHeadersProvider; -use adi_proxy::ADIError; use std::io; use std::path::PathBuf; +use adi_proxy::ADIError; use thiserror::Error; pub mod adi_proxy; @@ -58,7 +58,7 @@ pub enum AnisetteError { #[error("Missing Libraries")] MissingLibraries, #[error("{0}")] - Anyhow(#[from] anyhow::Error), + Anyhow(#[from] anyhow::Error) } pub const DEFAULT_ANISETTE_URL: &str = "https://ani.f1sh.me/"; @@ -85,7 +85,7 @@ impl AnisetteConfiguration { anisette_url: DEFAULT_ANISETTE_URL.to_string(), anisette_url_v3: DEFAULT_ANISETTE_URL_V3.to_string(), configuration_path: PathBuf::new(), - macos_serial: "0".to_string(), + macos_serial: "0".to_string() } } @@ -102,11 +102,6 @@ impl AnisetteConfiguration { self } - pub fn set_anisette_url_v3(mut self, anisette_url_v3: String) -> AnisetteConfiguration { - self.anisette_url_v3 = anisette_url_v3; - self - } - pub fn set_macos_serial(mut self, macos_serial: String) -> AnisetteConfiguration { self.macos_serial = macos_serial; self @@ -162,11 +157,7 @@ impl AnisetteHeaders { #[cfg(feature = "remote-anisette-v3")] return Ok(AnisetteHeadersProviderRes::remote(Box::new( - remote_anisette_v3::RemoteAnisetteProviderV3::new( - configuration.anisette_url_v3, - configuration.configuration_path.clone(), - configuration.macos_serial.clone(), - ), + remote_anisette_v3::RemoteAnisetteProviderV3::new(configuration.anisette_url_v3, configuration.configuration_path.clone(), configuration.macos_serial.clone()), ))); #[cfg(feature = "remote-anisette")] @@ -212,4 +203,24 @@ mod tests { .is_ok() {} } + + #[cfg(not(feature = "async"))] + #[test] + fn fetch_anisette_auto() -> Result<()> { + use crate::{AnisetteConfiguration, AnisetteHeaders}; + use log::info; + use std::path::PathBuf; + + crate::tests::init_logger(); + + let mut provider = AnisetteHeaders::get_anisette_headers_provider( + AnisetteConfiguration::new() + .set_configuration_path(PathBuf::new().join("anisette_test")), + )?; + info!( + "Headers: {:?}", + provider.provider.get_authentication_headers()? + ); + Ok(()) + } }