1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Class for encapsulating & managing represent VM secrets.
16 
17 use anyhow::{anyhow, ensure, Context, Result};
18 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
19 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper;
20 use secretkeeper_comm::data_types::request::Request;
21 use binder::{Strong};
22 use coset::{CoseKey, CborSerializable, CborOrdering};
23 use dice_policy_builder::{TargetEntry, ConstraintSpec, ConstraintType, policy_for_dice_chain, MissingAction, WILDCARD_FULL_ARRAY};
24 use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
25 use keystore2_crypto::ZVec;
26 use openssl::hkdf::hkdf;
27 use openssl::md::Md;
28 use openssl::sha;
29 use secretkeeper_client::dice::OwnedDiceArtifactsWithExplicitKey;
30 use secretkeeper_client::SkSession;
31 use secretkeeper_comm::data_types::{Id, ID_SIZE, Secret, SECRET_SIZE};
32 use secretkeeper_comm::data_types::response::Response;
33 use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType};
34 use secretkeeper_comm::data_types::request_response_impl::{
35     StoreSecretRequest, GetSecretResponse, GetSecretRequest};
36 use secretkeeper_comm::data_types::error::SecretkeeperError;
37 use std::fs;
38 use zeroize::Zeroizing;
39 
40 const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
41 const AUTHORITY_HASH: i64 = -4670549;
42 const MODE: i64 = -4670551;
43 const CONFIG_DESC: i64 = -4670548;
44 const SECURITY_VERSION: i64 = -70005;
45 const SUBCOMPONENT_DESCRIPTORS: i64 = -71002;
46 const SUBCOMPONENT_SECURITY_VERSION: i64 = 2;
47 const SUBCOMPONENT_AUTHORITY_HASH: i64 = 4;
48 // See dice_for_avf_guest.cddl for the `component_name` used by different boot stages in guest VM.
49 const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
50 const GUEST_OS_COMPONENT_NAME: &str = "vm_entry";
51 const INSTANCE_HASH_KEY: i64 = -71003;
52 
53 // Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
54 const SALT_ENCRYPTED_STORE: &[u8] = &[
55     0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D, 0x6F,
56     0xB3, 0xF9, 0x40, 0xCE, 0xDD, 0x99, 0x40, 0xAA, 0xA7, 0x0E, 0x92, 0x73, 0x90, 0x86, 0x4A, 0x75,
57 ];
58 const SALT_PAYLOAD_SERVICE: &[u8] = &[
59     0x8B, 0x0F, 0xF0, 0xD3, 0xB1, 0x69, 0x2B, 0x95, 0x84, 0x2C, 0x9E, 0x3C, 0x99, 0x56, 0x7A, 0x22,
60     0x55, 0xF8, 0x08, 0x23, 0x81, 0x5F, 0xF5, 0x16, 0x20, 0x3E, 0xBE, 0xBA, 0xB7, 0xA8, 0x43, 0x92,
61 ];
62 
63 pub enum VmSecret {
64     // V2 secrets are derived from 2 independently secured secrets:
65     //      1. Secretkeeper protected secrets (skp secret).
66     //      2. Dice Sealing CDIs (Similar to V1).
67     //
68     // These are protected against rollback of boot images i.e. VM instance rebooted
69     // with downgraded images will not have access to VM's secret.
70     // V2 secrets require hardware support - Secretkeeper HAL, which (among other things)
71     // is backed by tamper-evident storage, providing rollback protection to these secrets.
72     V2 { dice_artifacts: OwnedDiceArtifactsWithExplicitKey, skp_secret: ZVec },
73     // V1 secrets are not protected against rollback of boot images.
74     // They are reliable only if rollback of images was prevented by verified boot ie,
75     // each stage (including pvmfw/Microdroid/Microdroid Manager) prevents downgrade of next
76     // stage. These are now legacy secrets & used only when Secretkeeper HAL is not supported
77     // by device.
78     V1 { dice_artifacts: OwnedDiceArtifacts },
79 }
80 
81 // For supporting V2 secrets, guest expects the public key to be present in the Linux device tree.
get_secretkeeper_identity() -> Result<CoseKey>82 fn get_secretkeeper_identity() -> Result<CoseKey> {
83     let key = fs::read(super::SECRETKEEPER_KEY)?;
84     let mut key = CoseKey::from_slice(&key)?;
85     key.canonicalize(CborOrdering::Lexicographic);
86     Ok(key)
87 }
88 
89 impl VmSecret {
new( dice_artifacts: OwnedDiceArtifacts, vm_service: &Strong<dyn IVirtualMachineService>, ) -> Result<Self>90     pub fn new(
91         dice_artifacts: OwnedDiceArtifacts,
92         vm_service: &Strong<dyn IVirtualMachineService>,
93     ) -> Result<Self> {
94         ensure!(dice_artifacts.bcc().is_some(), "Dice chain missing");
95         if !crate::should_defer_rollback_protection() {
96             return Ok(Self::V1 { dice_artifacts });
97         }
98 
99         let explicit_dice = OwnedDiceArtifactsWithExplicitKey::from_owned_artifacts(dice_artifacts)
100             .context("Failed to get Dice artifacts in explicit key format")?;
101         // For pVM, skp_secret are stored in Secretkeeper. For non-protected it is all 0s.
102         let mut skp_secret = Zeroizing::new([0u8; SECRET_SIZE]);
103         if super::is_strict_boot() {
104             let sk_service = get_secretkeeper_service(vm_service)?;
105             let mut session =
106                 SkSession::new(sk_service, &explicit_dice, Some(get_secretkeeper_identity()?))?;
107             let id = super::get_instance_id()?.ok_or(anyhow!("Missing instance_id"))?;
108             let explicit_dice_chain = explicit_dice
109                 .explicit_key_dice_chain()
110                 .ok_or(anyhow!("Missing explicit dice chain, this is unusual"))?;
111             let policy = sealing_policy(explicit_dice_chain)
112                 .map_err(|e| anyhow!("Failed to build a sealing_policy: {e}"))?;
113             if let Some(secret) = get_secret(&mut session, id, Some(policy.clone()))? {
114                 *skp_secret = secret;
115             } else {
116                 log::warn!(
117                     "No entry found in Secretkeeper for this VM instance, creating new secret."
118                 );
119                 *skp_secret = rand::random();
120                 store_secret(&mut session, id, skp_secret.clone(), policy)?;
121             }
122         }
123         Ok(Self::V2 {
124             dice_artifacts: explicit_dice,
125             skp_secret: ZVec::try_from(skp_secret.to_vec())?,
126         })
127     }
128 
dice_artifacts(&self) -> &dyn DiceArtifacts129     pub fn dice_artifacts(&self) -> &dyn DiceArtifacts {
130         match self {
131             Self::V2 { dice_artifacts, .. } => dice_artifacts,
132             Self::V1 { dice_artifacts } => dice_artifacts,
133         }
134     }
135 
get_vm_secret(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()>136     fn get_vm_secret(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()> {
137         match self {
138             Self::V2 { dice_artifacts, skp_secret } => {
139                 let mut hasher = sha::Sha256::new();
140                 hasher.update(dice_artifacts.cdi_seal());
141                 hasher.update(skp_secret);
142                 hkdf(key, Md::sha256(), &hasher.finish(), salt, identifier)?
143             }
144             Self::V1 { dice_artifacts } => {
145                 hkdf(key, Md::sha256(), dice_artifacts.cdi_seal(), salt, identifier)?
146             }
147         }
148         Ok(())
149     }
150 
151     /// Derive sealing key for payload with following identifier.
derive_payload_sealing_key(&self, identifier: &[u8], key: &mut [u8]) -> Result<()>152     pub fn derive_payload_sealing_key(&self, identifier: &[u8], key: &mut [u8]) -> Result<()> {
153         self.get_vm_secret(SALT_PAYLOAD_SERVICE, identifier, key)
154     }
155 
156     /// Derive encryptedstore key. This uses hardcoded random salt & fixed identifier.
derive_encryptedstore_key(&self, key: &mut [u8]) -> Result<()>157     pub fn derive_encryptedstore_key(&self, key: &mut [u8]) -> Result<()> {
158         self.get_vm_secret(SALT_ENCRYPTED_STORE, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), key)
159     }
160 }
161 
162 // Construct a sealing policy on the dice chain. VMs uses the following set of constraint for
163 // protecting secrets against rollback of boot images.
164 // 1. ExactMatch on AUTHORITY_HASH (Required ie, each DiceChainEntry must have it).
165 // 2. ExactMatch on MODE (Required) - Secret should be inaccessible if any of the runtime
166 //    configuration changes. For ex, the secrets stored with a boot stage being in Normal mode
167 //    should be inaccessible when the same stage is booted in Debug mode.
168 // 3. GreaterOrEqual on SECURITY_VERSION (Optional): The secrets will be accessible if version of
169 //    any image is greater or equal to the set version. This is an optional field, certain
170 //    components may chose to prevent booting of rollback images for ex, ABL is expected to provide
171 //    rollback protection of pvmfw. Such components may chose to not put SECURITY_VERSION in the
172 //    corresponding DiceChainEntry.
173 //  4. For each Subcomponent on the last DiceChainEntry (which corresponds to VM payload, See
174 //     microdroid_manager/src/vm_config.cddl):
175 //       - GreaterOrEqual on SECURITY_VERSION (Required)
176 //       - ExactMatch on AUTHORITY_HASH (Required).
177 //  5. ExactMatch on Instance Hash (Required) - This uniquely identifies one VM instance from
178 //     another even if they are running the exact same images.
sealing_policy(dice: &[u8]) -> Result<Vec<u8>, String>179 fn sealing_policy(dice: &[u8]) -> Result<Vec<u8>, String> {
180     let constraint_spec = vec![
181         ConstraintSpec::new(
182             ConstraintType::ExactMatch,
183             vec![AUTHORITY_HASH],
184             MissingAction::Fail,
185             TargetEntry::All,
186         ),
187         ConstraintSpec::new(
188             ConstraintType::ExactMatch,
189             vec![MODE],
190             MissingAction::Fail,
191             TargetEntry::All,
192         ),
193         ConstraintSpec::new(
194             ConstraintType::GreaterOrEqual,
195             vec![CONFIG_DESC, SECURITY_VERSION],
196             MissingAction::Ignore,
197             TargetEntry::All,
198         ),
199         ConstraintSpec::new(
200             ConstraintType::GreaterOrEqual,
201             vec![
202                 CONFIG_DESC,
203                 SUBCOMPONENT_DESCRIPTORS,
204                 WILDCARD_FULL_ARRAY,
205                 SUBCOMPONENT_SECURITY_VERSION,
206             ],
207             MissingAction::Fail,
208             TargetEntry::ByName(MICRODROID_PAYLOAD_COMPONENT_NAME.to_string()),
209         ),
210         ConstraintSpec::new(
211             ConstraintType::ExactMatch,
212             vec![
213                 CONFIG_DESC,
214                 SUBCOMPONENT_DESCRIPTORS,
215                 WILDCARD_FULL_ARRAY,
216                 SUBCOMPONENT_AUTHORITY_HASH,
217             ],
218             MissingAction::Fail,
219             TargetEntry::ByName(MICRODROID_PAYLOAD_COMPONENT_NAME.to_string()),
220         ),
221         ConstraintSpec::new(
222             ConstraintType::ExactMatch,
223             vec![CONFIG_DESC, INSTANCE_HASH_KEY],
224             MissingAction::Fail,
225             TargetEntry::ByName(GUEST_OS_COMPONENT_NAME.to_string()),
226         ),
227     ];
228 
229     policy_for_dice_chain(dice, constraint_spec)?
230         .to_vec()
231         .map_err(|e| format!("DicePolicy construction failed {e:?}"))
232 }
233 
store_secret( session: &mut SkSession, id: [u8; ID_SIZE], secret: Zeroizing<[u8; SECRET_SIZE]>, sealing_policy: Vec<u8>, ) -> Result<()>234 fn store_secret(
235     session: &mut SkSession,
236     id: [u8; ID_SIZE],
237     secret: Zeroizing<[u8; SECRET_SIZE]>,
238     sealing_policy: Vec<u8>,
239 ) -> Result<()> {
240     let store_request = StoreSecretRequest { id: Id(id), secret: Secret(*secret), sealing_policy };
241     log::info!("Secretkeeper operation: {:?}", store_request);
242 
243     let store_request = store_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
244     let store_response = session.secret_management_request(&store_request)?;
245     let store_response = ResponsePacket::from_slice(&store_response).map_err(anyhow_err)?;
246     let response_type = store_response.response_type().map_err(anyhow_err)?;
247     ensure!(
248         response_type == ResponseType::Success,
249         "Secretkeeper store failed with error: {:?}",
250         *SecretkeeperError::deserialize_from_packet(store_response).map_err(anyhow_err)?
251     );
252     Ok(())
253 }
254 
get_secret( session: &mut SkSession, id: [u8; ID_SIZE], updated_sealing_policy: Option<Vec<u8>>, ) -> Result<Option<[u8; SECRET_SIZE]>>255 fn get_secret(
256     session: &mut SkSession,
257     id: [u8; ID_SIZE],
258     updated_sealing_policy: Option<Vec<u8>>,
259 ) -> Result<Option<[u8; SECRET_SIZE]>> {
260     let get_request = GetSecretRequest { id: Id(id), updated_sealing_policy };
261     log::info!("Secretkeeper operation: {:?}", get_request);
262     let get_request = get_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
263     let get_response = session.secret_management_request(&get_request)?;
264     let get_response = ResponsePacket::from_slice(&get_response).map_err(anyhow_err)?;
265     let response_type = get_response.response_type().map_err(anyhow_err)?;
266     if response_type == ResponseType::Success {
267         let get_response =
268             *GetSecretResponse::deserialize_from_packet(get_response).map_err(anyhow_err)?;
269         Ok(Some(get_response.secret.0))
270     } else {
271         let error = SecretkeeperError::deserialize_from_packet(get_response).map_err(anyhow_err)?;
272         if *error == SecretkeeperError::EntryNotFound {
273             return Ok(None);
274         }
275         Err(anyhow!("Secretkeeper get failed: {error:?}"))
276     }
277 }
278 
279 #[inline]
anyhow_err<E: core::fmt::Debug>(err: E) -> anyhow::Error280 fn anyhow_err<E: core::fmt::Debug>(err: E) -> anyhow::Error {
281     anyhow!("{:?}", err)
282 }
283 
get_secretkeeper_service( host: &Strong<dyn IVirtualMachineService>, ) -> Result<Strong<dyn ISecretkeeper>>284 fn get_secretkeeper_service(
285     host: &Strong<dyn IVirtualMachineService>,
286 ) -> Result<Strong<dyn ISecretkeeper>> {
287     Ok(host
288         .getSecretkeeper()
289         // TODO rename this error!
290         .map_err(|e| {
291             super::MicrodroidError::FailedToConnectToVirtualizationService(format!(
292                 "Failed to get Secretkeeper: {e:?}"
293             ))
294         })?)
295 }
296