idevice/services/
amfi.rs

1//! Abstraction for Apple Mobile File Integrity
2
3use crate::{Idevice, IdeviceError, IdeviceService, obf};
4
5/// Client for interacting with the AMFI service on the device
6pub struct AmfiClient {
7    /// The underlying device connection with established amfi service
8    pub idevice: Idevice,
9}
10
11impl IdeviceService for AmfiClient {
12    /// Returns the amfi service name as registered with lockdownd
13    fn service_name() -> std::borrow::Cow<'static, str> {
14        obf!("com.apple.amfi.lockdown")
15    }
16
17    async fn from_stream(idevice: Idevice) -> Result<Self, crate::IdeviceError> {
18        Ok(Self::new(idevice))
19    }
20}
21
22impl AmfiClient {
23    /// Creates a new amfi client from an existing device connection
24    ///
25    /// # Arguments
26    /// * `idevice` - Pre-established device connection
27    pub fn new(idevice: Idevice) -> Self {
28        Self { idevice }
29    }
30
31    /// Shows the developer mode option in settings in iOS 18+
32    /// Settings -> Privacy & Security -> Developer Mode
33    pub async fn reveal_developer_mode_option_in_ui(&mut self) -> Result<(), IdeviceError> {
34        let request = crate::plist!({
35            "action": 0,
36        });
37        self.idevice.send_plist(request).await?;
38
39        let res = self.idevice.read_plist().await?;
40        if res.get("success").is_some() {
41            Ok(())
42        } else {
43            Err(IdeviceError::UnexpectedResponse)
44        }
45    }
46
47    /// Enables developer mode, triggering a reboot on iOS 18+
48    pub async fn enable_developer_mode(&mut self) -> Result<(), IdeviceError> {
49        let request = crate::plist!({
50            "action": 1,
51        });
52        self.idevice.send_plist(request).await?;
53
54        let res = self.idevice.read_plist().await?;
55        if res.get("success").is_some() {
56            Ok(())
57        } else {
58            Err(IdeviceError::UnexpectedResponse)
59        }
60    }
61
62    /// Shows the accept dialogue for enabling developer mode
63    pub async fn accept_developer_mode(&mut self) -> Result<(), IdeviceError> {
64        let request = crate::plist!({
65            "action": 2,
66        });
67        self.idevice.send_plist(request).await?;
68
69        let res = self.idevice.read_plist().await?;
70        if res.get("success").is_some() {
71            Ok(())
72        } else {
73            Err(IdeviceError::UnexpectedResponse)
74        }
75    }
76
77    /// Gets the developer mode status
78    pub async fn get_developer_mode_status(&mut self) -> Result<bool, IdeviceError> {
79        let request = crate::plist!({
80            "action": 3,
81        });
82        self.idevice.send_plist(request).await?;
83
84        let res = self.idevice.read_plist().await?;
85        match res.get("success").and_then(|x| x.as_boolean()) {
86            Some(true) => (),
87            _ => return Err(IdeviceError::UnexpectedResponse),
88        }
89
90        match res.get("status").and_then(|x| x.as_boolean()) {
91            Some(b) => Ok(b),
92            _ => Err(IdeviceError::UnexpectedResponse),
93        }
94    }
95
96    /// Trusts an app signer
97    pub async fn trust_app_signer(
98        &mut self,
99        uuid: impl Into<String>,
100    ) -> Result<bool, IdeviceError> {
101        let request = crate::plist!({
102            "action": 4,
103            "input_profile_uuid": uuid.into(),
104        });
105
106        self.idevice.send_plist(request).await?;
107
108        let res = self.idevice.read_plist().await?;
109        match res.get("success").and_then(|x| x.as_boolean()) {
110            Some(true) => (),
111            _ => return Err(IdeviceError::UnexpectedResponse),
112        }
113
114        match res.get("status").and_then(|x| x.as_boolean()) {
115            Some(b) => Ok(b),
116            _ => Err(IdeviceError::UnexpectedResponse),
117        }
118    }
119}