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
+}