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