1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! This library exposes [`PolicyGatedStorage`] and related traits that are useful for
18 //! Secretkeeper service implementation.
19 
20 use alloc::boxed::Box;
21 use alloc::vec;
22 use alloc::vec::Vec;
23 use ciborium::Value;
24 use coset::{AsCborValue, CborSerializable, CoseError, CoseError::UnexpectedItem};
25 use dice_policy::chain_matches_policy;
26 use log::error;
27 use log::info;
28 use secretkeeper_comm::data_types::error::{Error, SecretkeeperError};
29 use secretkeeper_comm::data_types::{Id, Secret};
30 
31 /// `PolicyGatedStorage` encapsulates the storage layer of Secretkeeper, which, in addition to
32 /// conventional storage, provides DICE policy based access control.  A client can restrict the
33 /// access to its stored entry.
34 ///
35 /// 1) Storage: `PolicyGatedStorage` allows storing a Secret (and sealing_policy) which is indexed
36 /// by an [`Id`]. Under the hood, it uses a Key-Value based storage, which should be provided on
37 /// initialization.  The security properties (Confidentiality/Integrity/Persistence) expected from
38 /// the Storage are listed in ISecretkeeper.aidl
39 ///
40 /// 2) Access control: Secretkeeper uses DICE policy based access control. Each secret is
41 /// associated with a sealing_policy, which is a DICE policy. This is a required input while
42 /// storing a secret. Further access to this secret is restricted to clients whose DICE chain
43 /// adheres to the sealing_policy.
44 pub struct PolicyGatedStorage {
45     secure_store: Box<dyn KeyValueStore>,
46 }
47 
48 impl PolicyGatedStorage {
49     /// Initialize Secretkeeper with a Key-Value store. Note: this Key-Value storage is the
50     /// only `persistent` part of Secretkeeper HAL.
init(secure_store: Box<dyn KeyValueStore>) -> Self51     pub fn init(secure_store: Box<dyn KeyValueStore>) -> Self {
52         Self { secure_store }
53     }
54 
55     /// Store or update a secret (only if the stored policy allows access to client).
56     ///
57     /// # Arguments
58     /// * `id`: Unique identifier of the `secret`. A client is allowed to have multiple entries
59     ///    each with a distinct `id`. If an entry corresponding to `id` is already present AND
60     ///    `dice_chain` matches the (already present) `sealing_policy` -> update the
61     ///    corresponding [`Secret`] & its `sealing_policy`.
62     ///
63     /// * `secret`: The [`Secret`] the client wishes to store.
64     ///
65     /// * `sealing_policy`: The DICE policy corresponding to the secret. Only clients with DICE
66     ///    chain that matches the sealing_policy are allowed to access Secret.
67     ///
68     /// * `dice_chain`: The serialized CBOR encoded DICE chain of the client, adhering to
69     ///    Android Profile for DICE.
70     ///    <https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/android.md>
71     ///    This method verifies that the dice_chain matches the provided sealing_policy but does
72     ///    not check that the DICE chain indeed belongs to the client.
73     ///    The caller must ensure that the DICE chain indeed belongs to client.
store( &mut self, id: Id, secret: Secret, sealing_policy: Vec<u8>, dice_chain: &[u8], ) -> Result<(), SecretkeeperError>74     pub fn store(
75         &mut self,
76         id: Id,
77         secret: Secret,
78         sealing_policy: Vec<u8>,
79         dice_chain: &[u8],
80     ) -> Result<(), SecretkeeperError> {
81         // Check if an entry for the id is already present & if so, whether the dice_chain matches
82         // the already present sealing_policy.
83         match self.get(&id, dice_chain, None) {
84             Ok(..) => {
85                 info!("Found an existing entry, access check succeeded, updating the secret");
86             }
87             Err(SecretkeeperError::EntryNotFound) => {
88                 info!("No existing entry, attempting to create a new entry..");
89             }
90             Err(e) => {
91                 info!("There may have been an existing entry, but reading it failed {:?}", e);
92                 return Err(e);
93             }
94         }
95 
96         // Check that the dice_chain matches the sealing_policy on the secret it is trying
97         // to store. This ensures client can not store a secret that it cannot access itself.
98         // Such requests are considered malformed.
99         chain_matches_policy(dice_chain, &sealing_policy).map_err(request_malformed)?;
100 
101         let id = id.0.as_slice();
102         let entry = Entry { secret, sealing_policy }.to_vec().map_err(serial_err)?;
103         self.secure_store.store(id, &entry).map_err(unexpected_err)?;
104         Ok(())
105     }
106 
107     /// Get the secret.
108     ///
109     /// # Arguments
110     /// * `id`: Unique identifier of the secret.
111     ///
112     /// * `dice_chain`: The serialized CBOR encoded DICE chain of the client, adhering to
113     ///    Android Profile for DICE.
114     ///    <https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/android.md>
115     ///    This method verifies that the dice_chain matches the provided sealing_policy but does
116     ///    not check that the DICE chain indeed belongs to the client. The caller must ensure that
117     ///    the DICE chain indeed belongs to client.
118     ///
119     /// * `updated_sealing_policy`: The updated dice_policy corresponding to the [`Secret`].
120     ///    This is an optional parameter and can be used to replace the sealing_policy associated
121     ///    with the [`Secret`].
get( &mut self, id: &Id, dice_chain: &[u8], updated_sealing_policy: Option<Vec<u8>>, ) -> Result<Secret, SecretkeeperError>122     pub fn get(
123         &mut self,
124         id: &Id,
125         dice_chain: &[u8],
126         updated_sealing_policy: Option<Vec<u8>>,
127     ) -> Result<Secret, SecretkeeperError> {
128         let id_bytes = id.0.as_slice();
129         match self.secure_store.get(id_bytes).map_err(unexpected_err)? {
130             Some(entry_serialized) => {
131                 let entry = Entry::from_slice(&entry_serialized).map_err(serial_err)?;
132                 chain_matches_policy(dice_chain, &entry.sealing_policy).map_err(policy_err)?;
133 
134                 // Replace the entry with updated_sealing_policy.
135                 if let Some(updated_sealing_policy) = updated_sealing_policy {
136                     if entry.sealing_policy != updated_sealing_policy {
137                         chain_matches_policy(dice_chain, &updated_sealing_policy)
138                             .map_err(policy_err)?;
139                         let new_entry = Entry {
140                             secret: entry.secret.clone(),
141                             sealing_policy: updated_sealing_policy,
142                         }
143                         .to_vec()
144                         .map_err(serial_err)?;
145                         self.secure_store.store(id_bytes, &new_entry).map_err(unexpected_err)?;
146                     }
147                 }
148                 Ok(entry.secret)
149             }
150             None => {
151                 info!("Entry for id: {:?} not found", id);
152                 Err(SecretkeeperError::EntryNotFound)
153             }
154         }
155     }
156 
157     /// Delete the `value` corresponding to the given `key`.
delete(&mut self, key: &Id) -> Result<(), SecretkeeperError>158     pub fn delete(&mut self, key: &Id) -> Result<(), SecretkeeperError> {
159         self.secure_store.delete(key.0.as_slice()).map_err(unexpected_err)
160     }
161 
162     /// Delete all stored key-value pairs.
delete_all(&mut self) -> Result<(), SecretkeeperError>163     pub fn delete_all(&mut self) -> Result<(), SecretkeeperError> {
164         self.secure_store.delete_all().map_err(unexpected_err)
165     }
166 }
167 
168 #[inline]
unexpected_err<E: core::fmt::Debug>(err: E) -> SecretkeeperError169 fn unexpected_err<E: core::fmt::Debug>(err: E) -> SecretkeeperError {
170     sk_err(SecretkeeperError::UnexpectedServerError, err)
171 }
172 
173 #[inline]
serial_err<E: core::fmt::Debug>(err: E) -> SecretkeeperError174 fn serial_err<E: core::fmt::Debug>(err: E) -> SecretkeeperError {
175     sk_err(SecretkeeperError::SerializationError, err)
176 }
177 
178 #[inline]
policy_err<E: core::fmt::Debug>(err: E) -> SecretkeeperError179 fn policy_err<E: core::fmt::Debug>(err: E) -> SecretkeeperError {
180     sk_err(SecretkeeperError::DicePolicyError, err)
181 }
182 #[inline]
request_malformed<E: core::fmt::Debug>(err: E) -> SecretkeeperError183 fn request_malformed<E: core::fmt::Debug>(err: E) -> SecretkeeperError {
184     sk_err(SecretkeeperError::RequestMalformed, err)
185 }
186 
sk_err<E: core::fmt::Debug>(sk_error: SecretkeeperError, err: E) -> SecretkeeperError187 fn sk_err<E: core::fmt::Debug>(sk_error: SecretkeeperError, err: E) -> SecretkeeperError {
188     error!("Got error {:?}, :{:?}", sk_error, err);
189     sk_error
190 }
191 
192 // Entry describes the `value` stored in the Key-Value store by PolicyGatedStorage.
193 // ```cddl
194 //  Entry = [
195 //    secret : bstr .size 32,
196 //    sealing_policy : bstr .cbor DicePolicy
197 //  ]
198 // ```
199 #[derive(Debug)]
200 struct Entry {
201     secret: Secret,
202     sealing_policy: Vec<u8>, // DicePolicy serialized into bytes
203 }
204 
205 impl AsCborValue for Entry {
from_cbor_value(value: Value) -> Result<Self, CoseError>206     fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
207         let [secret, sealing_policy] = value
208             .into_array()
209             .map_err(|_| UnexpectedItem("-", "Array"))?
210             .try_into()
211             .map_err(|_| UnexpectedItem("Array", "Array of size 2"))?;
212         let secret = Secret::from_cbor_value(secret)?;
213         let sealing_policy =
214             sealing_policy.into_bytes().map_err(|_| UnexpectedItem("-", "Bytes"))?;
215         Ok(Self { secret, sealing_policy })
216     }
217 
to_cbor_value(self) -> Result<Value, CoseError>218     fn to_cbor_value(self) -> Result<Value, CoseError> {
219         Ok(Value::Array(vec![self.secret.to_cbor_value()?, Value::from(self.sealing_policy)]))
220     }
221 }
222 
223 impl CborSerializable for Entry {}
224 
225 /// Defines the behavior of a simple Key-Value based storage, where both key & value are bytes.
226 /// Expected persistence property is dictated by the concrete type implementing the trait.
227 pub trait KeyValueStore {
228     /// Store a key-value pair. If the key is already present, replace the corresponding value.
store(&mut self, key: &[u8], value: &[u8]) -> Result<(), Error>229     fn store(&mut self, key: &[u8], value: &[u8]) -> Result<(), Error>;
230 
231     /// Get the `value` corresponding to the given `key`. Return None if the key is not found.
get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>232     fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>;
233 
234     /// Delete the `value` corresponding to the given `key`.
delete(&mut self, key: &[u8]) -> Result<(), Error>235     fn delete(&mut self, key: &[u8]) -> Result<(), Error>;
236 
237     /// Delete all stored key-value pairs.
delete_all(&mut self) -> Result<(), Error>238     fn delete_all(&mut self) -> Result<(), Error>;
239 }
240