verifyos_cli/parsers/
provisioning_profile.rs1use crate::parsers::plist_reader::InfoPlist;
2use std::path::Path;
3
4#[derive(Debug, thiserror::Error, miette::Diagnostic)]
5pub enum ProvisioningError {
6 #[error("Failed to read provisioning profile: {0}")]
7 Io(#[from] std::io::Error),
8 #[error("Failed to parse provisioning profile plist: {0}")]
9 Plist(#[from] crate::parsers::plist_reader::PlistError),
10 #[error("Provisioning profile plist not found in CMS blob")]
11 PlistNotFound,
12 #[error("Provisioning profile missing Entitlements dictionary")]
13 MissingEntitlements,
14}
15
16#[derive(Debug, Clone)]
17pub struct ProvisioningProfile {
18 pub entitlements: InfoPlist,
19}
20
21impl ProvisioningProfile {
22 pub fn from_embedded_file<P: AsRef<Path>>(path: P) -> Result<Self, ProvisioningError> {
23 let bytes = std::fs::read(path)?;
24 let plist_bytes = extract_plist_bytes(&bytes)?;
25 let profile_plist = InfoPlist::from_bytes(&plist_bytes)?;
26 let entitlements_dict = profile_plist
27 .get_dictionary("Entitlements")
28 .ok_or(ProvisioningError::MissingEntitlements)?;
29
30 Ok(Self {
31 entitlements: InfoPlist::from_dictionary(entitlements_dict.clone()),
32 })
33 }
34}
35
36fn extract_plist_bytes(data: &[u8]) -> Result<Vec<u8>, ProvisioningError> {
37 let start = find_subslice(data, b"<?xml").or_else(|| find_subslice(data, b"<plist"));
38 let end = find_subslice(data, b"</plist>").map(|i| i + b"</plist>".len());
39
40 match (start, end) {
41 (Some(s), Some(e)) if e > s => Ok(data[s..e].to_vec()),
42 _ => Err(ProvisioningError::PlistNotFound),
43 }
44}
45
46fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
47 haystack
48 .windows(needle.len())
49 .position(|window| window == needle)
50}