├── .gitignore ├── Cargo.toml └── src ├── anisette.rs ├── lib.rs └── request.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | *.py 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-sign" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ureq = { version = "2.5.0", features = ["json"] } 10 | serde = { version = "1.0.147", features = ["derive"] } 11 | serde_json = { version = "1.0.87" } 12 | base64 = { version = "0.13.1" } 13 | srp = { version = "0.6.0" } 14 | pbkdf2 = { version = "0.11.0" } 15 | sha2 = { version = "0.10.6" } 16 | rand = { version = "0.8.5" } 17 | rustls = { version = "0.20.7" } 18 | rustls-pemfile = { version = "1.0.1" } 19 | plist = { version = "1.3.1" } 20 | hmac = { version = "0.12.1" } -------------------------------------------------------------------------------- /src/anisette.rs: -------------------------------------------------------------------------------- 1 | // Jackson Coxson 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::Error; 6 | 7 | pub const SIDELOADLY_ANISETTE: &str = "https://sideloadly.io/anisette/irGb3Quww8zrhgqnzmrx"; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Clone)] 10 | pub struct AnisetteData { 11 | #[serde(rename(deserialize = "X-Apple-I-Client-Time"))] 12 | pub x_apple_i_client_time: String, 13 | #[serde(rename(deserialize = "X-Apple-I-MD"))] 14 | pub x_apple_i_md: String, 15 | #[serde(rename(deserialize = "X-Apple-I-MD-LU"))] 16 | pub x_apple_i_md_lu: String, 17 | #[serde(rename(deserialize = "X-Apple-I-MD-M"))] 18 | pub x_apple_i_md_m: String, 19 | #[serde(rename(deserialize = "X-Apple-I-MD-RINFO"))] 20 | pub x_apple_i_md_rinfo: String, 21 | #[serde(rename(deserialize = "X-Apple-I-SRL-NO"))] 22 | pub x_apple_i_srl_no: String, 23 | #[serde(rename(deserialize = "X-Apple-I-TimeZone"))] 24 | pub x_apple_i_timezone: String, 25 | #[serde(rename(deserialize = "X-Apple-Locale"))] 26 | pub x_apple_locale: String, 27 | #[serde(rename(deserialize = "X-MMe-Client-Info"))] 28 | pub x_mme_client_info: String, 29 | #[serde(rename(deserialize = "X-Mme-Device-Id"))] 30 | pub x_mme_device_id: String, 31 | } 32 | 33 | impl AnisetteData { 34 | /// Fetches the data at an anisette server 35 | pub fn from_url(url: impl Into) -> Result { 36 | let body = match ureq::get(&url.into()).call() { 37 | Ok(b) => match b.into_string() { 38 | Ok(b) => b, 39 | Err(_) => { 40 | return Err(Error::HttpRequest); 41 | } 42 | }, 43 | Err(_) => { 44 | return Err(Error::HttpRequest); 45 | } 46 | }; 47 | 48 | let body = match serde_json::from_str::(&body) { 49 | Ok(b) => b, 50 | Err(_) => return Err(Error::Parse), 51 | }; 52 | 53 | Ok(body) 54 | } 55 | 56 | pub fn to_cpd(&self) -> plist::Dictionary { 57 | let mut cpd = plist::Dictionary::new(); 58 | cpd.insert( 59 | "X-Apple-I-Client-Time".to_owned(), 60 | plist::Value::String(self.x_apple_i_client_time.clone()), 61 | ); 62 | cpd.insert( 63 | "X-Apple-I-MD".to_owned(), 64 | plist::Value::String(self.x_apple_i_md.clone()), 65 | ); 66 | cpd.insert( 67 | "X-Apple-I-MD-LU".to_owned(), 68 | plist::Value::String(self.x_apple_i_md_lu.clone()), 69 | ); 70 | cpd.insert( 71 | "X-Apple-I-MD-M".to_owned(), 72 | plist::Value::String(self.x_apple_i_md_m.clone()), 73 | ); 74 | 75 | let rinfo = self.x_apple_i_md_rinfo.parse::().unwrap(); 76 | cpd.insert( 77 | "X-Apple-I-MD-RINFO".to_owned(), 78 | plist::Value::Integer(rinfo.into()), 79 | ); 80 | cpd.insert( 81 | "X-Apple-I-SRL-NO".to_owned(), 82 | plist::Value::String(self.x_apple_i_srl_no.clone()), 83 | ); 84 | cpd.insert( 85 | "X-Apple-I-TimeZone".to_owned(), 86 | plist::Value::String(self.x_apple_i_timezone.clone()), 87 | ); 88 | cpd.insert( 89 | "X-Apple-Locale".to_owned(), 90 | plist::Value::String(self.x_apple_locale.clone()), 91 | ); 92 | cpd.insert( 93 | "X-Mme-Device-Id".to_owned(), 94 | plist::Value::String(self.x_mme_device_id.clone()), 95 | ); 96 | cpd.insert("bootstrap".to_owned(), plist::Value::Boolean(true)); 97 | cpd.insert("icscrec".to_owned(), plist::Value::Boolean(true)); 98 | cpd.insert("loc".to_owned(), plist::Value::String("en_GB".to_owned())); 99 | cpd.insert("pbe".to_owned(), plist::Value::Boolean(false)); 100 | cpd.insert("prkgen".to_owned(), plist::Value::Boolean(true)); 101 | cpd.insert("svct".to_owned(), plist::Value::String("iCloud".to_owned())); 102 | 103 | cpd 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Jackson Coxson 2 | 3 | pub mod anisette; 4 | pub mod request; 5 | 6 | #[derive(Debug)] 7 | pub enum Error { 8 | HttpRequest, 9 | Parse, 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | 15 | use super::*; 16 | 17 | #[test] 18 | fn fetch_anisette() { 19 | println!("heck"); 20 | let password = std::env::var("apple_password").unwrap(); 21 | let email = std::env::var("apple_email").unwrap(); 22 | let ad = anisette::AnisetteData::from_url(anisette::SIDELOADLY_ANISETTE).unwrap(); 23 | let _ = request::GsaClient::new(email, password, ad); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/request.rs: -------------------------------------------------------------------------------- 1 | // Jackson Coxson 2 | 3 | use std::sync::Arc; 4 | 5 | use rustls::{ClientConfig, RootCertStore}; 6 | use serde::{Deserialize, Serialize}; 7 | use sha2::{Digest, Sha256}; 8 | use srp::{ 9 | client::{SrpClient, SrpClientVerifier}, 10 | groups::G_2048, 11 | }; 12 | use ureq::AgentBuilder; 13 | 14 | use crate::anisette::AnisetteData; 15 | 16 | const GSA_ENDPOINT: &str = "https://gsa.apple.com/grandslam/GsService2"; 17 | const APPLE_ROOT: &[u8] = &[ 18 | 48, 130, 4, 187, 48, 130, 3, 163, 160, 3, 2, 1, 2, 2, 1, 2, 48, 13, 6, 9, 42, 134, 72, 134, 19 | 247, 13, 1, 1, 5, 5, 0, 48, 98, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 19, 48, 17, 20 | 6, 3, 85, 4, 10, 19, 10, 65, 112, 112, 108, 101, 32, 73, 110, 99, 46, 49, 38, 48, 36, 6, 3, 85, 21 | 4, 11, 19, 29, 65, 112, 112, 108, 101, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 22 | 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 49, 22, 48, 20, 6, 3, 85, 4, 3, 19, 23 | 13, 65, 112, 112, 108, 101, 32, 82, 111, 111, 116, 32, 67, 65, 48, 30, 23, 13, 48, 54, 48, 52, 24 | 50, 53, 50, 49, 52, 48, 51, 54, 90, 23, 13, 51, 53, 48, 50, 48, 57, 50, 49, 52, 48, 51, 54, 90, 25 | 48, 98, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 19, 48, 17, 6, 3, 85, 4, 10, 19, 10, 26 | 65, 112, 112, 108, 101, 32, 73, 110, 99, 46, 49, 38, 48, 36, 6, 3, 85, 4, 11, 19, 29, 65, 112, 27 | 112, 108, 101, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 28 | 116, 104, 111, 114, 105, 116, 121, 49, 22, 48, 20, 6, 3, 85, 4, 3, 19, 13, 65, 112, 112, 108, 29 | 101, 32, 82, 111, 111, 116, 32, 67, 65, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 30 | 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 10, 2, 130, 1, 1, 0, 228, 145, 169, 9, 31, 31 | 145, 219, 30, 71, 80, 235, 5, 237, 94, 121, 132, 45, 235, 54, 162, 87, 76, 85, 236, 139, 25, 32 | 137, 222, 249, 75, 108, 245, 7, 171, 34, 48, 2, 232, 24, 62, 248, 80, 9, 211, 127, 65, 168, 33 | 152, 249, 209, 202, 102, 156, 36, 107, 17, 208, 163, 187, 228, 27, 42, 195, 31, 149, 158, 122, 34 | 12, 164, 71, 139, 91, 212, 22, 55, 51, 203, 196, 15, 77, 206, 20, 105, 209, 201, 25, 114, 245, 35 | 93, 14, 213, 127, 95, 155, 242, 37, 3, 186, 85, 143, 77, 93, 13, 241, 100, 53, 35, 21, 75, 21, 36 | 89, 29, 179, 148, 247, 246, 156, 158, 207, 80, 186, 193, 88, 80, 103, 143, 8, 180, 32, 247, 37 | 203, 172, 44, 32, 111, 112, 182, 63, 1, 48, 140, 183, 67, 207, 15, 157, 61, 243, 43, 73, 40, 38 | 26, 200, 254, 206, 181, 185, 14, 217, 94, 28, 214, 203, 61, 181, 58, 173, 244, 15, 14, 0, 146, 39 | 11, 177, 33, 22, 46, 116, 213, 60, 13, 219, 98, 22, 171, 163, 113, 146, 71, 83, 85, 193, 175, 40 | 47, 65, 179, 248, 251, 227, 112, 205, 230, 163, 76, 69, 126, 31, 76, 107, 80, 150, 65, 137, 41 | 196, 116, 98, 11, 16, 131, 65, 135, 51, 138, 129, 177, 48, 88, 236, 90, 4, 50, 140, 104, 179, 42 | 143, 29, 222, 101, 115, 255, 103, 94, 101, 188, 73, 216, 118, 159, 51, 20, 101, 161, 119, 148, 43 | 201, 45, 2, 3, 1, 0, 1, 163, 130, 1, 122, 48, 130, 1, 118, 48, 14, 6, 3, 85, 29, 15, 1, 1, 255, 44 | 4, 4, 3, 2, 1, 6, 48, 15, 6, 3, 85, 29, 19, 1, 1, 255, 4, 5, 48, 3, 1, 1, 255, 48, 29, 6, 3, 45 | 85, 29, 14, 4, 22, 4, 20, 43, 208, 105, 71, 148, 118, 9, 254, 244, 107, 141, 46, 64, 166, 247, 46 | 71, 77, 127, 8, 94, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, 128, 20, 43, 208, 105, 71, 148, 47 | 118, 9, 254, 244, 107, 141, 46, 64, 166, 247, 71, 77, 127, 8, 94, 48, 130, 1, 17, 6, 3, 85, 29, 48 | 32, 4, 130, 1, 8, 48, 130, 1, 4, 48, 130, 1, 0, 6, 9, 42, 134, 72, 134, 247, 99, 100, 5, 1, 48, 49 | 129, 242, 48, 42, 6, 8, 43, 6, 1, 5, 5, 7, 2, 1, 22, 30, 104, 116, 116, 112, 115, 58, 47, 47, 50 | 119, 119, 119, 46, 97, 112, 112, 108, 101, 46, 99, 111, 109, 47, 97, 112, 112, 108, 101, 99, 51 | 97, 47, 48, 129, 195, 6, 8, 43, 6, 1, 5, 5, 7, 2, 2, 48, 129, 182, 26, 129, 179, 82, 101, 108, 52 | 105, 97, 110, 99, 101, 32, 111, 110, 32, 116, 104, 105, 115, 32, 99, 101, 114, 116, 105, 102, 53 | 105, 99, 97, 116, 101, 32, 98, 121, 32, 97, 110, 121, 32, 112, 97, 114, 116, 121, 32, 97, 115, 54 | 115, 117, 109, 101, 115, 32, 97, 99, 99, 101, 112, 116, 97, 110, 99, 101, 32, 111, 102, 32, 55 | 116, 104, 101, 32, 116, 104, 101, 110, 32, 97, 112, 112, 108, 105, 99, 97, 98, 108, 101, 32, 56 | 115, 116, 97, 110, 100, 97, 114, 100, 32, 116, 101, 114, 109, 115, 32, 97, 110, 100, 32, 99, 57 | 111, 110, 100, 105, 116, 105, 111, 110, 115, 32, 111, 102, 32, 117, 115, 101, 44, 32, 99, 101, 58 | 114, 116, 105, 102, 105, 99, 97, 116, 101, 32, 112, 111, 108, 105, 99, 121, 32, 97, 110, 100, 59 | 32, 99, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 112, 114, 97, 99, 116, 60 | 105, 99, 101, 32, 115, 116, 97, 116, 101, 109, 101, 110, 116, 115, 46, 48, 13, 6, 9, 42, 134, 61 | 72, 134, 247, 13, 1, 1, 5, 5, 0, 3, 130, 1, 1, 0, 92, 54, 153, 76, 45, 120, 183, 237, 140, 155, 62 | 220, 243, 119, 155, 242, 118, 210, 119, 48, 79, 193, 31, 133, 131, 133, 27, 153, 61, 71, 55, 63 | 242, 169, 155, 64, 142, 44, 212, 177, 144, 18, 216, 190, 244, 115, 155, 238, 210, 100, 15, 203, 64 | 121, 79, 52, 216, 162, 62, 249, 120, 255, 107, 200, 7, 236, 125, 57, 131, 139, 83, 32, 211, 56, 65 | 196, 177, 191, 154, 79, 10, 107, 255, 43, 252, 89, 167, 5, 9, 124, 23, 64, 86, 17, 30, 116, 66 | 211, 183, 139, 35, 59, 71, 163, 213, 111, 36, 226, 235, 209, 183, 112, 223, 15, 69, 225, 39, 67 | 202, 241, 109, 120, 237, 231, 181, 23, 23, 168, 220, 126, 34, 53, 202, 37, 213, 217, 15, 214, 68 | 107, 212, 162, 36, 35, 17, 247, 161, 172, 143, 115, 129, 96, 198, 27, 91, 9, 47, 146, 178, 248, 69 | 68, 72, 240, 96, 56, 158, 21, 245, 61, 38, 103, 32, 138, 51, 106, 247, 13, 130, 207, 222, 235, 70 | 163, 47, 249, 83, 106, 91, 100, 192, 99, 51, 119, 247, 58, 7, 44, 86, 235, 218, 15, 33, 14, 71 | 218, 186, 115, 25, 79, 181, 217, 54, 127, 193, 135, 85, 217, 167, 153, 185, 50, 66, 251, 216, 72 | 213, 113, 158, 126, 161, 82, 183, 27, 189, 147, 66, 36, 18, 42, 199, 15, 29, 182, 77, 156, 94, 73 | 99, 200, 75, 128, 23, 80, 170, 138, 213, 218, 228, 252, 208, 9, 7, 55, 176, 117, 117, 33, 74 | ]; 75 | 76 | pub struct GsaClient { 77 | // client: SrpClient<'a, Sha256>, 78 | // a: [u8; 64], 79 | // a_pub: Vec, 80 | // b_pub: Vec, 81 | // salt: Vec, 82 | // username: String, 83 | // password: String, 84 | } 85 | 86 | #[derive(Debug, Serialize, Deserialize)] 87 | pub struct InitRequestBody { 88 | #[serde(rename = "A2k")] 89 | a_pub: plist::Value, 90 | cpd: plist::Dictionary, 91 | #[serde(rename = "o")] 92 | operation: String, 93 | ps: Vec, 94 | #[serde(rename = "u")] 95 | username: String, 96 | } 97 | 98 | #[derive(Debug, Serialize, Deserialize, Clone)] 99 | pub struct RequestHeader { 100 | #[serde(rename = "Version")] 101 | version: String, 102 | } 103 | 104 | impl GsaClient { 105 | pub fn new(username: String, password: String, anisette: AnisetteData) -> Self { 106 | let client = SrpClient::::new(&G_2048); 107 | let a: Vec = (0..64).map(|_| rand::random::()).collect(); 108 | let a_pub = client.compute_public_ephemeral(&a); 109 | 110 | let header = RequestHeader { 111 | version: "1.0.1".to_string(), 112 | }; 113 | let body = InitRequestBody { 114 | a_pub: plist::Value::Data(a_pub), 115 | cpd: anisette.to_cpd(), 116 | operation: "init".to_string(), 117 | ps: vec!["s2k".to_string(), "s2k_fo".to_string()], 118 | username: username.clone(), 119 | }; 120 | 121 | #[derive(Debug, Serialize, Deserialize)] 122 | struct InitRequest { 123 | #[serde(rename = "Header")] 124 | header: RequestHeader, 125 | #[serde(rename = "Request")] 126 | request: InitRequestBody, 127 | } 128 | 129 | let packet = InitRequest { 130 | header: header.clone(), 131 | request: body, 132 | }; 133 | 134 | let mut buffer = Vec::new(); 135 | plist::to_writer_xml(&mut buffer, &packet).unwrap(); 136 | let buffer = String::from_utf8(buffer).unwrap(); 137 | println!("Body: {buffer}"); 138 | 139 | let mut store = RootCertStore::empty(); 140 | store.add_parsable_certificates(&[APPLE_ROOT.to_vec()]); 141 | let rustls_cli = ClientConfig::builder() 142 | .with_safe_defaults() 143 | .with_root_certificates(store) 144 | .with_no_client_auth(); 145 | let agent = AgentBuilder::new().tls_config(Arc::new(rustls_cli)).build(); 146 | let res = agent 147 | .post(GSA_ENDPOINT) 148 | .set("Content-Type", "text/x-xml-plist") 149 | .set("Accept", "*/*") 150 | .set("User-Agent", "akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0") 151 | .set("X-MMe-Client-Info", &anisette.x_mme_client_info) 152 | .send_string(&buffer) 153 | .unwrap(); 154 | 155 | let res = res.into_string().unwrap(); 156 | 157 | println!("{res}"); 158 | 159 | let res: plist::Dictionary = plist::from_bytes(res.as_bytes()).unwrap(); 160 | let res: plist::Value = res.get("Response").unwrap().to_owned(); 161 | let res = match res { 162 | plist::Value::Dictionary(dict) => dict, 163 | _ => panic!("Invalid response"), 164 | }; 165 | let salt = res.get("s").unwrap().as_data().unwrap(); 166 | let b_pub = res.get("B").unwrap().as_data().unwrap(); 167 | let iters = res.get("i").unwrap().as_signed_integer().unwrap(); 168 | let c = res.get("c").unwrap().as_string().unwrap(); 169 | 170 | let mut password_hasher = sha2::Sha256::new(); 171 | password_hasher.update(&password.as_bytes()); 172 | let hashed_password = password_hasher.finalize(); 173 | 174 | let mut password_buf = [0u8; 32]; 175 | pbkdf2::pbkdf2::>( 176 | &hashed_password, 177 | salt, 178 | iters as u32, 179 | &mut password_buf, 180 | ); 181 | 182 | let verifier: SrpClientVerifier = client 183 | .process_reply(&a, &[], &password_buf, salt, b_pub) 184 | .unwrap(); 185 | 186 | let m = verifier.proof(); 187 | println!("M: {:?}", m); 188 | 189 | #[derive(Debug, Serialize, Deserialize)] 190 | struct ChallengeRequestBody { 191 | #[serde(rename = "M1")] 192 | m: plist::Value, 193 | cpd: plist::Dictionary, 194 | c: String, 195 | #[serde(rename = "o")] 196 | operation: String, 197 | #[serde(rename = "u")] 198 | username: String, 199 | } 200 | 201 | let body = ChallengeRequestBody { 202 | m: plist::Value::Data(m.to_vec()), 203 | c: c.to_string(), 204 | cpd: anisette.to_cpd(), 205 | operation: "complete".to_string(), 206 | username, 207 | }; 208 | 209 | #[derive(Debug, Serialize, Deserialize)] 210 | struct ChallengeRequest { 211 | #[serde(rename = "Header")] 212 | header: RequestHeader, 213 | #[serde(rename = "Request")] 214 | request: ChallengeRequestBody, 215 | } 216 | 217 | let packet = ChallengeRequest { 218 | header, 219 | request: body, 220 | }; 221 | 222 | let mut buffer = Vec::new(); 223 | plist::to_writer_xml(&mut buffer, &packet).unwrap(); 224 | let buffer = String::from_utf8(buffer).unwrap(); 225 | println!("Body: {buffer}"); 226 | 227 | let res = agent 228 | .post(GSA_ENDPOINT) 229 | .set("Content-Type", "text/x-xml-plist") 230 | .set("Accept", "*/*") 231 | .set("User-Agent", "akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0") 232 | .set("X-MMe-Client-Info", &anisette.x_mme_client_info) 233 | .send_string(&buffer) 234 | .unwrap(); 235 | 236 | let res = res.into_string().unwrap(); 237 | 238 | println!("{res}"); 239 | 240 | todo!() 241 | } 242 | } 243 | --------------------------------------------------------------------------------