Merge "[bssl] Add HKDF wrapper to libbssl" into main
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index bac93a4..b494cfa 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -418,6 +418,7 @@
     ],
     properties: [
         "rollback_index",
+        "props",
     ],
 }
 
@@ -447,6 +448,12 @@
     soong_config_variables: {
         release_avf_enable_llpvm_changes: {
             rollback_index: 1,
+            props: [
+                {
+                    name: "com.android.virt.cap",
+                    value: "secretkeeper_protection",
+                },
+            ],
         },
     },
 }
@@ -487,6 +494,12 @@
     soong_config_variables: {
         release_avf_enable_llpvm_changes: {
             rollback_index: 1,
+            props: [
+                {
+                    name: "com.android.virt.cap",
+                    value: "secretkeeper_protection",
+                },
+            ],
         },
     },
 }
diff --git a/pvmfw/avb/Android.bp b/pvmfw/avb/Android.bp
index 73d188b..6df1c4d 100644
--- a/pvmfw/avb/Android.bp
+++ b/pvmfw/avb/Android.bp
@@ -42,6 +42,8 @@
         ":test_image_with_unknown_vm_type_prop",
         ":test_image_with_multiple_props",
         ":test_image_with_duplicated_capability",
+        ":test_image_with_rollback_index_5",
+        ":test_image_with_multiple_capabilities",
         ":unsigned_test_image",
     ],
     prefer_rlib: true,
@@ -194,3 +196,26 @@
     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,
+}
+
+avb_add_hash_footer {
+    name: "test_image_with_multiple_capabilities",
+    src: ":unsigned_test_image",
+    partition_name: "boot",
+    private_key: ":pvmfw_sign_key",
+    salt: "2134",
+    props: [
+        {
+            name: "com.android.virt.cap",
+            value: "remote_attest|secretkeeper_protection",
+        },
+    ],
+}
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..492d387 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,15 @@
     pub public_key: &'a [u8],
     /// VM capabilities.
     pub capabilities: Vec<Capability>,
+    /// Rollback index of kernel.
+    pub rollback_index: u64,
+}
+
+impl VerifiedBootData<'_> {
+    /// Returns whether the kernel have the given capability
+    pub fn has_capability(&self, cap: Capability) -> bool {
+        self.capabilities.contains(&cap)
+    }
 }
 
 /// This enum corresponds to the `DebugLevel` in `VirtualMachineConfig`.
@@ -48,15 +60,18 @@
 }
 
 /// VM Capability.
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum Capability {
     /// Remote attestation.
     RemoteAttest,
+    /// Secretkeeper protected secrets.
+    SecretkeeperProtection,
 }
 
 impl Capability {
     const KEY: &[u8] = b"com.android.virt.cap";
     const REMOTE_ATTEST: &[u8] = b"remote_attest";
+    const SECRETKEEPER_PROTECTION: &[u8] = b"secretkeeper_protection";
     const SEPARATOR: u8 = b'|';
 
     fn get_capabilities(property_value: &[u8]) -> Result<Vec<Self>, PvmfwVerifyError> {
@@ -65,6 +80,7 @@
         for v in property_value.split(|b| *b == Self::SEPARATOR) {
             let cap = match v {
                 Self::REMOTE_ATTEST => Self::RemoteAttest,
+                Self::SECRETKEEPER_PROTECTION => Self::SecretkeeperProtection,
                 _ => return Err(PvmfwVerifyError::UnknownVbmetaProperty),
             };
             if res.contains(&cap) {
@@ -153,6 +169,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 +191,7 @@
             initrd_digest: None,
             public_key: trusted_public_key,
             capabilities,
+            rollback_index,
         });
     }
 
@@ -196,5 +217,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..6344433 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";
@@ -31,6 +32,7 @@
 const TEST_IMG_WITH_NON_INITRD_HASHDESC_PATH: &str = "test_image_with_non_initrd_hashdesc.img";
 const TEST_IMG_WITH_INITRD_AND_NON_INITRD_DESC_PATH: &str =
     "test_image_with_initrd_and_non_initrd_desc.img";
+const TEST_IMG_WITH_MULTIPLE_CAPABILITIES: &str = "test_image_with_multiple_capabilities.img";
 const UNSIGNED_TEST_IMG_PATH: &str = "unsigned_test.img";
 
 const RANDOM_FOOTER_POS: usize = 30;
@@ -60,7 +62,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 +74,7 @@
         initrd_digest: None,
         public_key: &public_key,
         capabilities: vec![],
+        rollback_index: 0,
     };
     assert_eq!(expected_boot_data, verified_boot_data);
 
@@ -82,7 +85,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 +106,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 +118,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 +129,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 +139,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 +149,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 +159,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 +169,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 +180,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 +190,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 +209,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 +387,41 @@
         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(())
+}
+
+#[test]
+fn payload_with_multiple_capabilities() -> Result<()> {
+    let public_key = load_trusted_public_key()?;
+    let verified_boot_data = verify_payload(
+        &fs::read(TEST_IMG_WITH_MULTIPLE_CAPABILITIES)?,
+        /* initrd= */ None,
+        &public_key,
+    )
+    .map_err(|e| anyhow!("Verification failed. Error: {}", e))?;
+
+    assert!(verified_boot_data.has_capability(Capability::RemoteAttest));
+    assert!(verified_boot_data.has_capability(Capability::SecretkeeperProtection));
+    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];
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index d39d51c..b8cbf1b 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -112,10 +112,22 @@
         info!("Please disregard any previous libavb ERROR about initrd_normal.");
     }
 
-    if verified_boot_data.capabilities.contains(&Capability::RemoteAttest) {
+    if verified_boot_data.has_capability(Capability::RemoteAttest) {
         info!("Service VM capable of remote attestation detected");
     }
 
+    if verified_boot_data.has_capability(Capability::SecretkeeperProtection) {
+        info!("Guest OS is capable of Secretkeeper protection");
+        // For Secretkeeper based Antirollback protection, rollback_index of the image > 0
+        if verified_boot_data.rollback_index == 0 {
+            error!(
+                "Expected positive rollback_index, found {:?}",
+                verified_boot_data.rollback_index
+            );
+            return Err(RebootReason::InvalidPayload);
+        };
+    }
+
     let next_bcc = heap::aligned_boxed_slice(NEXT_BCC_SIZE, GUEST_PAGE_SIZE).ok_or_else(|| {
         error!("Failed to allocate the next-stage BCC");
         RebootReason::InternalError