Merge "pvmfw: use libavb_rs for verification" into main
diff --git a/apex/Android.bp b/apex/Android.bp
index a889d08..acbc0a1 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -143,10 +143,10 @@
         },
         release_avf_enable_vendor_modules: {
             prebuilts: [
-                "microdroid_gki_initrd_debuggable",
-                "microdroid_gki_initrd_normal",
-                "microdroid_gki_kernel",
-                "microdroid_gki.json",
+                "microdroid_gki-6.1_initrd_debuggable",
+                "microdroid_gki-6.1_initrd_normal",
+                "microdroid_gki-6.1_kernel",
+                "microdroid_gki-6.1.json",
             ],
         },
         release_avf_enable_remote_attestation: {
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 7393636..0c5bc72 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -410,21 +410,35 @@
            '--output_vbmeta_image', output]
     RunCommand(args, cmd)
 
+
+gki_versions = ['6.1']
+
 # dict of (key, file) for re-sign/verification. keys are un-versioned for readability.
-virt_apex_files = {
+virt_apex_non_gki_files = {
     'kernel': 'etc/fs/microdroid_kernel',
-    'gki_kernel': 'etc/fs/microdroid_gki_kernel',
     'vbmeta.img': 'etc/fs/microdroid_vbmeta.img',
     'super.img': 'etc/fs/microdroid_super.img',
     'initrd_normal.img': 'etc/microdroid_initrd_normal.img',
-    'gki_initrd_normal.img': 'etc/microdroid_gki_initrd_normal.img',
     'initrd_debuggable.img': 'etc/microdroid_initrd_debuggable.img',
-    'gki_initrd_debuggable.img': 'etc/microdroid_gki_initrd_debuggable.img',
 }
 
-
 def TargetFiles(input_dir):
-    return {k: os.path.join(input_dir, v) for k, v in virt_apex_files.items()}
+    ret = {k: os.path.join(input_dir, v) for k, v in virt_apex_non_gki_files.items()}
+
+    for ver in gki_versions:
+        kernel        = os.path.join(input_dir, f'etc/fs/microdroid_gki-{ver}_kernel')
+        initrd_normal = os.path.join(input_dir, f'etc/microdroid_gki-{ver}_initrd_normal.img')
+        initrd_debug  = os.path.join(input_dir, f'etc/microdroid_gki-{ver}_initrd_debuggable.img')
+
+        if os.path.isfile(kernel):
+            ret[f'gki-{ver}_kernel']                = kernel
+            ret[f'gki-{ver}_initrd_normal.img']     = initrd_normal
+            ret[f'gki-{ver}_initrd_debuggable.img'] = initrd_debug
+
+    return ret
+
+def IsInitrdImage(path):
+    return path.endswith('initrd_normal.img') or path.endswith('initrd_debuggable.img')
 
 
 def SignVirtApex(args):
@@ -461,13 +475,9 @@
                      images=images,
                      wait=images_f)
 
-    has_gki_kernel = os.path.isfile(files['gki_kernel'])
-
     vbmeta_bc_f = None
     if not args.do_not_update_bootconfigs:
-        initrd_files = [files['initrd_normal.img'], files['initrd_debuggable.img']]
-        if has_gki_kernel:
-            initrd_files += [files['gki_initrd_normal.img'], files['gki_initrd_debuggable.img']]
+        initrd_files = [v for k, v in files.items() if IsInitrdImage(k)]
         vbmeta_bc_f = Async(UpdateVbmetaBootconfig, args, initrd_files,
                             files['vbmeta.img'],
                             wait=[vbmeta_f])
@@ -493,8 +503,12 @@
 
     resign_kernel('kernel', 'initrd_normal.img', 'initrd_debuggable.img')
 
-    if has_gki_kernel:
-        resign_kernel('gki_kernel', 'gki_initrd_normal.img', 'gki_initrd_debuggable.img')
+    for ver in gki_versions:
+        if f'gki-{ver}_kernel' in files:
+            resign_kernel(
+                f'gki-{ver}_kernel',
+                f'gki-{ver}_initrd_normal.img',
+                f'gki-{ver}_initrd_debuggable.img')
 
 
 def VerifyVirtApex(args):
@@ -518,12 +532,11 @@
         assert info is not None, f'no avbinfo: {file}'
         assert info['Public key (sha1)'] == pubkey_digest, f'pubkey mismatch: {file}'
 
-    for f in files.values():
-        if f in (files['initrd_normal.img'], files['initrd_debuggable.img'],
-                 files['gki_initrd_normal.img'], files['gki_initrd_debuggable.img']):
+    for k, f in files.items():
+        if IsInitrdImage(k):
             # TODO(b/245277660): Verify that ramdisks contain the correct vbmeta digest
             continue
-        if f == files['super.img']:
+        if k == 'super.img':
             Async(check_avb_pubkey, system_a_img)
         else:
             # Check pubkey for other files using avbtool
diff --git a/libs/apexutil/Android.bp b/libs/apexutil/Android.bp
index f9b72c4..beff58d 100644
--- a/libs/apexutil/Android.bp
+++ b/libs/apexutil/Android.bp
@@ -9,7 +9,9 @@
     srcs: ["src/lib.rs"],
     edition: "2021",
     rustlibs: [
+        "libapex_manifest_rs",
         "liblog_rust",
+        "libprotobuf",
         "libthiserror",
         "libvbmeta_rust",
         "libzip",
@@ -26,7 +28,12 @@
     defaults: ["libapexutil_rust.defaults"],
     prefer_rlib: true,
     test_suites: ["general-tests"],
-    data: ["tests/data/*"],
+    // We're reusing test APEXes from system/apex/apexd
+    data: [
+        ":apex.apexd_test",
+        ":apex.apexd_test_v2_no_pb",
+        ":gen_key_mismatch_with_image_apex",
+    ],
     rustlibs: [
         "libhex",
     ],
diff --git a/libs/apexutil/src/lib.rs b/libs/apexutil/src/lib.rs
index 8a934e2..639135f 100644
--- a/libs/apexutil/src/lib.rs
+++ b/libs/apexutil/src/lib.rs
@@ -14,6 +14,8 @@
 
 //! Routines for handling APEX payload
 
+use apex_manifest::apex_manifest::ApexManifest;
+use protobuf::Message;
 use std::fs::File;
 use std::io::{self, Read};
 use thiserror::Error;
@@ -23,28 +25,32 @@
 
 const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
 const APEX_PAYLOAD_ENTRY: &str = "apex_payload.img";
+const APEX_MANIFEST_ENTRY: &str = "apex_manifest.pb";
 
 /// Errors from parsing an APEX.
 #[derive(Debug, Error)]
 pub enum ApexParseError {
     /// There was an IO error.
-    #[error("IO error")]
+    #[error("IO error: {0}")]
     Io(#[from] io::Error),
     /// The Zip archive was invalid.
-    #[error("Cannot read zip archive")]
+    #[error("Cannot read zip archive: {0}")]
     InvalidZip(&'static str),
-    /// The apex_pubkey file was missing from the APEX.
-    #[error("APEX doesn't contain apex_pubkey")]
-    PubkeyMissing,
-    /// The apex_payload.img file was missing from the APEX.
-    #[error("APEX doesn't contain apex_payload.img")]
-    PayloadMissing,
+    /// An expected file was missing from the APEX.
+    #[error("APEX doesn't contain {0}")]
+    MissingFile(&'static str),
     /// There was no hashtree descriptor in the APEX payload's VBMeta image.
     #[error("Non-hashtree descriptor found in payload's VBMeta image")]
     DescriptorNotHashtree,
     /// There was an error parsing the APEX payload's VBMeta image.
-    #[error("Could not parse payload's VBMeta image")]
+    #[error("Could not parse payload's VBMeta image: {0}")]
     PayloadVbmetaError(#[from] vbmeta::VbMetaImageParseError),
+    /// Data was missing from the VBMeta
+    #[error("Data missing from VBMeta: {0}")]
+    VbmetaMissingData(&'static str),
+    /// An error occurred parsing the APEX manifest as a protobuf
+    #[error("Error parsing manifest protobuf: {0}")]
+    ManifestProtobufError(#[from] protobuf::Error),
 }
 
 /// Errors from verifying an APEX.
@@ -58,29 +64,44 @@
     PayloadVbmetaError(#[from] vbmeta::VbMetaImageVerificationError),
     /// The APEX payload was not verified with the apex_pubkey.
     #[error("APEX pubkey mismatch")]
-    ApexPubkeyMistmatch,
+    ApexPubkeyMismatch,
 }
 
-/// Verification result holds public key and root digest of apex_payload.img
+/// Information extracted from the APEX during AVB verification.
+#[derive(Debug)]
 pub struct ApexVerificationResult {
+    /// The name of the APEX, from its manifest.
+    pub name: Option<String>,
+    /// The version of the APEX, from its manifest.
+    pub version: Option<i64>,
     /// The public key that verifies the payload signature.
     pub public_key: Vec<u8>,
     /// The root digest of the payload hashtree.
     pub root_digest: Vec<u8>,
 }
 
-/// Verify APEX payload by AVB verification and return public key and root digest
+/// Verify APEX payload by AVB verification and return information about the APEX.
+/// This verifies that the VBMeta is correctly signed by the public key specified in the APEX.
+/// It doesn't verify that that is the correct key, nor does it verify that the payload matches
+/// the signed root hash - that is handled by dm-verity once apexd has mounted the APEX.
 pub fn verify(path: &str) -> Result<ApexVerificationResult, ApexVerificationError> {
     let apex_file = File::open(path).map_err(ApexParseError::Io)?;
-    let (public_key, image_offset, image_size) = get_public_key_and_image_info(&apex_file)?;
+    let ApexZipInfo { public_key, image_offset, image_size, manifest } =
+        get_apex_zip_info(&apex_file)?;
     let vbmeta = VbMetaImage::verify_reader_region(apex_file, image_offset, image_size)?;
     let root_digest = find_root_digest(&vbmeta)?;
-    match vbmeta.public_key() {
-        Some(payload_public_key) if public_key == payload_public_key => {
-            Ok(ApexVerificationResult { public_key, root_digest })
-        }
-        _ => Err(ApexVerificationError::ApexPubkeyMistmatch),
+    let vbmeta_public_key =
+        vbmeta.public_key().ok_or(ApexParseError::VbmetaMissingData("public key"))?;
+    if vbmeta_public_key != public_key {
+        return Err(ApexVerificationError::ApexPubkeyMismatch);
     }
+    let (name, version) = if cfg!(dice_changes) {
+        let ApexManifestInfo { name, version } = decode_manifest(&manifest)?;
+        (Some(name), Some(version))
+    } else {
+        (None, None)
+    };
+    Ok(ApexVerificationResult { name, version, public_key, root_digest })
 }
 
 fn find_root_digest(vbmeta: &VbMetaImage) -> Result<Vec<u8>, ApexParseError> {
@@ -93,46 +114,52 @@
     Err(ApexParseError::DescriptorNotHashtree)
 }
 
-/// Gets the hash of the payload's verified VBMeta image data.
-pub fn get_payload_vbmeta_image_hash(path: &str) -> Result<Vec<u8>, ApexVerificationError> {
-    let apex_file = File::open(path).map_err(ApexParseError::Io)?;
-    let (_, offset, size) = get_public_key_and_image_info(&apex_file)?;
-    let vbmeta = VbMetaImage::verify_reader_region(apex_file, offset, size)?;
-    Ok(vbmeta.hash().ok_or(ApexVerificationError::ApexPubkeyMistmatch)?.to_vec())
+struct ApexZipInfo {
+    public_key: Vec<u8>,
+    image_offset: u64,
+    image_size: u64,
+    manifest: Vec<u8>,
 }
 
-fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64), ApexParseError> {
-    let mut z = ZipArchive::new(apex_file).map_err(|err| match err {
-        ZipError::Io(err) => ApexParseError::Io(err),
-        ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
-            ApexParseError::InvalidZip(s)
-        }
-        ZipError::FileNotFound => unreachable!(),
-    })?;
+fn get_apex_zip_info(apex_file: &File) -> Result<ApexZipInfo, ApexParseError> {
+    let mut z = ZipArchive::new(apex_file).map_err(|err| from_zip_error(err, "?"))?;
 
     let mut public_key = Vec::new();
     z.by_name(APEX_PUBKEY_ENTRY)
-        .map_err(|err| match err {
-            ZipError::Io(err) => ApexParseError::Io(err),
-            ZipError::FileNotFound => ApexParseError::PubkeyMissing,
-            ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
-                ApexParseError::InvalidZip(s)
-            }
-        })?
+        .map_err(|err| from_zip_error(err, APEX_PUBKEY_ENTRY))?
         .read_to_end(&mut public_key)?;
 
     let (image_offset, image_size) = z
         .by_name(APEX_PAYLOAD_ENTRY)
         .map(|f| (f.data_start(), f.size()))
-        .map_err(|err| match err {
-            ZipError::Io(err) => ApexParseError::Io(err),
-            ZipError::FileNotFound => ApexParseError::PayloadMissing,
-            ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
-                ApexParseError::InvalidZip(s)
-            }
-        })?;
+        .map_err(|err| from_zip_error(err, APEX_PAYLOAD_ENTRY))?;
 
-    Ok((public_key, image_offset, image_size))
+    let mut manifest = Vec::new();
+    z.by_name(APEX_MANIFEST_ENTRY)
+        .map_err(|err| from_zip_error(err, APEX_MANIFEST_ENTRY))?
+        .read_to_end(&mut manifest)?;
+
+    Ok(ApexZipInfo { public_key, image_offset, image_size, manifest })
+}
+
+struct ApexManifestInfo {
+    name: String,
+    version: i64,
+}
+
+fn decode_manifest(mut manifest: &[u8]) -> Result<ApexManifestInfo, ApexParseError> {
+    let manifest = ApexManifest::parse_from_reader(&mut manifest)?;
+    Ok(ApexManifestInfo { name: manifest.name, version: manifest.version })
+}
+
+fn from_zip_error(err: ZipError, name: &'static str) -> ApexParseError {
+    match err {
+        ZipError::Io(err) => ApexParseError::Io(err),
+        ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
+            ApexParseError::InvalidZip(s)
+        }
+        ZipError::FileNotFound => ApexParseError::MissingFile(name),
+    }
 }
 
 #[cfg(test)]
@@ -141,20 +168,68 @@
 
     #[test]
     fn apex_verification_returns_valid_result() {
-        let res = verify("tests/data/test.apex").unwrap();
-        // The expected hex is generated when we ran the method the first time.
+        let res = verify("apex.apexd_test.apex").unwrap();
+        let (expected_name, expected_version) = if cfg!(dice_changes) {
+            (Some("com.android.apex.test_package"), Some(1))
+        } else {
+            (None, None)
+        };
+        assert_eq!(res.name.as_deref(), expected_name);
+        assert_eq!(res.version, expected_version);
+        // The expected hex values were generated when we ran the method the first time.
         assert_eq!(
             hex::encode(res.root_digest),
-            "fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
+            "54265da77ae1fd619e39809ad99fedc576bb20c0c7a8002190fa64438436299f"
+        );
+        assert_eq!(
+            hex::encode(res.public_key),
+            "\
+            00001000963a5527aaf0145b3bb5f899a05034ccc76dafdd671dbf4e42c04df2eeba15\
+            6c884816d7d08ef8d834d4adc27979afed9eaf406694d0d600f0b6d31e3ab85da47d27\
+            9c223a1630e02332d920587617ea766a136057a3a3232a7c42f83fb3763e853be4026c\
+            067524a95fcbfcc6caadfb553210bb5385f5adc5caeb0e3f6a9aa56af88d8899d962eb\
+            807864feabeeacdd868697935fb4cc4843957e0d90ee4293c715c4e5b970e6545a17d1\
+            735f814c7d4dbdeaac97275a84f292e3715c158d38eb00eebd010dd2fa56595c0e5627\
+            06c7a94e566912f993e5e35c04b2a314d1bce1ceb10de6c50f8101ddb6ee993fc79959\
+            2e79ee73b77741ee5c076c89343684344a6d080e5529a046d506d104bf32903e39c363\
+            b020fee9d87e7c6ffdad120b630386e958416ac156bc2d7301836c79e926e8f185a640\
+            be05135e17018c88dde02cd7bd49655e9e9dff7f965fb8e68217236c18d23b6d7e7632\
+            184acb95b088598601c809d5e66c19f5e06b5e5ff1bbae7e3142959d9380db2d4a25c8\
+            757975232ea311016e830703a6023b0986e885f2eda066517fce09f33f359b6ef7cc5a\
+            2fdaced74257661bad184a653ea2d80d1af68de5821c06a472635f0276dc42d699f588\
+            ea6c46189ca1ad544bbd4951a766bc4119b0ea671cb16556762721723bf1db47c83c76\
+            a7cc2fd3b6029efec9908d9d4640294f6ea46f6e1a3195e9252c393e35698911a7c496\
+            138dc2dd8d9dcb470ae1c6d2224d13b160fb3ae4bc235f6133c2ff5f9232fb89adfdba\
+            48dcc47cf29a22cd47dcec0b1a179f352c9848a8e04ac37f35777a24312c821febc591\
+            84c8cdefc88e50b4d6bc9530ca743f4284c9773677d38527e6e8020fe367f0f16a6c49\
+            9a7f2da95ec6471f7382e5c0da98b531702cb55a560de7cafc7b6111aae0f896fb1fed\
+            d4997a954c6c083ef1fd3bb13fef3f95022523fb1fbe7f4a49e12e54a5206f95daa316\
+            ac009b7bee4039f769fd28033db6013df841c86d8345d44418fbc9f669e4ee3294b2ff\
+            29d048f53d768c0a41f9a280f0229d9912e8b2fb734617a9947be973ed1dc7bdeac9e2\
+            6028d59317098a44bacdb3b10ccde6ef02f7c94124461032a033701ce523b13142658c\
+            265385198903ccf227ad5ae88ec31e586cd8f855641fd2646dba8053d0d0924f132505\
+            8141f1c7433aa9686f48e3f3a972b56776eaf8bf22a740d1aea2ef473184d697de1dab\
+            9b62a227611c7500b11dea2e5eb8051807c0d1f2fe032acfd7701c017e629f99c74de5\
+            da4c2a542f17b9833beb14442aa7c2990b828473376ea03fdb4a650b88e821fe5026e8\
+            ffb7002d095c9877ee3a98a4488ed3287e9be4942a223f4e32bc26c2ebd02eec20dc82\
+            7493b44f4efaf9b2e175d4de2b07c32d6d359e234c9e50ef905ffa7f6907c313a3c9f4\
+            40d1efd5ec7cbeef06dcfd649f4c8219ad"
         );
     }
 
     #[test]
-    fn payload_vbmeta_has_valid_image_hash() {
-        let result = get_payload_vbmeta_image_hash("tests/data/test.apex").unwrap();
-        assert_eq!(
-            hex::encode(result),
-            "296e32a76544de9da01713e471403ab4667705ad527bb4f1fac0cf61e7ce122d"
-        );
+    fn apex_no_manifest_fails_verification() {
+        match verify("apex.apexd_test_v2_no_pb.apex").unwrap_err() {
+            ApexVerificationError::ParseError(ApexParseError::MissingFile(_)) => (),
+            e => panic!("Unexpected error {e}"),
+        }
+    }
+
+    #[test]
+    fn apex_signature_mismatch_fails_verification() {
+        match verify("apex.apexd_test_wrong_public_key.apex").unwrap_err() {
+            ApexVerificationError::ApexPubkeyMismatch => (),
+            e => panic!("Unexpected error {e}"),
+        }
     }
 }
diff --git a/libs/apexutil/tests/data/README.md b/libs/apexutil/tests/data/README.md
deleted file mode 100644
index 82ebec6..0000000
--- a/libs/apexutil/tests/data/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Test data
-
-- test.apex: copied from system/apexshim/prebuilts/x86/com.android.apex.cts.shim.v1.apex
\ No newline at end of file
diff --git a/libs/apexutil/tests/data/test.apex b/libs/apexutil/tests/data/test.apex
deleted file mode 100644
index fd79365..0000000
--- a/libs/apexutil/tests/data/test.apex
+++ /dev/null
Binary files differ
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 6436be3..a187259 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -102,6 +102,7 @@
             EC_KEY_set_public_key_affine_coordinates(ec_key.0.as_ptr(), x.as_ref(), y.as_ref())
         };
         check_int_result(ret, ApiName::EC_KEY_set_public_key_affine_coordinates)?;
+        ec_key.check_key()?;
         Ok(ec_key)
     }
 
diff --git a/libs/dice/open_dice/src/bcc.rs b/libs/dice/open_dice/src/bcc.rs
index 199e1a9..9c9545b 100644
--- a/libs/dice/open_dice/src/bcc.rs
+++ b/libs/dice/open_dice/src/bcc.rs
@@ -20,7 +20,7 @@
     DiceAndroidConfigValues, DiceAndroidFormatConfigDescriptor, DiceAndroidHandoverMainFlow,
     DiceAndroidHandoverParse, DiceAndroidMainFlow, DICE_ANDROID_CONFIG_COMPONENT_NAME,
     DICE_ANDROID_CONFIG_COMPONENT_VERSION, DICE_ANDROID_CONFIG_RESETTABLE,
-    DICE_ANDROID_CONFIG_SECURITY_VERSION,
+    DICE_ANDROID_CONFIG_RKP_VM_MARKER, DICE_ANDROID_CONFIG_SECURITY_VERSION,
 };
 use std::{ffi::CStr, ptr};
 
@@ -36,6 +36,8 @@
     pub resettable: bool,
     /// Monotonically increasing version of the component.
     pub security_version: Option<u64>,
+    /// Whether the component can take part in running the RKP VM.
+    pub rkp_vm_marker: bool,
 }
 
 /// Formats a configuration descriptor following the Android Profile for DICE specification.
@@ -58,6 +60,9 @@
         configs |= DICE_ANDROID_CONFIG_SECURITY_VERSION;
         version
     });
+    if values.rkp_vm_marker {
+        configs |= DICE_ANDROID_CONFIG_RKP_VM_MARKER;
+    }
 
     let values =
         DiceAndroidConfigValues { configs, component_name, component_version, security_version };
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index b42e416..c1caa56 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -332,22 +332,6 @@
     ],
 }
 
-android_filesystem {
-    name: "microdroid_gki_modules-6.1-arm64",
-    deps: [
-        "microdroid_gki_kernel_modules-6.1-arm64",
-    ],
-    type: "compressed_cpio",
-}
-
-android_filesystem {
-    name: "microdroid_gki_modules-6.1-x86_64",
-    deps: [
-        "microdroid_gki_kernel_modules-6.1-x86_64",
-    ],
-    type: "compressed_cpio",
-}
-
 genrule {
     name: "microdroid_bootconfig_arm64_gen",
     srcs: [
@@ -428,11 +412,6 @@
 }
 
 prebuilt_etc {
-    name: "microdroid_gki.json",
-    src: "microdroid_gki.json",
-}
-
-prebuilt_etc {
     name: "microdroid_manifest",
     src: "microdroid_manifest.xml",
     filename: "manifest.xml",
@@ -460,11 +439,8 @@
 // python -c "import hashlib; print(hashlib.sha256(b'initrd_normal').hexdigest())"
 initrd_normal_salt = "8041a07d54ac82290f6d90bac1fa8d7fdbc4db974d101d60faf294749d1ebaf8"
 
-avb_gen_vbmeta_image {
-    name: "microdroid_initrd_normal_hashdesc",
-    src: ":microdroid_initrd_normal",
-    partition_name: "initrd_normal",
-    salt: initrd_normal_salt,
+avb_gen_vbmeta_image_defaults {
+    name: "microdroid_initrd_defaults",
     enabled: false,
     arch: {
         // Microdroid kernel is only available in these architectures.
@@ -477,63 +453,38 @@
     },
 }
 
-avb_gen_vbmeta_image {
-    name: "microdroid_gki_initrd_normal_hashdesc",
-    src: ":microdroid_gki_initrd_normal",
+avb_gen_vbmeta_image_defaults {
+    name: "microdroid_initrd_normal_defaults",
+    defaults: ["microdroid_initrd_defaults"],
     partition_name: "initrd_normal",
     salt: initrd_normal_salt,
-    enabled: false,
-    arch: {
-        // Microdroid kernel is only available in these architectures.
-        arm64: {
-            enabled: true,
-        },
-        x86_64: {
-            enabled: true,
-        },
-    },
+}
+
+avb_gen_vbmeta_image {
+    name: "microdroid_initrd_normal_hashdesc",
+    defaults: ["microdroid_initrd_normal_defaults"],
+    src: ":microdroid_initrd_normal",
 }
 
 // python -c "import hashlib; print(hashlib.sha256(b'initrd_debug').hexdigest())"
 initrd_debug_salt = "8ab9dc9cb7e6456700ff6ef18c6b4c3acc24c5fa5381b829563f8d7a415d869a"
 
-avb_gen_vbmeta_image {
-    name: "microdroid_initrd_debug_hashdesc",
-    src: ":microdroid_initrd_debuggable",
+avb_gen_vbmeta_image_defaults {
+    name: "microdroid_initrd_debug_defaults",
+    defaults: ["microdroid_initrd_defaults"],
     partition_name: "initrd_debug",
     salt: initrd_debug_salt,
-    enabled: false,
-    arch: {
-        // Microdroid kernel is only available in these architectures.
-        arm64: {
-            enabled: true,
-        },
-        x86_64: {
-            enabled: true,
-        },
-    },
 }
 
 avb_gen_vbmeta_image {
-    name: "microdroid_gki_initrd_debug_hashdesc",
-    src: ":microdroid_gki_initrd_debuggable",
-    partition_name: "initrd_debug",
-    salt: initrd_debug_salt,
-    enabled: false,
-    arch: {
-        // Microdroid kernel is only available in these architectures.
-        arm64: {
-            enabled: true,
-        },
-        x86_64: {
-            enabled: true,
-        },
-    },
+    name: "microdroid_initrd_debug_hashdesc",
+    defaults: ["microdroid_initrd_debug_defaults"],
+    src: ":microdroid_initrd_debuggable",
 }
 
 soong_config_module_type {
-    name: "flag_aware_avb_add_hash_footer",
-    module_type: "avb_add_hash_footer",
+    name: "flag_aware_avb_add_hash_footer_defaults",
+    module_type: "avb_add_hash_footer_defaults",
     config_namespace: "ANDROID",
     bool_variables: [
         "release_avf_enable_llpvm_changes",
@@ -544,28 +495,21 @@
     ],
 }
 
-flag_aware_avb_add_hash_footer {
-    name: "microdroid_kernel_signed",
+flag_aware_avb_add_hash_footer_defaults {
+    name: "microdroid_kernel_signed_defaults",
     src: ":empty_file",
-    filename: "microdroid_kernel",
     partition_name: "boot",
     private_key: ":microdroid_sign_key",
     salt: bootloader_salt,
     enabled: false,
     arch: {
         arm64: {
-            src: ":microdroid_kernel_prebuilts-6.1-arm64",
             enabled: true,
         },
         x86_64: {
-            src: ":microdroid_kernel_prebuilts-6.1-x86_64",
             enabled: true,
         },
     },
-    include_descriptors_from_images: [
-        ":microdroid_initrd_normal_hashdesc",
-        ":microdroid_initrd_debug_hashdesc",
-    ],
     // Below are properties that are conditionally set depending on value of build flags.
     soong_config_variables: {
         release_avf_enable_llpvm_changes: {
@@ -580,40 +524,22 @@
     },
 }
 
-flag_aware_avb_add_hash_footer {
-    name: "microdroid_gki_kernel_signed",
-    src: ":empty_file",
-    filename: "microdroid_gki_kernel",
-    partition_name: "boot",
-    private_key: ":microdroid_sign_key",
-    salt: bootloader_salt,
-    enabled: false,
+avb_add_hash_footer {
+    name: "microdroid_kernel_signed",
+    defaults: ["microdroid_kernel_signed_defaults"],
+    filename: "microdroid_kernel",
     arch: {
         arm64: {
-            src: ":microdroid_gki_kernel_prebuilts-6.1-arm64",
-            enabled: true,
+            src: ":microdroid_kernel_prebuilts-6.1-arm64",
         },
         x86_64: {
-            src: ":microdroid_gki_kernel_prebuilts-6.1-x86_64",
-            enabled: true,
+            src: ":microdroid_kernel_prebuilts-6.1-x86_64",
         },
     },
     include_descriptors_from_images: [
-        ":microdroid_gki_initrd_normal_hashdesc",
-        ":microdroid_gki_initrd_debug_hashdesc",
+        ":microdroid_initrd_normal_hashdesc",
+        ":microdroid_initrd_debug_hashdesc",
     ],
-    // Below are properties that are conditionally set depending on value of build flags.
-    soong_config_variables: {
-        release_avf_enable_llpvm_changes: {
-            rollback_index: 1,
-            props: [
-                {
-                    name: "com.android.virt.cap",
-                    value: "secretkeeper_protection",
-                },
-            ],
-        },
-    },
 }
 
 prebuilt_etc {
@@ -630,16 +556,54 @@
     },
 }
 
+///////////////////////////////////////
+// GKI-6.1 modules
+///////////////////////////////////////
 prebuilt_etc {
-    name: "microdroid_gki_kernel",
+    name: "microdroid_gki-6.1.json",
+    src: "microdroid_gki-6.1.json",
+}
+
+avb_add_hash_footer {
+    name: "microdroid_gki-6.1_kernel_signed",
+    defaults: ["microdroid_kernel_signed_defaults"],
+    filename: "microdroid_gki-6.1_kernel",
+    arch: {
+        arm64: {
+            src: ":microdroid_gki_kernel_prebuilts-6.1-arm64",
+        },
+        x86_64: {
+            src: ":microdroid_gki_kernel_prebuilts-6.1-x86_64",
+        },
+    },
+    include_descriptors_from_images: [
+        ":microdroid_gki-6.1_initrd_normal_hashdesc",
+        ":microdroid_gki-6.1_initrd_debug_hashdesc",
+    ],
+}
+
+prebuilt_etc {
+    name: "microdroid_gki-6.1_kernel",
     src: ":empty_file",
     relative_install_path: "fs",
     arch: {
         arm64: {
-            src: ":microdroid_gki_kernel_signed",
+            src: ":microdroid_gki-6.1_kernel_signed",
         },
         x86_64: {
-            src: ":microdroid_gki_kernel_signed",
+            src: ":microdroid_gki-6.1_kernel_signed",
         },
     },
 }
+
+avb_gen_vbmeta_image {
+    name: "microdroid_gki-6.1_initrd_normal_hashdesc",
+    defaults: ["microdroid_initrd_normal_defaults"],
+    src: ":microdroid_gki-6.1_initrd_normal",
+}
+
+avb_gen_vbmeta_image {
+    name: "microdroid_gki-6.1_initrd_debug_hashdesc",
+    defaults: ["microdroid_initrd_debug_defaults"],
+    src: ":microdroid_gki-6.1_initrd_debuggable",
+}
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index 6cd84fa..8df4c0f 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -41,7 +41,7 @@
 }
 
 genrule {
-    name: "microdroid_gki_initrd_gen_arm64",
+    name: "microdroid_gki-6.1_initrd_gen_arm64",
     srcs: [
         ":microdroid_ramdisk",
         ":microdroid_fstab_ramdisk",
@@ -52,7 +52,7 @@
 }
 
 genrule {
-    name: "microdroid_gki_initrd_gen_x86_64",
+    name: "microdroid_gki-6.1_initrd_gen_x86_64",
     srcs: [
         ":microdroid_ramdisk",
         ":microdroid_fstab_ramdisk",
@@ -96,13 +96,13 @@
 }
 
 genrule {
-    name: "microdroid_gki_initrd_debuggable_arm64",
+    name: "microdroid_gki-6.1_initrd_debuggable_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
-        ":microdroid_gki_initrd_gen_arm64",
+        ":microdroid_gki-6.1_initrd_gen_arm64",
         ":microdroid_bootconfig_debuggable_src",
     ] + bootconfigs_arm64,
-    out: ["microdroid_gki_initrd_debuggable_arm64"],
+    out: ["microdroid_gki-6.1_initrd_debuggable_arm64"],
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
@@ -118,13 +118,13 @@
 }
 
 genrule {
-    name: "microdroid_gki_initrd_debuggable_x86_64",
+    name: "microdroid_gki-6.1_initrd_debuggable_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
-        ":microdroid_gki_initrd_gen_x86_64",
+        ":microdroid_gki-6.1_initrd_gen_x86_64",
         ":microdroid_bootconfig_debuggable_src",
     ] + bootconfigs_x86_64,
-    out: ["microdroid_gki_initrd_debuggable_x86_64"],
+    out: ["microdroid_gki-6.1_initrd_debuggable_x86_64"],
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
@@ -140,13 +140,13 @@
 }
 
 genrule {
-    name: "microdroid_gki_initrd_normal_arm64",
+    name: "microdroid_gki-6.1_initrd_normal_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
-        ":microdroid_gki_initrd_gen_arm64",
+        ":microdroid_gki-6.1_initrd_gen_arm64",
         ":microdroid_bootconfig_normal_src",
     ] + bootconfigs_arm64,
-    out: ["microdroid_gki_initrd_normal_arm64"],
+    out: ["microdroid_gki-6.1_initrd_normal_arm64"],
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
@@ -162,13 +162,13 @@
 }
 
 genrule {
-    name: "microdroid_gki_initrd_normal_x86_64",
+    name: "microdroid_gki-6.1_initrd_normal_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
-        ":microdroid_gki_initrd_gen_x86_64",
+        ":microdroid_gki-6.1_initrd_gen_x86_64",
         ":microdroid_bootconfig_normal_src",
     ] + bootconfigs_x86_64,
-    out: ["microdroid_gki_initrd_normal_x86_64"],
+    out: ["microdroid_gki-6.1_initrd_normal_x86_64"],
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
@@ -188,18 +188,18 @@
 }
 
 prebuilt_etc {
-    name: "microdroid_gki_initrd_debuggable",
+    name: "microdroid_gki-6.1_initrd_debuggable",
     // We don't have ramdisk for architectures other than x86_64 & arm64
     src: ":empty_file",
     arch: {
         x86_64: {
-            src: ":microdroid_gki_initrd_debuggable_x86_64",
+            src: ":microdroid_gki-6.1_initrd_debuggable_x86_64",
         },
         arm64: {
-            src: ":microdroid_gki_initrd_debuggable_arm64",
+            src: ":microdroid_gki-6.1_initrd_debuggable_arm64",
         },
     },
-    filename: "microdroid_gki_initrd_debuggable.img",
+    filename: "microdroid_gki-6.1_initrd_debuggable.img",
 }
 
 prebuilt_etc {
@@ -218,16 +218,16 @@
 }
 
 prebuilt_etc {
-    name: "microdroid_gki_initrd_normal",
+    name: "microdroid_gki-6.1_initrd_normal",
     // We don't have ramdisk for architectures other than x86_64 & arm64
     src: ":empty_file",
     arch: {
         x86_64: {
-            src: ":microdroid_gki_initrd_normal_x86_64",
+            src: ":microdroid_gki-6.1_initrd_normal_x86_64",
         },
         arm64: {
-            src: ":microdroid_gki_initrd_normal_arm64",
+            src: ":microdroid_gki-6.1_initrd_normal_arm64",
         },
     },
-    filename: "microdroid_gki_initrd_normal.img",
+    filename: "microdroid_gki-6.1_initrd_normal.img",
 }
diff --git a/microdroid/microdroid_gki.json b/microdroid/microdroid_gki-6.1.json
similarity index 84%
rename from microdroid/microdroid_gki.json
rename to microdroid/microdroid_gki-6.1.json
index d7ba53e..2115e51 100644
--- a/microdroid/microdroid_gki.json
+++ b/microdroid/microdroid_gki-6.1.json
@@ -1,5 +1,5 @@
 {
-  "kernel": "/apex/com.android.virt/etc/fs/microdroid_gki_kernel",
+  "kernel": "/apex/com.android.virt/etc/fs/microdroid_gki-6.1_kernel",
   "disks": [
     {
       "partitions": [
diff --git a/microdroid/payload/metadata.proto b/microdroid/payload/metadata.proto
index 6b999af..b03d466 100644
--- a/microdroid/payload/metadata.proto
+++ b/microdroid/payload/metadata.proto
@@ -37,14 +37,18 @@
 }
 
 message ApexPayload {
+  // Next id: 9
+
   // Required.
   string name = 1;
   string partition_name = 2;
 
   // Optional.
-  // When specified, apex payload should be verified with the public key and root digest.
+  // When specified, apex payload should be verified against these values.
   bytes public_key = 3;
   bytes root_digest = 4;
+  int64 manifest_version = 7;
+  string manifest_name = 8;
 
   // Required.
   // The timestamp in seconds when the APEX was last updated. This should match the value in
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 6b0775a..e6ddfb9 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 use crate::dice_driver::DiceDriver;
-use crate::instance::ApkData;
+use crate::instance::{ApexData, ApkData};
 use crate::{is_debuggable, MicrodroidData};
 use anyhow::{bail, Context, Result};
 use ciborium::{cbor, Value};
@@ -26,24 +26,23 @@
 /// Perform an open DICE derivation for the payload.
 pub fn dice_derivation(
     dice: DiceDriver,
-    verified_data: &MicrodroidData,
+    instance_data: &MicrodroidData,
     payload_metadata: &PayloadMetadata,
 ) -> Result<OwnedDiceArtifacts> {
-    let subcomponents = build_subcomponent_list(verified_data);
-
+    let subcomponents = build_subcomponent_list(instance_data);
     let config_descriptor = format_payload_config_descriptor(payload_metadata, &subcomponents)
         .context("Building config descriptor")?;
 
     // Calculate compound digests of code and authorities
     let mut code_hash_ctx = Sha512::new();
     let mut authority_hash_ctx = Sha512::new();
-    code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
-    authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
-    for extra_apk in &verified_data.extra_apks_data {
+    code_hash_ctx.update(instance_data.apk_data.root_hash.as_ref());
+    authority_hash_ctx.update(instance_data.apk_data.pubkey.as_ref());
+    for extra_apk in &instance_data.extra_apks_data {
         code_hash_ctx.update(extra_apk.root_hash.as_ref());
         authority_hash_ctx.update(extra_apk.pubkey.as_ref());
     }
-    for apex in &verified_data.apex_data {
+    for apex in &instance_data.apex_data {
         code_hash_ctx.update(apex.root_digest.as_ref());
         authority_hash_ctx.update(apex.public_key.as_ref());
     }
@@ -54,7 +53,7 @@
     let debuggable = is_debuggable()?;
 
     // Send the details to diced
-    let hidden = verified_data.salt.clone().try_into().unwrap();
+    let hidden = instance_data.salt.clone().try_into().unwrap();
     dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
 }
 
@@ -85,17 +84,29 @@
                 Box::new(sha512(&apk.pubkey)),
         }
     }
+
+    fn for_apex(apex: &'a ApexData) -> Self {
+        // Note that this is only reachable if the dice_changes flag is on, in which case
+        // the manifest data will always be present.
+        Self {
+            name: format!("apex:{}", apex.manifest_name.as_ref().unwrap()),
+            version: apex.manifest_version.unwrap() as u64,
+            code_hash: &apex.root_digest,
+            authority_hash: Box::new(sha512(&apex.public_key)),
+        }
+    }
 }
 
-fn build_subcomponent_list(verified_data: &MicrodroidData) -> Vec<Subcomponent> {
+fn build_subcomponent_list(instance_data: &MicrodroidData) -> Vec<Subcomponent> {
     if !cfg!(dice_changes) {
         return vec![];
     }
 
-    once(&verified_data.apk_data)
-        .chain(&verified_data.extra_apks_data)
-        .map(Subcomponent::for_apk)
-        .collect()
+    let apks = once(&instance_data.apk_data)
+        .chain(&instance_data.extra_apks_data)
+        .map(Subcomponent::for_apk);
+    let apexes = instance_data.apex_data.iter().map(Subcomponent::for_apex);
+    apks.chain(apexes).collect()
 }
 
 // Returns a configuration descriptor of the given payload. See vm_config.cddl for a definition
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 6c9e245..b0fc03d 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -304,6 +304,8 @@
 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
 pub struct ApexData {
     pub name: String,
+    pub manifest_name: Option<String>,
+    pub manifest_version: Option<i64>,
     pub public_key: Vec<u8>,
     pub root_digest: Vec<u8>,
     pub last_update_seconds: u64,
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 1b41e58..9e167a4 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -246,20 +246,20 @@
     }
 
     // Verify the payload before using it.
-    let verified_data = verify_payload(&metadata, saved_data.as_ref())
+    let extracted_data = verify_payload(&metadata, saved_data.as_ref())
         .context("Payload verification failed")
         .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
 
     // In case identity is ignored (by debug policy), we should reuse existing payload data, even
     // when the payload is changed. This is to keep the derived secret same as before.
-    let verified_data = if let Some(saved_data) = saved_data {
+    let instance_data = if let Some(saved_data) = saved_data {
         if !is_verified_boot() {
-            if saved_data != verified_data {
+            if saved_data != extracted_data {
                 info!("Detected an update of the payload, but continue (regarding debug policy)")
             }
         } else {
             ensure!(
-                saved_data == verified_data,
+                saved_data == extracted_data,
                 MicrodroidError::PayloadChanged(String::from(
                     "Detected an update of the payload which isn't supported yet."
                 ))
@@ -270,9 +270,9 @@
     } else {
         info!("Saving verified data.");
         instance
-            .write_microdroid_data(&verified_data, &dice)
+            .write_microdroid_data(&extracted_data, &dice)
             .context("Failed to write identity data")?;
-        verified_data
+        extracted_data
     };
 
     let payload_metadata = metadata.payload.ok_or_else(|| {
@@ -281,7 +281,7 @@
 
     // 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 dice_artifacts = dice_derivation(dice, &instance_data, &payload_metadata)?;
     let vm_secret = VmSecret::new(dice_artifacts).context("Failed to create VM secrets")?;
 
     if cfg!(dice_changes) {
@@ -326,10 +326,10 @@
         .ok_or_else(|| MicrodroidError::PayloadInvalidConfig("No task in VM config".to_string()))?;
 
     ensure!(
-        config.extra_apks.len() == verified_data.extra_apks_data.len(),
+        config.extra_apks.len() == instance_data.extra_apks_data.len(),
         "config expects {} extra apks, but found {}",
         config.extra_apks.len(),
-        verified_data.extra_apks_data.len()
+        instance_data.extra_apks_data.len()
     );
     mount_extra_apks(&config, &mut zipfuse)?;
 
diff --git a/microdroid_manager/src/payload.rs b/microdroid_manager/src/payload.rs
index a553ce4..98fe24b 100644
--- a/microdroid_manager/src/payload.rs
+++ b/microdroid_manager/src/payload.rs
@@ -17,8 +17,7 @@
 use crate::instance::ApexData;
 use crate::ioutil::wait_for_file;
 use anyhow::Result;
-use apexutil::verify;
-use log::info;
+use log::{info, warn};
 use microdroid_metadata::{read_metadata, ApexPayload, Metadata};
 use std::time::Duration;
 
@@ -38,13 +37,19 @@
         .apexes
         .iter()
         .map(|apex| {
-            let name = apex.name.clone();
             let apex_path = format!("/dev/block/by-name/{}", apex.partition_name);
-            let result = verify(&apex_path)?;
+            let extracted = apexutil::verify(&apex_path)?;
+            if let Some(manifest_name) = &extracted.name {
+                if &apex.name != manifest_name {
+                    warn!("Apex named {} is named {} in its manifest", apex.name, manifest_name);
+                }
+            };
             Ok(ApexData {
-                name,
-                public_key: result.public_key,
-                root_digest: result.root_digest,
+                name: apex.name.clone(),
+                manifest_name: extracted.name,
+                manifest_version: extracted.version,
+                public_key: extracted.public_key,
+                root_digest: extracted.root_digest,
                 last_update_seconds: apex.last_update_seconds,
                 is_factory: apex.is_factory,
             })
@@ -61,6 +66,8 @@
                 name: data.name.clone(),
                 public_key: data.public_key.clone(),
                 root_digest: data.root_digest.clone(),
+                manifest_name: data.manifest_name.clone().unwrap_or_default(),
+                manifest_version: data.manifest_version.unwrap_or_default(),
                 last_update_seconds: data.last_update_seconds,
                 is_factory: data.is_factory,
                 ..Default::default()
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 103619f..f49bbce 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -45,22 +45,69 @@
     cmd: "touch $(out)",
 }
 
-rust_test {
-    name: "libpvmfw.bootargs.test",
-    host_supported: true,
-    // For now, only bootargs.rs is written to be conditionally compiled with std.
-    srcs: ["src/bootargs.rs"],
+rust_defaults {
+    name: "libpvmfw.test.defaults",
     defaults: ["avf_build_flags_rust"],
     test_suites: ["general-tests"],
     test_options: {
         unit_test: true,
     },
+    prefer_rlib: true,
     rustlibs: [
         "libcstr",
+    ],
+}
+
+rust_test {
+    name: "libpvmfw.bootargs.test",
+    host_supported: true,
+    // For now, only bootargs.rs is written to be conditionally compiled with std.
+    srcs: ["src/bootargs.rs"],
+    defaults: ["libpvmfw.test.defaults"],
+    rustlibs: [
         "libzeroize",
     ],
 }
 
+rust_test {
+    name: "libpvmfw.device_assignment.test",
+    srcs: ["src/device_assignment.rs"],
+    defaults: ["libpvmfw.test.defaults"],
+    rustlibs: [
+        "liblibfdt",
+        "liblog_rust",
+        "libpvmfw_fdt_template",
+    ],
+    data: [
+        ":test_pvmfw_devices_vm_dtbo",
+        ":test_pvmfw_devices_vm_dtbo_without_symbols",
+        ":test_pvmfw_devices_with_rng",
+        ":test_pvmfw_devices_with_rng_iommu",
+        ":test_pvmfw_devices_with_multiple_devices_iommus",
+        ":test_pvmfw_devices_with_iommu_sharing",
+        ":test_pvmfw_devices_with_iommu_id_conflict",
+    ],
+    // To use libpvmfw_fdt_template for testing
+    enabled: false,
+    target: {
+        android_arm64: {
+            enabled: true,
+        },
+    },
+}
+
+rust_test {
+    name: "libpvmfw.dice.test",
+    srcs: ["src/dice.rs"],
+    defaults: ["libpvmfw.test.defaults"],
+    rustlibs: [
+        "libcbor_util",
+        "libciborium",
+        "libdiced_open_dice_nostd",
+        "libpvmfw_avb_nostd",
+    ],
+}
+
 genrule {
     name: "test_pvmfw_devices_vm_dtbo",
     defaults: ["dts_to_dtb"],
@@ -110,39 +157,6 @@
     out: ["test_pvmfw_devices_with_iommu_id_conflict.dtb"],
 }
 
-rust_test {
-    name: "libpvmfw.device_assignment.test",
-    srcs: ["src/device_assignment.rs"],
-    defaults: ["avf_build_flags_rust"],
-    test_suites: ["general-tests"],
-    test_options: {
-        unit_test: true,
-    },
-    prefer_rlib: true,
-    rustlibs: [
-        "libcstr",
-        "liblibfdt",
-        "liblog_rust",
-        "libpvmfw_fdt_template",
-    ],
-    data: [
-        ":test_pvmfw_devices_vm_dtbo",
-        ":test_pvmfw_devices_vm_dtbo_without_symbols",
-        ":test_pvmfw_devices_with_rng",
-        ":test_pvmfw_devices_with_rng_iommu",
-        ":test_pvmfw_devices_with_multiple_devices_iommus",
-        ":test_pvmfw_devices_with_iommu_sharing",
-        ":test_pvmfw_devices_with_iommu_id_conflict",
-    ],
-    // To use libpvmfw_fdt_template for testing
-    enabled: false,
-    target: {
-        android_arm64: {
-            enabled: true,
-        },
-    },
-}
-
 cc_binary {
     name: "pvmfw",
     defaults: ["vmbase_elf_defaults"],
diff --git a/pvmfw/TEST_MAPPING b/pvmfw/TEST_MAPPING
index f21318e..e948400 100644
--- a/pvmfw/TEST_MAPPING
+++ b/pvmfw/TEST_MAPPING
@@ -10,6 +10,9 @@
     },
     {
       "name" : "libpvmfw.device_assignment.test"
+    },
+    {
+      "name" : "libpvmfw.dice.test"
     }
   ]
 }
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index 112c24c..99bf589 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -14,16 +14,13 @@
 
 //! Support for DICE derivation and BCC generation.
 
-use core::ffi::c_void;
 use core::mem::size_of;
-use core::slice;
 use cstr::cstr;
 use diced_open_dice::{
     bcc_format_config_descriptor, bcc_handover_main_flow, hash, Config, DiceConfigValues, DiceMode,
     Hash, InputValues, HIDDEN_SIZE,
 };
-use pvmfw_avb::{DebugLevel, Digest, VerifiedBootData};
-use vmbase::memory::flushed_zeroize;
+use pvmfw_avb::{Capability, DebugLevel, Digest, VerifiedBootData};
 
 fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
     match debug_level {
@@ -46,6 +43,7 @@
     pub auth_hash: Hash,
     pub mode: DiceMode,
     pub security_version: u64,
+    pub rkp_vm_marker: bool,
 }
 
 impl PartialInputs {
@@ -55,8 +53,9 @@
         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;
+        let rkp_vm_marker = data.has_capability(Capability::RemoteAttest);
 
-        Ok(Self { code_hash, auth_hash, mode, security_version })
+        Ok(Self { code_hash, auth_hash, mode, security_version, rkp_vm_marker })
     }
 
     pub fn write_next_bcc(
@@ -66,15 +65,7 @@
         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")),
-            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];
+        let config = self.generate_config_descriptor(&mut config_descriptor_buffer)?;
 
         let dice_inputs = InputValues::new(
             self.code_hash,
@@ -86,17 +77,138 @@
         let _ = bcc_handover_main_flow(current_bcc_handover, &dice_inputs, next_bcc)?;
         Ok(())
     }
+
+    fn generate_config_descriptor<'a>(
+        &self,
+        config_descriptor_buffer: &'a mut [u8],
+    ) -> diced_open_dice::Result<&'a [u8]> {
+        let config_values = DiceConfigValues {
+            component_name: Some(cstr!("vm_entry")),
+            security_version: if cfg!(dice_changes) { Some(self.security_version) } else { None },
+            rkp_vm_marker: self.rkp_vm_marker,
+            ..Default::default()
+        };
+        let config_descriptor_size =
+            bcc_format_config_descriptor(&config_values, config_descriptor_buffer)?;
+        let config = &config_descriptor_buffer[..config_descriptor_size];
+        Ok(config)
+    }
 }
 
 /// Flushes data caches over the provided address range.
 ///
 /// # Safety
 ///
-/// The provided address and size must be to a valid address range (typically on the stack, .bss,
-/// .data, or provided BCC).
+/// The provided address and size must be to an address range that is valid for read and write
+/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
+/// (e.g. stack array).
 #[no_mangle]
-unsafe extern "C" fn DiceClearMemory(_ctx: *mut c_void, size: usize, addr: *mut c_void) {
-    // SAFETY: We must trust that the slice will be valid arrays/variables on the C code stack.
+#[cfg(not(test))]
+unsafe extern "C" fn DiceClearMemory(
+    _ctx: *mut core::ffi::c_void,
+    size: usize,
+    addr: *mut core::ffi::c_void,
+) {
+    use core::slice;
+    use vmbase::memory::flushed_zeroize;
+
+    // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
+    // always calls this on individual stack-allocated arrays which ensures that.
     let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
     flushed_zeroize(region)
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use ciborium::Value;
+    use std::collections::HashMap;
+    use std::vec;
+
+    const COMPONENT_NAME_KEY: i64 = -70002;
+    const COMPONENT_VERSION_KEY: i64 = -70003;
+    const RESETTABLE_KEY: i64 = -70004;
+    const SECURITY_VERSION_KEY: i64 = -70005;
+    const RKP_VM_MARKER_KEY: i64 = -70006;
+
+    const BASE_VB_DATA: VerifiedBootData = VerifiedBootData {
+        debug_level: DebugLevel::None,
+        kernel_digest: [1u8; size_of::<Digest>()],
+        initrd_digest: Some([2u8; size_of::<Digest>()]),
+        public_key: b"public key",
+        capabilities: vec![],
+        rollback_index: 42,
+    };
+
+    #[test]
+    fn base_data_conversion() {
+        let vb_data = BASE_VB_DATA;
+        let inputs = PartialInputs::new(&vb_data).unwrap();
+
+        assert_eq!(inputs.mode, DiceMode::kDiceModeNormal);
+        assert_eq!(inputs.security_version, 42);
+        assert!(!inputs.rkp_vm_marker);
+
+        // TODO(b/313608219): Consider checks for code_hash and possibly auth_hash.
+    }
+
+    #[test]
+    fn debuggable_conversion() {
+        let vb_data = VerifiedBootData { debug_level: DebugLevel::Full, ..BASE_VB_DATA };
+        let inputs = PartialInputs::new(&vb_data).unwrap();
+
+        assert_eq!(inputs.mode, DiceMode::kDiceModeDebug);
+    }
+
+    #[test]
+    fn rkp_vm_conversion() {
+        let vb_data =
+            VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
+        let inputs = PartialInputs::new(&vb_data).unwrap();
+
+        assert!(inputs.rkp_vm_marker);
+    }
+
+    #[test]
+    fn base_config_descriptor() {
+        let vb_data = BASE_VB_DATA;
+        let inputs = PartialInputs::new(&vb_data).unwrap();
+        let config_map = decode_config_descriptor(&inputs);
+
+        assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
+        assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
+        assert_eq!(config_map.get(&RESETTABLE_KEY), None);
+        if cfg!(dice_changes) {
+            assert_eq!(
+                config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
+                42.into()
+            );
+        } else {
+            assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
+        }
+        assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
+    }
+
+    #[test]
+    fn config_descriptor_with_rkp_vm() {
+        let vb_data =
+            VerifiedBootData { capabilities: vec![Capability::RemoteAttest], ..BASE_VB_DATA };
+        let inputs = PartialInputs::new(&vb_data).unwrap();
+        let config_map = decode_config_descriptor(&inputs);
+
+        assert!(config_map.get(&RKP_VM_MARKER_KEY).unwrap().is_null());
+    }
+
+    fn decode_config_descriptor(inputs: &PartialInputs) -> HashMap<i64, Value> {
+        let mut buffer = [0; 128];
+        let config_descriptor = inputs.generate_config_descriptor(&mut buffer).unwrap();
+
+        let cbor_map =
+            cbor_util::deserialize::<Value>(config_descriptor).unwrap().into_map().unwrap();
+
+        cbor_map
+            .into_iter()
+            .map(|(k, v)| ((k.into_integer().unwrap().try_into().unwrap()), v))
+            .collect()
+    }
+}
diff --git a/secretkeeper/comm/Android.bp b/secretkeeper/comm/Android.bp
deleted file mode 100644
index cb3e713..0000000
--- a/secretkeeper/comm/Android.bp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
-    name: "libsecretkeeper_comm.defaults",
-    crate_name: "secretkeeper_comm",
-    defaults: ["avf_build_flags_rust"],
-    edition: "2021",
-    lints: "android",
-    rustlibs: [
-        "libciborium",
-        "libcoset",
-    ],
-    proc_macros: ["libenumn"],
-    vendor_available: true,
-}
-
-rust_library {
-    name: "libsecretkeeper_comm_nostd",
-    defaults: ["libsecretkeeper_comm.defaults"],
-    srcs: ["src/lib.rs"],
-}
-
-rust_test {
-    name: "libsecretkeeper_comm.test",
-    defaults: [
-        "libsecretkeeper_comm.defaults",
-        "rdroidtest.defaults",
-    ],
-    srcs: ["tests/*.rs"],
-    test_suites: ["general-tests"],
-    rustlibs: [
-        "libsecretkeeper_comm_nostd",
-    ],
-}
diff --git a/secretkeeper/comm/src/cbor_convert.rs b/secretkeeper/comm/src/cbor_convert.rs
deleted file mode 100644
index ab6ca3f..0000000
--- a/secretkeeper/comm/src/cbor_convert.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Implements various useful CBOR conversion method.
-
-use crate::data_types::error::Error;
-use alloc::vec::Vec;
-use ciborium::Value;
-
-/// Decodes the provided binary CBOR-encoded value and returns a
-/// [`ciborium::Value`] struct wrapped in Result.
-pub fn value_from_bytes(mut bytes: &[u8]) -> Result<Value, Error> {
-    let value = ciborium::de::from_reader(&mut bytes).map_err(|_| Error::ConversionError)?;
-    // Ciborium tries to read one Value, but doesn't care if there is trailing data after it. We do
-    if !bytes.is_empty() {
-        return Err(Error::ConversionError);
-    }
-    Ok(value)
-}
-
-/// Encodes a [`ciborium::Value`] into bytes.
-pub fn value_to_bytes(value: &Value) -> Result<Vec<u8>, Error> {
-    let mut bytes: Vec<u8> = Vec::new();
-    ciborium::ser::into_writer(&value, &mut bytes).map_err(|_| Error::UnexpectedError)?;
-    Ok(bytes)
-}
-
-// Useful to convert [`ciborium::Value`] to integer, we return largest integer range for
-// convenience, callers should downcast into appropriate type.
-pub fn value_to_integer(value: &Value) -> Result<i128, Error> {
-    let num = value.as_integer().ok_or(Error::ConversionError)?.into();
-    Ok(num)
-}
diff --git a/secretkeeper/comm/src/data_types/error.rs b/secretkeeper/comm/src/data_types/error.rs
deleted file mode 100644
index 6a5e24f..0000000
--- a/secretkeeper/comm/src/data_types/error.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Error-like data structures. See `ResponsePacketError` in the CDDL
-
-// derive(N) generates a method that is missing a docstring.
-#![allow(missing_docs)]
-
-use crate::cbor_convert::value_to_integer;
-use crate::data_types::response::Response;
-use alloc::boxed::Box;
-use alloc::vec::Vec;
-use ciborium::Value;
-use enumn::N;
-
-/// 'Error code' corresponding to successful response.
-pub const ERROR_OK: u16 = 0; // All real errors must have non-zero error_codes
-
-/// Errors from Secretkeeper API. Keep in sync with `ErrorCode` defined for Secretkeeper HAL
-/// at SecretManagement.cddl
-#[derive(Clone, Copy, Debug, Eq, N, PartialEq)]
-pub enum SecretkeeperError {
-    // This is the Error code used if no other error codes explains the issue.
-    UnexpectedServerError = 1,
-    // Indicates the Request was malformed & hence couldn't be served.
-    RequestMalformed = 2,
-    // TODO(b/291228655): Add other errors such as DicePolicyError.
-}
-
-// [`SecretkeeperError`] is a valid [`Response`] type.
-// For more information see `ErrorCode` in SecretManagement.cddl alongside ISecretkeeper.aidl
-impl Response for SecretkeeperError {
-    fn new(response_cbor: Vec<Value>) -> Result<Box<Self>, Error> {
-        // TODO(b/291228655): This method currently discards the second value in response_cbor,
-        // which contains additional human-readable context in error. Include it!
-        if response_cbor.is_empty() || response_cbor.len() > 2 {
-            return Err(Error::ResponseMalformed);
-        }
-        let error_code: u16 = value_to_integer(&response_cbor[0])?.try_into()?;
-        SecretkeeperError::n(error_code)
-            .map_or_else(|| Err(Error::ResponseMalformed), |sk_err| Ok(Box::new(sk_err)))
-    }
-
-    fn error_code(&self) -> u16 {
-        *self as u16
-    }
-}
-
-/// Errors thrown internally by the library.
-#[derive(Debug, PartialEq)]
-pub enum Error {
-    /// Request was malformed.
-    RequestMalformed,
-    /// Response received from the server was malformed.
-    ResponseMalformed,
-    /// An error happened when serializing to/from a [`Value`].
-    CborValueError,
-    /// An error happened while casting a type to different type,
-    /// including one [`Value`] type to another.
-    ConversionError,
-    /// These are unexpected errors, which should never really happen.
-    UnexpectedError,
-}
-
-impl From<ciborium::value::Error> for Error {
-    fn from(_e: ciborium::value::Error) -> Self {
-        Self::CborValueError
-    }
-}
-
-impl From<ciborium::Value> for Error {
-    fn from(_e: ciborium::Value) -> Self {
-        Self::ConversionError
-    }
-}
-
-impl From<core::num::TryFromIntError> for Error {
-    fn from(_e: core::num::TryFromIntError) -> Self {
-        Self::ConversionError
-    }
-}
diff --git a/secretkeeper/comm/src/data_types/mod.rs b/secretkeeper/comm/src/data_types/mod.rs
deleted file mode 100644
index 096777f..0000000
--- a/secretkeeper/comm/src/data_types/mod.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Implements the data structures specified by SecretManagement.cddl in Secretkeeper HAL.
-//!  Data structures specified by SecretManagement.cddl in Secretkeeper HAL.
-//!  Note this library must stay in sync with:
-//!      platform/hardware/interfaces/security/\
-//!      secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl
-
-pub mod error;
-pub mod packet;
-pub mod request;
-pub mod request_response_impl;
-pub mod response;
diff --git a/secretkeeper/comm/src/data_types/packet.rs b/secretkeeper/comm/src/data_types/packet.rs
deleted file mode 100644
index 7a1e575..0000000
--- a/secretkeeper/comm/src/data_types/packet.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Defines the packet structures passed between functional layer & the layer below.
-
-pub use ciborium::Value;
-
-use crate::cbor_convert::{value_from_bytes, value_to_bytes, value_to_integer};
-use crate::data_types::error::Error;
-use crate::data_types::error::ERROR_OK;
-use crate::data_types::request_response_impl::Opcode;
-use alloc::vec::Vec;
-
-/// Encapsulate Request-like data that functional layer operates on. All structures
-/// that implements `data_types::request::Request` can be serialized to [`ResponsePacket`].
-/// Similarly all [`RequestPacket`] can be deserialized to concrete Requests.
-/// Keep in sync with HAL spec (in particular RequestPacket):
-///     security/secretkeeper/aidl/android/hardware/security/secretkeeper/SecretManagement.cddl
-#[derive(Clone, Debug, PartialEq)]
-pub struct RequestPacket(Vec<Value>);
-
-impl RequestPacket {
-    /// Construct a [`RequestPacket`] from array of `ciborium::Value`
-    pub fn from(request_cbor: Vec<Value>) -> Self {
-        Self(request_cbor)
-    }
-
-    /// Get the containing CBOR. This can be used for getting concrete response objects.
-    /// Keep in sync with [`crate::data_types::request::Request::serialize_to_packet()`]
-    pub fn into_inner(self) -> Vec<Value> {
-        self.0
-    }
-
-    /// Extract [`Opcode`] corresponding to this packet. As defined in by the spec, this is
-    /// the first value in the CBOR array.
-    pub fn opcode(&self) -> Result<Opcode, Error> {
-        if self.0.is_empty() {
-            return Err(Error::RequestMalformed);
-        }
-        let num: u16 = value_to_integer(&self.0[0])?.try_into()?;
-
-        Opcode::n(num).ok_or(Error::RequestMalformed)
-    }
-
-    /// Serialize the [`ResponsePacket`] to bytes
-    pub fn into_bytes(self) -> Result<Vec<u8>, Error> {
-        value_to_bytes(&Value::Array(self.0))
-    }
-
-    /// Deserialize the bytes into [`ResponsePacket`]
-    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
-        Ok(RequestPacket(value_from_bytes(bytes)?.into_array()?))
-    }
-}
-
-/// Encapsulate Response like data that the functional layer operates on. All structures
-/// that implements `data_types::response::Response` can be serialized to [`ResponsePacket`].
-/// Similarly all [`ResponsePacket`] can be deserialized to concrete Response.
-#[derive(Clone, Debug, PartialEq)]
-pub struct ResponsePacket(Vec<Value>);
-
-impl ResponsePacket {
-    /// Construct a [`ResponsePacket`] from array of `ciborium::Value`
-    pub fn from(response_cbor: Vec<Value>) -> Self {
-        Self(response_cbor)
-    }
-
-    /// Get raw content. This can be used for getting concrete response objects.
-    /// Keep in sync with `crate::data_types::response::Response::serialize_to_packet`
-    pub fn into_inner(self) -> Vec<Value> {
-        self.0
-    }
-
-    /// A [`ResponsePacket`] encapsulates different types of responses, find which one!
-    pub fn response_type(&self) -> Result<ResponseType, Error> {
-        if self.0.is_empty() {
-            return Err(Error::ResponseMalformed);
-        }
-        let error_code: u16 = value_to_integer(&self.0[0])?.try_into()?;
-        if error_code == ERROR_OK {
-            Ok(ResponseType::Success)
-        } else {
-            Ok(ResponseType::Error)
-        }
-    }
-
-    /// Serialize the [`ResponsePacket`] to bytes
-    pub fn into_bytes(self) -> Result<Vec<u8>, Error> {
-        value_to_bytes(&Value::Array(self.0))
-    }
-
-    /// Deserialize the bytes into [`ResponsePacket`]
-    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
-        Ok(ResponsePacket(value_from_bytes(bytes)?.into_array()?))
-    }
-}
-
-/// Responses can be different type - `Success`-like or `Error`-like.
-#[derive(Debug, Eq, PartialEq)]
-pub enum ResponseType {
-    /// Indicates successful operation. See `ResponsePacketSuccess` in SecretManagement.cddl
-    Success,
-    /// Indicate failed operation. See `ResponsePacketError` in SecretManagement.cddl
-    Error,
-}
diff --git a/secretkeeper/comm/src/data_types/request.rs b/secretkeeper/comm/src/data_types/request.rs
deleted file mode 100644
index 0d54bcd..0000000
--- a/secretkeeper/comm/src/data_types/request.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Defines the shared behaviour of all request like data structures.
-
-use crate::data_types::error::Error;
-use crate::data_types::packet::RequestPacket;
-use crate::data_types::request_response_impl::Opcode;
-use alloc::boxed::Box;
-use alloc::vec::Vec;
-use ciborium::Value;
-
-/// Collection of methods defined for Secretkeeper's request-like data structures,
-/// e.g. `GetVersionRequestPacket` in the HAL spec.
-///
-/// Keep in sync with SecretManagement.cddl, in particular `RequestPacket` type.
-pub trait Request {
-    /// [`Opcode`] of the request: Each Request type is associated with an opcode. See `Opcode` in
-    /// SecretManagement.cddl.
-    const OPCODE: Opcode;
-
-    /// Constructor of the [`Request`] object. Implementation of this constructor should check
-    /// the args' type adheres to the HAL spec.
-    ///
-    /// # Arguments
-    /// * `args` - The vector of arguments associated with this request. Each argument is a
-    ///   `ciborium::Value` type. See `Params` in `RequestPacket` in SecretManagement.cddl
-    fn new(args: Vec<Value>) -> Result<Box<Self>, Error>;
-
-    /// Get the 'arguments' of this request.
-    fn args(&self) -> Vec<Value>;
-
-    /// Serialize the request to a [`RequestPacket`], which, as per SecretManagement.cddl is:
-    /// ```
-    ///      RequestPacket<Opcode, Params> = [
-    ///         Opcode,
-    ///         Params
-    ///      ]
-    /// ```
-    fn serialize_to_packet(&self) -> RequestPacket {
-        let mut res = self.args();
-        res.insert(0, Value::from(Self::OPCODE as u16));
-        RequestPacket::from(res)
-    }
-
-    /// Construct the [`Request`] struct from given [`RequestPacket`].
-    fn deserialize_from_packet(packet: RequestPacket) -> Result<Box<Self>, Error> {
-        let mut req = packet.into_inner();
-        if req.get(0) != Some(&Value::from(Self::OPCODE as u16)) {
-            return Err(Error::RequestMalformed);
-        }
-        req.remove(0);
-        Self::new(req)
-    }
-}
diff --git a/secretkeeper/comm/src/data_types/request_response_impl.rs b/secretkeeper/comm/src/data_types/request_response_impl.rs
deleted file mode 100644
index a7d29cc..0000000
--- a/secretkeeper/comm/src/data_types/request_response_impl.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Implementation of request & response like data structures.
-
-// derive(N) generates a method that is missing a docstring.
-#![allow(missing_docs)]
-
-use crate::cbor_convert::value_to_integer;
-use crate::data_types::error::Error;
-use crate::data_types::error::ERROR_OK;
-use crate::data_types::request::Request;
-use crate::data_types::response::Response;
-use alloc::boxed::Box;
-use alloc::vec;
-use alloc::vec::Vec;
-use ciborium::Value;
-use enumn::N;
-
-/// Set of all possible `Opcode` supported by SecretManagement API of the HAL.
-/// See `Opcode` in SecretManagement.cddl
-#[derive(Clone, Copy, Debug, N, PartialEq)]
-#[non_exhaustive]
-pub enum Opcode {
-    /// Get version of the SecretManagement API.
-    GetVersion = 1,
-    /// Store a secret
-    StoreSecret = 2,
-    /// Get the secret
-    GetSecret = 3,
-}
-
-/// Corresponds to `GetVersionRequestPacket` defined in SecretManagement.cddl
-#[derive(Debug, Eq, PartialEq)]
-pub struct GetVersionRequest;
-
-impl Request for GetVersionRequest {
-    const OPCODE: Opcode = Opcode::GetVersion;
-
-    fn new(args: Vec<Value>) -> Result<Box<Self>, Error> {
-        if !args.is_empty() {
-            return Err(Error::RequestMalformed);
-        }
-        Ok(Box::new(Self))
-    }
-
-    fn args(&self) -> Vec<Value> {
-        Vec::new()
-    }
-}
-
-/// Success response corresponding to `GetVersionResponsePacket`.
-#[derive(Debug, Eq, PartialEq)]
-pub struct GetVersionResponse {
-    /// Version of SecretManagement API
-    version: u64,
-}
-
-impl GetVersionResponse {
-    pub fn new(version: u64) -> Self {
-        Self { version }
-    }
-    pub fn version(&self) -> u64 {
-        self.version
-    }
-}
-
-impl Response for GetVersionResponse {
-    fn new(res: Vec<Value>) -> Result<Box<Self>, Error> {
-        if res.len() != 2 {
-            return Err(Error::ResponseMalformed);
-        }
-        let error_code: u16 = value_to_integer(&res[0])?.try_into()?;
-        if error_code != ERROR_OK {
-            return Err(Error::ResponseMalformed);
-        }
-        let version: u64 = value_to_integer(&res[1])?.try_into()?;
-        Ok(Box::new(Self::new(version)))
-    }
-
-    fn result(&self) -> Vec<Value> {
-        vec![self.version.into()]
-    }
-}
diff --git a/secretkeeper/comm/src/data_types/response.rs b/secretkeeper/comm/src/data_types/response.rs
deleted file mode 100644
index e975ebc..0000000
--- a/secretkeeper/comm/src/data_types/response.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Defines the shared behaviour of all response like data structures.
-
-use crate::data_types::error::{Error, ERROR_OK};
-use crate::data_types::packet::ResponsePacket;
-use alloc::boxed::Box;
-use alloc::vec::Vec;
-use ciborium::Value;
-
-/// Shared behaviour of all Secretkeeper's response-like data structures,
-/// e.g. `GetVersionResponsePacket`. Note - A valid [`Response`] can be error as well, like
-/// `SecretkeeperError::RequestMalformed`.
-///
-/// Keep in sync with SecretManagement.cddl, in particular `ResponsePacket` type.
-pub trait Response {
-    /// Constructor of the Response object.
-    /// # Arguments
-    /// * `response_cbor`: A vector of `[ciborium::Value]` such that:
-    /// ```
-    ///     For success-like responses:
-    ///         ResponsePacketSuccess = [
-    ///             0,                          ; Indicates successful Response
-    ///             result : Result
-    ///         ]
-    ///     For error responses:
-    ///         ResponsePacketError = [
-    ///             error_code: ErrorCode,      ; Indicate the error
-    ///             error_message: tstr         ; Additional human-readable context
-    ///         ]
-    /// ```
-    /// See ResponsePacket<Result> in SecretManagement.cddl alongside ISecretkeeper.aidl
-    fn new(response_cbor: Vec<Value>) -> Result<Box<Self>, Error>;
-
-    /// The result in the `Response`. By default this is empty, but [`Response`] structures like
-    /// `GetVersionResponse` must overwrite these to return the expected non-empty result.
-    fn result(&self) -> Vec<Value> {
-        Vec::new()
-    }
-
-    /// Error code corresponding to the response. The default value is 0 but that will work only
-    /// for successful responses. Error-like response structures must overwrite this method.
-    fn error_code(&self) -> u16 {
-        ERROR_OK // Indicates success
-    }
-
-    /// Serialize the response to a [`ResponsePacket`].
-    fn serialize_to_packet(&self) -> ResponsePacket {
-        let mut res = self.result();
-        res.insert(0, Value::from(self.error_code()));
-        ResponsePacket::from(res)
-    }
-
-    /// Construct the response struct from given [`ResponsePacket`].
-    fn deserialize_from_packet(packet: ResponsePacket) -> Result<Box<Self>, Error> {
-        let res = packet.into_inner();
-        // Empty response packet is not allowed, all responses in Secretkeeper HAL at least
-        // have `error_code` or '0'; so throw an error!
-        if res.is_empty() {
-            return Err(Error::ResponseMalformed);
-        }
-        Self::new(res)
-    }
-}
diff --git a/secretkeeper/comm/src/lib.rs b/secretkeeper/comm/src/lib.rs
deleted file mode 100644
index 9a10ac0..0000000
--- a/secretkeeper/comm/src/lib.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! This library exposes data structures and methods that can be used by Secretkeeper HAL & client
-//! implementation. This is compatible with Secretkeeper HAL specification.
-
-#![no_std]
-extern crate alloc;
-
-mod cbor_convert;
-pub mod data_types;
diff --git a/secretkeeper/comm/tests/data_types.rs b/secretkeeper/comm/tests/data_types.rs
deleted file mode 100644
index 68964fd..0000000
--- a/secretkeeper/comm/tests/data_types.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! Unit tests for testing serialization & deserialization of exported data_types.
-
-use ciborium::Value;
-use secretkeeper_comm::data_types::error::{Error, SecretkeeperError, ERROR_OK};
-use secretkeeper_comm::data_types::packet::{RequestPacket, ResponsePacket, ResponseType};
-use secretkeeper_comm::data_types::request::Request;
-use secretkeeper_comm::data_types::request_response_impl::Opcode;
-use secretkeeper_comm::data_types::request_response_impl::{GetVersionRequest, GetVersionResponse};
-use secretkeeper_comm::data_types::response::Response;
-
-#[cfg(test)]
-rdroidtest::test_main!();
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use rdroidtest::test;
-
-    test!(request_serialization_deserialization);
-    fn request_serialization_deserialization() {
-        let req = GetVersionRequest {};
-        let packet = req.serialize_to_packet();
-        assert_eq!(packet.opcode().unwrap(), Opcode::GetVersion);
-        assert_eq!(
-            RequestPacket::from_bytes(&packet.clone().into_bytes().unwrap()).unwrap(),
-            packet
-        );
-        let req_deserialized = *GetVersionRequest::deserialize_from_packet(packet).unwrap();
-        assert_eq!(req, req_deserialized);
-    }
-
-    test!(success_response_serialization_deserialization);
-    fn success_response_serialization_deserialization() {
-        let response = GetVersionResponse::new(1);
-        let packet = response.serialize_to_packet();
-        assert_eq!(packet.response_type().unwrap(), ResponseType::Success);
-        assert_eq!(
-            ResponsePacket::from_bytes(&packet.clone().into_bytes().unwrap()).unwrap(),
-            packet
-        );
-        let response_deserialized = *GetVersionResponse::deserialize_from_packet(packet).unwrap();
-        assert_eq!(response, response_deserialized);
-    }
-
-    test!(error_response_serialization_deserialization);
-    fn error_response_serialization_deserialization() {
-        let response = SecretkeeperError::RequestMalformed;
-        let packet = response.serialize_to_packet();
-        assert_eq!(packet.response_type().unwrap(), ResponseType::Error);
-        assert_eq!(
-            ResponsePacket::from_bytes(&packet.clone().into_bytes().unwrap()).unwrap(),
-            packet
-        );
-        let response_deserialized = *SecretkeeperError::deserialize_from_packet(packet).unwrap();
-        assert_eq!(response, response_deserialized);
-    }
-
-    test!(request_creation);
-    fn request_creation() {
-        let req: GetVersionRequest = *Request::new(vec![]).unwrap();
-        assert_eq!(req, GetVersionRequest {});
-    }
-
-    test!(response_creation);
-    fn response_creation() {
-        let res: GetVersionResponse =
-            *Response::new(vec![Value::from(ERROR_OK), Value::from(5)]).unwrap();
-        assert_eq!(res.version(), 5);
-    }
-
-    test!(invalid_get_version_request_creation);
-    fn invalid_get_version_request_creation() {
-        // A request with non-zero arg is considered invalid.
-        assert_eq!(
-            <GetVersionRequest as Request>::new(vec![Value::Null]).unwrap_err(),
-            Error::RequestMalformed
-        );
-    }
-
-    test!(invalid_get_version_response_creation);
-    fn invalid_get_version_response_creation() {
-        // A response with non-zero error_code is an invalid success response.
-        assert_eq!(
-            <GetVersionResponse as Response>::new(vec![
-                Value::from(SecretkeeperError::RequestMalformed as u16),
-                Value::from(5)
-            ])
-            .unwrap_err(),
-            Error::ResponseMalformed
-        );
-
-        // A response with incorrect size of array is invalid.
-        assert_eq!(
-            <GetVersionResponse as Response>::new(vec![
-                Value::from(ERROR_OK),
-                Value::from(5),
-                Value::from(7)
-            ])
-            .unwrap_err(),
-            Error::ResponseMalformed
-        );
-
-        // A response with incorrect type is invalid.
-        <GetVersionResponse as Response>::new(vec![Value::from(ERROR_OK), Value::from("a tstr")])
-            .unwrap_err();
-    }
-
-    test!(invalid_error_response_creation);
-    fn invalid_error_response_creation() {
-        // A response with ERROR_OK(0) as the error_code is an invalid error response.
-        assert_eq!(
-            <SecretkeeperError as Response>::new(vec![Value::from(ERROR_OK)]).unwrap_err(),
-            Error::ResponseMalformed
-        );
-    }
-}
diff --git a/secretkeeper/dice_policy/Android.bp b/secretkeeper/dice_policy/Android.bp
deleted file mode 100644
index 4f1e8b6..0000000
--- a/secretkeeper/dice_policy/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
-    name: "libdice_policy.defaults",
-    crate_name: "dice_policy",
-    defaults: ["avf_build_flags_rust"],
-    srcs: ["src/lib.rs"],
-    edition: "2021",
-    prefer_rlib: true,
-    rustlibs: [
-        "libanyhow",
-        "libciborium",
-        "libcoset",
-        "libnum_traits",
-    ],
-    proc_macros: ["libnum_derive"],
-}
-
-rust_library {
-    name: "libdice_policy",
-    defaults: ["libdice_policy.defaults"],
-}
-
-rust_test {
-    name: "libdice_policy.test",
-    defaults: [
-        "libdice_policy.defaults",
-        "rdroidtest.defaults",
-    ],
-    test_suites: ["general-tests"],
-    rustlibs: [
-        "librustutils",
-        "libscopeguard",
-    ],
-}
diff --git a/secretkeeper/dice_policy/src/lib.rs b/secretkeeper/dice_policy/src/lib.rs
deleted file mode 100644
index 076ba3b..0000000
--- a/secretkeeper/dice_policy/src/lib.rs
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-//! A “DICE policy” is a format for setting constraints on a DICE chain. A DICE chain policy
-//! verifier takes a policy and a DICE chain, and returns a boolean indicating whether the
-//! DICE chain meets the constraints set out on a policy.
-//!
-//! This forms the foundation of Dice Policy aware Authentication (DPA-Auth), where the server
-//! authenticates a client by comparing its dice chain against a set policy.
-//!
-//! Another use is "sealing", where clients can use an appropriately constructed dice policy to
-//! seal a secret. Unsealing is only permitted if dice chain of the component requesting unsealing
-//! complies with the policy.
-//!
-//! A typical policy will assert things like:
-//! # DK_pub must have this value
-//! # The DICE chain must be exactly five certificates long
-//! # authorityHash in the third certificate must have this value
-//! securityVersion in the fourth certificate must be an integer greater than 8
-//!
-//! These constraints used to express policy are (for now) limited to following 2 types:
-//! 1. Exact Match: useful for enforcing rules like authority hash should be exactly equal.
-//! 2. Greater than or equal to: Useful for setting policies that seal
-//! Anti-rollback protected entities (should be accessible to versions >= present).
-//!
-//! Dice Policy CDDL:
-//!
-//! dicePolicy = [
-//! 1, ; dice policy version
-//! + nodeConstraintList ; for each entry in dice chain
-//! ]
-//!
-//! nodeConstraintList = [
-//!     * nodeConstraint
-//! ]
-//!
-//! ; We may add a hashConstraint item later
-//! nodeConstraint = exactMatchConstraint / geConstraint
-//!
-//! exactMatchConstraint = [1, keySpec, value]
-//! geConstraint = [2, keySpec, int]
-//!
-//! keySpec = [value+]
-//!
-//! value = bool / int / tstr / bstr
-
-use anyhow::{anyhow, bail, ensure, Context, Result};
-use ciborium::Value;
-use coset::{AsCborValue, CoseSign1};
-use num_derive::FromPrimitive;
-use num_traits::FromPrimitive;
-use std::borrow::Cow;
-use std::iter::zip;
-
-const DICE_POLICY_VERSION: u64 = 1;
-
-/// Constraint Types supported in Dice policy.
-#[repr(u16)]
-#[non_exhaustive]
-#[derive(Clone, Copy, Debug, FromPrimitive, PartialEq)]
-pub enum ConstraintType {
-    /// Enforce exact match criteria, indicating the policy should match
-    /// if the dice chain has exact same specified values.
-    ExactMatch = 1,
-    /// Enforce Greater than or equal to criteria. When applied on security_version, this
-    /// can be useful to set policy that matches dice chains with same or upgraded images.
-    GreaterOrEqual = 2,
-}
-
-/// ConstraintSpec is used to specify which constraint type to apply and
-/// on which all entries in a dice node.
-/// See documentation of `from_dice_chain()` for examples.
-pub struct ConstraintSpec {
-    constraint_type: ConstraintType,
-    // path is essentially a list of label/int.
-    // It identifies which entry (in a dice node) to be applying constraints on.
-    path: Vec<i64>,
-}
-
-impl ConstraintSpec {
-    /// Construct the ConstraintSpec.
-    pub fn new(constraint_type: ConstraintType, path: Vec<i64>) -> Result<Self> {
-        Ok(ConstraintSpec { constraint_type, path })
-    }
-}
-
-// TODO(b/291238565): Restrict (nested_)key & value type to (bool/int/tstr/bstr).
-// and maybe convert it into struct.
-/// Each constraint (on a dice node) is a tuple: (ConstraintType, constraint_path, value)
-#[derive(Debug, PartialEq)]
-struct Constraint(u16, Vec<i64>, Value);
-
-/// List of all constraints on a dice node.
-#[derive(Debug, PartialEq)]
-struct NodeConstraints(Box<[Constraint]>);
-
-/// Module for working with dice policy.
-#[derive(Debug, PartialEq)]
-pub struct DicePolicy {
-    version: u64,
-    node_constraints_list: Box<[NodeConstraints]>, // Constraint on each entry in dice chain.
-}
-
-impl DicePolicy {
-    /// Construct a dice policy from a given dice chain.
-    /// This can be used by clients to construct a policy to seal secrets.
-    /// Constraints on all but first dice node is applied using constraint_spec argument.
-    /// For the first node (which is a ROT key), the constraint is ExactMatch of the whole node.
-    ///
-    /// # Arguments
-    /// `dice_chain`: The serialized CBOR encoded Dice chain, adhering to Android Profile for DICE.
-    /// https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/android.md
-    ///
-    /// `constraint_spec`: List of constraints to be applied on dice node.
-    /// Each constraint is a ConstraintSpec object.
-    ///
-    /// Note: Dice node is treated as a nested map (& so the lookup is done in that fashion).
-    ///
-    /// Examples of constraint_spec:
-    ///  1. For exact_match on auth_hash & greater_or_equal on security_version
-    ///    constraint_spec =[
-    ///     (ConstraintType::ExactMatch, vec![AUTHORITY_HASH]),
-    ///     (ConstraintType::GreaterOrEqual, vec![CONFIG_DESC, COMPONENT_NAME]),
-    ///    ];
-    ///
-    /// 2. For hypothetical (and highly simplified) dice chain:
-    ///
-    ///    [ROT_KEY, [{1 : 'a', 2 : {200 : 5, 201 : 'b'}}]]
-    ///    The following can be used
-    ///    constraint_spec =[
-    ///     ConstraintSpec(ConstraintType::ExactMatch, vec![1]),         // exact_matches value 'a'
-    ///     ConstraintSpec(ConstraintType::GreaterOrEqual, vec![2, 200]),// matches any value >= 5
-    ///    ];
-    pub fn from_dice_chain(dice_chain: &[u8], constraint_spec: &[ConstraintSpec]) -> Result<Self> {
-        let dice_chain = deserialize_dice_chain(dice_chain)?;
-        let mut constraints_list: Vec<NodeConstraints> = Vec::with_capacity(dice_chain.len());
-        let mut it = dice_chain.into_iter();
-
-        constraints_list.push(NodeConstraints(Box::new([Constraint(
-            ConstraintType::ExactMatch as u16,
-            Vec::new(),
-            it.next().unwrap(),
-        )])));
-
-        for (n, value) in it.enumerate() {
-            let entry = cbor_value_from_cose_sign(value)
-                .with_context(|| format!("Unable to get Cose payload at: {}", n))?;
-            constraints_list.push(payload_to_constraints(entry, constraint_spec)?);
-        }
-
-        Ok(DicePolicy {
-            version: DICE_POLICY_VERSION,
-            node_constraints_list: constraints_list.into_boxed_slice(),
-        })
-    }
-
-    /// Dice chain policy verifier - Compare the input dice chain against this Dice policy.
-    /// The method returns Ok() if the dice chain meets the constraints set in Dice policy,
-    /// otherwise returns error in case of mismatch.
-    /// TODO(b/291238565) Create a separate error module for DicePolicy mismatches.
-    pub fn matches_dice_chain(&self, dice_chain: &[u8]) -> Result<()> {
-        let dice_chain = deserialize_dice_chain(dice_chain)?;
-        ensure!(
-            dice_chain.len() == self.node_constraints_list.len(),
-            format!(
-                "Dice chain size({}) does not match policy({})",
-                dice_chain.len(),
-                self.node_constraints_list.len()
-            )
-        );
-
-        for (n, (dice_node, node_constraints)) in
-            zip(dice_chain, self.node_constraints_list.iter()).enumerate()
-        {
-            let dice_node_payload = if n == 0 {
-                dice_node
-            } else {
-                cbor_value_from_cose_sign(dice_node)
-                    .with_context(|| format!("Unable to get Cose payload at: {}", n))?
-            };
-            check_constraints_on_node(node_constraints, &dice_node_payload)
-                .context(format!("Mismatch found at {}", n))?;
-        }
-        Ok(())
-    }
-}
-
-fn check_constraints_on_node(node_constraints: &NodeConstraints, dice_node: &Value) -> Result<()> {
-    for constraint in node_constraints.0.iter() {
-        check_constraint_on_node(constraint, dice_node)?;
-    }
-    Ok(())
-}
-
-fn check_constraint_on_node(constraint: &Constraint, dice_node: &Value) -> Result<()> {
-    let Constraint(cons_type, path, value_in_constraint) = constraint;
-    let value_in_node = lookup_value_in_nested_map(dice_node, path)?;
-    match ConstraintType::from_u16(*cons_type).ok_or(anyhow!("Unexpected Constraint type"))? {
-        ConstraintType::ExactMatch => ensure!(value_in_node == *value_in_constraint),
-        ConstraintType::GreaterOrEqual => {
-            let value_in_node = value_in_node
-                .as_integer()
-                .ok_or(anyhow!("Mismatch type: expected a CBOR integer"))?;
-            let value_min = value_in_constraint
-                .as_integer()
-                .ok_or(anyhow!("Mismatch type: expected a CBOR integer"))?;
-            ensure!(value_in_node >= value_min);
-        }
-    };
-    Ok(())
-}
-
-// Take the payload of a dice node & construct the constraints on it.
-fn payload_to_constraints(
-    payload: Value,
-    constraint_spec: &[ConstraintSpec],
-) -> Result<NodeConstraints> {
-    let mut node_constraints: Vec<Constraint> = Vec::with_capacity(constraint_spec.len());
-    for constraint_item in constraint_spec {
-        let constraint_path = constraint_item.path.to_vec();
-        if constraint_path.is_empty() {
-            bail!("Expected non-empty key spec");
-        }
-        let val = lookup_value_in_nested_map(&payload, &constraint_path)
-            .context(format!("Value not found for constraint_path {:?}", constraint_path))?;
-        let constraint = Constraint(constraint_item.constraint_type as u16, constraint_path, val);
-        node_constraints.push(constraint);
-    }
-    Ok(NodeConstraints(node_constraints.into_boxed_slice()))
-}
-
-// Lookup value corresponding to constraint path in nested map.
-// This function recursively calls itself.
-// The depth of recursion is limited by the size of constraint_path.
-fn lookup_value_in_nested_map(cbor_map: &Value, constraint_path: &[i64]) -> Result<Value> {
-    if constraint_path.is_empty() {
-        return Ok(cbor_map.clone());
-    }
-    let explicit_map = get_map_from_value(cbor_map)?;
-    let val = lookup_value_in_map(&explicit_map, constraint_path[0])
-        .ok_or(anyhow!("Value not found for constraint key: {:?}", constraint_path[0]))?;
-    lookup_value_in_nested_map(val, &constraint_path[1..])
-}
-
-fn get_map_from_value(cbor_map: &Value) -> Result<Cow<Vec<(Value, Value)>>> {
-    match cbor_map {
-        Value::Bytes(b) => value_from_bytes(b)?
-            .into_map()
-            .map(Cow::Owned)
-            .map_err(|e| anyhow!("Expected a CBOR map: {:?}", e)),
-        Value::Map(map) => Ok(Cow::Borrowed(map)),
-        _ => bail!("Expected a CBOR map {:?}", cbor_map),
-    }
-}
-
-fn lookup_value_in_map(map: &[(Value, Value)], key: i64) -> Option<&Value> {
-    let key = Value::Integer(key.into());
-    for (k, v) in map.iter() {
-        if k == &key {
-            return Some(v);
-        }
-    }
-    None
-}
-
-/// Extract the payload from the COSE Sign
-fn cbor_value_from_cose_sign(cbor: Value) -> Result<Value> {
-    let sign1 =
-        CoseSign1::from_cbor_value(cbor).map_err(|e| anyhow!("Error extracting CoseKey: {}", e))?;
-    match sign1.payload {
-        None => bail!("Missing payload"),
-        Some(payload) => Ok(value_from_bytes(&payload)?),
-    }
-}
-fn deserialize_dice_chain(dice_chain_bytes: &[u8]) -> Result<Vec<Value>> {
-    // TODO(b/298217847): Check if the given dice chain adheres to Explicit-key DiceCertChain
-    // format and if not, convert it.
-    let dice_chain =
-        value_from_bytes(dice_chain_bytes).context("Unable to decode top-level CBOR")?;
-    let dice_chain = match dice_chain {
-        Value::Array(array) if array.len() >= 2 => array,
-        _ => bail!("Expected an array of at least length 2, found: {:?}", dice_chain),
-    };
-    Ok(dice_chain)
-}
-
-/// Decodes the provided binary CBOR-encoded value and returns a
-/// ciborium::Value struct wrapped in Result.
-fn value_from_bytes(mut bytes: &[u8]) -> Result<Value> {
-    let value = ciborium::de::from_reader(&mut bytes)?;
-    // Ciborium tries to read one Value, & doesn't care if there is trailing data after it. We do.
-    if !bytes.is_empty() {
-        bail!("Unexpected trailing data while converting to CBOR value");
-    }
-    Ok(value)
-}
-
-#[cfg(test)]
-rdroidtest::test_main!();
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use ciborium::cbor;
-    use coset::{CoseKey, Header, ProtectedHeader};
-    use rdroidtest::test;
-
-    const AUTHORITY_HASH: i64 = -4670549;
-    const CONFIG_DESC: i64 = -4670548;
-    const COMPONENT_NAME: i64 = -70002;
-    const KEY_MODE: i64 = -4670551;
-
-    // Helper struct to encapsulate artifacts that are useful for unit tests.
-    struct TestArtifacts {
-        // A dice chain.
-        input_dice: Vec<u8>,
-        // A list of ConstraintSpec that can be applied on the input_dice to get a dice policy.
-        constraint_spec: Vec<ConstraintSpec>,
-        // The expected dice policy if above constraint_spec is applied to input_dice.
-        expected_dice_policy: DicePolicy,
-        // Another dice chain, which is almost same as the input_dice, but (roughly) imitates
-        // an 'updated' one, ie, some int entries are higher than corresponding entry
-        // in input_chain.
-        updated_input_dice: Vec<u8>,
-    }
-
-    impl TestArtifacts {
-        // Get an example instance of TestArtifacts. This uses a hard coded, hypothetical
-        // chain of certificates & a list of constraint_spec on this.
-        fn get_example() -> Self {
-            const EXAMPLE_NUM_1: i64 = 59765;
-            const EXAMPLE_NUM_2: i64 = 59766;
-            const EXAMPLE_STRING: &str = "testing_dice_policy";
-            const UNCONSTRAINED_STRING: &str = "unconstrained_string";
-            const ANOTHER_UNCONSTRAINED_STRING: &str = "another_unconstrained_string";
-
-            let rot_key = CoseKey::default().to_cbor_value().unwrap();
-            let input_dice = Self::get_dice_chain_helper(
-                rot_key.clone(),
-                EXAMPLE_NUM_1,
-                EXAMPLE_STRING,
-                UNCONSTRAINED_STRING,
-            );
-
-            // Now construct constraint_spec on the input dice, note this will use the keys
-            // which are also hardcoded within the get_dice_chain_helper.
-
-            let constraint_spec = vec![
-                ConstraintSpec::new(ConstraintType::ExactMatch, vec![1]).unwrap(),
-                // Notice how key "2" is (deliberately) absent in ConstraintSpec
-                // so policy should not constrain it.
-                ConstraintSpec::new(ConstraintType::GreaterOrEqual, vec![3, 100]).unwrap(),
-            ];
-            let expected_dice_policy = DicePolicy {
-                version: 1,
-                node_constraints_list: Box::new([
-                    NodeConstraints(Box::new([Constraint(
-                        ConstraintType::ExactMatch as u16,
-                        vec![],
-                        rot_key.clone(),
-                    )])),
-                    NodeConstraints(Box::new([
-                        Constraint(
-                            ConstraintType::ExactMatch as u16,
-                            vec![1],
-                            Value::Text(EXAMPLE_STRING.to_string()),
-                        ),
-                        Constraint(
-                            ConstraintType::GreaterOrEqual as u16,
-                            vec![3, 100],
-                            Value::from(EXAMPLE_NUM_1),
-                        ),
-                    ])),
-                ]),
-            };
-
-            let updated_input_dice = Self::get_dice_chain_helper(
-                rot_key.clone(),
-                EXAMPLE_NUM_2,
-                EXAMPLE_STRING,
-                ANOTHER_UNCONSTRAINED_STRING,
-            );
-            Self { input_dice, constraint_spec, expected_dice_policy, updated_input_dice }
-        }
-
-        // Helper method method to generate a dice chain with a given rot_key.
-        // Other arguments are ad-hoc values in the nested map. Callers use these to
-        // construct appropriate constrains in dice policies.
-        fn get_dice_chain_helper(
-            rot_key: Value,
-            version: i64,
-            constrained_string: &str,
-            unconstrained_string: &str,
-        ) -> Vec<u8> {
-            let nested_payload = cbor!({
-                100 => version
-            })
-            .unwrap();
-
-            let payload = cbor!({
-                1 => constrained_string,
-                2 => unconstrained_string,
-                3 => Value::Bytes(value_to_bytes(&nested_payload).unwrap()),
-            })
-            .unwrap();
-            let payload = value_to_bytes(&payload).unwrap();
-            let dice_node = CoseSign1 {
-                protected: ProtectedHeader::default(),
-                unprotected: Header::default(),
-                payload: Some(payload),
-                signature: b"ddef".to_vec(),
-            }
-            .to_cbor_value()
-            .unwrap();
-            let input_dice = Value::Array([rot_key.clone(), dice_node].to_vec());
-
-            value_to_bytes(&input_dice).unwrap()
-        }
-    }
-
-    test!(policy_structure_check);
-    fn policy_structure_check() {
-        let example = TestArtifacts::get_example();
-        let policy =
-            DicePolicy::from_dice_chain(&example.input_dice, &example.constraint_spec).unwrap();
-
-        // Assert policy is exactly as expected!
-        assert_eq!(policy, example.expected_dice_policy);
-    }
-
-    test!(policy_matches_original_dice_chain);
-    fn policy_matches_original_dice_chain() {
-        let example = TestArtifacts::get_example();
-        assert!(
-            DicePolicy::from_dice_chain(&example.input_dice, &example.constraint_spec)
-                .unwrap()
-                .matches_dice_chain(&example.input_dice)
-                .is_ok(),
-            "The dice chain did not match the policy constructed out of it!"
-        );
-    }
-
-    test!(policy_matches_updated_dice_chain);
-    fn policy_matches_updated_dice_chain() {
-        let example = TestArtifacts::get_example();
-        assert!(
-            DicePolicy::from_dice_chain(&example.input_dice, &example.constraint_spec)
-                .unwrap()
-                .matches_dice_chain(&example.updated_input_dice)
-                .is_ok(),
-            "The updated dice chain did not match the original policy!"
-        );
-    }
-
-    test!(policy_mismatch_downgraded_dice_chain);
-    fn policy_mismatch_downgraded_dice_chain() {
-        let example = TestArtifacts::get_example();
-        assert!(
-            DicePolicy::from_dice_chain(&example.updated_input_dice, &example.constraint_spec)
-                .unwrap()
-                .matches_dice_chain(&example.input_dice)
-                .is_err(),
-            "The (downgraded) dice chain matched the policy constructed out of the 'updated'\
-            dice chain!!"
-        );
-    }
-
-    test!(policy_dice_size_is_same);
-    fn policy_dice_size_is_same() {
-        // This is the number of certs in compos bcc (including the first ROT)
-        // To analyze a bcc use hwtrust tool from /tools/security/remote_provisioning/hwtrust
-        // `hwtrust --verbose dice-chain [path]/composbcc`
-        let compos_dice_chain_size: usize = 5;
-        let input_dice = include_bytes!("../testdata/composbcc");
-        let constraint_spec = [
-            ConstraintSpec::new(ConstraintType::ExactMatch, vec![AUTHORITY_HASH]).unwrap(),
-            ConstraintSpec::new(ConstraintType::ExactMatch, vec![KEY_MODE]).unwrap(),
-            ConstraintSpec::new(ConstraintType::GreaterOrEqual, vec![CONFIG_DESC, COMPONENT_NAME])
-                .unwrap(),
-        ];
-        let policy = DicePolicy::from_dice_chain(input_dice, &constraint_spec).unwrap();
-        assert_eq!(policy.node_constraints_list.len(), compos_dice_chain_size);
-    }
-
-    /// Encodes a ciborium::Value into bytes.
-    fn value_to_bytes(value: &Value) -> Result<Vec<u8>> {
-        let mut bytes: Vec<u8> = Vec::new();
-        ciborium::ser::into_writer(&value, &mut bytes)?;
-        Ok(bytes)
-    }
-}
diff --git a/secretkeeper/dice_policy/testdata/composbcc b/secretkeeper/dice_policy/testdata/composbcc
deleted file mode 100644
index fb3e006..0000000
--- a/secretkeeper/dice_policy/testdata/composbcc
+++ /dev/null
Binary files differ
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index acd182c..c63ed4c 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -66,6 +66,7 @@
 use log::{debug, error, info, warn};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
 use nix::unistd::pipe;
+use regex::Regex;
 use rpcbinder::RpcServer;
 use rustutils::system_properties;
 use semver::VersionReq;
@@ -101,8 +102,6 @@
 
 const MICRODROID_OS_NAME: &str = "microdroid";
 
-const MICRODROID_GKI_OS_NAME: &str = "microdroid_gki";
-
 const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
 
 /// Roughly estimated sufficient size for storing vendor public key into DTBO.
@@ -115,6 +114,8 @@
     pub static ref GLOBAL_SERVICE: Strong<dyn IVirtualizationServiceInternal> =
         wait_for_interface(BINDER_SERVICE_IDENTIFIER)
             .expect("Could not connect to VirtualizationServiceInternal");
+    static ref MICRODROID_GKI_OS_NAME_PATTERN: Regex =
+        Regex::new(r"^microdroid_gki-\d+\.\d+$").expect("Failed to construct Regex");
 }
 
 fn create_or_update_idsig_file(
@@ -708,12 +709,12 @@
 
 fn is_valid_os(os_name: &str) -> bool {
     if os_name == MICRODROID_OS_NAME {
-        return true;
+        true
+    } else if cfg!(vendor_modules) && MICRODROID_GKI_OS_NAME_PATTERN.is_match(os_name) {
+        PathBuf::from(format!("/apex/com.android.virt/etc/{}.json", os_name)).exists()
+    } else {
+        false
     }
-    if cfg!(vendor_modules) && os_name == MICRODROID_GKI_OS_NAME {
-        return true;
-    }
-    false
 }
 
 fn load_app_config(
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 87278bc..9a92f13 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -27,6 +27,7 @@
 use clap::{Args, Parser};
 use create_idsig::command_create_idsig;
 use create_partition::command_create_partition;
+use glob::glob;
 use run::{command_run, command_run_app, command_run_microdroid};
 use std::num::NonZeroU16;
 use std::path::{Path, PathBuf};
@@ -107,10 +108,10 @@
     #[arg(long)]
     devices: Vec<PathBuf>,
 
-    /// If set, use GKI instead of microdroid kernel
+    /// Version of GKI to use. If set, use instead of microdroid kernel
     #[cfg(vendor_modules)]
     #[arg(long)]
-    gki: bool,
+    gki: Option<String>,
 }
 
 impl MicrodroidConfig {
@@ -125,13 +126,13 @@
     }
 
     #[cfg(vendor_modules)]
-    fn gki(&self) -> bool {
-        self.gki
+    fn gki(&self) -> Option<&str> {
+        self.gki.as_deref()
     }
 
     #[cfg(not(vendor_modules))]
-    fn gki(&self) -> bool {
-        false
+    fn gki(&self) -> Option<&str> {
+        None
     }
 
     #[cfg(device_assignment)]
@@ -315,6 +316,12 @@
     Ok(())
 }
 
+fn extract_gki_version(gki_config: &Path) -> Option<&str> {
+    let name = gki_config.file_name()?;
+    let name_str = name.to_str()?;
+    name_str.strip_prefix("microdroid_gki-")?.strip_suffix(".json")
+}
+
 /// Print information about supported VM types.
 fn command_info() -> Result<(), Error> {
     let non_protected_vm_supported = hypervisor_props::is_vm_supported()?;
@@ -354,6 +361,12 @@
     let devices = devices.into_iter().map(|x| x.node).collect::<Vec<_>>();
     println!("Assignable devices: {}", serde_json::to_string(&devices)?);
 
+    let gki_configs =
+        glob("/apex/com.android.virt/etc/microdroid_gki-*.json")?.collect::<Result<Vec<_>, _>>()?;
+    let gki_versions =
+        gki_configs.iter().filter_map(|x| extract_gki_version(x)).collect::<Vec<_>>();
+    println!("Available gki versions: {}", serde_json::to_string(&gki_versions)?);
+
     Ok(())
 }
 
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 44ba9af..8721e71 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -111,8 +111,11 @@
         }
         Payload::ConfigPath(config_path)
     } else if let Some(payload_binary_name) = config.payload_binary_name {
-        let os_name =
-            if config.microdroid.gki() { "microdroid_gki" } else { "microdroid" }.to_owned();
+        let os_name = if let Some(ver) = config.microdroid.gki() {
+            format!("microdroid_gki-{ver}")
+        } else {
+            "microdroid".to_owned()
+        };
         Payload::PayloadConfig(VirtualMachinePayloadConfig {
             payloadBinaryName: payload_binary_name,
             osName: os_name,