VmSecret struct: Encapsulate secret mechanisms

The current secrets are derived from Dice sealing CDIs , called V1. The
new mechanism will will derive secret from Dice Sealing CDIs and
Rollback protected secret (using Secretkeeper HAL). This patch create
scaffolding code to describe these.

This is guarded by feature flag: release_avf_enable_llpvm_changes,
which when disabled, pVMs use V1 secrets.

Bug: 291216276
Bug: 291213394
Test: atest MicrodroidTests#encryptedStorageIsPersistent
Change-Id: I7d610ba97b6f3e45c757546614dc3216e9d0e78f
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
+}