blob: df5d3184a9eb1f6f095102dd966ff8ff1f72d2ea [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 Panwar95084df2023-07-22 11:47:45 +000023use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
24use keystore2_crypto::ZVec;
25use openssl::hkdf::hkdf;
26use openssl::md::Md;
27use openssl::sha;
Shikha Panwar5d6a6752023-12-14 22:08:26 +000028use secretkeeper_client::SkSession;
29use secretkeeper_comm::data_types::{Id, ID_SIZE, Secret, SECRET_SIZE};
30use secretkeeper_comm::data_types::response::Response;
31use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType};
32use secretkeeper_comm::data_types::request_response_impl::{
33 StoreSecretRequest, GetSecretResponse, GetSecretRequest};
34use secretkeeper_comm::data_types::error::SecretkeeperError;
35use zeroize::Zeroizing;
Shikha Panwar95084df2023-07-22 11:47:45 +000036
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000037const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
38
Shikha Panwar3d3a70a2023-08-21 20:02:08 +000039// Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
40const SALT_ENCRYPTED_STORE: &[u8] = &[
41 0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D, 0x6F,
42 0xB3, 0xF9, 0x40, 0xCE, 0xDD, 0x99, 0x40, 0xAA, 0xA7, 0x0E, 0x92, 0x73, 0x90, 0x86, 0x4A, 0x75,
43];
44const SALT_PAYLOAD_SERVICE: &[u8] = &[
45 0x8B, 0x0F, 0xF0, 0xD3, 0xB1, 0x69, 0x2B, 0x95, 0x84, 0x2C, 0x9E, 0x3C, 0x99, 0x56, 0x7A, 0x22,
46 0x55, 0xF8, 0x08, 0x23, 0x81, 0x5F, 0xF5, 0x16, 0x20, 0x3E, 0xBE, 0xBA, 0xB7, 0xA8, 0x43, 0x92,
47];
48
Shikha Panwar5d6a6752023-12-14 22:08:26 +000049// TODO(b/291213394): Remove this once policy is generated from dice_chain
50const HYPOTHETICAL_DICE_POLICY: [u8; 43] = [
51 0x83, 0x01, 0x81, 0x83, 0x01, 0x80, 0xA1, 0x01, 0x00, 0x82, 0x83, 0x01, 0x81, 0x01, 0x73, 0x74,
52 0x65, 0x73, 0x74, 0x69, 0x6E, 0x67, 0x5F, 0x64, 0x69, 0x63, 0x65, 0x5F, 0x70, 0x6F, 0x6C, 0x69,
53 0x63, 0x79, 0x83, 0x02, 0x82, 0x03, 0x18, 0x64, 0x19, 0xE9, 0x75,
54];
55// TODO(b/291213394): Differentiate the Id of nPVM based on 'salt'
56const ID_NP_VM: [u8; ID_SIZE] = [
57 0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42,
58 0xE5, 0x8A, 0xAF, 0x33, 0x6C, 0x11, 0xDC, 0xC8, 0x6F, 0xAE, 0x12, 0x5C, 0x26, 0x44, 0x6B, 0x86,
59 0xCC, 0x24, 0xFD, 0xBF, 0x91, 0x4A, 0x54, 0x84, 0xF9, 0x01, 0x59, 0x25, 0x70, 0x89, 0x38, 0x8D,
60 0x5E, 0xE6, 0x91, 0xDF, 0x68, 0x60, 0x69, 0x26, 0xBE, 0xFE, 0x79, 0x58, 0xF7, 0xEA, 0x81, 0x7D,
61];
62const SKP_SECRET_NP_VM: [u8; SECRET_SIZE] = [
63 0xA9, 0x89, 0x97, 0xFE, 0xAE, 0x97, 0x55, 0x4B, 0x32, 0x35, 0xF0, 0xE8, 0x93, 0xDA, 0xEA, 0x24,
64 0x06, 0xAC, 0x36, 0x8B, 0x3C, 0x95, 0x50, 0x16, 0x67, 0x71, 0x65, 0x26, 0xEB, 0xD0, 0xC3, 0x98,
65];
66
Shikha Panwar95084df2023-07-22 11:47:45 +000067pub enum VmSecret {
68 // V2 secrets are derived from 2 independently secured secrets:
69 // 1. Secretkeeper protected secrets (skp secret).
70 // 2. Dice Sealing CDIs (Similar to V1).
71 //
72 // These are protected against rollback of boot images i.e. VM instance rebooted
73 // with downgraded images will not have access to VM's secret.
74 // V2 secrets require hardware support - Secretkeeper HAL, which (among other things)
75 // is backed by tamper-evident storage, providing rollback protection to these secrets.
76 V2 { dice: OwnedDiceArtifacts, skp_secret: ZVec },
77 // V1 secrets are not protected against rollback of boot images.
78 // They are reliable only if rollback of images was prevented by verified boot ie,
79 // each stage (including pvmfw/Microdroid/Microdroid Manager) prevents downgrade of next
80 // stage. These are now legacy secrets & used only when Secretkeeper HAL is not supported
81 // by device.
82 V1 { dice: OwnedDiceArtifacts },
83}
84
Shikha Panwar5d6a6752023-12-14 22:08:26 +000085fn get_id() -> [u8; ID_SIZE] {
86 if super::is_strict_boot() {
87 todo!("Id for protected VM is not implemented");
88 } else {
89 ID_NP_VM
90 }
91}
92
Shikha Panwar95084df2023-07-22 11:47:45 +000093impl VmSecret {
Shikha Panwar5d6a6752023-12-14 22:08:26 +000094 pub fn new(
95 dice_artifacts: OwnedDiceArtifacts,
96 vm_service: &Strong<dyn IVirtualMachineService>,
97 ) -> Result<VmSecret> {
98 ensure!(dice_artifacts.bcc().is_some(), "Dice chain missing");
99
100 if let Some(sk_service) = is_sk_supported(vm_service)? {
101 let id = get_id();
102 let mut skp_secret = Zeroizing::new([0u8; SECRET_SIZE]);
103 if super::is_strict_boot() {
104 if super::is_new_instance() {
105 *skp_secret = rand::random();
106 store_secret(sk_service.clone(), id, skp_secret.clone(), &dice_artifacts)?;
107 } else {
108 // Subsequent run of the pVM -> get the secret stored in Secretkeeper.
109 *skp_secret = get_secret(sk_service.clone(), id, &dice_artifacts)?;
110 }
111 } else {
112 // TODO(b/291213394): Non protected VM don't need to use Secretkeeper, remove this
113 // once we have sufficient testing on protected VM.
114 store_secret(sk_service.clone(), id, SKP_SECRET_NP_VM.into(), &dice_artifacts)?;
115 *skp_secret = get_secret(sk_service.clone(), id, &dice_artifacts)?;
116 }
117 return Ok(Self::V2 {
118 dice: dice_artifacts,
119 skp_secret: ZVec::try_from(skp_secret.to_vec())?,
120 });
Shikha Panwar95084df2023-07-22 11:47:45 +0000121 }
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000122 // Use V1 secrets if Secretkeeper is not supported.
Shikha Panwar95084df2023-07-22 11:47:45 +0000123 Ok(Self::V1 { dice: dice_artifacts })
124 }
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000125
Shikha Panwar95084df2023-07-22 11:47:45 +0000126 pub fn dice(&self) -> &OwnedDiceArtifacts {
127 match self {
128 Self::V2 { dice, .. } => dice,
129 Self::V1 { dice } => dice,
130 }
131 }
132
133 fn get_vm_secret(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()> {
134 match self {
135 Self::V2 { dice, skp_secret } => {
136 let mut hasher = sha::Sha256::new();
137 hasher.update(dice.cdi_seal());
138 hasher.update(skp_secret);
139 hkdf(key, Md::sha256(), &hasher.finish(), salt, identifier)?
140 }
141 Self::V1 { dice } => hkdf(key, Md::sha256(), dice.cdi_seal(), salt, identifier)?,
142 }
143 Ok(())
144 }
145
Shikha Panwar3d3a70a2023-08-21 20:02:08 +0000146 /// Derive sealing key for payload with following identifier.
147 pub fn derive_payload_sealing_key(&self, identifier: &[u8], key: &mut [u8]) -> Result<()> {
148 self.get_vm_secret(SALT_PAYLOAD_SERVICE, identifier, key)
149 }
150
151 /// Derive encryptedstore key. This uses hardcoded random salt & fixed identifier.
152 pub fn derive_encryptedstore_key(&self, key: &mut [u8]) -> Result<()> {
153 self.get_vm_secret(SALT_ENCRYPTED_STORE, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), key)
Shikha Panwar95084df2023-07-22 11:47:45 +0000154 }
155}
156
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000157fn store_secret(
158 secretkeeper: binder::Strong<dyn ISecretkeeper>,
159 id: [u8; ID_SIZE],
160 secret: Zeroizing<[u8; SECRET_SIZE]>,
161 _dice_chain: &OwnedDiceArtifacts,
162) -> Result<()> {
163 // Start a new secretkeeper session!
164 let session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
165 let store_request = StoreSecretRequest {
166 id: Id(id),
167 secret: Secret(*secret),
168 // TODO(b/291233371): Construct policy out of dice_chain.
169 sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
Shikha Panwar95084df2023-07-22 11:47:45 +0000170 };
Shikha Panwar5d6a6752023-12-14 22:08:26 +0000171 log::info!("Secretkeeper operation: {:?}", store_request);
172
173 let store_request = store_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
174 let store_response = session.secret_management_request(&store_request).map_err(anyhow_err)?;
175 let store_response = ResponsePacket::from_slice(&store_response).map_err(anyhow_err)?;
176 let response_type = store_response.response_type().map_err(anyhow_err)?;
177 ensure!(
178 response_type == ResponseType::Success,
179 "Secretkeeper store failed with error: {:?}",
180 *SecretkeeperError::deserialize_from_packet(store_response).map_err(anyhow_err)?
181 );
182 Ok(())
183}
184
185fn get_secret(
186 secretkeeper: binder::Strong<dyn ISecretkeeper>,
187 id: [u8; ID_SIZE],
188 _dice_chain: &OwnedDiceArtifacts,
189) -> Result<[u8; SECRET_SIZE]> {
190 // Start a new secretkeeper session!
191 let session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
192 let get_request = GetSecretRequest {
193 id: Id(id),
194 // TODO(b/291233371): Construct policy out of dice_chain.
195 updated_sealing_policy: None,
196 };
197 log::info!("Secretkeeper operation: {:?}", get_request);
198
199 let get_request = get_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
200 let get_response = session.secret_management_request(&get_request).map_err(anyhow_err)?;
201 let get_response = ResponsePacket::from_slice(&get_response).map_err(anyhow_err)?;
202 let response_type = get_response.response_type().map_err(anyhow_err)?;
203 ensure!(
204 response_type == ResponseType::Success,
205 "Secretkeeper get failed with error: {:?}",
206 *SecretkeeperError::deserialize_from_packet(get_response).map_err(anyhow_err)?
207 );
208 let get_response =
209 *GetSecretResponse::deserialize_from_packet(get_response).map_err(anyhow_err)?;
210 Ok(get_response.secret.0)
211}
212
213#[inline]
214fn anyhow_err<E: core::fmt::Debug>(err: E) -> anyhow::Error {
215 anyhow!("{:?}", err)
216}
217
218// Get the secretkeeper connection if supported. Host can be consulted whether the device supports
219// secretkeeper but that should be used with caution for protected VM.
220fn is_sk_supported(
221 host: &Strong<dyn IVirtualMachineService>,
222) -> Result<Option<Strong<dyn ISecretkeeper>>> {
223 let sk = if cfg!(llpvm_changes) {
224 if super::is_strict_boot() {
225 // TODO: For protected VM check for Secretkeeper authentication data in device tree.
226 None
227 } else {
228 // For non-protected VM, believe what host claims.
229 host.getSecretkeeper()
230 // TODO rename this error!
231 .map_err(|e| {
232 super::MicrodroidError::FailedToConnectToVirtualizationService(e.to_string())
233 })?
234 }
235 } else {
236 // LLPVM flag is disabled
237 None
238 };
239 Ok(sk)
Shikha Panwar95084df2023-07-22 11:47:45 +0000240}