VmSecret struct: Encapsulate secret mechanisms am: 95084dfd85
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Virtualization/+/2677579
Change-Id: I113990ec50781c2ed35dc1fbb3c3c53ea8164cbc
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index cde4419..4fa696f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -34,6 +34,7 @@
config_namespace: "ANDROID",
bool_variables: [
"release_avf_enable_dice_changes",
+ "release_avf_enable_llpvm_changes",
"release_avf_enable_multi_tenant_microdroid_vm",
"release_avf_enable_vendor_modules",
],
@@ -48,6 +49,9 @@
release_avf_enable_dice_changes: {
cfgs: ["dice_changes"],
},
+ release_avf_enable_llpvm_changes: {
+ cfgs: ["llpvm_changes"],
+ },
release_avf_enable_multi_tenant_microdroid_vm: {
cfgs: ["payload_not_root"],
},
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 27ec7a5..27905c9 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -32,16 +32,6 @@
use std::ptr::null_mut;
use std::slice;
-/// Derives a sealing key from the DICE sealing CDI.
-pub fn derive_sealing_key(
- dice_artifacts: &dyn DiceArtifacts,
- salt: &[u8],
- info: &[u8],
- key: &mut [u8],
-) -> Result<()> {
- Ok(hkdf(key, Md::sha256(), dice_artifacts.cdi_seal(), salt, info)?)
-}
-
/// Artifacts that are mapped into the process address space from the driver.
pub enum DiceDriver<'a> {
Real {
@@ -109,7 +99,7 @@
// input key material is already cryptographically strong.
let mut key = ZVec::new(key_length)?;
let salt = &[];
- derive_sealing_key(self.dice_artifacts(), salt, identifier, &mut key)?;
+ hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
Ok(key)
}
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 1c79452..4172329 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -20,8 +20,9 @@
mod payload;
mod swap;
mod vm_payload_service;
+mod vm_secret;
-use crate::dice::{DiceDriver, derive_sealing_key, format_payload_config_descriptor};
+use crate::dice::{DiceDriver, format_payload_config_descriptor};
use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
use crate::vm_payload_service::register_vm_payload_service;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
@@ -63,6 +64,7 @@
use std::process::{Child, Command, Stdio};
use std::str;
use std::time::{Duration, SystemTime};
+use vm_secret::VmSecret;
const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
@@ -416,11 +418,12 @@
// To minimize the exposure to untrusted data, derive dice profile as soon as possible.
info!("DICE derivation for payload");
let dice_artifacts = dice_derivation(dice, &verified_data, &payload_metadata)?;
+ let vm_secret = VmSecret::new(dice_artifacts).context("Failed to create VM secrets")?;
// Run encryptedstore binary to prepare the storage
let encryptedstore_child = if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
info!("Preparing encryptedstore ...");
- Some(prepare_encryptedstore(&dice_artifacts).context("encryptedstore run")?)
+ Some(prepare_encryptedstore(&vm_secret).context("encryptedstore run")?)
} else {
None
};
@@ -477,7 +480,7 @@
register_vm_payload_service(
allow_restricted_apis,
service.clone(),
- dice_artifacts,
+ vm_secret,
vm_payload_service_fd,
)?;
@@ -917,7 +920,7 @@
buf.iter().map(|b| format!("{:02X}", b)).collect()
}
-fn prepare_encryptedstore(dice_artifacts: &OwnedDiceArtifacts) -> Result<Child> {
+fn prepare_encryptedstore(vm_secret: &VmSecret) -> Result<Child> {
// Use a fixed salt to scope the derivation to this API.
// Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
// TODO(b/241541860) : Move this (& other salts) to a salt container, i.e. a global enum
@@ -927,8 +930,7 @@
0x4A, 0x75,
];
let mut key = ZVec::new(ENCRYPTEDSTORE_KEYSIZE)?;
- derive_sealing_key(dice_artifacts, &salt, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), &mut key)?;
-
+ vm_secret.derive_sealing_key(&salt, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), &mut key)?;
let mut cmd = Command::new(ENCRYPTEDSTORE_BIN);
cmd.arg("--blkdevice")
.arg(ENCRYPTEDSTORE_BACKING_DEVICE)
diff --git a/microdroid_manager/src/vm_payload_service.rs b/microdroid_manager/src/vm_payload_service.rs
index 1e0b574..f9d917e 100644
--- a/microdroid_manager/src/vm_payload_service.rs
+++ b/microdroid_manager/src/vm_payload_service.rs
@@ -14,23 +14,23 @@
//! Implementation of the AIDL interface `IVmPayloadService`.
-use crate::dice::derive_sealing_key;
use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
BnVmPayloadService, IVmPayloadService, VM_PAYLOAD_SERVICE_SOCKET_NAME};
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use anyhow::{anyhow, Context, Result};
use avflog::LogResult;
use binder::{Interface, BinderFeatures, ExceptionCode, Strong, IntoBinderResult};
-use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
+use diced_open_dice::DiceArtifacts;
use log::info;
use rpcbinder::RpcServer;
use std::os::unix::io::OwnedFd;
+use crate::vm_secret::{VmSecret};
/// Implementation of `IVmPayloadService`.
struct VmPayloadService {
allow_restricted_apis: bool,
virtual_machine_service: Strong<dyn IVirtualMachineService>,
- dice: OwnedDiceArtifacts,
+ secret: VmSecret,
}
impl IVmPayloadService for VmPayloadService {
@@ -49,17 +49,18 @@
0x7A, 0x22, 0x55, 0xF8, 0x08, 0x23, 0x81, 0x5F, 0xF5, 0x16, 0x20, 0x3E, 0xBE, 0xBA,
0xB7, 0xA8, 0x43, 0x92,
];
- let mut secret = vec![0; size.try_into().unwrap()];
- derive_sealing_key(&self.dice, &salt, identifier, &mut secret)
+ let mut instance_secret = vec![0; size.try_into().unwrap()];
+ self.secret
+ .derive_sealing_key(&salt, identifier, &mut instance_secret)
.context("Failed to derive VM instance secret")
.with_log()
.or_service_specific_exception(-1)?;
- Ok(secret)
+ Ok(instance_secret)
}
fn getDiceAttestationChain(&self) -> binder::Result<Vec<u8>> {
self.check_restricted_apis_allowed()?;
- if let Some(bcc) = self.dice.bcc() {
+ if let Some(bcc) = self.secret.dice().bcc() {
Ok(bcc.to_vec())
} else {
Err(anyhow!("bcc is none")).or_binder_exception(ExceptionCode::ILLEGAL_STATE)
@@ -68,7 +69,7 @@
fn getDiceAttestationCdi(&self) -> binder::Result<Vec<u8>> {
self.check_restricted_apis_allowed()?;
- Ok(self.dice.cdi_attest().to_vec())
+ Ok(self.secret.dice().cdi_attest().to_vec())
}
fn requestCertificate(&self, csr: &[u8]) -> binder::Result<Vec<u8>> {
@@ -84,9 +85,9 @@
fn new(
allow_restricted_apis: bool,
vm_service: Strong<dyn IVirtualMachineService>,
- dice: OwnedDiceArtifacts,
- ) -> Self {
- Self { allow_restricted_apis, virtual_machine_service: vm_service, dice }
+ secret: VmSecret,
+ ) -> VmPayloadService {
+ Self { allow_restricted_apis, virtual_machine_service: vm_service, secret }
}
fn check_restricted_apis_allowed(&self) -> binder::Result<()> {
@@ -104,11 +105,11 @@
pub(crate) fn register_vm_payload_service(
allow_restricted_apis: bool,
vm_service: Strong<dyn IVirtualMachineService>,
- dice: OwnedDiceArtifacts,
+ secret: VmSecret,
vm_payload_service_fd: OwnedFd,
) -> Result<()> {
let vm_payload_binder = BnVmPayloadService::new_binder(
- VmPayloadService::new(allow_restricted_apis, vm_service, dice),
+ VmPayloadService::new(allow_restricted_apis, vm_service, secret),
BinderFeatures::default(),
);
diff --git a/microdroid_manager/src/vm_secret.rs b/microdroid_manager/src/vm_secret.rs
new file mode 100644
index 0000000..3308e0d
--- /dev/null
+++ b/microdroid_manager/src/vm_secret.rs
@@ -0,0 +1,90 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Class for encapsulating & managing represent VM secrets.
+
+use anyhow::Result;
+use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
+use keystore2_crypto::ZVec;
+use openssl::hkdf::hkdf;
+use openssl::md::Md;
+use openssl::sha;
+
+// Size of the secret stored in Secretkeeper.
+const SK_SECRET_SIZE: usize = 64;
+
+pub enum VmSecret {
+ // V2 secrets are derived from 2 independently secured secrets:
+ // 1. Secretkeeper protected secrets (skp secret).
+ // 2. Dice Sealing CDIs (Similar to V1).
+ //
+ // These are protected against rollback of boot images i.e. VM instance rebooted
+ // with downgraded images will not have access to VM's secret.
+ // V2 secrets require hardware support - Secretkeeper HAL, which (among other things)
+ // is backed by tamper-evident storage, providing rollback protection to these secrets.
+ V2 { dice: OwnedDiceArtifacts, skp_secret: ZVec },
+ // V1 secrets are not protected against rollback of boot images.
+ // They are reliable only if rollback of images was prevented by verified boot ie,
+ // each stage (including pvmfw/Microdroid/Microdroid Manager) prevents downgrade of next
+ // stage. These are now legacy secrets & used only when Secretkeeper HAL is not supported
+ // by device.
+ V1 { dice: OwnedDiceArtifacts },
+}
+
+impl VmSecret {
+ pub fn new(dice_artifacts: OwnedDiceArtifacts) -> Result<VmSecret> {
+ if is_sk_supported() {
+ // TODO(b/291213394): Change this to real Sk protected secret.
+ let fake_skp_secret = ZVec::new(SK_SECRET_SIZE)?;
+ return Ok(Self::V2 { dice: dice_artifacts, skp_secret: fake_skp_secret });
+ }
+ Ok(Self::V1 { dice: dice_artifacts })
+ }
+ pub fn dice(&self) -> &OwnedDiceArtifacts {
+ match self {
+ Self::V2 { dice, .. } => dice,
+ Self::V1 { dice } => dice,
+ }
+ }
+
+ fn get_vm_secret(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()> {
+ match self {
+ Self::V2 { dice, skp_secret } => {
+ let mut hasher = sha::Sha256::new();
+ hasher.update(dice.cdi_seal());
+ hasher.update(skp_secret);
+ hkdf(key, Md::sha256(), &hasher.finish(), salt, identifier)?
+ }
+ Self::V1 { dice } => hkdf(key, Md::sha256(), dice.cdi_seal(), salt, identifier)?,
+ }
+ Ok(())
+ }
+
+ /// Derives a sealing key of `key_length` bytes from the VmSecret.
+ /// Essentially key expansion.
+ pub fn derive_sealing_key(&self, salt: &[u8], identifier: &[u8], key: &mut [u8]) -> Result<()> {
+ self.get_vm_secret(salt, identifier, key)
+ }
+}
+
+// Does the hardware support Secretkeeper.
+fn is_sk_supported() -> bool {
+ if cfg!(llpvm_changes) {
+ return false;
+ };
+ // TODO(b/292209416): This value should be extracted from device tree.
+ // Note: this does not affect the security of pVM. pvmfw & microdroid_manager continue to block
+ // upgraded images. Setting this true is equivalent to including constant salt in vm secrets.
+ true
+}