From ec9af89d747820c14f9f83d55d5abbbbe7acf2c9 Mon Sep 17 00:00:00 2001 From: nab138 Date: Sun, 8 Feb 2026 22:43:33 -0500 Subject: [PATCH] Retrieve certificates --- Cargo.lock | 219 ++++++++++++++++++++-- README.md | 6 + examples/minimal/Cargo.toml | 2 +- examples/minimal/src/main.rs | 10 +- isideload/Cargo.toml | 4 +- isideload/src/anisette/remote_v3/state.rs | 2 +- isideload/src/sideload/builder.rs | 42 ++++- isideload/src/sideload/certificate.rs | 140 +++++++++----- isideload/src/sideload/sideloader.rs | 30 ++- isideload/src/util/storage.rs | 1 - 10 files changed, 373 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ffdbcbe..d339c76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ checksum = "04097e08a47d9ad181c2e1f4a5fabc9ae06ce8839a333ba9a949bcb0d31fd2a3" dependencies = [ "cipher", "cpubits", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -43,6 +43,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.100" @@ -164,6 +173,16 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bcder" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7c42c9913f68cf9390a225e81ad56a5c515347287eb98baa710090ca1de86d" +dependencies = [ + "bytes", + "smallvec", +] + [[package]] name = "bitflags" version = "2.10.0" @@ -254,6 +273,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.0", +] + +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link", +] + [[package]] name = "cipher" version = "0.5.0" @@ -307,6 +348,12 @@ version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const-oid" version = "0.10.2" @@ -354,6 +401,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -455,13 +511,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "zeroize", +] + [[package]] name = "der" version = "0.8.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" dependencies = [ - "const-oid", + "const-oid 0.10.2", "pem-rfc7468", "zeroize", ] @@ -506,7 +572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b42f1d9edf5207c137646b568a0168ca0ec25b7f9eaf7f9961da51a3d91cea" dependencies = [ "block-buffer 0.11.0", - "const-oid", + "const-oid 0.10.2", "crypto-common 0.2.0", "subtle", ] @@ -901,6 +967,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -1082,7 +1172,7 @@ dependencies = [ "pbkdf2", "plist", "plist-macro", - "rand", + "rand 0.10.0", "rcgen", "reqwest", "rootcause", @@ -1096,7 +1186,7 @@ dependencies = [ "tokio-tungstenite", "tracing", "uuid", - "x509-parser", + "x509-certificate", ] [[package]] @@ -1403,8 +1493,8 @@ version = "0.8.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" dependencies = [ - "der", - "spki", + "der 0.8.0-rc.10", + "spki 0.8.0-rc.4", ] [[package]] @@ -1413,8 +1503,8 @@ version = "0.11.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b226d2cc389763951db8869584fd800cbbe2962bf454e2edeb5172b31ee99774" dependencies = [ - "der", - "spki", + "der 0.8.0-rc.10", + "spki 0.8.0-rc.4", ] [[package]] @@ -1451,7 +1541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63641a86fddf4b5274f31c43734458ec7acd3133016dbaa37e4e247e1e9acd46" dependencies = [ "cpubits", - "cpufeatures", + "cpufeatures 0.2.17", "universal-hash", ] @@ -1537,7 +1627,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand", + "rand 0.9.2", "ring", "rustc-hash", "rustls", @@ -1588,6 +1678,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom 0.4.1", + "rand_core 0.10.0", +] + [[package]] name = "rand_chacha" version = "0.9.0" @@ -1598,6 +1699,15 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + [[package]] name = "rand_core" version = "0.9.5" @@ -1709,15 +1819,15 @@ version = "0.10.0-rc.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b342b99544549f37509ed7fd42b0cea04bfd9ce07c16ca56094cf0fbeefbbcd" dependencies = [ - "const-oid", + "const-oid 0.10.2", "crypto-bigint", "crypto-primes", "digest 0.11.0-rc.11", "pkcs1", "pkcs8", "rand_core 0.10.0", - "signature", - "spki", + "signature 3.0.0-rc.10", + "spki 0.8.0-rc.4", "zeroize", ] @@ -1935,7 +2045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -1946,7 +2056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.11.0-rc.11", ] @@ -1965,6 +2075,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "signature" version = "3.0.0-rc.10" @@ -2003,6 +2122,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + [[package]] name = "spki" version = "0.8.0-rc.4" @@ -2010,7 +2139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" dependencies = [ "base64ct", - "der", + "der 0.8.0-rc.10", ] [[package]] @@ -2389,7 +2518,7 @@ dependencies = [ "http", "httparse", "log", - "rand", + "rand 0.9.2", "rustls", "rustls-pki-types", "sha1", @@ -2676,6 +2805,41 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -3036,6 +3200,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x509-certificate" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca9eb9a0c822c67129d5b8fcc2806c6bc4f50496b420825069a440669bcfbf7f" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der 0.7.10", + "hex", + "pem", + "ring", + "signature 2.2.0", + "spki 0.7.3", + "thiserror 2.0.18", + "zeroize", +] + [[package]] name = "x509-parser" version = "0.18.1" diff --git a/README.md b/README.md index e5dd7cf..8cf3427 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ A Rust library for sideloading iOS applications using an Apple ID. Used in [Cros This branch is home to isideload-next, the next major version of isideload. It features a redesigned API, improved error handling, better entitlement handling, and more. It is not ready! +## TODO + +- [ ] Signing apps +- [ ] Installing apps +- [ ] Remove dependency on ring + ## Licensing This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 89f7a4d..d469a2a 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -10,4 +10,4 @@ plist-macro = "0.1.3" tokio = { version = "1.49.0", features = ["rt-multi-thread", "macros"] } tracing = "0.1.44" tracing-subscriber = "0.3.22" -idevice = { version = "0.1.52", features = ["usbmuxd"]} \ No newline at end of file +idevice = { version = "0.1.52", features = ["usbmuxd"] } \ No newline at end of file diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index cd33ea5..ee54c99 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -42,10 +42,10 @@ async fn main() { .login(apple_password, get_2fa_code) .await; - match &account { - Ok(a) => println!("Logged in. {}", a), - Err(e) => panic!("Failed to log in to Apple ID: {:?}", e), - } + // match &account { + // Ok(a) => println!("Logged in. {}", a), + // Err(e) => panic!("Failed to log in to Apple ID: {:?}", e), + // } let mut account = account.unwrap(); @@ -70,7 +70,7 @@ async fn main() { .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - let mut sideloader = SideloaderBuilder::new(dev_session) + let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string()) .team_selection(TeamSelection::Prompt(|teams| { println!("Please select a team:"); for (index, team) in teams.iter().enumerate() { diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 798caf2..0a1f44c 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -23,7 +23,7 @@ reqwest = { version = "0.13.1", features = ["json", "gzip"] } thiserror = "2.0.17" async-trait = "0.1.89" serde = "1.0.228" -rand = "0.9.2" +rand = "0.10.0" uuid = {version = "1.20.0", features = ["v4"] } tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } @@ -42,5 +42,5 @@ aes-gcm = "0.11.0-rc.3" rsa = { version = "0.10.0-rc.15" } tokio = "1.49.0" keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } -x509-parser = "0.18.1" +x509-certificate = "0.25" rcgen = { version = "0.14.7", default-features = false, features = ["aws_lc_rs", "pem"] } diff --git a/isideload/src/anisette/remote_v3/state.rs b/isideload/src/anisette/remote_v3/state.rs index 5be5d5a..a2a1a07 100644 --- a/isideload/src/anisette/remote_v3/state.rs +++ b/isideload/src/anisette/remote_v3/state.rs @@ -1,7 +1,7 @@ // Serialization/Desieralization borrowed from https://github.com/SideStore/apple-private-apis/blob/master/omnisette/src/remote_anisette_v3.rs use plist::Data; -use rand::Rng; +use rand::RngExt; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use sha2::{Digest, Sha256}; use uuid::Uuid; diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index c7b4972..c305767 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -26,32 +26,58 @@ impl Display for TeamSelection { } } +pub enum MaxCertsBehavior { + /// If the maximum number of certificates is reached, delete all existing certificates and create a new one + Revoke, + /// If the maximum number of certificates is reached, return an error instead of creating a new certificate + Error, +} + pub struct SideloaderBuilder { - team_selection: TeamSelection, - storage: Box, developer_session: DeveloperSession, + apple_email: String, + team_selection: Option, + max_certs_behavior: Option, + storage: Option>, + machine_name: Option, } impl SideloaderBuilder { - pub fn new(developer_session: DeveloperSession) -> Self { + pub fn new(developer_session: DeveloperSession, apple_email: String) -> Self { SideloaderBuilder { - team_selection: TeamSelection::First, - storage: Box::new(crate::util::storage::new_storage()), + team_selection: None, + storage: None, developer_session, + machine_name: None, + apple_email, + max_certs_behavior: None, } } pub fn team_selection(mut self, selection: TeamSelection) -> Self { - self.team_selection = selection; + self.team_selection = Some(selection); self } pub fn storage(mut self, storage: Box) -> Self { - self.storage = storage; + self.storage = Some(storage); + self + } + + pub fn machine_name(mut self, machine_name: String) -> Self { + self.machine_name = Some(machine_name); self } pub fn build(self) -> Sideloader { - Sideloader::new(self.team_selection, self.storage, self.developer_session) + Sideloader::new( + self.developer_session, + self.apple_email, + self.team_selection.unwrap_or(TeamSelection::First), + self.max_certs_behavior.unwrap_or(MaxCertsBehavior::Revoke), + self.machine_name.unwrap_or_else(|| "isideload".to_string()), + self.storage + .unwrap_or_else(|| Box::new(crate::util::storage::new_storage())), + ) } } diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs index 06bada3..be21d2f 100644 --- a/isideload/src/sideload/certificate.rs +++ b/isideload/src/sideload/certificate.rs @@ -1,22 +1,27 @@ -use rcgen::{CertificateParams, DistinguishedName, DnType, PKCS_RSA_SHA256}; +use rcgen::{CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_RSA_SHA256}; use rootcause::prelude::*; use rsa::{ RsaPrivateKey, - pkcs8::{DecodePrivateKey, EncodePublicKey}, + pkcs1::EncodeRsaPublicKey, + pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, }; + use sha2::{Digest, Sha256}; use tracing::{error, info}; +use x509_certificate::X509Certificate; use crate::{ dev::{ certificates::CertificatesApi, developer_session::DeveloperSession, teams::DeveloperTeam, }, + sideload::builder::MaxCertsBehavior, util::storage::SideloadingStorage, }; pub struct CertificateIdentity { pub machine_id: String, pub machine_name: String, + pub certificate: X509Certificate, } impl CertificateIdentity { @@ -26,70 +31,82 @@ impl CertificateIdentity { developer_session: &mut DeveloperSession, team: &DeveloperTeam, storage: &dyn SideloadingStorage, + max_certs_behavior: &MaxCertsBehavior, ) -> Result { - let stored = Self::retrieve_from_storage( - machine_name, - apple_email, - developer_session, - team, - storage, - ) - .await; - if let Ok(Some(cert)) = stored { + let pr = Self::retrieve_private_key(apple_email, storage).await?; + + let found = Self::find_matching(&pr, machine_name, developer_session, team).await; + if let Ok(Some(cert)) = found { + info!("Found matching certificate"); return Ok(cert); } - if let Err(e) = stored { - error!("Failed to load stored certificate: {:?}", e); - } else { - info!("No stored certificate found"); + if let Err(e) = found { + error!("Failed to check for matching certificate: {:?}", e); } - - todo!("generate CSR") + info!("Requesting new certificate"); + Self::request_certificate( + &pr, + machine_name.to_string(), + developer_session, + team, + max_certs_behavior, + ) + .await } - async fn retrieve_from_storage( - machine_name: &str, + async fn retrieve_private_key( apple_email: &str, - developer_session: &mut DeveloperSession, - team: &DeveloperTeam, storage: &dyn SideloadingStorage, - ) -> Result, Report> { + ) -> Result { let mut hasher = Sha256::new(); hasher.update(apple_email.as_bytes()); let email_hash = hex::encode(hasher.finalize()); let private_key = storage.retrieve(&format!("{}/key", email_hash))?; - if private_key.is_none() { - return Ok(None); + if private_key.is_some() { + return Ok(RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?); } - let private_key = RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?; - let public_key_der = private_key.to_public_key().to_public_key_der()?; + let mut rng = rand::rng(); + let private_key = RsaPrivateKey::new(&mut rng, 2048)?; + storage.store( + &format!("{}/key", email_hash), + &private_key.to_pkcs8_pem(Default::default())?.to_string(), + )?; + + Ok(private_key) + } + + async fn find_matching( + private_key: &RsaPrivateKey, + machine_name: &str, + developer_session: &mut DeveloperSession, + team: &DeveloperTeam, + ) -> Result, Report> { + let public_key_der = private_key + .to_public_key() + .to_pkcs1_der()? + .as_bytes() + .to_vec(); for cert in developer_session .list_ios_certs(team) .await? .iter() .filter(|c| { - c.cert_content.is_some() && c.machine_name.as_deref().unwrap_or("") == machine_name + c.cert_content.is_some() + && c.machine_name.as_deref().unwrap_or("") == machine_name + && c.machine_id.is_some() }) { let x509_cert = - x509_parser::parse_x509_certificate(cert.cert_content.as_ref().unwrap().as_ref())?; - if x509_cert - .1 - .tbs_certificate - .subject_pki - .subject_public_key - .data - == public_key_der.as_ref() - { + X509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?; + + if public_key_der == x509_cert.public_key_data().as_ref() { return Ok(Some(Self { - machine_id: cert - .machine_id - .clone() - .unwrap_or_else(|| x509_cert.1.tbs_certificate.subject.to_string()), - machine_name: machine_name.to_string(), + machine_id: cert.machine_id.clone().unwrap_or_default(), + machine_name: cert.machine_name.clone().unwrap_or_default(), + certificate: x509_cert, })); } } @@ -98,16 +115,46 @@ impl CertificateIdentity { } async fn request_certificate( - machine_name: &str, + private_key: &RsaPrivateKey, + machine_name: String, developer_session: &mut DeveloperSession, team: &DeveloperTeam, + max_certs_behavior: &MaxCertsBehavior, ) -> Result { - Ok(()) + let csr = Self::build_csr(private_key).context("Failed to generate CSR")?; + + let request = developer_session + .submit_development_csr(team, csr, machine_name, None) + .await?; + + // TODO: Handle max certs behavior properly instead of just always revoking + + let apple_certs = developer_session.list_ios_certs(team).await?; + + let apple_cert = apple_certs + .iter() + .find(|c| c.certificate_id == Some(request.cert_request_id.clone())) + .ok_or_else(|| report!("Failed to find certificate after submitting CSR"))?; + + let x509_cert = X509Certificate::from_der( + apple_cert + .cert_content + .as_ref() + .ok_or_else(|| report!("Certificate content missing"))? + .as_ref(), + )?; + + Ok(Self { + machine_id: apple_cert.machine_id.clone().unwrap_or_default(), + machine_name: apple_cert.machine_name.clone().unwrap_or_default(), + certificate: x509_cert, + }) } fn build_csr(private_key: &RsaPrivateKey) -> Result { let mut params = CertificateParams::new(vec![])?; let mut dn = DistinguishedName::new(); + dn.push(DnType::CountryName, "US"); dn.push(DnType::StateOrProvinceName, "STATE"); dn.push(DnType::LocalityName, "LOCAL"); @@ -115,6 +162,11 @@ impl CertificateIdentity { dn.push(DnType::CommonName, "CN"); params.distinguished_name = dn; - Ok(()) + let subject_key = KeyPair::from_pkcs8_pem_and_sign_algo( + &private_key.to_pkcs8_pem(LineEnding::LF)?, + &PKCS_RSA_SHA256, + )?; + + Ok(params.serialize_request(&subject_key)?.pem()?) } } diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index a91f3fc..39bbaed 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -4,7 +4,7 @@ use crate::{ devices::DevicesApi, teams::{DeveloperTeam, TeamsApi}, }, - sideload::TeamSelection, + sideload::{TeamSelection, builder::MaxCertsBehavior, certificate::CertificateIdentity}, util::{device::IdeviceInfo, storage::SideloadingStorage}, }; @@ -18,18 +18,27 @@ pub struct Sideloader { team_selection: TeamSelection, storage: Box, dev_session: DeveloperSession, + machine_name: String, + apple_email: String, + max_certs_behavior: MaxCertsBehavior, } impl Sideloader { pub fn new( - team_selection: TeamSelection, - storage: Box, dev_session: DeveloperSession, + apple_email: String, + team_selection: TeamSelection, + max_certs_behavior: MaxCertsBehavior, + machine_name: String, + storage: Box, ) -> Self { Sideloader { team_selection, storage, dev_session, + machine_name, + apple_email, + max_certs_behavior, } } @@ -46,6 +55,21 @@ impl Sideloader { .ensure_device_registered(&team, &device_info.name, &device_info.udid, None) .await?; + let cert_identity = CertificateIdentity::retrieve( + &self.machine_name, + &self.apple_email, + &mut self.dev_session, + &team, + self.storage.as_ref(), + &self.max_certs_behavior, + ) + .await?; + + // info!( + // "Using certificate for machine {} with ID {}", + // cert_identity.machine_name, cert_identity.machine_id + // ); + Ok(()) } diff --git a/isideload/src/util/storage.rs b/isideload/src/util/storage.rs index 16f7cfd..27fbb03 100644 --- a/isideload/src/util/storage.rs +++ b/isideload/src/util/storage.rs @@ -1,6 +1,5 @@ use std::{collections::HashMap, sync::Mutex}; -use base64::prelude::*; use rootcause::prelude::*; pub trait SideloadingStorage: Send + Sync {