Revert^2 "[attestation] Validate client VM's Microdroid kernel in service VM"

This reverts commit bd51176b023dd2069cb7225c5edfd6910d0d709b.

Reason for revert: Reland the change aosp/2855677 and adapt the python
script in environments where Microdroid is not supported such as x86.

PS1 is the same as the original code. There's no change for the
supportive environments x86_64 and arm64.

Test: atest rialto_test
Bug: 271275206
Bug: 315936000
Change-Id: I11941e7e131ffbb86f71b955b9de42a8d436bfc9
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index cfdac2d..5b4735d 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -16,15 +16,20 @@
 //! client VM.
 
 use crate::cert;
-use crate::dice::{validate_client_vm_dice_chain_prefix_match, ClientVmDiceChain};
+use crate::dice::{
+    validate_client_vm_dice_chain_prefix_match, ClientVmDiceChain, DiceChainEntryPayload,
+};
 use crate::keyblob::decrypt_private_key;
 use alloc::vec::Vec;
-use bssl_avf::{rand_bytes, sha256, EcKey, PKey};
+use bssl_avf::{rand_bytes, sha256, Digester, EcKey, PKey};
+use cbor_util::value_to_array;
+use ciborium::value::Value;
 use core::result;
-use coset::{CborSerializable, CoseSign};
+use coset::{AsCborValue, CborSerializable, CoseSign, CoseSign1};
 use der::{Decode, Encode};
-use diced_open_dice::DiceArtifacts;
+use diced_open_dice::{DiceArtifacts, HASH_SIZE};
 use log::error;
+use microdroid_kernel_hashes::{INITRD_DEBUG_HASH, INITRD_NORMAL_HASH, KERNEL_HASH};
 use service_vm_comm::{ClientVmAttestationParams, Csr, CsrPayload, RequestProcessingError};
 use x509_cert::{certificate::Certificate, name::Name};
 
@@ -47,13 +52,22 @@
     // Validates the prefix of the Client VM DICE chain in the CSR.
     let service_vm_dice_chain =
         dice_artifacts.bcc().ok_or(RequestProcessingError::MissingDiceChain)?;
+    let service_vm_dice_chain =
+        value_to_array(Value::from_slice(service_vm_dice_chain)?, "service_vm_dice_chain")?;
     let client_vm_dice_chain =
-        validate_client_vm_dice_chain_prefix_match(&csr.dice_cert_chain, service_vm_dice_chain)?;
+        value_to_array(Value::from_slice(&csr.dice_cert_chain)?, "client_vm_dice_chain")?;
+    validate_client_vm_dice_chain_prefix_match(&client_vm_dice_chain, &service_vm_dice_chain)?;
     // Validates the signatures in the Client VM DICE chain and extracts the partially decoded
     // DiceChainEntryPayloads.
     let client_vm_dice_chain =
         ClientVmDiceChain::validate_signatures_and_parse_dice_chain(client_vm_dice_chain)?;
 
+    // The last entry in the service VM DICE chain describes the service VM, which should
+    // be signed with the same key as the kernel image.
+    let service_vm_entry = service_vm_dice_chain.last().unwrap();
+    validate_kernel_authority_hash(client_vm_dice_chain.microdroid_kernel(), service_vm_entry)?;
+    validate_kernel_code_hash(&client_vm_dice_chain)?;
+
     // AAD is empty as defined in service_vm/comm/client_vm_csr.cddl.
     let aad = &[];
 
@@ -121,3 +135,60 @@
     let digest = sha256(message)?;
     key.ecdsa_sign(&digest)
 }
+
+/// Validates that the authority hash of the Microdroid kernel in the Client VM DICE chain
+/// matches the authority hash of the service VM entry in the service VM DICE chain, because
+/// the Microdroid kernel is signed with the same key as the one used for the service VM.
+fn validate_kernel_authority_hash(
+    kernel: &DiceChainEntryPayload,
+    service_vm_entry: &Value,
+) -> Result<()> {
+    if expected_kernel_authority_hash(service_vm_entry)? == kernel.authority_hash {
+        Ok(())
+    } else {
+        error!("The authority hash of the Microdroid kernel does not match the expected value");
+        Err(RequestProcessingError::InvalidDiceChain)
+    }
+}
+
+/// Validates that the kernel code hash in the Client VM DICE chain matches the code hashes
+/// embedded during the build time.
+fn validate_kernel_code_hash(dice_chain: &ClientVmDiceChain) -> Result<()> {
+    let kernel = dice_chain.microdroid_kernel();
+    if expected_kernel_code_hash_normal()? == kernel.code_hash {
+        return Ok(());
+    }
+    if expected_kernel_code_hash_debug()? == kernel.code_hash {
+        if dice_chain.all_entries_are_secure() {
+            error!("The Microdroid kernel has debug initrd but the DICE chain is secure");
+            return Err(RequestProcessingError::InvalidDiceChain);
+        }
+        return Ok(());
+    }
+    error!("The kernel code hash in the Client VM DICE chain does not match any expected values");
+    Err(RequestProcessingError::InvalidDiceChain)
+}
+
+fn expected_kernel_code_hash_normal() -> bssl_avf::Result<Vec<u8>> {
+    let mut code_hash = [0u8; 64];
+    code_hash[0..32].copy_from_slice(KERNEL_HASH);
+    code_hash[32..].copy_from_slice(INITRD_NORMAL_HASH);
+    Digester::sha512().digest(&code_hash)
+}
+
+fn expected_kernel_code_hash_debug() -> bssl_avf::Result<Vec<u8>> {
+    let mut code_hash = [0u8; 64];
+    code_hash[0..32].copy_from_slice(KERNEL_HASH);
+    code_hash[32..].copy_from_slice(INITRD_DEBUG_HASH);
+    Digester::sha512().digest(&code_hash)
+}
+
+fn expected_kernel_authority_hash(service_vm_entry: &Value) -> Result<[u8; HASH_SIZE]> {
+    let cose_sign1 = CoseSign1::from_cbor_value(service_vm_entry.clone())?;
+    let payload = cose_sign1.payload.ok_or_else(|| {
+        error!("No payload found in the service VM DICE chain entry");
+        RequestProcessingError::InternalError
+    })?;
+    let service_vm = DiceChainEntryPayload::from_slice(&payload)?;
+    Ok(service_vm.authority_hash)
+}
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index 557b678..8c804da 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -126,7 +126,7 @@
         Ok(())
     }
 
-    fn microdroid_kernel(&self) -> &DiceChainEntryPayload {
+    pub(crate) fn microdroid_kernel(&self) -> &DiceChainEntryPayload {
         &self.payloads[self.payloads.len() - 2]
     }
 
@@ -147,15 +147,11 @@
 /// Validates that the `client_vm_dice_chain` matches the `service_vm_dice_chain` up to the pvmfw
 /// entry.
 ///
-/// Returns a CBOR value array of the client VM's DICE chain if the verification succeeds.
+/// Returns `Ok(())` if the verification succeeds.
 pub(crate) fn validate_client_vm_dice_chain_prefix_match(
-    client_vm_dice_chain: &[u8],
-    service_vm_dice_chain: &[u8],
-) -> Result<Vec<Value>> {
-    let client_vm_dice_chain =
-        value_to_array(Value::from_slice(client_vm_dice_chain)?, "client_vm_dice_chain")?;
-    let service_vm_dice_chain =
-        value_to_array(Value::from_slice(service_vm_dice_chain)?, "service_vm_dice_chain")?;
+    client_vm_dice_chain: &[Value],
+    service_vm_dice_chain: &[Value],
+) -> Result<()> {
     if service_vm_dice_chain.len() < 3 {
         // The service VM's DICE chain must contain the root key and at least two other entries
         // that describe:
@@ -180,7 +176,7 @@
         );
         return Err(RequestProcessingError::InvalidDiceChain);
     }
-    Ok(client_vm_dice_chain)
+    Ok(())
 }
 
 #[derive(Debug, Clone)]
@@ -208,11 +204,8 @@
     #[allow(dead_code)]
     subject_public_key: PublicKey,
     mode: DiceMode,
-    /// TODO(b/271275206): Verify Microdroid kernel authority and code hashes.
-    #[allow(dead_code)]
-    code_hash: [u8; HASH_SIZE],
-    #[allow(dead_code)]
-    authority_hash: [u8; HASH_SIZE],
+    pub(crate) code_hash: [u8; HASH_SIZE],
+    pub(crate) authority_hash: [u8; HASH_SIZE],
     config_descriptor: ConfigDescriptor,
 }
 
@@ -230,42 +223,42 @@
             error!("No payload found in the DICE chain entry");
             RequestProcessingError::InvalidDiceChain
         })?;
-        let entries = value_to_map(Value::from_slice(&payload)?, "DiceChainEntryPayload")?;
-        build_payload(entries)
+        Self::from_slice(&payload)
     }
-}
 
-fn build_payload(entries: Vec<(Value, Value)>) -> Result<DiceChainEntryPayload> {
-    let mut builder = PayloadBuilder::default();
-    for (key, value) in entries.into_iter() {
-        let key: i64 = value_to_num(key, "DiceChainEntryPayload key")?;
-        match key {
-            SUBJECT_PUBLIC_KEY => {
-                let subject_public_key = value_to_bytes(value, "subject_public_key")?;
-                let subject_public_key = CoseKey::from_slice(&subject_public_key)?.try_into()?;
-                builder.subject_public_key(subject_public_key)?;
+    pub(crate) fn from_slice(data: &[u8]) -> Result<Self> {
+        let entries = value_to_map(Value::from_slice(data)?, "DiceChainEntryPayload")?;
+        let mut builder = PayloadBuilder::default();
+        for (key, value) in entries.into_iter() {
+            let key: i64 = value_to_num(key, "DiceChainEntryPayload key")?;
+            match key {
+                SUBJECT_PUBLIC_KEY => {
+                    let subject_public_key = value_to_bytes(value, "subject_public_key")?;
+                    let subject_public_key =
+                        CoseKey::from_slice(&subject_public_key)?.try_into()?;
+                    builder.subject_public_key(subject_public_key)?;
+                }
+                MODE => builder.mode(to_mode(value)?)?,
+                CODE_HASH => {
+                    let code_hash = value_to_byte_array(value, "DiceChainEntryPayload code_hash")?;
+                    builder.code_hash(code_hash)?;
+                }
+                AUTHORITY_HASH => {
+                    let authority_hash =
+                        value_to_byte_array(value, "DiceChainEntryPayload authority_hash")?;
+                    builder.authority_hash(authority_hash)?;
+                }
+                CONFIG_DESC => {
+                    let config_descriptor = value_to_bytes(value, "config_descriptor")?;
+                    let config_descriptor = ConfigDescriptor::from_slice(&config_descriptor)?;
+                    builder.config_descriptor(config_descriptor)?;
+                }
+                _ => {}
             }
-            MODE => builder.mode(to_mode(value)?)?,
-            CODE_HASH => {
-                let code_hash = value_to_byte_array(value, "DiceChainEntryPayload code_hash")?;
-                builder.code_hash(code_hash)?;
-            }
-            AUTHORITY_HASH => {
-                let authority_hash =
-                    value_to_byte_array(value, "DiceChainEntryPayload authority_hash")?;
-                builder.authority_hash(authority_hash)?;
-            }
-            CONFIG_DESC => {
-                let config_descriptor = value_to_bytes(value, "config_descriptor")?;
-                let config_descriptor = ConfigDescriptor::from_slice(&config_descriptor)?;
-                builder.config_descriptor(config_descriptor)?;
-            }
-            _ => {}
         }
+        builder.build()
     }
-    builder.build()
 }
-
 /// Represents a partially decoded `ConfigurationDescriptor`.
 ///
 /// The whole `ConfigurationDescriptor` is defined in: