pvmfw: Rollback index of kernel & security_version

Capture rollback_index of guest kernel. Rollback indexes are available
in AvbSlotVerifyData returned from avb_slot_verify(). This is a slice of
uint64 where the position of the rollback_index is determined by
rollback_index_location (which defaults to 0).

This is then used as the 'security_version' in the dice config, that
can be used by guests OS to provide AntiRollback protection to secrets.

Note on TrunkStableFlagging - This is guarded by flag llpvm_changes,
based on which security_version is added to  dice.

Test: #payload_with_rollback_index
Test: flash pvmfw => get dice chain from Compos => verify-dice-chain
contaisn security version = 1 for guest OS
Bug: 296830692

Change-Id: I0d6d993d8b2d1b98dcc39fb90895a59c7a699d7d
diff --git a/pvmfw/avb/Android.bp b/pvmfw/avb/Android.bp
index 73d188b..f7362d8 100644
--- a/pvmfw/avb/Android.bp
+++ b/pvmfw/avb/Android.bp
@@ -42,6 +42,7 @@
         ":test_image_with_unknown_vm_type_prop",
         ":test_image_with_multiple_props",
         ":test_image_with_duplicated_capability",
+        ":test_image_with_rollback_index_5",
         ":unsigned_test_image",
     ],
     prefer_rlib: true,
@@ -194,3 +195,12 @@
     private_key: ":pvmfw_sign_key",
     salt: "1111",
 }
+
+avb_add_hash_footer {
+    name: "test_image_with_rollback_index_5",
+    src: ":unsigned_test_image",
+    partition_name: "boot",
+    private_key: ":pvmfw_sign_key",
+    salt: "1211",
+    rollback_index: 5,
+}
diff --git a/pvmfw/avb/src/ops.rs b/pvmfw/avb/src/ops.rs
index 539291b..c7b8b01 100644
--- a/pvmfw/avb/src/ops.rs
+++ b/pvmfw/avb/src/ops.rs
@@ -229,10 +229,14 @@
     _rollback_index_location: usize,
     out_rollback_index: *mut u64,
 ) -> AvbIOResult {
-    // Rollback protection is not yet implemented, but this method is required by
-    // `avb_slot_verify()`.
-    // We set `out_rollback_index` to 0 to ensure that the default rollback index (0)
-    // is never smaller than it, thus the rollback index check will pass.
+    // This method is used by `avb_slot_verify()` to read the stored_rollback_index at
+    // rollback_index_location.
+
+    // TODO(291213394) : Refine this comment once capability for rollback protection is defined.
+    // pvmfw does not compare stored_rollback_index with rollback_index for Antirollback protection
+    // Hence, we set `out_rollback_index` to 0 to ensure that the
+    // rollback_index (including default: 0) is never smaller than it,
+    // thus the rollback index check will pass.
     result_to_io_enum(write(out_rollback_index, 0))
 }
 
@@ -334,4 +338,8 @@
             unsafe { slice::from_raw_parts(data.loaded_partitions, data.num_loaded_partitions) };
         Ok(loaded_partitions)
     }
+
+    pub(crate) fn rollback_indexes(&self) -> &[u64] {
+        &self.as_ref().rollback_indexes
+    }
 }
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index ac945e2..1a16f9d 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -23,6 +23,9 @@
 use avb_bindgen::{AvbPartitionData, AvbVBMetaData};
 use core::ffi::c_char;
 
+// We use this for the rollback_index field if AvbSlotVerifyDataWrap has empty rollback_indexes
+const DEFAULT_ROLLBACK_INDEX: u64 = 0;
+
 /// Verified data returned when the payload verification succeeds.
 #[derive(Debug, PartialEq, Eq)]
 pub struct VerifiedBootData<'a> {
@@ -36,6 +39,8 @@
     pub public_key: &'a [u8],
     /// VM capabilities.
     pub capabilities: Vec<Capability>,
+    /// Rollback index of kernel.
+    pub rollback_index: u64,
 }
 
 /// This enum corresponds to the `DebugLevel` in `VirtualMachineConfig`.
@@ -153,6 +158,10 @@
     let kernel_verify_result = ops.verify_partition(PartitionName::Kernel.as_cstr())?;
 
     let vbmeta_images = kernel_verify_result.vbmeta_images()?;
+    // TODO(b/302093437): Use explicit rollback_index_location instead of default
+    // location (first element).
+    let rollback_index =
+        *kernel_verify_result.rollback_indexes().first().unwrap_or(&DEFAULT_ROLLBACK_INDEX);
     verify_only_one_vbmeta_exists(vbmeta_images)?;
     let vbmeta_image = vbmeta_images[0];
     verify_vbmeta_is_from_kernel_partition(&vbmeta_image)?;
@@ -171,6 +180,7 @@
             initrd_digest: None,
             public_key: trusted_public_key,
             capabilities,
+            rollback_index,
         });
     }
 
@@ -196,5 +206,6 @@
         initrd_digest: Some(*initrd_descriptor.digest),
         public_key: trusted_public_key,
         capabilities,
+        rollback_index,
     })
 }
diff --git a/pvmfw/avb/tests/api_test.rs b/pvmfw/avb/tests/api_test.rs
index e7e4dcc..46f5228 100644
--- a/pvmfw/avb/tests/api_test.rs
+++ b/pvmfw/avb/tests/api_test.rs
@@ -23,6 +23,7 @@
 use utils::*;
 
 const TEST_IMG_WITH_ONE_HASHDESC_PATH: &str = "test_image_with_one_hashdesc.img";
+const TEST_IMG_WITH_ROLLBACK_INDEX_5: &str = "test_image_with_rollback_index_5.img";
 const TEST_IMG_WITH_PROP_DESC_PATH: &str = "test_image_with_prop_desc.img";
 const TEST_IMG_WITH_SERVICE_VM_PROP_PATH: &str = "test_image_with_service_vm_prop.img";
 const TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH: &str = "test_image_with_unknown_vm_type_prop.img";
@@ -60,7 +61,7 @@
     let public_key = load_trusted_public_key()?;
     let verified_boot_data = verify_payload(
         &fs::read(TEST_IMG_WITH_ONE_HASHDESC_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &public_key,
     )
     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
@@ -72,6 +73,7 @@
         initrd_digest: None,
         public_key: &public_key,
         capabilities: vec![],
+        rollback_index: 0,
     };
     assert_eq!(expected_boot_data, verified_boot_data);
 
@@ -82,7 +84,7 @@
 fn payload_with_non_initrd_descriptor_fails_verification_with_no_initrd() -> Result<()> {
     assert_payload_verification_fails(
         &fs::read(TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &load_trusted_public_key()?,
         PvmfwVerifyError::InvalidDescriptors(avb::IoError::NoSuchPartition),
     )
@@ -103,7 +105,7 @@
     let public_key = load_trusted_public_key()?;
     let verified_boot_data = verify_payload(
         &fs::read(TEST_IMG_WITH_SERVICE_VM_PROP_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &public_key,
     )
     .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
@@ -115,6 +117,7 @@
         initrd_digest: None,
         public_key: &public_key,
         capabilities: vec![Capability::RemoteAttest],
+        rollback_index: 0,
     };
     assert_eq!(expected_boot_data, verified_boot_data);
 
@@ -125,7 +128,7 @@
 fn payload_with_unknown_vm_type_fails_verification_with_no_initrd() -> Result<()> {
     assert_payload_verification_fails(
         &fs::read(TEST_IMG_WITH_UNKNOWN_VM_TYPE_PROP_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &load_trusted_public_key()?,
         PvmfwVerifyError::UnknownVbmetaProperty,
     )
@@ -135,7 +138,7 @@
 fn payload_with_multiple_props_fails_verification_with_no_initrd() -> Result<()> {
     assert_payload_verification_fails(
         &fs::read(TEST_IMG_WITH_MULTIPLE_PROPS_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &load_trusted_public_key()?,
         PvmfwVerifyError::InvalidDescriptors(avb::IoError::Io),
     )
@@ -145,7 +148,7 @@
 fn payload_with_duplicated_capability_fails_verification_with_no_initrd() -> Result<()> {
     assert_payload_verification_fails(
         &fs::read(TEST_IMG_WITH_DUPLICATED_CAP_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &load_trusted_public_key()?,
         avb::SlotVerifyError::InvalidMetadata.into(),
     )
@@ -155,7 +158,7 @@
 fn payload_with_prop_descriptor_fails_verification_with_no_initrd() -> Result<()> {
     assert_payload_verification_fails(
         &fs::read(TEST_IMG_WITH_PROP_DESC_PATH)?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &load_trusted_public_key()?,
         PvmfwVerifyError::UnknownVbmetaProperty,
     )
@@ -165,7 +168,7 @@
 fn payload_expecting_initrd_fails_verification_with_no_initrd() -> Result<()> {
     assert_payload_verification_fails(
         &load_latest_signed_kernel()?,
-        /*initrd=*/ None,
+        /* initrd= */ None,
         &load_trusted_public_key()?,
         avb::SlotVerifyError::InvalidMetadata.into(),
     )
@@ -176,7 +179,7 @@
     assert_payload_verification_with_initrd_fails(
         &load_latest_signed_kernel()?,
         &load_latest_initrd_normal()?,
-        /*trusted_public_key=*/ &[0u8; 0],
+        /* trusted_public_key= */ &[0u8; 0],
         avb::SlotVerifyError::PublicKeyRejected.into(),
     )
 }
@@ -186,7 +189,7 @@
     assert_payload_verification_with_initrd_fails(
         &load_latest_signed_kernel()?,
         &load_latest_initrd_normal()?,
-        /*trusted_public_key=*/ &[0u8; 512],
+        /* trusted_public_key= */ &[0u8; 512],
         avb::SlotVerifyError::PublicKeyRejected.into(),
     )
 }
@@ -205,7 +208,7 @@
 fn payload_with_an_invalid_initrd_fails_verification() -> Result<()> {
     assert_payload_verification_with_initrd_fails(
         &load_latest_signed_kernel()?,
-        /*initrd=*/ &fs::read(UNSIGNED_TEST_IMG_PATH)?,
+        /* initrd= */ &fs::read(UNSIGNED_TEST_IMG_PATH)?,
         &load_trusted_public_key()?,
         avb::SlotVerifyError::Verification.into(),
     )
@@ -383,3 +386,26 @@
         avb::SlotVerifyError::Verification.into(),
     )
 }
+
+#[test]
+fn payload_with_rollback_index() -> Result<()> {
+    let public_key = load_trusted_public_key()?;
+    let verified_boot_data = verify_payload(
+        &fs::read(TEST_IMG_WITH_ROLLBACK_INDEX_5)?,
+        /* initrd= */ None,
+        &public_key,
+    )
+    .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
+
+    let kernel_digest = hash(&[&hex::decode("1211")?, &fs::read(UNSIGNED_TEST_IMG_PATH)?]);
+    let expected_boot_data = VerifiedBootData {
+        debug_level: DebugLevel::None,
+        kernel_digest,
+        initrd_digest: None,
+        public_key: &public_key,
+        capabilities: vec![],
+        rollback_index: 5,
+    };
+    assert_eq!(expected_boot_data, verified_boot_data);
+    Ok(())
+}
diff --git a/pvmfw/avb/tests/utils.rs b/pvmfw/avb/tests/utils.rs
index 86d2398..70eba5f 100644
--- a/pvmfw/avb/tests/utils.rs
+++ b/pvmfw/avb/tests/utils.rs
@@ -117,6 +117,7 @@
         initrd_digest,
         public_key: &public_key,
         capabilities: vec![],
+        rollback_index: if cfg!(llpvm_changes) { 1 } else { 0 },
     };
     assert_eq!(expected_boot_data, verified_boot_data);
 
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index 9542429..cc31f34 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -45,6 +45,7 @@
     pub code_hash: Hash,
     pub auth_hash: Hash,
     pub mode: DiceMode,
+    pub security_version: u64,
 }
 
 impl PartialInputs {
@@ -52,8 +53,10 @@
         let code_hash = to_dice_hash(data)?;
         let auth_hash = hash(data.public_key)?;
         let mode = to_dice_mode(data.debug_level);
+        // We use rollback_index from vbmeta as the security_version field in dice certificate.
+        let security_version = data.rollback_index;
 
-        Ok(Self { code_hash, auth_hash, mode })
+        Ok(Self { code_hash, auth_hash, mode, security_version })
     }
 
     pub fn write_next_bcc(
@@ -63,8 +66,12 @@
         next_bcc: &mut [u8],
     ) -> diced_open_dice::Result<()> {
         let mut config_descriptor_buffer = [0; 128];
-        let config_values =
-            DiceConfigValues { component_name: Some(cstr!("vm_entry")), ..Default::default() };
+        let config_values = DiceConfigValues {
+            component_name: Some(cstr!("vm_entry")),
+            security_version: if cfg!(llpvm_changes) { Some(self.security_version) } else { None },
+            ..Default::default()
+        };
+
         let config_descriptor_size =
             bcc_format_config_descriptor(&config_values, &mut config_descriptor_buffer)?;
         let config = &config_descriptor_buffer[..config_descriptor_size];