apple_codesign/
environment_constraints.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Launch constraints and library constraints.
6
7use {
8    crate::{
9        plist_der::{der_decode_plist, der_encode_plist},
10        AppleCodesignError, Result,
11    },
12    plist::{Dictionary, Value},
13    std::path::Path,
14};
15
16/// Represents the DER encoded form of environment constraints.
17///
18/// Instances can be converted into a [Value] using `.into()`.
19#[derive(Clone, Debug)]
20pub struct EncodedEnvironmentConstraints {
21    /// We're not sure what this is.
22    ///
23    /// Value always appears to be 0.
24    pub ccat: u64,
25
26    /// We're not sure what this is.
27    ///
28    /// Value always appears to be 1.
29    ///
30    /// We hypothesize it might be a compatibility version number.
31    pub comp: u64,
32
33    /// The user-provided constraints, as a mapping.
34    pub requirements: Dictionary,
35
36    /// We're not sure what this is.
37    ///
38    /// Value always appears to be 1.
39    ///
40    /// We hypothesize it is a version number.
41    pub vers: u64,
42}
43
44impl Default for EncodedEnvironmentConstraints {
45    fn default() -> Self {
46        Self {
47            ccat: 0,
48            comp: 1,
49            requirements: Default::default(),
50            vers: 1,
51        }
52    }
53}
54
55impl From<EncodedEnvironmentConstraints> for Value {
56    fn from(value: EncodedEnvironmentConstraints) -> Self {
57        let mut dict = Dictionary::default();
58
59        dict.insert("ccat".into(), value.ccat.into());
60        dict.insert("comp".into(), value.comp.into());
61        dict.insert("reqs".into(), value.requirements.into());
62        dict.insert("vers".into(), value.vers.into());
63
64        dict.into()
65    }
66}
67
68impl TryFrom<Value> for EncodedEnvironmentConstraints {
69    type Error = AppleCodesignError;
70
71    fn try_from(value: Value) -> Result<Self, Self::Error> {
72        let mut res = Self::default();
73
74        match value {
75            Value::Dictionary(dict) => {
76                for (k, v) in dict {
77                    match k.as_str() {
78                        "ccat" => match v {
79                            Value::Integer(v) => {
80                                res.ccat = v.as_signed().ok_or_else(|| {
81                                    AppleCodesignError::EnvironmentConstraint(
82                                        "failed to convert ccat to i64".into(),
83                                    )
84                                })? as u64;
85                            }
86                            _ => {
87                                return Err(AppleCodesignError::EnvironmentConstraint(
88                                    "ccat is not an integer".into(),
89                                ));
90                            }
91                        },
92                        "comp" => match v {
93                            Value::Integer(v) => {
94                                res.comp = v.as_signed().ok_or_else(|| {
95                                    AppleCodesignError::EnvironmentConstraint(
96                                        "failed to convert comp to i64".into(),
97                                    )
98                                })? as u64;
99                            }
100                            _ => {
101                                return Err(AppleCodesignError::EnvironmentConstraint(
102                                    "comp is not an integer".into(),
103                                ));
104                            }
105                        },
106                        "reqs" => match v {
107                            Value::Dictionary(v) => {
108                                res.requirements = v;
109                            }
110                            _ => {
111                                return Err(AppleCodesignError::EnvironmentConstraint(
112                                    "reqs is not a dictionary".into(),
113                                ));
114                            }
115                        },
116                        "vers" => match v {
117                            Value::Integer(v) => {
118                                res.vers = v.as_signed().ok_or_else(|| {
119                                    AppleCodesignError::EnvironmentConstraint(
120                                        "failed to convert vers to i64".into(),
121                                    )
122                                })? as u64;
123                            }
124                            _ => {
125                                return Err(AppleCodesignError::EnvironmentConstraint(
126                                    "vers is not an integer".into(),
127                                ));
128                            }
129                        },
130                        _ => {
131                            return Err(AppleCodesignError::EnvironmentConstraint(format!(
132                                "unknown key in plist: {}",
133                                k
134                            )));
135                        }
136                    }
137                }
138
139                Ok(res)
140            }
141            _ => Err(AppleCodesignError::EnvironmentConstraint(
142                "plist value is not a dictionary".to_string(),
143            )),
144        }
145    }
146}
147
148impl EncodedEnvironmentConstraints {
149    /// Attempt to decode an instance from DER.
150    pub fn from_der(data: impl AsRef<[u8]>) -> Result<Self> {
151        let value = der_decode_plist(data)?;
152
153        Self::try_from(value)
154    }
155
156    /// Obtain an instance from a requirements plist.
157    pub fn from_requirements_plist(value: Value) -> Result<Self> {
158        match value {
159            Value::Dictionary(v) => Ok(Self {
160                requirements: v,
161                ..Default::default()
162            }),
163            _ => Err(AppleCodesignError::EnvironmentConstraint(
164                "supplied plist is not a dictionary".into(),
165            )),
166        }
167    }
168
169    /// Attempt to construct an instance by reading requirements plist data from a file.
170    ///
171    /// Source file can be XML or binary encoding.
172    pub fn from_requirements_plist_file(path: impl AsRef<Path>) -> Result<Self> {
173        let value = Value::from_file(path.as_ref())?;
174        Self::from_requirements_plist(value)
175    }
176
177    /// Encode the instance to DER.
178    pub fn der_encode(&self) -> Result<Vec<u8>> {
179        let value = Value::from(self.clone());
180
181        der_encode_plist(&value)
182    }
183
184    /// Obtain just the requirements as a plist [Value].
185    pub fn requirements_plist(&self) -> Value {
186        Value::Dictionary(self.requirements.clone())
187    }
188}