1*3f8e9d82SAndroid Build Coastguard Worker /*
2*3f8e9d82SAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project
3*3f8e9d82SAndroid Build Coastguard Worker *
4*3f8e9d82SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*3f8e9d82SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*3f8e9d82SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*3f8e9d82SAndroid Build Coastguard Worker *
8*3f8e9d82SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*3f8e9d82SAndroid Build Coastguard Worker *
10*3f8e9d82SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*3f8e9d82SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*3f8e9d82SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*3f8e9d82SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*3f8e9d82SAndroid Build Coastguard Worker * limitations under the License.
15*3f8e9d82SAndroid Build Coastguard Worker */
16*3f8e9d82SAndroid Build Coastguard Worker
17*3f8e9d82SAndroid Build Coastguard Worker //! This library supports constructing Dice Policies on a Dice chains, enabling various ways to
18*3f8e9d82SAndroid Build Coastguard Worker //! specify the constraints. This adheres to the Dice Policy spec at DicePolicy.cddl & works with
19*3f8e9d82SAndroid Build Coastguard Worker //! the rust structs exported by libdice_policy.
20*3f8e9d82SAndroid Build Coastguard Worker
21*3f8e9d82SAndroid Build Coastguard Worker #![allow(missing_docs)] // Sadly needed due to use of enumn::N
22*3f8e9d82SAndroid Build Coastguard Worker
23*3f8e9d82SAndroid Build Coastguard Worker use ciborium::Value;
24*3f8e9d82SAndroid Build Coastguard Worker use dice_policy::{
25*3f8e9d82SAndroid Build Coastguard Worker check_is_explicit_key_dice_chain, deserialize_cbor_array, lookup_in_nested_container,
26*3f8e9d82SAndroid Build Coastguard Worker payload_value_from_cose_sign, Constraint, DicePolicy, NodeConstraints, DICE_POLICY_VERSION,
27*3f8e9d82SAndroid Build Coastguard Worker EXACT_MATCH_CONSTRAINT, GREATER_OR_EQUAL_CONSTRAINT,
28*3f8e9d82SAndroid Build Coastguard Worker };
29*3f8e9d82SAndroid Build Coastguard Worker use enumn::N;
30*3f8e9d82SAndroid Build Coastguard Worker use itertools::Either;
31*3f8e9d82SAndroid Build Coastguard Worker use std::borrow::Cow;
32*3f8e9d82SAndroid Build Coastguard Worker use std::iter::once;
33*3f8e9d82SAndroid Build Coastguard Worker
34*3f8e9d82SAndroid Build Coastguard Worker type Error = String;
35*3f8e9d82SAndroid Build Coastguard Worker
36*3f8e9d82SAndroid Build Coastguard Worker /// Supported version of dice policy spec.
37*3f8e9d82SAndroid Build Coastguard Worker pub const SUPPORTED_DICE_POLICY_VERSION: u64 = 1;
38*3f8e9d82SAndroid Build Coastguard Worker /// Wildcard key to specify all indexes of an array in a nested container. The library only
39*3f8e9d82SAndroid Build Coastguard Worker /// supports 1 Wildcard key per `ConstraintSpec`.
40*3f8e9d82SAndroid Build Coastguard Worker pub const WILDCARD_FULL_ARRAY: i64 = -1;
41*3f8e9d82SAndroid Build Coastguard Worker
42*3f8e9d82SAndroid Build Coastguard Worker const CONFIG_DESC: i64 = -4670548;
43*3f8e9d82SAndroid Build Coastguard Worker const COMPONENT_NAME: i64 = -70002;
44*3f8e9d82SAndroid Build Coastguard Worker const PATH_TO_COMPONENT_NAME: [i64; 2] = [CONFIG_DESC, COMPONENT_NAME];
45*3f8e9d82SAndroid Build Coastguard Worker
46*3f8e9d82SAndroid Build Coastguard Worker /// Constraint Types supported in Dice policy.
47*3f8e9d82SAndroid Build Coastguard Worker #[repr(u16)]
48*3f8e9d82SAndroid Build Coastguard Worker #[non_exhaustive]
49*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug, PartialEq, N)]
50*3f8e9d82SAndroid Build Coastguard Worker pub enum ConstraintType {
51*3f8e9d82SAndroid Build Coastguard Worker /// Enforce exact match criteria, indicating the policy should match
52*3f8e9d82SAndroid Build Coastguard Worker /// if the dice chain has exact same specified values.
53*3f8e9d82SAndroid Build Coastguard Worker ExactMatch = EXACT_MATCH_CONSTRAINT,
54*3f8e9d82SAndroid Build Coastguard Worker /// Enforce Greater than or equal to criteria. When applied on security_version, this
55*3f8e9d82SAndroid Build Coastguard Worker /// can be useful to set policy that matches dice chains with same or upgraded images.
56*3f8e9d82SAndroid Build Coastguard Worker GreaterOrEqual = GREATER_OR_EQUAL_CONSTRAINT,
57*3f8e9d82SAndroid Build Coastguard Worker }
58*3f8e9d82SAndroid Build Coastguard Worker
59*3f8e9d82SAndroid Build Coastguard Worker /// ConstraintSpec is used to specify which constraint type to apply and on which all paths in a
60*3f8e9d82SAndroid Build Coastguard Worker /// DiceChainEntry.
61*3f8e9d82SAndroid Build Coastguard Worker /// See documentation of `policy_for_dice_chain()` for examples.
62*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone)]
63*3f8e9d82SAndroid Build Coastguard Worker pub struct ConstraintSpec {
64*3f8e9d82SAndroid Build Coastguard Worker constraint_type: ConstraintType,
65*3f8e9d82SAndroid Build Coastguard Worker // `path` is essentially a series of indexes (of map keys or array positions) to locate a
66*3f8e9d82SAndroid Build Coastguard Worker // particular value nested inside a DiceChainEntry.
67*3f8e9d82SAndroid Build Coastguard Worker path: Vec<i64>,
68*3f8e9d82SAndroid Build Coastguard Worker // If set to `Ignore`, the constraint is skipped if the `path` is missing in a
69*3f8e9d82SAndroid Build Coastguard Worker // DiceChainEntry while constructing a policy. Note that this does not affect policy
70*3f8e9d82SAndroid Build Coastguard Worker // matching.
71*3f8e9d82SAndroid Build Coastguard Worker if_path_missing: MissingAction,
72*3f8e9d82SAndroid Build Coastguard Worker // Which DiceChainEntry to apply the constraint on.
73*3f8e9d82SAndroid Build Coastguard Worker target_entry: TargetEntry,
74*3f8e9d82SAndroid Build Coastguard Worker }
75*3f8e9d82SAndroid Build Coastguard Worker
76*3f8e9d82SAndroid Build Coastguard Worker impl ConstraintSpec {
77*3f8e9d82SAndroid Build Coastguard Worker /// Constraint the value corresponding to `path`. `constraint_type` specifies how and
78*3f8e9d82SAndroid Build Coastguard Worker /// `if_path_missing` specifies what to do if the `path` is missing in a DiceChainEntry.
new( constraint_type: ConstraintType, path: Vec<i64>, if_path_missing: MissingAction, target_entry: TargetEntry, ) -> Self79*3f8e9d82SAndroid Build Coastguard Worker pub fn new(
80*3f8e9d82SAndroid Build Coastguard Worker constraint_type: ConstraintType,
81*3f8e9d82SAndroid Build Coastguard Worker path: Vec<i64>,
82*3f8e9d82SAndroid Build Coastguard Worker if_path_missing: MissingAction,
83*3f8e9d82SAndroid Build Coastguard Worker target_entry: TargetEntry,
84*3f8e9d82SAndroid Build Coastguard Worker ) -> Self {
85*3f8e9d82SAndroid Build Coastguard Worker Self { constraint_type, path, if_path_missing, target_entry }
86*3f8e9d82SAndroid Build Coastguard Worker }
87*3f8e9d82SAndroid Build Coastguard Worker
has_wildcard_entry(&self) -> bool88*3f8e9d82SAndroid Build Coastguard Worker fn has_wildcard_entry(&self) -> bool {
89*3f8e9d82SAndroid Build Coastguard Worker self.path.contains(&WILDCARD_FULL_ARRAY)
90*3f8e9d82SAndroid Build Coastguard Worker }
91*3f8e9d82SAndroid Build Coastguard Worker
92*3f8e9d82SAndroid Build Coastguard Worker // Expand the ConstrainSpec into an iterable list. This expand to 1 or more specs depending
93*3f8e9d82SAndroid Build Coastguard Worker // on presence of Wildcard entry.
expand( &self, node_payload: &Value, ) -> impl Iterator<Item = Result<Cow<ConstraintSpec>, Error>>94*3f8e9d82SAndroid Build Coastguard Worker fn expand(
95*3f8e9d82SAndroid Build Coastguard Worker &self,
96*3f8e9d82SAndroid Build Coastguard Worker node_payload: &Value,
97*3f8e9d82SAndroid Build Coastguard Worker ) -> impl Iterator<Item = Result<Cow<ConstraintSpec>, Error>> {
98*3f8e9d82SAndroid Build Coastguard Worker if self.has_wildcard_entry() {
99*3f8e9d82SAndroid Build Coastguard Worker expand_wildcard_entry(self, node_payload).map_or_else(
100*3f8e9d82SAndroid Build Coastguard Worker |e| Either::Left(once(Err(e))),
101*3f8e9d82SAndroid Build Coastguard Worker |cs| Either::Right(cs.into_iter().map(|c| Ok(Cow::Owned(c)))),
102*3f8e9d82SAndroid Build Coastguard Worker )
103*3f8e9d82SAndroid Build Coastguard Worker } else {
104*3f8e9d82SAndroid Build Coastguard Worker Either::Left(once(Ok(Cow::Borrowed(self))))
105*3f8e9d82SAndroid Build Coastguard Worker }
106*3f8e9d82SAndroid Build Coastguard Worker }
107*3f8e9d82SAndroid Build Coastguard Worker
use_once(&self) -> bool108*3f8e9d82SAndroid Build Coastguard Worker fn use_once(&self) -> bool {
109*3f8e9d82SAndroid Build Coastguard Worker matches!(self.target_entry, TargetEntry::ByName(_))
110*3f8e9d82SAndroid Build Coastguard Worker }
111*3f8e9d82SAndroid Build Coastguard Worker }
112*3f8e9d82SAndroid Build Coastguard Worker
113*3f8e9d82SAndroid Build Coastguard Worker /// Indicates the DiceChainEntry in the chain. Note this covers only DiceChainEntries in
114*3f8e9d82SAndroid Build Coastguard Worker /// ExplicitKeyDiceCertChain, ie, this does not include the first 2 dice nodes.
115*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone, PartialEq, Eq, Debug)]
116*3f8e9d82SAndroid Build Coastguard Worker pub enum TargetEntry {
117*3f8e9d82SAndroid Build Coastguard Worker /// All Entries
118*3f8e9d82SAndroid Build Coastguard Worker All,
119*3f8e9d82SAndroid Build Coastguard Worker /// Specify a particular DiceChain entry by component name. Lookup is done from the end of the
120*3f8e9d82SAndroid Build Coastguard Worker /// chain, i.e., the last entry with given component_name will be targeted.
121*3f8e9d82SAndroid Build Coastguard Worker ByName(String),
122*3f8e9d82SAndroid Build Coastguard Worker }
123*3f8e9d82SAndroid Build Coastguard Worker
124*3f8e9d82SAndroid Build Coastguard Worker // Extract the component name from the DiceChainEntry, if present.
try_extract_component_name(node_payload: &Value) -> Option<String>125*3f8e9d82SAndroid Build Coastguard Worker fn try_extract_component_name(node_payload: &Value) -> Option<String> {
126*3f8e9d82SAndroid Build Coastguard Worker let component_name = lookup_in_nested_container(node_payload, &PATH_TO_COMPONENT_NAME)
127*3f8e9d82SAndroid Build Coastguard Worker .unwrap_or_else(|e| {
128*3f8e9d82SAndroid Build Coastguard Worker log::warn!("Lookup for component_name in the node failed {e:?}- ignoring!");
129*3f8e9d82SAndroid Build Coastguard Worker None
130*3f8e9d82SAndroid Build Coastguard Worker })?;
131*3f8e9d82SAndroid Build Coastguard Worker component_name.into_text().ok()
132*3f8e9d82SAndroid Build Coastguard Worker }
133*3f8e9d82SAndroid Build Coastguard Worker
is_target_node(node_payload: &Value, spec: &ConstraintSpec) -> bool134*3f8e9d82SAndroid Build Coastguard Worker fn is_target_node(node_payload: &Value, spec: &ConstraintSpec) -> bool {
135*3f8e9d82SAndroid Build Coastguard Worker match &spec.target_entry {
136*3f8e9d82SAndroid Build Coastguard Worker TargetEntry::All => true,
137*3f8e9d82SAndroid Build Coastguard Worker TargetEntry::ByName(name) => {
138*3f8e9d82SAndroid Build Coastguard Worker Some(name) == try_extract_component_name(node_payload).as_ref()
139*3f8e9d82SAndroid Build Coastguard Worker }
140*3f8e9d82SAndroid Build Coastguard Worker }
141*3f8e9d82SAndroid Build Coastguard Worker }
142*3f8e9d82SAndroid Build Coastguard Worker
143*3f8e9d82SAndroid Build Coastguard Worker // Position of the first index marked `WILDCARD_FULL_ARRAY` in path.
wildcard_position(path: &[i64]) -> Result<usize, Error>144*3f8e9d82SAndroid Build Coastguard Worker fn wildcard_position(path: &[i64]) -> Result<usize, Error> {
145*3f8e9d82SAndroid Build Coastguard Worker let Some(pos) = path.iter().position(|&key| key == WILDCARD_FULL_ARRAY) else {
146*3f8e9d82SAndroid Build Coastguard Worker return Err("InvalidInput: Path does not have wildcard key".to_string());
147*3f8e9d82SAndroid Build Coastguard Worker };
148*3f8e9d82SAndroid Build Coastguard Worker Ok(pos)
149*3f8e9d82SAndroid Build Coastguard Worker }
150*3f8e9d82SAndroid Build Coastguard Worker
151*3f8e9d82SAndroid Build Coastguard Worker // Size of array at specified `path` in the nested container.
size_of_array_in_nested_container(container: &Value, path: &[i64]) -> Result<usize, Error>152*3f8e9d82SAndroid Build Coastguard Worker fn size_of_array_in_nested_container(container: &Value, path: &[i64]) -> Result<usize, Error> {
153*3f8e9d82SAndroid Build Coastguard Worker if let Some(Value::Array(array)) = lookup_in_nested_container(container, path)? {
154*3f8e9d82SAndroid Build Coastguard Worker Ok(array.len())
155*3f8e9d82SAndroid Build Coastguard Worker } else {
156*3f8e9d82SAndroid Build Coastguard Worker Err("InternalError: Expected nested array not found".to_string())
157*3f8e9d82SAndroid Build Coastguard Worker }
158*3f8e9d82SAndroid Build Coastguard Worker }
159*3f8e9d82SAndroid Build Coastguard Worker
160*3f8e9d82SAndroid Build Coastguard Worker // Expand `ConstraintSpec` containing `WILDCARD_FULL_ARRAY` to entries with path covering each
161*3f8e9d82SAndroid Build Coastguard Worker // array index.
expand_wildcard_entry( spec: &ConstraintSpec, target_node: &Value, ) -> Result<Vec<ConstraintSpec>, Error>162*3f8e9d82SAndroid Build Coastguard Worker fn expand_wildcard_entry(
163*3f8e9d82SAndroid Build Coastguard Worker spec: &ConstraintSpec,
164*3f8e9d82SAndroid Build Coastguard Worker target_node: &Value,
165*3f8e9d82SAndroid Build Coastguard Worker ) -> Result<Vec<ConstraintSpec>, Error> {
166*3f8e9d82SAndroid Build Coastguard Worker let pos = wildcard_position(&spec.path)?;
167*3f8e9d82SAndroid Build Coastguard Worker // Notice depth of the array in nested container is same as the position in path.
168*3f8e9d82SAndroid Build Coastguard Worker let size = size_of_array_in_nested_container(target_node, &spec.path[..pos])?;
169*3f8e9d82SAndroid Build Coastguard Worker Ok((0..size as i64).map(|i| replace_key(spec, pos, i)).collect())
170*3f8e9d82SAndroid Build Coastguard Worker }
171*3f8e9d82SAndroid Build Coastguard Worker
172*3f8e9d82SAndroid Build Coastguard Worker // Get a ConstraintSpec same as `spec` except for changing the key at `pos` in its `path`
173*3f8e9d82SAndroid Build Coastguard Worker // This method panics if `pos` in out of index of path.
replace_key(spec: &ConstraintSpec, pos: usize, new_key: i64) -> ConstraintSpec174*3f8e9d82SAndroid Build Coastguard Worker fn replace_key(spec: &ConstraintSpec, pos: usize, new_key: i64) -> ConstraintSpec {
175*3f8e9d82SAndroid Build Coastguard Worker let mut spec: ConstraintSpec = spec.clone();
176*3f8e9d82SAndroid Build Coastguard Worker spec.path[pos] = new_key;
177*3f8e9d82SAndroid Build Coastguard Worker spec
178*3f8e9d82SAndroid Build Coastguard Worker }
179*3f8e9d82SAndroid Build Coastguard Worker
180*3f8e9d82SAndroid Build Coastguard Worker /// Used to define a `ConstraintSpec` requirement. This allows client to set behavior of
181*3f8e9d82SAndroid Build Coastguard Worker /// policy construction in case the `path` is missing in a DiceChainEntry.
182*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone, Eq, PartialEq)]
183*3f8e9d82SAndroid Build Coastguard Worker pub enum MissingAction {
184*3f8e9d82SAndroid Build Coastguard Worker /// Indicates that a constraint `path` is required in each DiceChainEntry. Fail if missing!
185*3f8e9d82SAndroid Build Coastguard Worker Fail,
186*3f8e9d82SAndroid Build Coastguard Worker /// Indicates that a constraint `path` may be missing in some DiceChainEntry. Ignore!
187*3f8e9d82SAndroid Build Coastguard Worker Ignore,
188*3f8e9d82SAndroid Build Coastguard Worker }
189*3f8e9d82SAndroid Build Coastguard Worker
190*3f8e9d82SAndroid Build Coastguard Worker /// Construct a dice policy on a given dice chain.
191*3f8e9d82SAndroid Build Coastguard Worker /// This can be used by clients to construct a policy to seal secrets.
192*3f8e9d82SAndroid Build Coastguard Worker /// Constraints on all but (first & second) dice nodes is applied using constraint_spec
193*3f8e9d82SAndroid Build Coastguard Worker /// argument. For the first and second node (version and root public key), the constraint is
194*3f8e9d82SAndroid Build Coastguard Worker /// ExactMatch of the whole node.
195*3f8e9d82SAndroid Build Coastguard Worker ///
196*3f8e9d82SAndroid Build Coastguard Worker /// # Arguments
197*3f8e9d82SAndroid Build Coastguard Worker /// `dice_chain`: The serialized CBOR encoded Dice chain, adhering to Explicit-key
198*3f8e9d82SAndroid Build Coastguard Worker /// DiceCertChain format. See definition at ExplicitKeyDiceCertChain.cddl
199*3f8e9d82SAndroid Build Coastguard Worker ///
200*3f8e9d82SAndroid Build Coastguard Worker /// `constraint_spec`: List of constraints to be applied on dice node.
201*3f8e9d82SAndroid Build Coastguard Worker /// Each constraint is a ConstraintSpec object.
202*3f8e9d82SAndroid Build Coastguard Worker ///
203*3f8e9d82SAndroid Build Coastguard Worker /// Note: Dice node is treated as a nested structure (map or array) (& so the lookup is done in
204*3f8e9d82SAndroid Build Coastguard Worker /// that fashion).
205*3f8e9d82SAndroid Build Coastguard Worker ///
206*3f8e9d82SAndroid Build Coastguard Worker /// Examples of constraint_spec:
207*3f8e9d82SAndroid Build Coastguard Worker /// 1. For exact_match on auth_hash & greater_or_equal on security_version
208*3f8e9d82SAndroid Build Coastguard Worker /// ```
209*3f8e9d82SAndroid Build Coastguard Worker /// constraint_spec =[
210*3f8e9d82SAndroid Build Coastguard Worker /// (ConstraintType::ExactMatch, vec![AUTHORITY_HASH]),
211*3f8e9d82SAndroid Build Coastguard Worker /// (ConstraintType::GreaterOrEqual, vec![CONFIG_DESC, COMPONENT_NAME]),
212*3f8e9d82SAndroid Build Coastguard Worker /// ];
213*3f8e9d82SAndroid Build Coastguard Worker /// ```
214*3f8e9d82SAndroid Build Coastguard Worker ///
215*3f8e9d82SAndroid Build Coastguard Worker /// 2. For hypothetical (and highly simplified) dice chain:
216*3f8e9d82SAndroid Build Coastguard Worker ///
217*3f8e9d82SAndroid Build Coastguard Worker /// ```
218*3f8e9d82SAndroid Build Coastguard Worker /// [1, ROT_KEY, [{1 : 'a', 2 : {200 : 5, 201 : 'b'}}]]
219*3f8e9d82SAndroid Build Coastguard Worker /// The following can be used
220*3f8e9d82SAndroid Build Coastguard Worker /// constraint_spec =[
221*3f8e9d82SAndroid Build Coastguard Worker /// ConstraintSpec(ConstraintType::ExactMatch, vec![1]), // exact_matches value 'a'
222*3f8e9d82SAndroid Build Coastguard Worker /// ConstraintSpec(ConstraintType::GreaterOrEqual, vec![2, 200]),// matches any value >= 5
223*3f8e9d82SAndroid Build Coastguard Worker /// ];
224*3f8e9d82SAndroid Build Coastguard Worker /// ```
policy_for_dice_chain( explicit_key_dice_chain: &[u8], mut constraint_spec: Vec<ConstraintSpec>, ) -> Result<DicePolicy, Error>225*3f8e9d82SAndroid Build Coastguard Worker pub fn policy_for_dice_chain(
226*3f8e9d82SAndroid Build Coastguard Worker explicit_key_dice_chain: &[u8],
227*3f8e9d82SAndroid Build Coastguard Worker mut constraint_spec: Vec<ConstraintSpec>,
228*3f8e9d82SAndroid Build Coastguard Worker ) -> Result<DicePolicy, Error> {
229*3f8e9d82SAndroid Build Coastguard Worker let dice_chain = deserialize_cbor_array(explicit_key_dice_chain)?;
230*3f8e9d82SAndroid Build Coastguard Worker check_is_explicit_key_dice_chain(&dice_chain)?;
231*3f8e9d82SAndroid Build Coastguard Worker let mut constraints_list: Vec<NodeConstraints> = Vec::with_capacity(dice_chain.len());
232*3f8e9d82SAndroid Build Coastguard Worker let chain_entries_len = dice_chain.len() - 2;
233*3f8e9d82SAndroid Build Coastguard Worker // Iterate on the reversed DICE chain! This is important because some constraint_spec
234*3f8e9d82SAndroid Build Coastguard Worker // (with `TargetEntry::ByType`) are applied on the last DiceChainEntry matching the spec.
235*3f8e9d82SAndroid Build Coastguard Worker let mut it = dice_chain.into_iter().rev();
236*3f8e9d82SAndroid Build Coastguard Worker for i in 0..chain_entries_len {
237*3f8e9d82SAndroid Build Coastguard Worker let entry = payload_value_from_cose_sign(it.next().unwrap())
238*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("Unable to get Cose payload at pos {i} from end: {e:?}"))?;
239*3f8e9d82SAndroid Build Coastguard Worker constraints_list.push(constraints_on_dice_node(entry, &mut constraint_spec).map_err(
240*3f8e9d82SAndroid Build Coastguard Worker |e| format!("Unable to get constraints for payload at {i} from end: {e:?}"),
241*3f8e9d82SAndroid Build Coastguard Worker )?);
242*3f8e9d82SAndroid Build Coastguard Worker }
243*3f8e9d82SAndroid Build Coastguard Worker
244*3f8e9d82SAndroid Build Coastguard Worker // 1st & 2nd dice node of Explicit-key DiceCertChain format are
245*3f8e9d82SAndroid Build Coastguard Worker // EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION & DiceCertChainInitialPayload.
246*3f8e9d82SAndroid Build Coastguard Worker constraints_list.push(NodeConstraints(Box::new([Constraint::new(
247*3f8e9d82SAndroid Build Coastguard Worker ConstraintType::ExactMatch as u16,
248*3f8e9d82SAndroid Build Coastguard Worker Vec::new(),
249*3f8e9d82SAndroid Build Coastguard Worker it.next().unwrap(),
250*3f8e9d82SAndroid Build Coastguard Worker )?])));
251*3f8e9d82SAndroid Build Coastguard Worker
252*3f8e9d82SAndroid Build Coastguard Worker constraints_list.push(NodeConstraints(Box::new([Constraint::new(
253*3f8e9d82SAndroid Build Coastguard Worker ConstraintType::ExactMatch as u16,
254*3f8e9d82SAndroid Build Coastguard Worker Vec::new(),
255*3f8e9d82SAndroid Build Coastguard Worker it.next().unwrap(),
256*3f8e9d82SAndroid Build Coastguard Worker )?])));
257*3f8e9d82SAndroid Build Coastguard Worker
258*3f8e9d82SAndroid Build Coastguard Worker constraints_list.reverse();
259*3f8e9d82SAndroid Build Coastguard Worker Ok(DicePolicy {
260*3f8e9d82SAndroid Build Coastguard Worker version: DICE_POLICY_VERSION,
261*3f8e9d82SAndroid Build Coastguard Worker node_constraints_list: constraints_list.into_boxed_slice(),
262*3f8e9d82SAndroid Build Coastguard Worker })
263*3f8e9d82SAndroid Build Coastguard Worker }
264*3f8e9d82SAndroid Build Coastguard Worker
265*3f8e9d82SAndroid Build Coastguard Worker // Take the ['node_payload'] of a dice node & construct the [`NodeConstraints`] on it. If the value
266*3f8e9d82SAndroid Build Coastguard Worker // corresponding to the a [`constraint_spec`] is not present in payload & iff it is marked
267*3f8e9d82SAndroid Build Coastguard Worker // `MissingAction::Ignore`, the corresponding constraint will be missing from the NodeConstraints.
268*3f8e9d82SAndroid Build Coastguard Worker // Not all constraint_spec applies to all DiceChainEntries, see `TargetEntry::ByName`.
constraints_on_dice_node( node_payload: Value, constraint_spec: &mut Vec<ConstraintSpec>, ) -> Result<NodeConstraints, Error>269*3f8e9d82SAndroid Build Coastguard Worker fn constraints_on_dice_node(
270*3f8e9d82SAndroid Build Coastguard Worker node_payload: Value,
271*3f8e9d82SAndroid Build Coastguard Worker constraint_spec: &mut Vec<ConstraintSpec>,
272*3f8e9d82SAndroid Build Coastguard Worker ) -> Result<NodeConstraints, Error> {
273*3f8e9d82SAndroid Build Coastguard Worker let mut node_constraints: Vec<Constraint> = Vec::new();
274*3f8e9d82SAndroid Build Coastguard Worker let constraint_spec_with_retention_marker =
275*3f8e9d82SAndroid Build Coastguard Worker constraint_spec.iter().map(|c| (c.clone(), is_target_node(&node_payload, c)));
276*3f8e9d82SAndroid Build Coastguard Worker
277*3f8e9d82SAndroid Build Coastguard Worker for (constraint_item, is_target) in constraint_spec_with_retention_marker.clone() {
278*3f8e9d82SAndroid Build Coastguard Worker if !is_target {
279*3f8e9d82SAndroid Build Coastguard Worker continue;
280*3f8e9d82SAndroid Build Coastguard Worker }
281*3f8e9d82SAndroid Build Coastguard Worker // Some constraint spec may have wildcard entries, expand those!
282*3f8e9d82SAndroid Build Coastguard Worker for constraint_item_expanded in constraint_item.expand(&node_payload) {
283*3f8e9d82SAndroid Build Coastguard Worker let constraint_item_expanded = constraint_item_expanded?;
284*3f8e9d82SAndroid Build Coastguard Worker if let Some(constraint) =
285*3f8e9d82SAndroid Build Coastguard Worker constraint_on_dice_node(&node_payload, &constraint_item_expanded)?
286*3f8e9d82SAndroid Build Coastguard Worker {
287*3f8e9d82SAndroid Build Coastguard Worker node_constraints.push(constraint);
288*3f8e9d82SAndroid Build Coastguard Worker }
289*3f8e9d82SAndroid Build Coastguard Worker }
290*3f8e9d82SAndroid Build Coastguard Worker }
291*3f8e9d82SAndroid Build Coastguard Worker
292*3f8e9d82SAndroid Build Coastguard Worker *constraint_spec = constraint_spec_with_retention_marker
293*3f8e9d82SAndroid Build Coastguard Worker .filter_map(|(c, is_target)| if is_target && c.use_once() { None } else { Some(c) })
294*3f8e9d82SAndroid Build Coastguard Worker .collect();
295*3f8e9d82SAndroid Build Coastguard Worker Ok(NodeConstraints(node_constraints.into_boxed_slice()))
296*3f8e9d82SAndroid Build Coastguard Worker }
297*3f8e9d82SAndroid Build Coastguard Worker
constraint_on_dice_node( node_payload: &Value, constraint_spec: &ConstraintSpec, ) -> Result<Option<Constraint>, Error>298*3f8e9d82SAndroid Build Coastguard Worker fn constraint_on_dice_node(
299*3f8e9d82SAndroid Build Coastguard Worker node_payload: &Value,
300*3f8e9d82SAndroid Build Coastguard Worker constraint_spec: &ConstraintSpec,
301*3f8e9d82SAndroid Build Coastguard Worker ) -> Result<Option<Constraint>, Error> {
302*3f8e9d82SAndroid Build Coastguard Worker if constraint_spec.path.is_empty() {
303*3f8e9d82SAndroid Build Coastguard Worker return Err("Expected non-empty key spec".to_string());
304*3f8e9d82SAndroid Build Coastguard Worker }
305*3f8e9d82SAndroid Build Coastguard Worker let constraint = match lookup_in_nested_container(node_payload, &constraint_spec.path) {
306*3f8e9d82SAndroid Build Coastguard Worker Ok(Some(val)) => Some(Constraint::new(
307*3f8e9d82SAndroid Build Coastguard Worker constraint_spec.constraint_type as u16,
308*3f8e9d82SAndroid Build Coastguard Worker constraint_spec.path.to_vec(),
309*3f8e9d82SAndroid Build Coastguard Worker val,
310*3f8e9d82SAndroid Build Coastguard Worker )?),
311*3f8e9d82SAndroid Build Coastguard Worker Ok(None) => {
312*3f8e9d82SAndroid Build Coastguard Worker if constraint_spec.if_path_missing == MissingAction::Ignore {
313*3f8e9d82SAndroid Build Coastguard Worker log::warn!("Value not found for {:?}, - skipping!", constraint_spec.path);
314*3f8e9d82SAndroid Build Coastguard Worker None
315*3f8e9d82SAndroid Build Coastguard Worker } else {
316*3f8e9d82SAndroid Build Coastguard Worker return Err(format!(
317*3f8e9d82SAndroid Build Coastguard Worker "Value not found for : {:?}, constraint\
318*3f8e9d82SAndroid Build Coastguard Worker spec is marked to fail on missing path",
319*3f8e9d82SAndroid Build Coastguard Worker constraint_spec.path
320*3f8e9d82SAndroid Build Coastguard Worker ));
321*3f8e9d82SAndroid Build Coastguard Worker }
322*3f8e9d82SAndroid Build Coastguard Worker }
323*3f8e9d82SAndroid Build Coastguard Worker Err(e) => {
324*3f8e9d82SAndroid Build Coastguard Worker if constraint_spec.if_path_missing == MissingAction::Ignore {
325*3f8e9d82SAndroid Build Coastguard Worker log::warn!(
326*3f8e9d82SAndroid Build Coastguard Worker "Error ({e:?}) getting Value for {:?}, - skipping!",
327*3f8e9d82SAndroid Build Coastguard Worker constraint_spec.path
328*3f8e9d82SAndroid Build Coastguard Worker );
329*3f8e9d82SAndroid Build Coastguard Worker None
330*3f8e9d82SAndroid Build Coastguard Worker } else {
331*3f8e9d82SAndroid Build Coastguard Worker return Err(format!(
332*3f8e9d82SAndroid Build Coastguard Worker "Error ({e:?}) getting Value for {:?}, constraint\
333*3f8e9d82SAndroid Build Coastguard Worker spec is marked to fail on missing path",
334*3f8e9d82SAndroid Build Coastguard Worker constraint_spec.path
335*3f8e9d82SAndroid Build Coastguard Worker ));
336*3f8e9d82SAndroid Build Coastguard Worker }
337*3f8e9d82SAndroid Build Coastguard Worker }
338*3f8e9d82SAndroid Build Coastguard Worker };
339*3f8e9d82SAndroid Build Coastguard Worker Ok(constraint)
340*3f8e9d82SAndroid Build Coastguard Worker }
341