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 //! A “DICE policy” is a format for setting constraints on a DICE chain. A DICE chain policy
18*3f8e9d82SAndroid Build Coastguard Worker //! verifier takes a policy and a DICE chain, and returns a boolean indicating whether the
19*3f8e9d82SAndroid Build Coastguard Worker //! DICE chain meets the constraints set out on a policy.
20*3f8e9d82SAndroid Build Coastguard Worker //!
21*3f8e9d82SAndroid Build Coastguard Worker //! This forms the foundation of Dice Policy aware Authentication (DPA-Auth), where the server
22*3f8e9d82SAndroid Build Coastguard Worker //! authenticates a client by comparing its dice chain against a set policy.
23*3f8e9d82SAndroid Build Coastguard Worker //!
24*3f8e9d82SAndroid Build Coastguard Worker //! Another use is "sealing", where clients can use an appropriately constructed dice policy to
25*3f8e9d82SAndroid Build Coastguard Worker //! seal a secret. Unsealing is only permitted if dice chain of the component requesting unsealing
26*3f8e9d82SAndroid Build Coastguard Worker //! complies with the policy.
27*3f8e9d82SAndroid Build Coastguard Worker //!
28*3f8e9d82SAndroid Build Coastguard Worker //! A typical policy will assert things like:
29*3f8e9d82SAndroid Build Coastguard Worker //! # DK_pub must have this value
30*3f8e9d82SAndroid Build Coastguard Worker //! # The DICE chain must be exactly five certificates long
31*3f8e9d82SAndroid Build Coastguard Worker //! # authorityHash in the third certificate must have this value
32*3f8e9d82SAndroid Build Coastguard Worker //! securityVersion in the fourth certificate must be an integer greater than 8
33*3f8e9d82SAndroid Build Coastguard Worker //!
34*3f8e9d82SAndroid Build Coastguard Worker //! These constraints used to express policy are (for now) limited to following 2 types:
35*3f8e9d82SAndroid Build Coastguard Worker //! 1. Exact Match: useful for enforcing rules like authority hash should be exactly equal.
36*3f8e9d82SAndroid Build Coastguard Worker //! 2. Greater than or equal to: Useful for setting policies that seal
37*3f8e9d82SAndroid Build Coastguard Worker //! Anti-rollback protected entities (should be accessible to versions >= present).
38*3f8e9d82SAndroid Build Coastguard Worker //!
39*3f8e9d82SAndroid Build Coastguard Worker //! Dice Policy CDDL (keep in sync with DicePolicy.cddl):
40*3f8e9d82SAndroid Build Coastguard Worker //!
41*3f8e9d82SAndroid Build Coastguard Worker //! ```
42*3f8e9d82SAndroid Build Coastguard Worker //! dicePolicy = [
43*3f8e9d82SAndroid Build Coastguard Worker //! 1, ; dice policy version
44*3f8e9d82SAndroid Build Coastguard Worker //! + nodeConstraintList ; for each entry in dice chain
45*3f8e9d82SAndroid Build Coastguard Worker //! ]
46*3f8e9d82SAndroid Build Coastguard Worker //!
47*3f8e9d82SAndroid Build Coastguard Worker //! nodeConstraintList = [
48*3f8e9d82SAndroid Build Coastguard Worker //! * nodeConstraint
49*3f8e9d82SAndroid Build Coastguard Worker //! ]
50*3f8e9d82SAndroid Build Coastguard Worker //!
51*3f8e9d82SAndroid Build Coastguard Worker //! ; We may add a hashConstraint item later
52*3f8e9d82SAndroid Build Coastguard Worker //! nodeConstraint = exactMatchConstraint / geConstraint
53*3f8e9d82SAndroid Build Coastguard Worker //!
54*3f8e9d82SAndroid Build Coastguard Worker //! exactMatchConstraint = [1, keySpec, value]
55*3f8e9d82SAndroid Build Coastguard Worker //! geConstraint = [2, keySpec, int]
56*3f8e9d82SAndroid Build Coastguard Worker //!
57*3f8e9d82SAndroid Build Coastguard Worker //! keySpec = [value+]
58*3f8e9d82SAndroid Build Coastguard Worker //!
59*3f8e9d82SAndroid Build Coastguard Worker //! value = bool / int / tstr / bstr
60*3f8e9d82SAndroid Build Coastguard Worker //! ```
61*3f8e9d82SAndroid Build Coastguard Worker
62*3f8e9d82SAndroid Build Coastguard Worker use ciborium::Value;
63*3f8e9d82SAndroid Build Coastguard Worker use coset::{AsCborValue, CborSerializable, CoseError, CoseError::UnexpectedItem, CoseSign1};
64*3f8e9d82SAndroid Build Coastguard Worker use std::borrow::Cow;
65*3f8e9d82SAndroid Build Coastguard Worker use std::iter::zip;
66*3f8e9d82SAndroid Build Coastguard Worker
67*3f8e9d82SAndroid Build Coastguard Worker type Error = String;
68*3f8e9d82SAndroid Build Coastguard Worker
69*3f8e9d82SAndroid Build Coastguard Worker /// Version of the Dice policy spec
70*3f8e9d82SAndroid Build Coastguard Worker pub const DICE_POLICY_VERSION: u64 = 1;
71*3f8e9d82SAndroid Build Coastguard Worker /// Identifier for `exactMatchConstraint` as per spec
72*3f8e9d82SAndroid Build Coastguard Worker pub const EXACT_MATCH_CONSTRAINT: u16 = 1;
73*3f8e9d82SAndroid Build Coastguard Worker /// Identifier for `geConstraint` as per spec
74*3f8e9d82SAndroid Build Coastguard Worker pub const GREATER_OR_EQUAL_CONSTRAINT: u16 = 2;
75*3f8e9d82SAndroid Build Coastguard Worker
76*3f8e9d82SAndroid Build Coastguard Worker /// Given an Android dice chain, check if it matches the given policy. This method returns
77*3f8e9d82SAndroid Build Coastguard Worker /// Ok(()) in case of successful match, otherwise returns error in case of failure.
chain_matches_policy(dice_chain: &[u8], policy: &[u8]) -> Result<(), Error>78*3f8e9d82SAndroid Build Coastguard Worker pub fn chain_matches_policy(dice_chain: &[u8], policy: &[u8]) -> Result<(), Error> {
79*3f8e9d82SAndroid Build Coastguard Worker DicePolicy::from_slice(policy)
80*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("DicePolicy decoding failed {e:?}"))?
81*3f8e9d82SAndroid Build Coastguard Worker .matches_dice_chain(dice_chain)
82*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("DicePolicy matching failed {e:?}"))?;
83*3f8e9d82SAndroid Build Coastguard Worker Ok(())
84*3f8e9d82SAndroid Build Coastguard Worker }
85*3f8e9d82SAndroid Build Coastguard Worker
86*3f8e9d82SAndroid Build Coastguard Worker // TODO(b/291238565): (nested_)key & value type should be (bool/int/tstr/bstr). Status quo, only
87*3f8e9d82SAndroid Build Coastguard Worker // integer (nested_)key is supported.
88*3f8e9d82SAndroid Build Coastguard Worker // and maybe convert it into struct.
89*3f8e9d82SAndroid Build Coastguard Worker /// Each constraint (on a dice node) is a tuple: (ConstraintType, constraint_path, value)
90*3f8e9d82SAndroid Build Coastguard Worker /// This is Rust equivalent of `nodeConstraint` from CDDL above. Keep in sync!
91*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone, Debug, PartialEq)]
92*3f8e9d82SAndroid Build Coastguard Worker pub struct Constraint(u16, Vec<i64>, Value);
93*3f8e9d82SAndroid Build Coastguard Worker
94*3f8e9d82SAndroid Build Coastguard Worker impl Constraint {
95*3f8e9d82SAndroid Build Coastguard Worker /// Construct a new Constraint
new(constraint_type: u16, path: Vec<i64>, value: Value) -> Result<Self, Error>96*3f8e9d82SAndroid Build Coastguard Worker pub fn new(constraint_type: u16, path: Vec<i64>, value: Value) -> Result<Self, Error> {
97*3f8e9d82SAndroid Build Coastguard Worker if constraint_type != EXACT_MATCH_CONSTRAINT
98*3f8e9d82SAndroid Build Coastguard Worker && constraint_type != GREATER_OR_EQUAL_CONSTRAINT
99*3f8e9d82SAndroid Build Coastguard Worker {
100*3f8e9d82SAndroid Build Coastguard Worker return Err(format!("Invalid Constraint type: {constraint_type}"));
101*3f8e9d82SAndroid Build Coastguard Worker }
102*3f8e9d82SAndroid Build Coastguard Worker Ok(Self(constraint_type, path, value))
103*3f8e9d82SAndroid Build Coastguard Worker }
104*3f8e9d82SAndroid Build Coastguard Worker }
105*3f8e9d82SAndroid Build Coastguard Worker
106*3f8e9d82SAndroid Build Coastguard Worker impl AsCborValue for Constraint {
from_cbor_value(value: Value) -> Result<Self, CoseError>107*3f8e9d82SAndroid Build Coastguard Worker fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
108*3f8e9d82SAndroid Build Coastguard Worker let [constrained_type, constraint_path, val] = value
109*3f8e9d82SAndroid Build Coastguard Worker .into_array()
110*3f8e9d82SAndroid Build Coastguard Worker .map_err(|_| UnexpectedItem("-", "Array"))?
111*3f8e9d82SAndroid Build Coastguard Worker .try_into()
112*3f8e9d82SAndroid Build Coastguard Worker .map_err(|_| UnexpectedItem("Array", "Array of size 3"))?;
113*3f8e9d82SAndroid Build Coastguard Worker let constrained_type: u16 = value_to_integer(&constrained_type)?
114*3f8e9d82SAndroid Build Coastguard Worker .try_into()
115*3f8e9d82SAndroid Build Coastguard Worker .map_err(|_| UnexpectedItem("Integer", "u16"))?;
116*3f8e9d82SAndroid Build Coastguard Worker let path_res: Vec<i64> = constraint_path
117*3f8e9d82SAndroid Build Coastguard Worker .into_array()
118*3f8e9d82SAndroid Build Coastguard Worker .map_err(|_| UnexpectedItem("-", "Array"))?
119*3f8e9d82SAndroid Build Coastguard Worker .iter()
120*3f8e9d82SAndroid Build Coastguard Worker .map(value_to_integer)
121*3f8e9d82SAndroid Build Coastguard Worker .collect::<Result<_, _>>()?;
122*3f8e9d82SAndroid Build Coastguard Worker Ok(Self(constrained_type, path_res, val))
123*3f8e9d82SAndroid Build Coastguard Worker }
124*3f8e9d82SAndroid Build Coastguard Worker
to_cbor_value(self) -> Result<Value, CoseError>125*3f8e9d82SAndroid Build Coastguard Worker fn to_cbor_value(self) -> Result<Value, CoseError> {
126*3f8e9d82SAndroid Build Coastguard Worker Ok(Value::Array(vec![
127*3f8e9d82SAndroid Build Coastguard Worker Value::from(self.0),
128*3f8e9d82SAndroid Build Coastguard Worker Value::Array(self.1.into_iter().map(Value::from).collect()),
129*3f8e9d82SAndroid Build Coastguard Worker self.2,
130*3f8e9d82SAndroid Build Coastguard Worker ]))
131*3f8e9d82SAndroid Build Coastguard Worker }
132*3f8e9d82SAndroid Build Coastguard Worker }
133*3f8e9d82SAndroid Build Coastguard Worker
134*3f8e9d82SAndroid Build Coastguard Worker /// List of all constraints on a dice node.
135*3f8e9d82SAndroid Build Coastguard Worker /// This is Rust equivalent of `nodeConstraintList` in the CDDL above. Keep in sync!
136*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone, Debug, PartialEq)]
137*3f8e9d82SAndroid Build Coastguard Worker pub struct NodeConstraints(pub Box<[Constraint]>);
138*3f8e9d82SAndroid Build Coastguard Worker
139*3f8e9d82SAndroid Build Coastguard Worker impl AsCborValue for NodeConstraints {
from_cbor_value(value: Value) -> Result<Self, CoseError>140*3f8e9d82SAndroid Build Coastguard Worker fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
141*3f8e9d82SAndroid Build Coastguard Worker let res: Vec<Constraint> = value
142*3f8e9d82SAndroid Build Coastguard Worker .into_array()
143*3f8e9d82SAndroid Build Coastguard Worker .map_err(|_| UnexpectedItem("-", "Array"))?
144*3f8e9d82SAndroid Build Coastguard Worker .into_iter()
145*3f8e9d82SAndroid Build Coastguard Worker .map(Constraint::from_cbor_value)
146*3f8e9d82SAndroid Build Coastguard Worker .collect::<Result<_, _>>()?;
147*3f8e9d82SAndroid Build Coastguard Worker if res.is_empty() {
148*3f8e9d82SAndroid Build Coastguard Worker return Err(UnexpectedItem("Empty array", "Non empty array"));
149*3f8e9d82SAndroid Build Coastguard Worker }
150*3f8e9d82SAndroid Build Coastguard Worker Ok(Self(res.into_boxed_slice()))
151*3f8e9d82SAndroid Build Coastguard Worker }
152*3f8e9d82SAndroid Build Coastguard Worker
to_cbor_value(self) -> Result<Value, CoseError>153*3f8e9d82SAndroid Build Coastguard Worker fn to_cbor_value(self) -> Result<Value, CoseError> {
154*3f8e9d82SAndroid Build Coastguard Worker let res: Vec<Value> = self
155*3f8e9d82SAndroid Build Coastguard Worker .0
156*3f8e9d82SAndroid Build Coastguard Worker .into_vec()
157*3f8e9d82SAndroid Build Coastguard Worker .into_iter()
158*3f8e9d82SAndroid Build Coastguard Worker .map(Constraint::to_cbor_value)
159*3f8e9d82SAndroid Build Coastguard Worker .collect::<Result<_, _>>()?;
160*3f8e9d82SAndroid Build Coastguard Worker Ok(Value::Array(res))
161*3f8e9d82SAndroid Build Coastguard Worker }
162*3f8e9d82SAndroid Build Coastguard Worker }
163*3f8e9d82SAndroid Build Coastguard Worker
164*3f8e9d82SAndroid Build Coastguard Worker /// This is Rust equivalent of `dicePolicy` in the CDDL above. Keep in sync!
165*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone, Debug, PartialEq)]
166*3f8e9d82SAndroid Build Coastguard Worker pub struct DicePolicy {
167*3f8e9d82SAndroid Build Coastguard Worker /// Dice policy version
168*3f8e9d82SAndroid Build Coastguard Worker pub version: u64,
169*3f8e9d82SAndroid Build Coastguard Worker /// List of `NodeConstraints`, one for each node of Dice chain.
170*3f8e9d82SAndroid Build Coastguard Worker pub node_constraints_list: Box<[NodeConstraints]>,
171*3f8e9d82SAndroid Build Coastguard Worker }
172*3f8e9d82SAndroid Build Coastguard Worker
173*3f8e9d82SAndroid Build Coastguard Worker impl AsCborValue for DicePolicy {
from_cbor_value(value: Value) -> Result<Self, CoseError>174*3f8e9d82SAndroid Build Coastguard Worker fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
175*3f8e9d82SAndroid Build Coastguard Worker let mut arr = value.into_array().map_err(|_| UnexpectedItem("-", "Array"))?;
176*3f8e9d82SAndroid Build Coastguard Worker if arr.len() < 2 {
177*3f8e9d82SAndroid Build Coastguard Worker return Err(UnexpectedItem("Array", "Array with at least 2 elements"));
178*3f8e9d82SAndroid Build Coastguard Worker }
179*3f8e9d82SAndroid Build Coastguard Worker let (version, node_cons_list) = (value_to_integer(arr.first().unwrap())?, arr.split_off(1));
180*3f8e9d82SAndroid Build Coastguard Worker let version: u64 = version.try_into().map_err(|_| UnexpectedItem("-", "u64"))?;
181*3f8e9d82SAndroid Build Coastguard Worker let node_cons_list: Vec<NodeConstraints> = node_cons_list
182*3f8e9d82SAndroid Build Coastguard Worker .into_iter()
183*3f8e9d82SAndroid Build Coastguard Worker .map(NodeConstraints::from_cbor_value)
184*3f8e9d82SAndroid Build Coastguard Worker .collect::<Result<_, _>>()?;
185*3f8e9d82SAndroid Build Coastguard Worker Ok(Self { version, node_constraints_list: node_cons_list.into_boxed_slice() })
186*3f8e9d82SAndroid Build Coastguard Worker }
187*3f8e9d82SAndroid Build Coastguard Worker
to_cbor_value(self) -> Result<Value, CoseError>188*3f8e9d82SAndroid Build Coastguard Worker fn to_cbor_value(self) -> Result<Value, CoseError> {
189*3f8e9d82SAndroid Build Coastguard Worker let mut res: Vec<Value> = Vec::with_capacity(1 + self.node_constraints_list.len());
190*3f8e9d82SAndroid Build Coastguard Worker res.push(Value::from(self.version));
191*3f8e9d82SAndroid Build Coastguard Worker for node_cons in self.node_constraints_list.into_vec() {
192*3f8e9d82SAndroid Build Coastguard Worker res.push(node_cons.to_cbor_value()?)
193*3f8e9d82SAndroid Build Coastguard Worker }
194*3f8e9d82SAndroid Build Coastguard Worker Ok(Value::Array(res))
195*3f8e9d82SAndroid Build Coastguard Worker }
196*3f8e9d82SAndroid Build Coastguard Worker }
197*3f8e9d82SAndroid Build Coastguard Worker
198*3f8e9d82SAndroid Build Coastguard Worker impl CborSerializable for DicePolicy {}
199*3f8e9d82SAndroid Build Coastguard Worker
200*3f8e9d82SAndroid Build Coastguard Worker impl DicePolicy {
201*3f8e9d82SAndroid Build Coastguard Worker /// Dice chain policy verifier - Compare the input dice chain against this Dice policy.
202*3f8e9d82SAndroid Build Coastguard Worker /// The method returns Ok() if the dice chain meets the constraints set in Dice policy,
203*3f8e9d82SAndroid Build Coastguard Worker /// otherwise returns error in case of mismatch.
204*3f8e9d82SAndroid Build Coastguard Worker /// TODO(b/291238565) Create a separate error module for DicePolicy mismatches.
matches_dice_chain(&self, dice_chain: &[u8]) -> Result<(), Error>205*3f8e9d82SAndroid Build Coastguard Worker pub fn matches_dice_chain(&self, dice_chain: &[u8]) -> Result<(), Error> {
206*3f8e9d82SAndroid Build Coastguard Worker let dice_chain = deserialize_cbor_array(dice_chain)?;
207*3f8e9d82SAndroid Build Coastguard Worker check_is_explicit_key_dice_chain(&dice_chain)?;
208*3f8e9d82SAndroid Build Coastguard Worker if dice_chain.len() != self.node_constraints_list.len() {
209*3f8e9d82SAndroid Build Coastguard Worker return Err(format!(
210*3f8e9d82SAndroid Build Coastguard Worker "Dice chain size({}) does not match policy({})",
211*3f8e9d82SAndroid Build Coastguard Worker dice_chain.len(),
212*3f8e9d82SAndroid Build Coastguard Worker self.node_constraints_list.len()
213*3f8e9d82SAndroid Build Coastguard Worker ));
214*3f8e9d82SAndroid Build Coastguard Worker }
215*3f8e9d82SAndroid Build Coastguard Worker
216*3f8e9d82SAndroid Build Coastguard Worker for (n, (dice_node, node_constraints)) in
217*3f8e9d82SAndroid Build Coastguard Worker zip(dice_chain, self.node_constraints_list.iter()).enumerate()
218*3f8e9d82SAndroid Build Coastguard Worker {
219*3f8e9d82SAndroid Build Coastguard Worker let dice_node_payload = if n <= 1 {
220*3f8e9d82SAndroid Build Coastguard Worker // 1st & 2nd dice node of Explicit-key DiceCertChain format are
221*3f8e9d82SAndroid Build Coastguard Worker // EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION & DiceCertChainInitialPayload. The rest are
222*3f8e9d82SAndroid Build Coastguard Worker // DiceChainEntry which is a CoseSign1.
223*3f8e9d82SAndroid Build Coastguard Worker dice_node
224*3f8e9d82SAndroid Build Coastguard Worker } else {
225*3f8e9d82SAndroid Build Coastguard Worker payload_value_from_cose_sign(dice_node)
226*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("Unable to get Cose payload at {n}: {e:?}"))?
227*3f8e9d82SAndroid Build Coastguard Worker };
228*3f8e9d82SAndroid Build Coastguard Worker check_constraints_on_node(node_constraints, &dice_node_payload)
229*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("Mismatch found at {n}: {e:?}"))?;
230*3f8e9d82SAndroid Build Coastguard Worker }
231*3f8e9d82SAndroid Build Coastguard Worker Ok(())
232*3f8e9d82SAndroid Build Coastguard Worker }
233*3f8e9d82SAndroid Build Coastguard Worker }
234*3f8e9d82SAndroid Build Coastguard Worker
check_constraints_on_node( node_constraints: &NodeConstraints, dice_node: &Value, ) -> Result<(), Error>235*3f8e9d82SAndroid Build Coastguard Worker fn check_constraints_on_node(
236*3f8e9d82SAndroid Build Coastguard Worker node_constraints: &NodeConstraints,
237*3f8e9d82SAndroid Build Coastguard Worker dice_node: &Value,
238*3f8e9d82SAndroid Build Coastguard Worker ) -> Result<(), Error> {
239*3f8e9d82SAndroid Build Coastguard Worker for constraint in node_constraints.0.iter() {
240*3f8e9d82SAndroid Build Coastguard Worker check_constraint_on_node(constraint, dice_node)?;
241*3f8e9d82SAndroid Build Coastguard Worker }
242*3f8e9d82SAndroid Build Coastguard Worker Ok(())
243*3f8e9d82SAndroid Build Coastguard Worker }
244*3f8e9d82SAndroid Build Coastguard Worker
check_constraint_on_node(constraint: &Constraint, dice_node: &Value) -> Result<(), Error>245*3f8e9d82SAndroid Build Coastguard Worker fn check_constraint_on_node(constraint: &Constraint, dice_node: &Value) -> Result<(), Error> {
246*3f8e9d82SAndroid Build Coastguard Worker let Constraint(cons_type, path, value_in_constraint) = constraint;
247*3f8e9d82SAndroid Build Coastguard Worker let value_in_node = lookup_in_nested_container(dice_node, path)?
248*3f8e9d82SAndroid Build Coastguard Worker .ok_or(format!("Value not found for constraint_path {path:?})"))?;
249*3f8e9d82SAndroid Build Coastguard Worker match *cons_type {
250*3f8e9d82SAndroid Build Coastguard Worker EXACT_MATCH_CONSTRAINT => {
251*3f8e9d82SAndroid Build Coastguard Worker if value_in_node != *value_in_constraint {
252*3f8e9d82SAndroid Build Coastguard Worker return Err(format!(
253*3f8e9d82SAndroid Build Coastguard Worker "Policy mismatch. Expected {value_in_constraint:?}; found {value_in_node:?}"
254*3f8e9d82SAndroid Build Coastguard Worker ));
255*3f8e9d82SAndroid Build Coastguard Worker }
256*3f8e9d82SAndroid Build Coastguard Worker }
257*3f8e9d82SAndroid Build Coastguard Worker GREATER_OR_EQUAL_CONSTRAINT => {
258*3f8e9d82SAndroid Build Coastguard Worker let value_in_node = value_in_node
259*3f8e9d82SAndroid Build Coastguard Worker .as_integer()
260*3f8e9d82SAndroid Build Coastguard Worker .ok_or("Mismatch type: expected a CBOR integer".to_string())?;
261*3f8e9d82SAndroid Build Coastguard Worker let value_min = value_in_constraint
262*3f8e9d82SAndroid Build Coastguard Worker .as_integer()
263*3f8e9d82SAndroid Build Coastguard Worker .ok_or("Mismatch type: expected a CBOR integer".to_string())?;
264*3f8e9d82SAndroid Build Coastguard Worker if value_in_node < value_min {
265*3f8e9d82SAndroid Build Coastguard Worker return Err(format!(
266*3f8e9d82SAndroid Build Coastguard Worker "Policy mismatch. Expected >= {value_min:?}; found {value_in_node:?}"
267*3f8e9d82SAndroid Build Coastguard Worker ));
268*3f8e9d82SAndroid Build Coastguard Worker }
269*3f8e9d82SAndroid Build Coastguard Worker }
270*3f8e9d82SAndroid Build Coastguard Worker cons_type => return Err(format!("Unexpected constraint type {cons_type:?}")),
271*3f8e9d82SAndroid Build Coastguard Worker };
272*3f8e9d82SAndroid Build Coastguard Worker Ok(())
273*3f8e9d82SAndroid Build Coastguard Worker }
274*3f8e9d82SAndroid Build Coastguard Worker
275*3f8e9d82SAndroid Build Coastguard Worker /// Lookup value corresponding to constraint path in nested container.
276*3f8e9d82SAndroid Build Coastguard Worker /// This function recursively calls itself.
277*3f8e9d82SAndroid Build Coastguard Worker /// The depth of recursion is limited by the size of constraint_path.
lookup_in_nested_container( container: &Value, constraint_path: &[i64], ) -> Result<Option<Value>, Error>278*3f8e9d82SAndroid Build Coastguard Worker pub fn lookup_in_nested_container(
279*3f8e9d82SAndroid Build Coastguard Worker container: &Value,
280*3f8e9d82SAndroid Build Coastguard Worker constraint_path: &[i64],
281*3f8e9d82SAndroid Build Coastguard Worker ) -> Result<Option<Value>, Error> {
282*3f8e9d82SAndroid Build Coastguard Worker if constraint_path.is_empty() {
283*3f8e9d82SAndroid Build Coastguard Worker return Ok(Some(container.clone()));
284*3f8e9d82SAndroid Build Coastguard Worker }
285*3f8e9d82SAndroid Build Coastguard Worker let explicit_container = get_container_from_value(container)?;
286*3f8e9d82SAndroid Build Coastguard Worker lookup_value_in_container(&explicit_container, constraint_path[0])
287*3f8e9d82SAndroid Build Coastguard Worker .map_or_else(|| Ok(None), |val| lookup_in_nested_container(val, &constraint_path[1..]))
288*3f8e9d82SAndroid Build Coastguard Worker }
289*3f8e9d82SAndroid Build Coastguard Worker
get_container_from_value(container: &Value) -> Result<Container, Error>290*3f8e9d82SAndroid Build Coastguard Worker fn get_container_from_value(container: &Value) -> Result<Container, Error> {
291*3f8e9d82SAndroid Build Coastguard Worker match container {
292*3f8e9d82SAndroid Build Coastguard Worker // Value can be Map/Array/Encoded Map. Encoded Arrays are not yet supported (or required).
293*3f8e9d82SAndroid Build Coastguard Worker // Note: Encoded Map is used for Configuration descriptor entry in DiceChainEntryPayload.
294*3f8e9d82SAndroid Build Coastguard Worker Value::Bytes(b) => Value::from_slice(b)
295*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("{e:?}"))?
296*3f8e9d82SAndroid Build Coastguard Worker .into_map()
297*3f8e9d82SAndroid Build Coastguard Worker .map(|m| Container::Map(Cow::Owned(m)))
298*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("Expected a CBOR map: {:?}", e)),
299*3f8e9d82SAndroid Build Coastguard Worker Value::Map(map) => Ok(Container::Map(Cow::Borrowed(map))),
300*3f8e9d82SAndroid Build Coastguard Worker Value::Array(array) => Ok(Container::Array(array)),
301*3f8e9d82SAndroid Build Coastguard Worker _ => Err(format!("Expected an array/map/bytes {container:?}")),
302*3f8e9d82SAndroid Build Coastguard Worker }
303*3f8e9d82SAndroid Build Coastguard Worker }
304*3f8e9d82SAndroid Build Coastguard Worker
305*3f8e9d82SAndroid Build Coastguard Worker #[derive(Clone)]
306*3f8e9d82SAndroid Build Coastguard Worker enum Container<'a> {
307*3f8e9d82SAndroid Build Coastguard Worker Map(Cow<'a, Vec<(Value, Value)>>),
308*3f8e9d82SAndroid Build Coastguard Worker Array(&'a Vec<Value>),
309*3f8e9d82SAndroid Build Coastguard Worker }
310*3f8e9d82SAndroid Build Coastguard Worker
lookup_value_in_container<'a>(container: &'a Container<'a>, key: i64) -> Option<&'a Value>311*3f8e9d82SAndroid Build Coastguard Worker fn lookup_value_in_container<'a>(container: &'a Container<'a>, key: i64) -> Option<&'a Value> {
312*3f8e9d82SAndroid Build Coastguard Worker match container {
313*3f8e9d82SAndroid Build Coastguard Worker Container::Array(array) => array.get(key as usize),
314*3f8e9d82SAndroid Build Coastguard Worker Container::Map(map) => {
315*3f8e9d82SAndroid Build Coastguard Worker let key = Value::Integer(key.into());
316*3f8e9d82SAndroid Build Coastguard Worker let mut val = None;
317*3f8e9d82SAndroid Build Coastguard Worker for (k, v) in map.iter() {
318*3f8e9d82SAndroid Build Coastguard Worker if k == &key {
319*3f8e9d82SAndroid Build Coastguard Worker val = Some(v);
320*3f8e9d82SAndroid Build Coastguard Worker break;
321*3f8e9d82SAndroid Build Coastguard Worker }
322*3f8e9d82SAndroid Build Coastguard Worker }
323*3f8e9d82SAndroid Build Coastguard Worker val
324*3f8e9d82SAndroid Build Coastguard Worker }
325*3f8e9d82SAndroid Build Coastguard Worker }
326*3f8e9d82SAndroid Build Coastguard Worker }
327*3f8e9d82SAndroid Build Coastguard Worker
328*3f8e9d82SAndroid Build Coastguard Worker /// This library only works with Explicit-key DiceCertChain format. Further we require it to have
329*3f8e9d82SAndroid Build Coastguard Worker /// at least 1 DiceChainEntry. Note that this is a lightweight check so that we fail early for
330*3f8e9d82SAndroid Build Coastguard Worker /// legacy chains.
check_is_explicit_key_dice_chain(dice_chain: &[Value]) -> Result<(), Error>331*3f8e9d82SAndroid Build Coastguard Worker pub fn check_is_explicit_key_dice_chain(dice_chain: &[Value]) -> Result<(), Error> {
332*3f8e9d82SAndroid Build Coastguard Worker if matches!(dice_chain, [Value::Integer(_version), Value::Bytes(_public_key), _entry, ..]) {
333*3f8e9d82SAndroid Build Coastguard Worker Ok(())
334*3f8e9d82SAndroid Build Coastguard Worker } else {
335*3f8e9d82SAndroid Build Coastguard Worker Err("Chain is not in explicit key format".to_string())
336*3f8e9d82SAndroid Build Coastguard Worker }
337*3f8e9d82SAndroid Build Coastguard Worker }
338*3f8e9d82SAndroid Build Coastguard Worker
339*3f8e9d82SAndroid Build Coastguard Worker /// Extract the payload from the COSE Sign
payload_value_from_cose_sign(cbor: Value) -> Result<Value, Error>340*3f8e9d82SAndroid Build Coastguard Worker pub fn payload_value_from_cose_sign(cbor: Value) -> Result<Value, Error> {
341*3f8e9d82SAndroid Build Coastguard Worker let sign1 = CoseSign1::from_cbor_value(cbor)
342*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("Error extracting CoseSign1: {e:?}"))?;
343*3f8e9d82SAndroid Build Coastguard Worker match sign1.payload {
344*3f8e9d82SAndroid Build Coastguard Worker None => Err("Missing payload".to_string()),
345*3f8e9d82SAndroid Build Coastguard Worker Some(payload) => Value::from_slice(&payload).map_err(|e| format!("{e:?}")),
346*3f8e9d82SAndroid Build Coastguard Worker }
347*3f8e9d82SAndroid Build Coastguard Worker }
348*3f8e9d82SAndroid Build Coastguard Worker
349*3f8e9d82SAndroid Build Coastguard Worker /// Decode a CBOR array
deserialize_cbor_array(cbor_array_bytes: &[u8]) -> Result<Vec<Value>, Error>350*3f8e9d82SAndroid Build Coastguard Worker pub fn deserialize_cbor_array(cbor_array_bytes: &[u8]) -> Result<Vec<Value>, Error> {
351*3f8e9d82SAndroid Build Coastguard Worker let cbor_array = Value::from_slice(cbor_array_bytes)
352*3f8e9d82SAndroid Build Coastguard Worker .map_err(|e| format!("Unable to decode top-level CBOR: {e:?}"))?;
353*3f8e9d82SAndroid Build Coastguard Worker let cbor_array =
354*3f8e9d82SAndroid Build Coastguard Worker cbor_array.into_array().map_err(|e| format!("Expected an array found: {e:?}"))?;
355*3f8e9d82SAndroid Build Coastguard Worker Ok(cbor_array)
356*3f8e9d82SAndroid Build Coastguard Worker }
357*3f8e9d82SAndroid Build Coastguard Worker
358*3f8e9d82SAndroid Build Coastguard Worker // Useful to convert [`ciborium::Value`] to integer. Note we already downgrade the returned
359*3f8e9d82SAndroid Build Coastguard Worker // integer to i64 for convenience. Value::Integer is capable of storing bigger numbers.
value_to_integer(value: &Value) -> Result<i64, CoseError>360*3f8e9d82SAndroid Build Coastguard Worker fn value_to_integer(value: &Value) -> Result<i64, CoseError> {
361*3f8e9d82SAndroid Build Coastguard Worker let num = value
362*3f8e9d82SAndroid Build Coastguard Worker .as_integer()
363*3f8e9d82SAndroid Build Coastguard Worker .ok_or(CoseError::UnexpectedItem("-", "Integer"))?
364*3f8e9d82SAndroid Build Coastguard Worker .try_into()
365*3f8e9d82SAndroid Build Coastguard Worker .map_err(|_| CoseError::UnexpectedItem("Integer", "i64"))?;
366*3f8e9d82SAndroid Build Coastguard Worker Ok(num)
367*3f8e9d82SAndroid Build Coastguard Worker }
368