apple_codesign/
environment_constraints.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Launch constraints and library constraints.

use {
    crate::{
        plist_der::{der_decode_plist, der_encode_plist},
        AppleCodesignError, Result,
    },
    plist::{Dictionary, Value},
    std::path::Path,
};

/// Represents the DER encoded form of environment constraints.
///
/// Instances can be converted into a [Value] using `.into()`.
#[derive(Clone, Debug)]
pub struct EncodedEnvironmentConstraints {
    /// We're not sure what this is.
    ///
    /// Value always appears to be 0.
    pub ccat: u64,

    /// We're not sure what this is.
    ///
    /// Value always appears to be 1.
    ///
    /// We hypothesize it might be a compatibility version number.
    pub comp: u64,

    /// The user-provided constraints, as a mapping.
    pub requirements: Dictionary,

    /// We're not sure what this is.
    ///
    /// Value always appears to be 1.
    ///
    /// We hypothesize it is a version number.
    pub vers: u64,
}

impl Default for EncodedEnvironmentConstraints {
    fn default() -> Self {
        Self {
            ccat: 0,
            comp: 1,
            requirements: Default::default(),
            vers: 1,
        }
    }
}

impl From<EncodedEnvironmentConstraints> for Value {
    fn from(value: EncodedEnvironmentConstraints) -> Self {
        let mut dict = Dictionary::default();

        dict.insert("ccat".into(), value.ccat.into());
        dict.insert("comp".into(), value.comp.into());
        dict.insert("reqs".into(), value.requirements.into());
        dict.insert("vers".into(), value.vers.into());

        dict.into()
    }
}

impl TryFrom<Value> for EncodedEnvironmentConstraints {
    type Error = AppleCodesignError;

    fn try_from(value: Value) -> Result<Self, Self::Error> {
        let mut res = Self::default();

        match value {
            Value::Dictionary(dict) => {
                for (k, v) in dict {
                    match k.as_str() {
                        "ccat" => match v {
                            Value::Integer(v) => {
                                res.ccat = v.as_signed().ok_or_else(|| {
                                    AppleCodesignError::EnvironmentConstraint(
                                        "failed to convert ccat to i64".into(),
                                    )
                                })? as u64;
                            }
                            _ => {
                                return Err(AppleCodesignError::EnvironmentConstraint(
                                    "ccat is not an integer".into(),
                                ));
                            }
                        },
                        "comp" => match v {
                            Value::Integer(v) => {
                                res.comp = v.as_signed().ok_or_else(|| {
                                    AppleCodesignError::EnvironmentConstraint(
                                        "failed to convert comp to i64".into(),
                                    )
                                })? as u64;
                            }
                            _ => {
                                return Err(AppleCodesignError::EnvironmentConstraint(
                                    "comp is not an integer".into(),
                                ));
                            }
                        },
                        "reqs" => match v {
                            Value::Dictionary(v) => {
                                res.requirements = v;
                            }
                            _ => {
                                return Err(AppleCodesignError::EnvironmentConstraint(
                                    "reqs is not a dictionary".into(),
                                ));
                            }
                        },
                        "vers" => match v {
                            Value::Integer(v) => {
                                res.vers = v.as_signed().ok_or_else(|| {
                                    AppleCodesignError::EnvironmentConstraint(
                                        "failed to convert vers to i64".into(),
                                    )
                                })? as u64;
                            }
                            _ => {
                                return Err(AppleCodesignError::EnvironmentConstraint(
                                    "vers is not an integer".into(),
                                ));
                            }
                        },
                        _ => {
                            return Err(AppleCodesignError::EnvironmentConstraint(format!(
                                "unknown key in plist: {}",
                                k
                            )));
                        }
                    }
                }

                Ok(res)
            }
            _ => Err(AppleCodesignError::EnvironmentConstraint(
                "plist value is not a dictionary".to_string(),
            )),
        }
    }
}

impl EncodedEnvironmentConstraints {
    /// Attempt to decode an instance from DER.
    pub fn from_der(data: impl AsRef<[u8]>) -> Result<Self> {
        let value = der_decode_plist(data)?;

        Self::try_from(value)
    }

    /// Obtain an instance from a requirements plist.
    pub fn from_requirements_plist(value: Value) -> Result<Self> {
        match value {
            Value::Dictionary(v) => Ok(Self {
                requirements: v,
                ..Default::default()
            }),
            _ => Err(AppleCodesignError::EnvironmentConstraint(
                "supplied plist is not a dictionary".into(),
            )),
        }
    }

    /// Attempt to construct an instance by reading requirements plist data from a file.
    ///
    /// Source file can be XML or binary encoding.
    pub fn from_requirements_plist_file(path: impl AsRef<Path>) -> Result<Self> {
        let value = Value::from_file(path.as_ref())?;
        Self::from_requirements_plist(value)
    }

    /// Encode the instance to DER.
    pub fn der_encode(&self) -> Result<Vec<u8>> {
        let value = Value::from(self.clone());

        der_encode_plist(&value)
    }

    /// Obtain just the requirements as a plist [Value].
    pub fn requirements_plist(&self) -> Value {
        Value::Dictionary(self.requirements.clone())
    }
}