use crate::{
maps::{IGNORED_PROPERTIES, MANIFEST_PROPERTY_TAG_MAP, OBJECT_TAG_MAP},
signers::Signers,
};
use anyhow::anyhow;
use image4::{der::Encode, property::Value as ManifestValue, Tag};
use plist::Value as PlistValue;
use std::collections::BTreeMap;
fn plist_to_manifest(prop: &PlistValue) -> anyhow::Result<ManifestValue> {
match prop {
PlistValue::Array(_) | PlistValue::Dictionary(_) => {
Err(anyhow!("unexpected constructed property"))
}
&PlistValue::Boolean(v) => Ok(ManifestValue::Boolean(v)),
PlistValue::Data(v) => Ok(ManifestValue::Data(v.clone())),
PlistValue::Date(_) => Err(anyhow!("manifests do not support dates")),
PlistValue::Real(_) => Err(anyhow!("manifests do not support real values")),
PlistValue::Integer(v) => v
.as_unsigned()
.ok_or_else(|| anyhow!("bad integer"))
.map(ManifestValue::Integer),
PlistValue::String(v) => Ok(ManifestValue::Data(v.clone().into_bytes())),
PlistValue::Uid(_) => Err(anyhow!("manifests do not support uids")),
_ => Err(anyhow!("unsupported manifest value")),
}
}
fn handle_req_key_value(
key: &str,
value: &PlistValue,
manifest_props: &mut BTreeMap<Tag, ManifestValue>,
manifest: &mut BTreeMap<Tag, ManifestValue>,
) -> anyhow::Result<()> {
if let Some(tag) = MANIFEST_PROPERTY_TAG_MAP.get(key) {
let value = plist_to_manifest(value)?;
debug!("Adding property \"{key}\" with tag '{tag}' and value {value:?}.");
manifest_props.insert(*tag, value);
} else if let Some(tag) = OBJECT_TAG_MAP.get(key) {
let mut object_props = BTreeMap::new();
let PlistValue::Dictionary(dict) = value else {
return Err(anyhow!("unexpected type for object properties dict"));
};
debug!("Adding object \"{key}\" with tag '{tag}'.");
for (obj_k, obj_v) in dict {
match obj_k.as_str() {
"Digest" => object_props.insert(Tag::from(b"DGST"), plist_to_manifest(obj_v)?),
"Trusted" => object_props.insert(Tag::from(b"EKEY"), plist_to_manifest(obj_v)?),
"EPRO" => object_props.insert(Tag::from(b"EPRO"), plist_to_manifest(obj_v)?),
"ESEC" => object_props.insert(Tag::from(b"ESEC"), plist_to_manifest(obj_v)?),
"BuildString" => None,
other => return Err(anyhow!("unknown object property {other}")),
};
}
manifest.insert(*tag, ManifestValue::Dict(object_props));
} else if IGNORED_PROPERTIES.contains(key) {
trace!("Skipping ignored property {key} = {value:?}")
} else if key.starts_with('@') {
trace!("Skipping optional property {key} = {value:?}.");
} else {
return Err(anyhow!("unknown entry \"{key}\""));
}
Ok(())
}
pub fn append_ap_img4_ticket(
req: &BTreeMap<String, PlistValue>,
resp: &mut BTreeMap<String, PlistValue>,
signers: &Signers,
ticket_name: &str,
) -> anyhow::Result<()> {
let mut manifest: BTreeMap<Tag, ManifestValue> = BTreeMap::new();
let mut manifest_props: BTreeMap<Tag, ManifestValue> = BTreeMap::new();
manifest_props.insert(Tag::from(b"CEPO"), 1u32.into());
manifest_props.insert(Tag::from(b"srvn"), ManifestValue::Data([0; 20].to_vec()));
manifest_props.insert(
Tag::from(b"love"),
ManifestValue::Data(b"22.6.82.0.0,0".to_vec()),
);
for (key, value) in req {
handle_req_key_value(key, value, &mut manifest_props, &mut manifest)?;
}
manifest.insert(Tag::from(b"MANP"), ManifestValue::Dict(manifest_props));
let mut root = BTreeMap::new();
root.insert(Tag::from(b"MANB"), ManifestValue::Dict(manifest));
let signed = if req.contains_key("Ap,LocalPolicy") {
&signers.local_policy
} else {
&signers.ticket
}
.encode_and_sign(&root)?;
let mut encoded = Vec::new();
signed.encode_to_vec(&mut encoded)?;
resp.insert(ticket_name.to_string(), PlistValue::Data(encoded));
Ok(())
}