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
16pub struct ProvisioningProfile {
17 pub entitlements: InfoPlist,
18}
19
20impl ProvisioningProfile {
21 pub fn from_embedded_file<P: AsRef<Path>>(path: P) -> Result<Self, ProvisioningError> {
22 let bytes = std::fs::read(path)?;
23 let plist_bytes = extract_plist_bytes(&bytes)?;
24 let profile_plist = InfoPlist::from_bytes(&plist_bytes)?;
25 let entitlements_dict = profile_plist
26 .get_dictionary("Entitlements")
27 .ok_or(ProvisioningError::MissingEntitlements)?;
28
29 Ok(Self {
30 entitlements: InfoPlist::from_dictionary(entitlements_dict.clone()),
31 })
32 }
33}
34
35fn extract_plist_bytes(data: &[u8]) -> Result<Vec<u8>, ProvisioningError> {
36 let start = find_subslice(data, b"<?xml").or_else(|| find_subslice(data, b"<plist"));
37 let end = find_subslice(data, b"</plist>").map(|i| i + b"</plist>".len());
38
39 match (start, end) {
40 (Some(s), Some(e)) if e > s => Ok(data[s..e].to_vec()),
41 _ => Err(ProvisioningError::PlistNotFound),
42 }
43}
44
45fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option<usize> {
46 haystack
47 .windows(needle.len())
48 .position(|window| window == needle)
49}