blob: 3e10bae11055e178ae5bb81ed799c3fed87ca1d9 [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 Panwar5d6a6752023-12-14 22:08:26 +000017use anyhow::{anyhow, ensure, Result};
18use 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};
22use coset::CborSerializable;
Shikha Panwar6b178322023-12-23 00:05:17 +000023use dice_policy::{ConstraintSpec, ConstraintType, DicePolicy};
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;
37use zeroize::Zeroizing;
Shikha Panwar95084df2023-07-22 11:47:45 +000038
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000039const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
Shikha Panwar6b178322023-12-23 00:05:17 +000040const AUTHORITY_HASH: i64 = -4670549;
41const MODE: i64 = -4670551;
42const CONFIG_DESC: i64 = -4670548;
43const SECURITY_VERSION: i64 = -70005;
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000044
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000045// Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
46const SALT_ENCRYPTED_STORE: &[u8] = &[
47 0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D, 0x6F,
48 0xB3, 0xF9, 0x40, 0xCE, 0xDD, 0x99, 0x40, 0xAA, 0xA7, 0x0E, 0x92, 0x73, 0x90, 0x86, 0x4A, 0x75,
49];
50const SALT_PAYLOAD_SERVICE: &[u8] = &[
51 0x8B, 0x0F, 0xF0, 0xD3, 0xB1, 0x69, 0x2B, 0x95, 0x84, 0x2C, 0x9E, 0x3C, 0x99, 0x56, 0x7A, 0x22,
52 0x55, 0xF8, 0x08, 0x23, 0x81, 0x5F, 0xF5, 0x16, 0x20, 0x3E, 0xBE, 0xBA, 0xB7, 0xA8, 0x43, 0x92,
53];
54
Shikha Panwar5d6a6752023-12-14 22:08:26 +000055// TODO(b/291213394): Remove this once policy is generated from dice_chain
Shikha Panwar6b178322023-12-23 00:05:17 +000056const HYPOTHETICAL_DICE_POLICY: [u8; 49] = [
57 0x84, 0x01, 0x81, 0x83, 0x01, 0x80, 0x01, 0x81, 0x83, 0x01, 0x80, 0x43, 0xa1, 0x01, 0x00, 0x82,
58 0x83, 0x01, 0x81, 0x01, 0x73, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x69, 0x63,
59 0x65, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x83, 0x02, 0x82, 0x03, 0x18, 0x64, 0x19, 0xe9,
60 0x75,
Shikha Panwar5d6a6752023-12-14 22:08:26 +000061];
62// TODO(b/291213394): Differentiate the Id of nPVM based on 'salt'
63const ID_NP_VM: [u8; ID_SIZE] = [
64 0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42,
65 0xE5, 0x8A, 0xAF, 0x33, 0x6C, 0x11, 0xDC, 0xC8, 0x6F, 0xAE, 0x12, 0x5C, 0x26, 0x44, 0x6B, 0x86,
66 0xCC, 0x24, 0xFD, 0xBF, 0x91, 0x4A, 0x54, 0x84, 0xF9, 0x01, 0x59, 0x25, 0x70, 0x89, 0x38, 0x8D,
67 0x5E, 0xE6, 0x91, 0xDF, 0x68, 0x60, 0x69, 0x26, 0xBE, 0xFE, 0x79, 0x58, 0xF7, 0xEA, 0x81, 0x7D,
68];
69const SKP_SECRET_NP_VM: [u8; SECRET_SIZE] = [
70 0xA9, 0x89, 0x97, 0xFE, 0xAE, 0x97, 0x55, 0x4B, 0x32, 0x35, 0xF0, 0xE8, 0x93, 0xDA, 0xEA, 0x24,
71 0x06, 0xAC, 0x36, 0x8B, 0x3C, 0x95, 0x50, 0x16, 0x67, 0x71, 0x65, 0x26, 0xEB, 0xD0, 0xC3, 0x98,
72];
73
Shikha Panwar95084df2023-07-22 11:47:45 +000074pub enum VmSecret {
75 // V2 secrets are derived from 2 independently secured secrets:
76 // 1. Secretkeeper protected secrets (skp secret).
77 // 2. Dice Sealing CDIs (Similar to V1).
78 //
79 // These are protected against rollback of boot images i.e. VM instance rebooted
80 // with downgraded images will not have access to VM's secret.
81 // V2 secrets require hardware support - Secretkeeper HAL, which (among other things)
82 // is backed by tamper-evident storage, providing rollback protection to these secrets.
Shikha Panwar6b178322023-12-23 00:05:17 +000083 V2 { dice_artifacts: OwnedDiceArtifactsWithExplicitKey, skp_secret: ZVec },
Shikha Panwar95084df2023-07-22 11:47:45 +000084 // V1 secrets are not protected against rollback of boot images.
85 // They are reliable only if rollback of images was prevented by verified boot ie,
86 // each stage (including pvmfw/Microdroid/Microdroid Manager) prevents downgrade of next
87 // stage. These are now legacy secrets & used only when Secretkeeper HAL is not supported
88 // by device.
Shikha Panwar6b178322023-12-23 00:05:17 +000089 V1 { dice_artifacts: OwnedDiceArtifacts },
Shikha Panwar95084df2023-07-22 11:47:45 +000090}
91
Shikha Panwar5d6a6752023-12-14 22:08:26 +000092fn get_id() -> [u8; ID_SIZE] {
93 if super::is_strict_boot() {
94 todo!("Id for protected VM is not implemented");
95 } else {
96 ID_NP_VM
97 }
98}
99
Shikha Panwar95084df2023-07-22 11:47:45 +0000100impl VmSecret {
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000101 pub fn new(
102 dice_artifacts: OwnedDiceArtifacts,
103 vm_service: &Strong<dyn IVirtualMachineService>,
104 ) -> Result<VmSecret> {
105 ensure!(dice_artifacts.bcc().is_some(), "Dice chain missing");
106
107 if let Some(sk_service) = is_sk_supported(vm_service)? {
Shikha Panwar6b178322023-12-23 00:05:17 +0000108 let explicit_dice =
109 OwnedDiceArtifactsWithExplicitKey::from_owned_artifacts(dice_artifacts)?;
110 let explicit_dice_chain = explicit_dice
111 .explicit_key_dice_chain()
112 .ok_or(anyhow!("Missing explicit dice chain, this is unusual"))?;
113 let _policy = sealing_policy(explicit_dice_chain).map_err(anyhow_err)?;
114
115 // Start a new session with Secretkeeper!
116 let mut session = SkSession::new(sk_service)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000117 let id = get_id();
118 let mut skp_secret = Zeroizing::new([0u8; SECRET_SIZE]);
119 if super::is_strict_boot() {
120 if super::is_new_instance() {
121 *skp_secret = rand::random();
Shikha Panwar6b178322023-12-23 00:05:17 +0000122 store_secret(
123 &mut session,
124 id,
125 skp_secret.clone(),
126 // TODO: Use _policy instead!
127 HYPOTHETICAL_DICE_POLICY.to_vec(),
128 )?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000129 } else {
130 // Subsequent run of the pVM -> get the secret stored in Secretkeeper.
Shikha Panwar6b178322023-12-23 00:05:17 +0000131 *skp_secret = get_secret(&mut session, id, None)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000132 }
133 } else {
134 // TODO(b/291213394): Non protected VM don't need to use Secretkeeper, remove this
135 // once we have sufficient testing on protected VM.
Shikha Panwar6b178322023-12-23 00:05:17 +0000136 store_secret(
137 &mut session,
138 id,
139 SKP_SECRET_NP_VM.into(),
140 // TODO: Use the _policy above.
141 HYPOTHETICAL_DICE_POLICY.to_vec(),
142 )?;
143 *skp_secret = get_secret(&mut session, id, None)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000144 }
145 return Ok(Self::V2 {
Shikha Panwar6b178322023-12-23 00:05:17 +0000146 dice_artifacts: explicit_dice,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000147 skp_secret: ZVec::try_from(skp_secret.to_vec())?,
148 });
Shikha Panwar95084df2023-07-22 11:47:45 +0000149 }
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000150 // Use V1 secrets if Secretkeeper is not supported.
Shikha Panwar6b178322023-12-23 00:05:17 +0000151 Ok(Self::V1 { dice_artifacts })
Shikha Panwar95084df2023-07-22 11:47:45 +0000152 }
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000153
Shikha Panwar6b178322023-12-23 00:05:17 +0000154 pub fn dice_artifacts(&self) -> &dyn DiceArtifacts {
Shikha Panwar95084df2023-07-22 11:47:45 +0000155 match self {
Shikha Panwar6b178322023-12-23 00:05:17 +0000156 Self::V2 { dice_artifacts, .. } => dice_artifacts,
157 Self::V1 { dice_artifacts } => dice_artifacts,
Shikha Panwar95084df2023-07-22 11:47:45 +0000158 }
159 }
160
161 fn get_vm_secret(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()> {
162 match self {
Shikha Panwar6b178322023-12-23 00:05:17 +0000163 Self::V2 { dice_artifacts, skp_secret } => {
Shikha Panwar95084df2023-07-22 11:47:45 +0000164 let mut hasher = sha::Sha256::new();
Shikha Panwar6b178322023-12-23 00:05:17 +0000165 hasher.update(dice_artifacts.cdi_seal());
Shikha Panwar95084df2023-07-22 11:47:45 +0000166 hasher.update(skp_secret);
167 hkdf(key, Md::sha256(), &hasher.finish(), salt, identifier)?
168 }
Shikha Panwar6b178322023-12-23 00:05:17 +0000169 Self::V1 { dice_artifacts } => {
170 hkdf(key, Md::sha256(), dice_artifacts.cdi_seal(), salt, identifier)?
171 }
Shikha Panwar95084df2023-07-22 11:47:45 +0000172 }
173 Ok(())
174 }
175
Shikha Panwar3d3a70a2023-08-21 20:02:08 +0000176 /// Derive sealing key for payload with following identifier.
177 pub fn derive_payload_sealing_key(&self, identifier: &[u8], key: &mut [u8]) -> Result<()> {
178 self.get_vm_secret(SALT_PAYLOAD_SERVICE, identifier, key)
179 }
180
181 /// Derive encryptedstore key. This uses hardcoded random salt & fixed identifier.
182 pub fn derive_encryptedstore_key(&self, key: &mut [u8]) -> Result<()> {
183 self.get_vm_secret(SALT_ENCRYPTED_STORE, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), key)
Shikha Panwar95084df2023-07-22 11:47:45 +0000184 }
185}
186
Shikha Panwar6b178322023-12-23 00:05:17 +0000187// Construct a sealing policy on the dice chain. VMs uses the following set of constraint for
188// protecting secrets against rollback of boot images.
189// 1. ExactMatch on AUTHORITY_HASH (Required ie, each DiceChainEntry must have it).
190// 2. ExactMatch on MODE (Required) - Secret should be inaccessible if any of the runtime
191// configuration changes. For ex, the secrets stored with a boot stage being in Normal mode
192// should be inaccessible when the same stage is booted in Debug mode.
193// 3. GreaterOrEqual on SECURITY_VERSION (Optional): The secrets will be accessible if version of
194// any image is greater or equal to the set version. This is an optional field, certain
195// components may chose to prevent booting of rollback images for ex, ABL is expected to provide
196// rollback protection of pvmfw. Such components may chose to not put SECURITY_VERSION in the
197// corresponding DiceChainEntry.
198// TODO(b/291219197) : Add constraints on Extra apks as well!
199fn sealing_policy(dice: &[u8]) -> Result<Vec<u8>, String> {
200 let constraint_spec = [
201 ConstraintSpec::new(
202 ConstraintType::ExactMatch,
203 vec![AUTHORITY_HASH],
204 /* Optional */ false,
205 ),
206 ConstraintSpec::new(ConstraintType::ExactMatch, vec![MODE], /* Optional */ false),
207 ConstraintSpec::new(
208 ConstraintType::GreaterOrEqual,
209 vec![CONFIG_DESC, SECURITY_VERSION],
210 /* Optional */ true,
211 ),
212 ];
213
214 DicePolicy::from_dice_chain(dice, &constraint_spec)?
215 .to_vec()
216 .map_err(|e| format!("DicePolicy construction failed {e:?}"))
217}
218
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000219fn store_secret(
Shikha Panwar6b178322023-12-23 00:05:17 +0000220 session: &mut SkSession,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000221 id: [u8; ID_SIZE],
222 secret: Zeroizing<[u8; SECRET_SIZE]>,
Shikha Panwar6b178322023-12-23 00:05:17 +0000223 sealing_policy: Vec<u8>,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000224) -> Result<()> {
Shikha Panwar6b178322023-12-23 00:05:17 +0000225 let store_request = StoreSecretRequest { id: Id(id), secret: Secret(*secret), sealing_policy };
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000226 log::info!("Secretkeeper operation: {:?}", store_request);
227
228 let store_request = store_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
Shikha Panwar6b178322023-12-23 00:05:17 +0000229 let store_response = session.secret_management_request(&store_request)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000230 let store_response = ResponsePacket::from_slice(&store_response).map_err(anyhow_err)?;
231 let response_type = store_response.response_type().map_err(anyhow_err)?;
232 ensure!(
233 response_type == ResponseType::Success,
234 "Secretkeeper store failed with error: {:?}",
235 *SecretkeeperError::deserialize_from_packet(store_response).map_err(anyhow_err)?
236 );
237 Ok(())
238}
239
240fn get_secret(
Shikha Panwar6b178322023-12-23 00:05:17 +0000241 session: &mut SkSession,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000242 id: [u8; ID_SIZE],
Shikha Panwar6b178322023-12-23 00:05:17 +0000243 updated_sealing_policy: Option<Vec<u8>>,
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000244) -> Result<[u8; SECRET_SIZE]> {
Shikha Panwar6b178322023-12-23 00:05:17 +0000245 let get_request = GetSecretRequest { id: Id(id), updated_sealing_policy };
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000246 log::info!("Secretkeeper operation: {:?}", get_request);
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000247 let get_request = get_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
Shikha Panwar6b178322023-12-23 00:05:17 +0000248 let get_response = session.secret_management_request(&get_request)?;
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000249 let get_response = ResponsePacket::from_slice(&get_response).map_err(anyhow_err)?;
250 let response_type = get_response.response_type().map_err(anyhow_err)?;
251 ensure!(
252 response_type == ResponseType::Success,
253 "Secretkeeper get failed with error: {:?}",
254 *SecretkeeperError::deserialize_from_packet(get_response).map_err(anyhow_err)?
255 );
256 let get_response =
257 *GetSecretResponse::deserialize_from_packet(get_response).map_err(anyhow_err)?;
258 Ok(get_response.secret.0)
259}
260
261#[inline]
262fn anyhow_err<E: core::fmt::Debug>(err: E) -> anyhow::Error {
263 anyhow!("{:?}", err)
264}
265
266// Get the secretkeeper connection if supported. Host can be consulted whether the device supports
267// secretkeeper but that should be used with caution for protected VM.
268fn is_sk_supported(
269 host: &Strong<dyn IVirtualMachineService>,
270) -> Result<Option<Strong<dyn ISecretkeeper>>> {
271 let sk = if cfg!(llpvm_changes) {
272 if super::is_strict_boot() {
273 // TODO: For protected VM check for Secretkeeper authentication data in device tree.
274 None
275 } else {
276 // For non-protected VM, believe what host claims.
277 host.getSecretkeeper()
278 // TODO rename this error!
279 .map_err(|e| {
280 super::MicrodroidError::FailedToConnectToVirtualizationService(e.to_string())
281 })?
282 }
283 } else {
284 // LLPVM flag is disabled
285 None
286 };
287 Ok(sk)
Shikha Panwar95084df2023-07-22 11:47:45 +0000288}