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];