diff --git a/Cargo.lock b/Cargo.lock index 31e7245..4c62fa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,6 +118,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.10.0" @@ -397,6 +403,30 @@ dependencies = [ "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", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.8.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be645fee2afe89d293b96c19e4456e6ac69520fc9c6b8a58298550138e361ffe" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.5.5" @@ -466,6 +496,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "flate2" version = "1.1.9" @@ -1004,6 +1040,7 @@ dependencies = [ "rootcause", "serde", "serde_json", + "sha1 0.11.0-rc.5", "sha2", "srp", "thiserror 2.0.18", @@ -1011,6 +1048,7 @@ dependencies = [ "tokio-tungstenite", "tracing", "uuid", + "x509-cert", ] [[package]] @@ -1224,6 +1262,15 @@ dependencies = [ "hmac", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -1723,6 +1770,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1" +version = "0.11.0-rc.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.0-rc.11", +] + [[package]] name = "sha2" version = "0.11.0-rc.5" @@ -1777,6 +1835,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "srp" version = "0.7.0-rc.1" @@ -1958,6 +2026,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio" version = "1.49.0" @@ -2156,7 +2245,7 @@ dependencies = [ "rand", "rustls", "rustls-pki-types", - "sha1", + "sha1 0.10.6", "thiserror 2.0.18", "utf-8", ] @@ -2794,6 +2883,18 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x509-cert" +version = "0.3.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e21aad3a769f25f3d2d0cbf30ea8b50a1d602354bd6ab687fad112821608ba6" +dependencies = [ + "const-oid", + "der", + "spki", + "tls_codec", +] + [[package]] name = "yoke" version = "0.8.1" diff --git a/examples/minimal/src/main.rs b/examples/minimal/src/main.rs index 9430ffc..cd33ea5 100644 --- a/examples/minimal/src/main.rs +++ b/examples/minimal/src/main.rs @@ -49,7 +49,7 @@ async fn main() { let mut account = account.unwrap(); - let mut dev_session = DeveloperSession::from_account(&mut account) + let dev_session = DeveloperSession::from_account(&mut account) .await .expect("Failed to create developer session"); @@ -70,26 +70,28 @@ async fn main() { .unwrap() .to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo"); - let builder = SideloaderBuilder::new().team_selection(TeamSelection::Prompt(|teams| { - println!("Please select a team:"); - for (index, team) in teams.iter().enumerate() { - println!( - "{}: {} ({})", - index + 1, - team.name.as_deref().unwrap_or(""), - team.team_id - ); - } - let mut input = String::new(); - std::io::stdin().read_line(&mut input).unwrap(); - let selection = input.trim().parse::().ok()?; - if selection == 0 || selection > teams.len() { - return None; - } - Some(teams[selection - 1].team_id.clone()) - })); + let mut sideloader = SideloaderBuilder::new(dev_session) + .team_selection(TeamSelection::Prompt(|teams| { + println!("Please select a team:"); + for (index, team) in teams.iter().enumerate() { + println!( + "{}: {} ({})", + index + 1, + team.name.as_deref().unwrap_or(""), + team.team_id + ); + } + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + let selection = input.trim().parse::().ok()?; + if selection == 0 || selection > teams.len() { + return None; + } + Some(teams[selection - 1].team_id.clone()) + })) + .build(); - // let result = bu(&provider, &mut dev_session, app_path, &sideload_config).await; + let result = sideloader.install_app(&provider, app_path).await; match result { Ok(_) => println!("App installed successfully"), Err(e) => panic!("Failed to install app: {:?}", e), diff --git a/isideload/Cargo.toml b/isideload/Cargo.toml index 6e39db5..42c4d52 100644 --- a/isideload/Cargo.toml +++ b/isideload/Cargo.toml @@ -25,7 +25,6 @@ async-trait = "0.1.89" serde = "1.0.228" rand = "0.9.2" uuid = {version = "1.20.0", features = ["v4"] } -sha2 = "0.11.0-rc.5" tracing = "0.1.44" tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] } rootcause = "0.11.1" @@ -33,6 +32,8 @@ futures-util = "0.3.31" serde_json = "1.0.149" base64 = "0.22.1" hex = "0.4.3" +sha1 = "0.11.0-rc.5" +sha2 = "0.11.0-rc.5" srp = "0.7.0-rc.1" pbkdf2 = "0.13.0-rc.9" hmac = "0.13.0-rc.5" @@ -41,3 +42,4 @@ aes = "0.9.0-rc.4" aes-gcm = "0.11.0-rc.3" tokio = "1.49.0" keyring = { version = "3.6.3", features = ["apple-native", "linux-native-sync-persistent", "windows-native"], optional = true } +x509-cert = "0.3.0-rc.4" diff --git a/isideload/src/dev/certificates.rs b/isideload/src/dev/certificates.rs index 0b6b2e9..cd485a7 100644 --- a/isideload/src/dev/certificates.rs +++ b/isideload/src/dev/certificates.rs @@ -97,6 +97,33 @@ pub trait CertificatesApi { Ok(certs) } + async fn list_ios_certs( + &mut self, + team: &DeveloperTeam, + ) -> Result, Report> { + let certs = self + .list_all_development_certs(team, DeveloperDeviceType::Ios) + .await?; + + Ok(certs + .into_iter() + .filter(|c| { + if let Some(platform) = &c.certificate_platform { + platform.to_lowercase() == "ios" + } else if let Some(cert_type) = &c.certificate_type { + if let Some(platform) = &cert_type.platform { + platform.to_lowercase() == "ios" + } else { + // I don't know how consistently these field is populated because apple apis are stupid, and I don't want to break things so just assume + true + } + } else { + true + } + }) + .collect()) + } + async fn revoke_development_cert( &mut self, team: &DeveloperTeam, diff --git a/isideload/src/sideload/builder.rs b/isideload/src/sideload/builder.rs index 51a8ea1..c7b4972 100644 --- a/isideload/src/sideload/builder.rs +++ b/isideload/src/sideload/builder.rs @@ -1,6 +1,10 @@ use std::fmt::Display; -use crate::{dev::teams::DeveloperTeam, util::storage::SideloadingStorage}; +use crate::{ + dev::{developer_session::DeveloperSession, teams::DeveloperTeam}, + sideload::sideloader::Sideloader, + util::storage::SideloadingStorage, +}; /// Configuration for selecting a developer team during sideloading /// @@ -23,22 +27,18 @@ impl Display for TeamSelection { } pub struct SideloaderBuilder { - pub team_selection: TeamSelection, - pub storage: Box, -} - -impl Default for SideloaderBuilder { - fn default() -> Self { - SideloaderBuilder { - team_selection: TeamSelection::First, - storage: Box::new(crate::util::storage::new_storage()), - } - } + team_selection: TeamSelection, + storage: Box, + developer_session: DeveloperSession, } impl SideloaderBuilder { - pub fn new() -> Self { - Self::default() + pub fn new(developer_session: DeveloperSession) -> Self { + SideloaderBuilder { + team_selection: TeamSelection::First, + storage: Box::new(crate::util::storage::new_storage()), + developer_session, + } } pub fn team_selection(mut self, selection: TeamSelection) -> Self { @@ -50,4 +50,8 @@ impl SideloaderBuilder { self.storage = storage; self } + + pub fn build(self) -> Sideloader { + Sideloader::new(self.team_selection, self.storage, self.developer_session) + } } diff --git a/isideload/src/sideload/certificate.rs b/isideload/src/sideload/certificate.rs index c56d52b..393c631 100644 --- a/isideload/src/sideload/certificate.rs +++ b/isideload/src/sideload/certificate.rs @@ -1,6 +1,63 @@ +use rootcause::prelude::*; +use tracing::{error, info}; + +use crate::{ + dev::{ + certificates::CertificatesApi, developer_session::DeveloperSession, teams::DeveloperTeam, + }, + util::storage::SideloadingStorage, +}; + pub struct CertificateIdentity { pub machine_id: String, pub machine_name: String, } -impl CertificateIdentity {} +impl CertificateIdentity { + pub async fn retrieve( + machine_name: &str, + developer_session: DeveloperSession, + team: &DeveloperTeam, + storage: &dyn SideloadingStorage, + ) -> Result { + let stored = + Self::retrieve_from_storage(machine_name, developer_session, team, storage).await; + if let Ok(Some(cert)) = stored { + return Ok(cert); + } + + if let Err(e) = stored { + error!("Failed to load certificate from storage: {:?}", e); + } else { + info!("No stored certificate found, generating"); + } + + todo!("generate CSR") + } + + async fn retrieve_from_storage( + machine_name: &str, + developer_session: DeveloperSession, + team: &DeveloperTeam, + storage: &dyn SideloadingStorage, + ) -> Result, Report> { + let cert = storage.retrieve_data("cert")?; + if cert.is_none() { + return Ok(None); + } + let cert = cert.unwrap(); + let private_key = storage.retrieve_data("key")?; + if private_key.is_none() { + return Ok(None); + } + + for cert in developer_session + .list_ios_certs(team) + .await? + .iter() + .filter(|c| c.machine_name.unwrap_or_default() == machine_name) + {} + + Ok(()) + } +} diff --git a/isideload/src/sideload/sideloader.rs b/isideload/src/sideload/sideloader.rs index 904ce4a..a91f3fc 100644 --- a/isideload/src/sideload/sideloader.rs +++ b/isideload/src/sideload/sideloader.rs @@ -15,12 +15,24 @@ use rootcause::prelude::*; use tracing::info; pub struct Sideloader { - pub team_selection: TeamSelection, - pub storage: Box, - pub dev_session: DeveloperSession, + team_selection: TeamSelection, + storage: Box, + dev_session: DeveloperSession, } impl Sideloader { + pub fn new( + team_selection: TeamSelection, + storage: Box, + dev_session: DeveloperSession, + ) -> Self { + Sideloader { + team_selection, + storage, + dev_session, + } + } + pub async fn install_app( &mut self, device_provider: &impl IdeviceProvider,