blob: b23acf77aa9e79029de7f0622fa0aec204c6304d [file] [log] [blame]
Shikha Panwar95084df2023-07-22 11:47:45 +00001// 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
Shikha Panwarf18ffc32024-03-04 16:57:02 +000017use anyhow::{anyhow, ensure, Context, Result};
Shikha Panwar5d6a6752023-12-14 22:08:26 +000018use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
19use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper;
20use secretkeeper_comm::data_types::request::Request;
21use binder::{Strong};
Shikha Panwar14abe442024-02-23 15:04:27 +000022use coset::{CoseKey, CborSerializable, CborOrdering};
Shikha Panwar100ae192024-06-12 14:23:16 +000023use dice_policy_builder::{TargetEntry, ConstraintSpec, ConstraintType, policy_for_dice_chain, MissingAction, WILDCARD_FULL_ARRAY};
Shikha Panwar95084df2023-07-22 11:47:45 +000024use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
25use keystore2_crypto::ZVec;
26use openssl::hkdf::hkdf;
27use openssl::md::Md;
28use openssl::sha;
Shikha Panwar6b178322023-12-23 00:05:17 +000029use secretkeeper_client::dice::OwnedDiceArtifactsWithExplicitKey;
Shikha Panwar5d6a6752023-12-14 22:08:26 +000030use secretkeeper_client::SkSession;
31use secretkeeper_comm::data_types::{Id, ID_SIZE, Secret, SECRET_SIZE};
32use secretkeeper_comm::data_types::response::Response;
33use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType};
34use secretkeeper_comm::data_types::request_response_impl::{
35 StoreSecretRequest, GetSecretResponse, GetSecretRequest};
36use secretkeeper_comm::data_types::error::SecretkeeperError;
Shikha Panwar14abe442024-02-23 15:04:27 +000037use std::fs;
Shikha Panwar5d6a6752023-12-14 22:08:26 +000038use zeroize::Zeroizing;
Shikha Panwar95084df2023-07-22 11:47:45 +000039
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000040const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
Shikha Panwar6b178322023-12-23 00:05:17 +000041const AUTHORITY_HASH: i64 = -4670549;
42const MODE: i64 = -4670551;
43const CONFIG_DESC: i64 = -4670548;
44const SECURITY_VERSION: i64 = -70005;
Shikha Panwar101ac8f2024-01-19 11:20:18 +000045const SUBCOMPONENT_DESCRIPTORS: i64 = -71002;
46const SUBCOMPONENT_SECURITY_VERSION: i64 = 2;
47const SUBCOMPONENT_AUTHORITY_HASH: i64 = 4;
Shikha Panwar100ae192024-06-12 14:23:16 +000048// See dice_for_avf_guest.cddl for the `component_name` used by different boot stages in guest VM.
49const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid Payload";
50const GUEST_OS_COMPONENT_NAME: &str = "vm_entry";
51const INSTANCE_HASH_KEY: i64 = -71003;
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000052
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000053// Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
54const 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];
58const 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
Shikha Panwar95084df2023-07-22 11:47:45 +000063pub 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.
Shikha Panwar6b178322023-12-23 00:05:17 +000072 V2 { dice_artifacts: OwnedDiceArtifactsWithExplicitKey, skp_secret: ZVec },
Shikha Panwar95084df2023-07-22 11:47:45 +000073 // 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.
Shikha Panwar6b178322023-12-23 00:05:17 +000078 V1 { dice_artifacts: OwnedDiceArtifacts },
Shikha Panwar95084df2023-07-22 11:47:45 +000079}
80
Shikha Panwar14abe442024-02-23 15:04:27 +000081// For supporting V2 secrets, guest expects the public key to be present in the Linux device tree.
82fn 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
Shikha Panwar95084df2023-07-22 11:47:45 +000089impl VmSecret {
Shikha Panwar5d6a6752023-12-14 22:08:26 +000090 pub fn new(
91 dice_artifacts: OwnedDiceArtifacts,
92 vm_service: &Strong<dyn IVirtualMachineService>,
Shikha Panwar0503cb02024-01-05 10:11:28 +000093 ) -> Result<Self> {
Shikha Panwar5d6a6752023-12-14 22:08:26 +000094 ensure!(dice_artifacts.bcc().is_some(), "Dice chain missing");
Shikha Panware45e9422024-02-28 21:18:10 +000095 if !crate::should_defer_rollback_protection() {
Shikha Panwar0503cb02024-01-05 10:11:28 +000096 return Ok(Self::V1 { dice_artifacts });
Shikha Panware45e9422024-02-28 21:18:10 +000097 }
Shikha Panwar14abe442024-02-23 15:04:27 +000098
Shikha Panwarf18ffc32024-03-04 16:57:02 +000099 let explicit_dice = OwnedDiceArtifactsWithExplicitKey::from_owned_artifacts(dice_artifacts)
100 .context("Failed to get Dice artifacts in explicit key format")?;
Shikha Panwar14abe442024-02-23 15:04:27 +0000101 // For pVM, skp_secret are stored in Secretkeeper. For non-protected it is all 0s.
Shikha Panwar0503cb02024-01-05 10:11:28 +0000102 let mut skp_secret = Zeroizing::new([0u8; SECRET_SIZE]);
103 if super::is_strict_boot() {
Shikha Panware45e9422024-02-28 21:18:10 +0000104 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"))?;
Shikha Panwar14abe442024-02-23 15:04:27 +0000108 let explicit_dice_chain = explicit_dice
109 .explicit_key_dice_chain()
110 .ok_or(anyhow!("Missing explicit dice chain, this is unusual"))?;
Shikha Panwarf18ffc32024-03-04 16:57:02 +0000111 let policy = sealing_policy(explicit_dice_chain)
112 .map_err(|e| anyhow!("Failed to build a sealing_policy: {e}"))?;
Shikha Panwar73ba0d42024-03-20 14:43:21 +0000113 if let Some(secret) = get_secret(&mut session, id, Some(policy.clone()))? {
114 *skp_secret = secret;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000115 } else {
Shikha Panwar73ba0d42024-03-20 14:43:21 +0000116 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)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000121 }
Shikha Panwar95084df2023-07-22 11:47:45 +0000122 }
Shikha Panwar0503cb02024-01-05 10:11:28 +0000123 Ok(Self::V2 {
124 dice_artifacts: explicit_dice,
125 skp_secret: ZVec::try_from(skp_secret.to_vec())?,
126 })
Shikha Panwar95084df2023-07-22 11:47:45 +0000127 }
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000128
Shikha Panwar6b178322023-12-23 00:05:17 +0000129 pub fn dice_artifacts(&self) -> &dyn DiceArtifacts {
Shikha Panwar95084df2023-07-22 11:47:45 +0000130 match self {
Shikha Panwar6b178322023-12-23 00:05:17 +0000131 Self::V2 { dice_artifacts, .. } => dice_artifacts,
132 Self::V1 { dice_artifacts } => dice_artifacts,
Shikha Panwar95084df2023-07-22 11:47:45 +0000133 }
134 }
135
136 fn get_vm_secret(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()> {
137 match self {
Shikha Panwar6b178322023-12-23 00:05:17 +0000138 Self::V2 { dice_artifacts, skp_secret } => {
Shikha Panwar95084df2023-07-22 11:47:45 +0000139 let mut hasher = sha::Sha256::new();
Shikha Panwar6b178322023-12-23 00:05:17 +0000140 hasher.update(dice_artifacts.cdi_seal());
Shikha Panwar95084df2023-07-22 11:47:45 +0000141 hasher.update(skp_secret);
142 hkdf(key, Md::sha256(), &hasher.finish(), salt, identifier)?
143 }
Shikha Panwar6b178322023-12-23 00:05:17 +0000144 Self::V1 { dice_artifacts } => {
145 hkdf(key, Md::sha256(), dice_artifacts.cdi_seal(), salt, identifier)?
146 }
Shikha Panwar95084df2023-07-22 11:47:45 +0000147 }
148 Ok(())
149 }
150
Shikha Panwar3d3a70a2023-08-21 20:02:08 +0000151 /// Derive sealing key for payload with following identifier.
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.
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)
Shikha Panwar95084df2023-07-22 11:47:45 +0000159 }
160}
161
Shikha Panwar6b178322023-12-23 00:05:17 +0000162// 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.
Shikha Panwar101ac8f2024-01-19 11:20:18 +0000173// 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).
Shikha Panwar100ae192024-06-12 14:23:16 +0000177// 5. ExactMatch on Instance Hash (Required) - This uniquely identifies one VM instance from
178// another even if they are running the exact same images.
Shikha Panwar6b178322023-12-23 00:05:17 +0000179fn sealing_policy(dice: &[u8]) -> Result<Vec<u8>, String> {
Shikha Panwar100ae192024-06-12 14:23:16 +0000180 let constraint_spec = vec![
Shikha Panwar101ac8f2024-01-19 11:20:18 +0000181 ConstraintSpec::new(
182 ConstraintType::ExactMatch,
183 vec![AUTHORITY_HASH],
184 MissingAction::Fail,
Shikha Panwar100ae192024-06-12 14:23:16 +0000185 TargetEntry::All,
Shikha Panwar101ac8f2024-01-19 11:20:18 +0000186 ),
187 ConstraintSpec::new(
188 ConstraintType::ExactMatch,
189 vec![MODE],
190 MissingAction::Fail,
Shikha Panwar100ae192024-06-12 14:23:16 +0000191 TargetEntry::All,
Shikha Panwar101ac8f2024-01-19 11:20:18 +0000192 ),
Shikha Panwar6b178322023-12-23 00:05:17 +0000193 ConstraintSpec::new(
194 ConstraintType::GreaterOrEqual,
195 vec![CONFIG_DESC, SECURITY_VERSION],
Shikha Panwar091bfbe2024-01-15 09:37:06 +0000196 MissingAction::Ignore,
Shikha Panwar100ae192024-06-12 14:23:16 +0000197 TargetEntry::All,
Shikha Panwar101ac8f2024-01-19 11:20:18 +0000198 ),
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,
Shikha Panwar100ae192024-06-12 14:23:16 +0000208 TargetEntry::ByName(MICRODROID_PAYLOAD_COMPONENT_NAME.to_string()),
Shikha Panwar101ac8f2024-01-19 11:20:18 +0000209 ),
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,
Shikha Panwar100ae192024-06-12 14:23:16 +0000219 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()),
Shikha Panwar6b178322023-12-23 00:05:17 +0000226 ),
227 ];
228
Shikha Panwar100ae192024-06-12 14:23:16 +0000229 policy_for_dice_chain(dice, constraint_spec)?
Shikha Panwar6b178322023-12-23 00:05:17 +0000230 .to_vec()
231 .map_err(|e| format!("DicePolicy construction failed {e:?}"))
232}
233
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000234fn store_secret(
Shikha Panwar6b178322023-12-23 00:05:17 +0000235 session: &mut SkSession,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000236 id: [u8; ID_SIZE],
237 secret: Zeroizing<[u8; SECRET_SIZE]>,
Shikha Panwar6b178322023-12-23 00:05:17 +0000238 sealing_policy: Vec<u8>,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000239) -> Result<()> {
Shikha Panwar6b178322023-12-23 00:05:17 +0000240 let store_request = StoreSecretRequest { id: Id(id), secret: Secret(*secret), sealing_policy };
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000241 log::info!("Secretkeeper operation: {:?}", store_request);
242
243 let store_request = store_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
Shikha Panwar6b178322023-12-23 00:05:17 +0000244 let store_response = session.secret_management_request(&store_request)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000245 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
255fn get_secret(
Shikha Panwar6b178322023-12-23 00:05:17 +0000256 session: &mut SkSession,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000257 id: [u8; ID_SIZE],
Shikha Panwar6b178322023-12-23 00:05:17 +0000258 updated_sealing_policy: Option<Vec<u8>>,
Shikha Panwar73ba0d42024-03-20 14:43:21 +0000259) -> Result<Option<[u8; SECRET_SIZE]>> {
Shikha Panwar6b178322023-12-23 00:05:17 +0000260 let get_request = GetSecretRequest { id: Id(id), updated_sealing_policy };
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000261 log::info!("Secretkeeper operation: {:?}", get_request);
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000262 let get_request = get_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
Shikha Panwar6b178322023-12-23 00:05:17 +0000263 let get_response = session.secret_management_request(&get_request)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000264 let get_response = ResponsePacket::from_slice(&get_response).map_err(anyhow_err)?;
265 let response_type = get_response.response_type().map_err(anyhow_err)?;
Shikha Panwar73ba0d42024-03-20 14:43:21 +0000266 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 }
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000277}
278
279#[inline]
280fn anyhow_err<E: core::fmt::Debug>(err: E) -> anyhow::Error {
281 anyhow!("{:?}", err)
282}
283
Shikha Panware45e9422024-02-28 21:18:10 +0000284fn get_secretkeeper_service(
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000285 host: &Strong<dyn IVirtualMachineService>,
Shikha Panware45e9422024-02-28 21:18:10 +0000286) -> 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 })?)
Shikha Panwar95084df2023-07-22 11:47:45 +0000295}