Merge "Add android version to gki version name" into main
diff --git a/apkdmverity/Android.bp b/apkdmverity/Android.bp
index c4c90cd..0cb8ca1 100644
--- a/apkdmverity/Android.bp
+++ b/apkdmverity/Android.bp
@@ -15,6 +15,7 @@
         "libbitflags",
         "libclap",
         "libdm_rust",
+        "libhex",
         "libitertools",
         "liblibc",
         "libnix",
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index d9e9e2b..0ecb0ea 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -46,7 +46,7 @@
 
     for (apk, idsig, name, roothash) in apks.tuples() {
         let roothash = if roothash != "none" {
-            Some(util::parse_hexstring(roothash).expect("failed to parse roothash"))
+            Some(hex::decode(roothash).expect("failed to parse roothash"))
         } else {
             None
         };
@@ -108,8 +108,10 @@
             bail!("The size of {:?} is not multiple of {}.", &apk, BLOCK_SIZE)
         }
         (
-            loopdevice::attach(&apk, 0, apk_size, /*direct_io*/ true, /*writable*/ false)
-                .context("Failed to attach APK to a loop device")?,
+            loopdevice::attach(
+                &apk, 0, apk_size, /* direct_io */ true, /* writable */ false,
+            )
+            .context("Failed to attach APK to a loop device")?,
             apk_size,
         )
     };
@@ -123,9 +125,10 @@
     // Due to unknown reason(b/191344832), we can't enable "direct IO" for the IDSIG file (backing
     // the hash). For now we don't use "direct IO" but it seems OK since the IDSIG file is very
     // small and the benefit of direct-IO would be negliable.
-    let hash_device =
-        loopdevice::attach(&idsig, offset, size, /*direct_io*/ false, /*writable*/ false)
-            .context("Failed to attach idsig to a loop device")?;
+    let hash_device = loopdevice::attach(
+        &idsig, offset, size, /* direct_io */ false, /* writable */ false,
+    )
+    .context("Failed to attach idsig to a loop device")?;
 
     // Build a dm-verity target spec from the information from the idsig file. The apk and the
     // idsig files are used as the data device and the hash device, respectively.
@@ -338,7 +341,7 @@
         // of the data device is done in the scopeguard for the return value of `enable_verity`
         // below. Only the idsig_loop_device needs detatching.
         let apk_loop_device = loopdevice::attach(
-            &apk_path, 0, apk_size, /*direct_io*/ true, /*writable*/ false,
+            &apk_path, 0, apk_size, /* direct_io */ true, /* writable */ false,
         )
         .unwrap();
         let idsig_loop_device = scopeguard::guard(
@@ -346,8 +349,8 @@
                 &idsig_path,
                 0,
                 idsig_size,
-                /*direct_io*/ false,
-                /*writable*/ false,
+                /* direct_io */ false,
+                /* writable */ false,
             )
             .unwrap(),
             |dev| loopdevice::detach(dev).unwrap(),
diff --git a/authfs/src/fsverity/builder.rs b/authfs/src/fsverity/builder.rs
index 8585fdf..6d724ca 100644
--- a/authfs/src/fsverity/builder.rs
+++ b/authfs/src/fsverity/builder.rs
@@ -159,7 +159,7 @@
     #[test]
     fn merkle_tree_empty_file() -> Result<()> {
         assert_eq!(
-            to_u8_vec("3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95"),
+            hex::decode("3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95")?,
             generate_fsverity_digest_sequentially(&Vec::new())?
         );
         Ok(())
@@ -169,7 +169,7 @@
     fn merkle_tree_file_size_less_than_or_equal_to_4k() -> Result<()> {
         // Test a file that contains 4096 '\01's.
         assert_eq!(
-            to_u8_vec("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af"),
+            hex::decode("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af")?,
             generate_fsverity_digest_sequentially(&vec![1; 4096])?
         );
         Ok(())
@@ -180,24 +180,24 @@
         // Test files that contains >4096 '\01's.
 
         assert_eq!(
-            to_u8_vec("2901b849fda2d91e3929524561c4a47e77bb64734319759507b2029f18b9cc52"),
+            hex::decode("2901b849fda2d91e3929524561c4a47e77bb64734319759507b2029f18b9cc52")?,
             generate_fsverity_digest_sequentially(&vec![1; 4097])?
         );
 
         assert_eq!(
-            to_u8_vec("2a476d58eb80394052a3a783111e1458ac3ecf68a7878183fed86ca0ff47ec0d"),
+            hex::decode("2a476d58eb80394052a3a783111e1458ac3ecf68a7878183fed86ca0ff47ec0d")?,
             generate_fsverity_digest_sequentially(&vec![1; 8192])?
         );
 
         // Test with max size that still fits in 2 levels.
         assert_eq!(
-            to_u8_vec("26b7c190a34e19f420808ee7ec233b09fa6c34543b5a9d2950530114c205d14f"),
+            hex::decode("26b7c190a34e19f420808ee7ec233b09fa6c34543b5a9d2950530114c205d14f")?,
             generate_fsverity_digest_sequentially(&vec![1; 524288])?
         );
 
         // Test with data that requires 3 levels.
         assert_eq!(
-            to_u8_vec("316835d9be1c95b5cd55d07ae7965d651689efad186e26cbf680e40b683a3262"),
+            hex::decode("316835d9be1c95b5cd55d07ae7965d651689efad186e26cbf680e40b683a3262")?,
             generate_fsverity_digest_sequentially(&vec![1; 524289])?
         );
         Ok(())
@@ -215,7 +215,7 @@
         tree.update_hash(2, &hash, CHUNK_SIZE * 3);
 
         assert_eq!(
-            to_u8_vec("7d3c0d2e1dc54230b20ed875f5f3a4bd3f9873df601936b3ca8127d4db3548f3"),
+            hex::decode("7d3c0d2e1dc54230b20ed875f5f3a4bd3f9873df601936b3ca8127d4db3548f3")?,
             tree.calculate_fsverity_digest()?
         );
         Ok(())
@@ -268,12 +268,4 @@
         }
         Ok(tree.calculate_fsverity_digest()?)
     }
-
-    fn to_u8_vec(hex_str: &str) -> Vec<u8> {
-        assert!(hex_str.len() % 2 == 0);
-        (0..hex_str.len())
-            .step_by(2)
-            .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap())
-            .collect()
-    }
 }
diff --git a/authfs/src/fsverity/editor.rs b/authfs/src/fsverity/editor.rs
index 4af6e80..c84500b 100644
--- a/authfs/src/fsverity/editor.rs
+++ b/authfs/src/fsverity/editor.rs
@@ -373,7 +373,7 @@
         let file = VerifiedFileEditor::new(InMemoryEditor::new());
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95")
+            hex::decode("3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95")?
                 .as_slice()
         );
         Ok(())
@@ -386,7 +386,7 @@
         assert_eq!(file.write_at(&[1; 4096], 0)?, 4096);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af")
+            hex::decode("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af")?
                 .as_slice()
         );
 
@@ -395,7 +395,7 @@
         assert_eq!(file.write_at(&[1; 4097], 0)?, 4097);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("2901b849fda2d91e3929524561c4a47e77bb64734319759507b2029f18b9cc52")
+            hex::decode("2901b849fda2d91e3929524561c4a47e77bb64734319759507b2029f18b9cc52")?
                 .as_slice()
         );
 
@@ -404,7 +404,7 @@
         assert_eq!(file.write_at(&[1; 10000], 0)?, 10000);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("7545409b556071554d18973a29b96409588c7cda4edd00d5586b27a11e1a523b")
+            hex::decode("7545409b556071554d18973a29b96409588c7cda4edd00d5586b27a11e1a523b")?
                 .as_slice()
         );
         Ok(())
@@ -417,7 +417,7 @@
         assert_eq!(file.write_at(&[1; 5], 3)?, 5);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("a23fc5130d3d7b3323fc4b4a5e79d5d3e9ddf3a3f5872639e867713512c6702f")
+            hex::decode("a23fc5130d3d7b3323fc4b4a5e79d5d3e9ddf3a3f5872639e867713512c6702f")?
                 .as_slice()
         );
 
@@ -426,7 +426,7 @@
         assert_eq!(file.write_at(&[1; 6000], 4000)?, 6000);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("d16d4c1c186d757e646f76208b21254f50d7f07ea07b1505ff48b2a6f603f989")
+            hex::decode("d16d4c1c186d757e646f76208b21254f50d7f07ea07b1505ff48b2a6f603f989")?
                 .as_slice()
         );
         Ok(())
@@ -439,7 +439,7 @@
         assert_eq!(file.write_at(&[1; 4096], 4096)?, 4096);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("4df2aefd8c2a9101d1d8770dca3ede418232eabce766bb8e020395eae2e97103")
+            hex::decode("4df2aefd8c2a9101d1d8770dca3ede418232eabce766bb8e020395eae2e97103")?
                 .as_slice()
         );
 
@@ -448,7 +448,7 @@
         assert_eq!(file.write_at(&[1; 5000], 6000)?, 5000);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("47d5da26f6934484e260630a69eb2eebb21b48f69bc8fbf8486d1694b7dba94f")
+            hex::decode("47d5da26f6934484e260630a69eb2eebb21b48f69bc8fbf8486d1694b7dba94f")?
                 .as_slice()
         );
 
@@ -457,7 +457,7 @@
         assert_eq!(file.write_at(&[1; 5], 16381)?, 5);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("8bd118821fb4aff26bb4b51d485cc481a093c68131b7f4f112e9546198449752")
+            hex::decode("8bd118821fb4aff26bb4b51d485cc481a093c68131b7f4f112e9546198449752")?
                 .as_slice()
         );
         Ok(())
@@ -470,34 +470,34 @@
         assert_eq!(file.write_at(&[1; 2048], 4096 + 2048)?, 2048);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("4c433d8640c888b629dc673d318cbb8d93b1eebcc784d9353e07f09f0dcfe707")
+            hex::decode("4c433d8640c888b629dc673d318cbb8d93b1eebcc784d9353e07f09f0dcfe707")?
                 .as_slice()
         );
         assert_eq!(file.write_at(&[1; 2048], 2048)?, 2048);
         assert_eq!(file.write_at(&[1; 2048], 4096)?, 2048);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("2a476d58eb80394052a3a783111e1458ac3ecf68a7878183fed86ca0ff47ec0d")
+            hex::decode("2a476d58eb80394052a3a783111e1458ac3ecf68a7878183fed86ca0ff47ec0d")?
                 .as_slice()
         );
         assert_eq!(file.write_at(&[0; 2048], 2048)?, 2048);
         assert_eq!(file.write_at(&[0; 2048], 4096)?, 2048);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("4c433d8640c888b629dc673d318cbb8d93b1eebcc784d9353e07f09f0dcfe707")
+            hex::decode("4c433d8640c888b629dc673d318cbb8d93b1eebcc784d9353e07f09f0dcfe707")?
                 .as_slice()
         );
         assert_eq!(file.write_at(&[1; 4096], 2048)?, 4096);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("2a476d58eb80394052a3a783111e1458ac3ecf68a7878183fed86ca0ff47ec0d")
+            hex::decode("2a476d58eb80394052a3a783111e1458ac3ecf68a7878183fed86ca0ff47ec0d")?
                 .as_slice()
         );
         assert_eq!(file.write_at(&[1; 2048], 8192)?, 2048);
         assert_eq!(file.write_at(&[1; 2048], 8192 + 2048)?, 2048);
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("23cbac08371e6ee838ebcc7ae6512b939d2226e802337be7b383c3e046047d24")
+            hex::decode("23cbac08371e6ee838ebcc7ae6512b939d2226e802337be7b383c3e046047d24")?
                 .as_slice()
         );
         Ok(())
@@ -555,7 +555,7 @@
 
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("fef1b4f19bb7a2cd944d7cdee44d1accb12726389ca5b0f61ac0f548ae40876f")
+            hex::decode("fef1b4f19bb7a2cd944d7cdee44d1accb12726389ca5b0f61ac0f548ae40876f")?
                 .as_slice()
         );
         Ok(())
@@ -572,7 +572,7 @@
 
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("9e0e2745c21e4e74065240936d2047340d96a466680c3c9d177b82433e7a0bb1")
+            hex::decode("9e0e2745c21e4e74065240936d2047340d96a466680c3c9d177b82433e7a0bb1")?
                 .as_slice()
         );
         Ok(())
@@ -589,7 +589,7 @@
 
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("fef1b4f19bb7a2cd944d7cdee44d1accb12726389ca5b0f61ac0f548ae40876f")
+            hex::decode("fef1b4f19bb7a2cd944d7cdee44d1accb12726389ca5b0f61ac0f548ae40876f")?
                 .as_slice()
         );
         Ok(())
@@ -621,17 +621,9 @@
 
         assert_eq!(
             file.calculate_fsverity_digest()?,
-            to_u8_vec("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af")
+            hex::decode("cd0875ca59c7d37e962c5e8f5acd3770750ac80225e2df652ce5672fd34500af")?
                 .as_slice()
         );
         Ok(())
     }
-
-    fn to_u8_vec(hex_str: &str) -> Vec<u8> {
-        assert!(hex_str.len() % 2 == 0);
-        (0..hex_str.len())
-            .step_by(2)
-            .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16).unwrap())
-            .collect()
-    }
 }
diff --git a/libs/bssl/Android.bp b/libs/bssl/Android.bp
index e1f4ffd..bed3dfb 100644
--- a/libs/bssl/Android.bp
+++ b/libs/bssl/Android.bp
@@ -23,6 +23,7 @@
     rustlibs: [
         "libbssl_avf_error_nostd",
         "libbssl_ffi_nostd",
+        "libcbor_util_nostd",
         "libciborium_nostd",
         "libcoset_nostd",
         "liblog_rust_nostd",
diff --git a/libs/bssl/error/Android.bp b/libs/bssl/error/Android.bp
index dc2902e..000e385 100644
--- a/libs/bssl/error/Android.bp
+++ b/libs/bssl/error/Android.bp
@@ -21,6 +21,8 @@
         "libcore.rust_sysroot",
     ],
     rustlibs: [
+        "libcoset_nostd",
+        "liblog_rust_nostd",
         "libserde_nostd",
     ],
 }
@@ -32,6 +34,8 @@
         "std",
     ],
     rustlibs: [
+        "libcoset",
+        "liblog_rust",
         "libserde",
     ],
 }
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index df79104..7f01c6c 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -38,6 +38,9 @@
     /// Failed to decode the COSE_Key.
     CoseKeyDecodingFailed,
 
+    /// An error occurred when interacting with the coset crate.
+    CosetError,
+
     /// Unimplemented operation.
     Unimplemented,
 }
@@ -50,11 +53,21 @@
             }
             Self::InternalError => write!(f, "An unexpected internal error occurred"),
             Self::CoseKeyDecodingFailed => write!(f, "Failed to decode the COSE_Key"),
+            Self::CosetError => {
+                write!(f, "An error occurred when interacting with the coset crate")
+            }
             Self::Unimplemented => write!(f, "Unimplemented operation"),
         }
     }
 }
 
+impl From<coset::CoseError> for Error {
+    fn from(e: coset::CoseError) -> Self {
+        log::error!("Coset error: {e}");
+        Self::CosetError
+    }
+}
+
 /// BoringSSL API names.
 #[allow(missing_docs)]
 #[allow(non_camel_case_types)]
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 0c944cd..894934d 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -17,9 +17,7 @@
 
 use crate::cbb::CbbFixed;
 use crate::cbs::Cbs;
-use crate::util::{
-    check_int_result, get_label_value, get_label_value_as_bytes, to_call_failed_error,
-};
+use crate::util::{check_int_result, to_call_failed_error};
 use alloc::vec;
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
@@ -31,6 +29,7 @@
     EC_KEY_set_public_key_affine_coordinates, EC_POINT_get_affine_coordinates,
     NID_X9_62_prime256v1, NID_secp384r1, BIGNUM, EC_GROUP, EC_KEY, EC_POINT,
 };
+use cbor_util::{get_label_value, get_label_value_as_bytes};
 use ciborium::Value;
 use core::ptr::{self, NonNull};
 use coset::{
diff --git a/libs/bssl/src/evp.rs b/libs/bssl/src/evp.rs
index 86f99a8..fe3d88e 100644
--- a/libs/bssl/src/evp.rs
+++ b/libs/bssl/src/evp.rs
@@ -17,9 +17,7 @@
 use crate::cbb::CbbFixed;
 use crate::digest::{Digester, DigesterContext};
 use crate::ec_key::EcKey;
-use crate::util::{
-    check_int_result, get_label_value, get_label_value_as_bytes, to_call_failed_error,
-};
+use crate::util::{check_int_result, to_call_failed_error};
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
 use bssl_ffi::{
@@ -27,6 +25,7 @@
     EVP_PKEY_new_raw_public_key, EVP_PKEY_set1_EC_KEY, EVP_marshal_public_key, EVP_PKEY,
     EVP_PKEY_ED25519, EVP_PKEY_X25519,
 };
+use cbor_util::{get_label_value, get_label_value_as_bytes};
 use ciborium::Value;
 use core::ptr::{self, NonNull};
 use coset::{
diff --git a/libs/bssl/src/util.rs b/libs/bssl/src/util.rs
index 7473fe1..880c85b 100644
--- a/libs/bssl/src/util.rs
+++ b/libs/bssl/src/util.rs
@@ -16,8 +16,6 @@
 
 use crate::err::get_error_reason_code;
 use bssl_avf_error::{ApiName, Error, Result};
-use ciborium::Value;
-use coset::{CoseKey, Label};
 use log::error;
 
 pub(crate) fn check_int_result(ret: i32, api_name: ApiName) -> Result<()> {
@@ -37,14 +35,3 @@
 pub(crate) fn to_call_failed_error(api_name: ApiName) -> Error {
     Error::CallFailed(api_name, get_error_reason_code())
 }
-
-pub(crate) fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
-    Ok(get_label_value(key, label)?.as_bytes().ok_or_else(|| {
-        error!("Value not a bstr.");
-        Error::CoseKeyDecodingFailed
-    })?)
-}
-
-pub(crate) fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
-    Ok(&key.params.iter().find(|(k, _)| k == &label).ok_or(Error::CoseKeyDecodingFailed)?.1)
-}
diff --git a/libs/cborutil/Android.bp b/libs/cborutil/Android.bp
index 4758c4b..96dbf09 100644
--- a/libs/cborutil/Android.bp
+++ b/libs/cborutil/Android.bp
@@ -24,6 +24,7 @@
     rustlibs: [
         "libciborium_nostd",
         "libcoset_nostd",
+        "liblog_rust_nostd",
         "libserde_nostd",
     ],
 }
@@ -37,6 +38,7 @@
     rustlibs: [
         "libciborium",
         "libcoset",
+        "liblog_rust",
         "libserde",
     ],
 }
diff --git a/libs/cborutil/src/lib.rs b/libs/cborutil/src/lib.rs
index 2ec5af4..6e834f1 100644
--- a/libs/cborutil/src/lib.rs
+++ b/libs/cborutil/src/lib.rs
@@ -18,8 +18,11 @@
 
 extern crate alloc;
 
+use alloc::string::String;
 use alloc::vec::Vec;
-use coset::{CoseError, Result};
+use ciborium::value::{Integer, Value};
+use coset::{CoseError, CoseKey, Label, Result};
+use log::error;
 use serde::{de::DeserializeOwned, Serialize};
 
 /// Serializes the given data to a CBOR-encoded byte vector.
@@ -39,3 +42,88 @@
         Err(CoseError::ExtraneousData)
     }
 }
+
+/// Converts the provided value `v` to a value array.
+pub fn value_to_array(v: Value, context: &'static str) -> Result<Vec<Value>> {
+    v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context))
+}
+
+/// Converts the provided value `v` to a text string.
+pub fn value_to_text(v: Value, context: &'static str) -> Result<String> {
+    v.into_text().map_err(|e| to_unexpected_item_error(&e, "tstr", context))
+}
+
+/// Converts the provided value `v` to a map.
+pub fn value_to_map(v: Value, context: &'static str) -> Result<Vec<(Value, Value)>> {
+    v.into_map().map_err(|e| to_unexpected_item_error(&e, "map", context))
+}
+
+/// Converts the provided value `v` to a number.
+pub fn value_to_num<T: TryFrom<Integer>>(v: Value, context: &'static str) -> Result<T> {
+    let num = v.into_integer().map_err(|e| to_unexpected_item_error(&e, "int", context))?;
+    num.try_into().map_err(|_| {
+        error!("The provided value '{num:?}' is not a valid number: {context}");
+        CoseError::OutOfRangeIntegerValue
+    })
+}
+
+/// Converts the provided value `v` to a byte array of length `N`.
+pub fn value_to_byte_array<const N: usize>(v: Value, context: &'static str) -> Result<[u8; N]> {
+    let arr = value_to_bytes(v, context)?;
+    arr.try_into().map_err(|e| {
+        error!("The provided value '{context}' is not an array of length {N}: {e:?}");
+        CoseError::UnexpectedItem("bstr", "array of length {N}")
+    })
+}
+
+/// Converts the provided value `v` to bytes array.
+pub fn value_to_bytes(v: Value, context: &'static str) -> Result<Vec<u8>> {
+    v.into_bytes().map_err(|e| to_unexpected_item_error(&e, "bstr", context))
+}
+
+/// Builds a `CoseError::UnexpectedItem` error when the provided value `v` is not of the expected
+/// type `expected_type` and logs the error message with the provided `context`.
+pub fn to_unexpected_item_error(
+    v: &Value,
+    expected_type: &'static str,
+    context: &'static str,
+) -> CoseError {
+    let v_type = cbor_value_type(v);
+    assert!(v_type != expected_type);
+    error!("The provided value type '{v_type}' is not of type '{expected_type}': {context}");
+    CoseError::UnexpectedItem(v_type, expected_type)
+}
+
+/// Reads the type of the provided value `v`.
+pub fn cbor_value_type(v: &Value) -> &'static str {
+    match v {
+        Value::Integer(_) => "int",
+        Value::Bytes(_) => "bstr",
+        Value::Float(_) => "float",
+        Value::Text(_) => "tstr",
+        Value::Bool(_) => "bool",
+        Value::Null => "nul",
+        Value::Tag(_, _) => "tag",
+        Value::Array(_) => "array",
+        Value::Map(_) => "map",
+        _ => "other",
+    }
+}
+
+/// Returns the value of the given label in the given COSE key as bytes.
+pub fn get_label_value_as_bytes(key: &CoseKey, label: Label) -> Result<&[u8]> {
+    let v = get_label_value(key, label)?;
+    Ok(v.as_bytes().ok_or_else(|| {
+        to_unexpected_item_error(v, "bstr", "Get label value in CoseKey as bytes")
+    })?)
+}
+
+/// Returns the value of the given label in the given COSE key.
+pub fn get_label_value(key: &CoseKey, label: Label) -> Result<&Value> {
+    Ok(&key
+        .params
+        .iter()
+        .find(|(k, _)| k == &label)
+        .ok_or(CoseError::UnexpectedItem("", "Label not found in CoseKey"))?
+        .1)
+}
diff --git a/libs/devicemapper/src/util.rs b/libs/devicemapper/src/util.rs
index e8df424..cc071e4 100644
--- a/libs/devicemapper/src/util.rs
+++ b/libs/devicemapper/src/util.rs
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-use anyhow::{anyhow, bail, Result};
+use anyhow::{bail, Result};
 use nix::sys::stat::FileStat;
 use std::fs::File;
 use std::os::unix::fs::FileTypeExt;
@@ -52,24 +52,6 @@
     Ok(())
 }
 
-/// Returns hexadecimal reprentation of a given byte array.
-pub fn hexstring_from(s: &[u8]) -> String {
-    s.iter().map(|byte| format!("{:02x}", byte)).reduce(|i, j| i + &j).unwrap_or_default()
-}
-
-/// Parses a hexadecimal string into a byte array
-pub fn parse_hexstring(s: &str) -> Result<Vec<u8>> {
-    let len = s.len();
-    if len % 2 != 0 {
-        bail!("length {} is not even", len)
-    } else {
-        (0..len)
-            .step_by(2)
-            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|e| anyhow!(e)))
-            .collect()
-    }
-}
-
 /// fstat that accepts a path rather than FD
 pub fn fstat(p: &Path) -> Result<FileStat> {
     let f = File::open(p)?;
diff --git a/libs/devicemapper/src/verity.rs b/libs/devicemapper/src/verity.rs
index 24584f8..bbd9d38 100644
--- a/libs/devicemapper/src/verity.rs
+++ b/libs/devicemapper/src/verity.rs
@@ -151,7 +151,7 @@
         };
 
         let root_digest = if let Some(root_digest) = self.root_digest {
-            hexstring_from(root_digest)
+            hex::encode(root_digest)
         } else {
             bail!("root digest is not set")
         };
@@ -159,7 +159,7 @@
         let salt = if self.salt.is_none() || self.salt.unwrap().is_empty() {
             "-".to_string() // Note. It's not an empty string!
         } else {
-            hexstring_from(self.salt.unwrap())
+            hex::encode(self.salt.unwrap())
         };
 
         // Step2: serialize the information according to the spec, which is ...
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index 000f723..a524655 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -323,6 +323,29 @@
     }
 }
 
+/// Iterator over descendants
+#[derive(Debug)]
+pub struct DescendantsIterator<'a> {
+    node: Option<(FdtNode<'a>, usize)>,
+}
+
+impl<'a> DescendantsIterator<'a> {
+    pub(crate) fn new(node: &'a FdtNode) -> Self {
+        Self { node: Some((*node, 0)) }
+    }
+}
+
+impl<'a> Iterator for DescendantsIterator<'a> {
+    type Item = (FdtNode<'a>, usize);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let (node, depth) = self.node?;
+        self.node = node.next_node(depth).ok().flatten().filter(|(_, depth)| *depth > 0);
+
+        self.node
+    }
+}
+
 /// Iterator over properties
 #[derive(Debug)]
 pub struct PropertyIterator<'a> {
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index b513649..aae75f7 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -20,8 +20,8 @@
 mod iterators;
 
 pub use iterators::{
-    AddressRange, CellIterator, CompatibleIterator, MemRegIterator, PropertyIterator,
-    RangesIterator, Reg, RegIterator, SubnodeIterator,
+    AddressRange, CellIterator, CompatibleIterator, DescendantsIterator, MemRegIterator,
+    PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator,
 };
 
 use core::cmp::max;
@@ -486,6 +486,23 @@
         Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
     }
 
+    /// Returns an iterator of descendants
+    pub fn descendants(&'a self) -> DescendantsIterator<'a> {
+        DescendantsIterator::new(self)
+    }
+
+    fn next_node(&self, depth: usize) -> Result<Option<(Self, usize)>> {
+        let mut next_depth: c_int = depth.try_into().unwrap();
+        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let ret = unsafe {
+            libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
+        };
+        let Ok(next_depth) = usize::try_from(next_depth) else {
+            return Ok(None);
+        };
+        Ok(fdt_err_or_option(ret)?.map(|offset| (FdtNode { fdt: self.fdt, offset }, next_depth)))
+    }
+
     /// Returns an iterator of properties
     pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
         PropertyIterator::new(self)
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index 63cbdee..d5d6ece 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -308,3 +308,23 @@
     // Just check whether borrow checker doesn't complain this.
     memory.setprop_inplace(cstr!("device_type"), b"MEMORY\0").unwrap();
 }
+
+#[test]
+fn node_descendants() {
+    let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+    let node_z = fdt.node(cstr!("/node_z")).unwrap().unwrap();
+    let descendants: Vec<_> =
+        node_z.descendants().map(|(node, depth)| (node.name().unwrap(), depth)).collect();
+
+    assert_eq!(
+        descendants,
+        vec![
+            (cstr!("node_za"), 1),
+            (cstr!("node_zb"), 1),
+            (cstr!("node_zz"), 1),
+            (cstr!("node_zzz"), 2)
+        ]
+    );
+}
diff --git a/pvmfw/avb/src/partition.rs b/pvmfw/avb/src/partition.rs
index 3fe9479..c05a0ac 100644
--- a/pvmfw/avb/src/partition.rs
+++ b/pvmfw/avb/src/partition.rs
@@ -29,9 +29,9 @@
 impl PartitionName {
     pub(crate) const NUM_OF_KNOWN_PARTITIONS: usize = 3;
 
-    const KERNEL_PARTITION_NAME: &[u8] = b"boot\0";
-    const INITRD_NORMAL_PARTITION_NAME: &[u8] = b"initrd_normal\0";
-    const INITRD_DEBUG_PARTITION_NAME: &[u8] = b"initrd_debug\0";
+    const KERNEL_PARTITION_NAME: &'static [u8] = b"boot\0";
+    const INITRD_NORMAL_PARTITION_NAME: &'static [u8] = b"initrd_normal\0";
+    const INITRD_DEBUG_PARTITION_NAME: &'static [u8] = b"initrd_debug\0";
 
     pub(crate) fn as_cstr(&self) -> &CStr {
         CStr::from_bytes_with_nul(self.as_bytes()).unwrap()
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index ac015e0..a85dbbb 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -68,9 +68,9 @@
 }
 
 impl Capability {
-    const KEY: &[u8] = b"com.android.virt.cap";
-    const REMOTE_ATTEST: &[u8] = b"remote_attest";
-    const SECRETKEEPER_PROTECTION: &[u8] = b"secretkeeper_protection";
+    const KEY: &'static [u8] = b"com.android.virt.cap";
+    const REMOTE_ATTEST: &'static [u8] = b"remote_attest";
+    const SECRETKEEPER_PROTECTION: &'static [u8] = b"secretkeeper_protection";
     const SEPARATOR: u8 = b'|';
 
     fn get_capabilities(property_value: &[u8]) -> Result<Vec<Self>, PvmfwVerifyError> {
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index 7023b95..4957df2 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -288,6 +288,6 @@
 
     unsafe fn from_raw_range_mut(ptr: usize, range: NonEmptyRange) -> &'a mut [u8] {
         // SAFETY: The caller must ensure that the range is valid from ptr.
-        unsafe { slice::from_raw_parts_mut((ptr + range.start) as *mut u8, range.end()) }
+        unsafe { slice::from_raw_parts_mut((ptr + range.start) as *mut u8, range.len()) }
     }
 }
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 5fbc767..7a89b75 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -644,6 +644,11 @@
         RebootReason::InvalidFdt
     })?;
 
+    fdt.unpack().map_err(|e| {
+        error!("Failed to unpack DT for patching: {e}");
+        RebootReason::InvalidFdt
+    })?;
+
     if let Some(device_assignment_info) = &info.device_assignment {
         let vm_dtbo = vm_dtbo.unwrap();
         device_assignment_info.filter(vm_dtbo).map_err(|e| {
@@ -663,6 +668,11 @@
 
     patch_device_tree(fdt, &info)?;
 
+    fdt.pack().map_err(|e| {
+        error!("Failed to unpack DT after patching: {e}");
+        RebootReason::InvalidFdt
+    })?;
+
     Ok(info)
 }
 
@@ -745,11 +755,6 @@
 }
 
 fn patch_device_tree(fdt: &mut Fdt, info: &DeviceTreeInfo) -> Result<(), RebootReason> {
-    fdt.unpack().map_err(|e| {
-        error!("Failed to unpack DT for patching: {e}");
-        RebootReason::InvalidFdt
-    })?;
-
     if let Some(initrd_range) = &info.initrd_range {
         patch_initrd_range(fdt, initrd_range).map_err(|e| {
             error!("Failed to patch initrd range to DT: {e}");
@@ -805,11 +810,6 @@
         })?;
     }
 
-    fdt.pack().map_err(|e| {
-        error!("Failed to pack DT after patching: {e}");
-        RebootReason::InvalidFdt
-    })?;
-
     Ok(())
 }
 
diff --git a/service_vm/comm/Android.bp b/service_vm/comm/Android.bp
index bdfc099..bf923a4 100644
--- a/service_vm/comm/Android.bp
+++ b/service_vm/comm/Android.bp
@@ -23,6 +23,7 @@
     rustlibs: [
         "libbssl_avf_error_nostd",
         "libciborium_nostd",
+        "libcbor_util_nostd",
         "libcoset_nostd",
         "libder_nostd",
         "liblog_rust_nostd",
@@ -36,6 +37,7 @@
     rustlibs: [
         "libbssl_avf_error",
         "libciborium",
+        "libcbor_util",
         "libcoset",
         "liblog_rust",
         "libserde",
diff --git a/service_vm/comm/src/csr.rs b/service_vm/comm/src/csr.rs
index f14787f..a87d28f 100644
--- a/service_vm/comm/src/csr.rs
+++ b/service_vm/comm/src/csr.rs
@@ -17,9 +17,9 @@
 
 use alloc::vec;
 use alloc::vec::Vec;
+use cbor_util::{cbor_value_type, value_to_bytes};
 use ciborium::Value;
 use coset::{self, CborSerializable, CoseError};
-use log::error;
 
 /// Represents a CSR sent from the client VM to the service VM for attestation.
 ///
@@ -99,37 +99,3 @@
         })
     }
 }
-
-/// Converts the provided value `v` to bytes array.
-pub fn value_to_bytes(v: Value, context: &'static str) -> coset::Result<Vec<u8>> {
-    v.into_bytes().map_err(|e| to_unexpected_item_error(&e, "bstr", context))
-}
-
-/// Builds a `CoseError::UnexpectedItem` error when the provided value `v` is not of the expected
-/// type `expected_type` and logs the error message with the provided `context`.
-pub fn to_unexpected_item_error(
-    v: &Value,
-    expected_type: &'static str,
-    context: &'static str,
-) -> CoseError {
-    let v_type = cbor_value_type(v);
-    assert!(v_type != expected_type);
-    error!("The provided value type '{v_type}' is not of type '{expected_type}': {context}");
-    CoseError::UnexpectedItem(v_type, expected_type)
-}
-
-/// Reads the type of the provided value `v`.
-pub fn cbor_value_type(v: &Value) -> &'static str {
-    match v {
-        Value::Integer(_) => "int",
-        Value::Bytes(_) => "bstr",
-        Value::Float(_) => "float",
-        Value::Text(_) => "tstr",
-        Value::Bool(_) => "bool",
-        Value::Null => "nul",
-        Value::Tag(_, _) => "tag",
-        Value::Array(_) => "array",
-        Value::Map(_) => "map",
-        _ => "other",
-    }
-}
diff --git a/service_vm/comm/src/lib.rs b/service_vm/comm/src/lib.rs
index 343d48e..bb85a26 100644
--- a/service_vm/comm/src/lib.rs
+++ b/service_vm/comm/src/lib.rs
@@ -23,7 +23,7 @@
 mod message;
 mod vsock;
 
-pub use csr::{cbor_value_type, to_unexpected_item_error, value_to_bytes, Csr, CsrPayload};
+pub use csr::{Csr, CsrPayload};
 pub use message::{
     ClientVmAttestationParams, EcdsaP256KeyPair, GenerateCertificateRequestParams, Request,
     RequestProcessingError, Response, ServiceVmRequest,
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index 15cfbc9..557b678 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -16,7 +16,11 @@
 
 use alloc::string::String;
 use alloc::vec::Vec;
-use ciborium::value::{Integer, Value};
+use cbor_util::{
+    cbor_value_type, value_to_array, value_to_byte_array, value_to_bytes, value_to_map,
+    value_to_num, value_to_text,
+};
+use ciborium::value::Value;
 use core::cell::OnceCell;
 use core::result;
 use coset::{
@@ -24,9 +28,7 @@
 };
 use diced_open_dice::{DiceMode, HASH_SIZE};
 use log::error;
-use service_vm_comm::{
-    cbor_value_type, to_unexpected_item_error, value_to_bytes, RequestProcessingError,
-};
+use service_vm_comm::RequestProcessingError;
 
 type Result<T> = result::Result<T, RequestProcessingError>;
 
@@ -399,33 +401,6 @@
     }
 }
 
-fn value_to_array(v: Value, context: &'static str) -> coset::Result<Vec<Value>> {
-    v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context))
-}
-
-fn value_to_text(v: Value, context: &'static str) -> coset::Result<String> {
-    v.into_text().map_err(|e| to_unexpected_item_error(&e, "tstr", context))
-}
-
-fn value_to_map(v: Value, context: &'static str) -> coset::Result<Vec<(Value, Value)>> {
-    v.into_map().map_err(|e| to_unexpected_item_error(&e, "map", context))
-}
-
-fn value_to_num<T: TryFrom<Integer>>(v: Value, context: &'static str) -> Result<T> {
-    let num = v.into_integer().map_err(|e| to_unexpected_item_error(&e, "int", context))?;
-    num.try_into().map_err(|_| {
-        error!("The provided value '{num:?}' is not a valid number: {context}");
-        RequestProcessingError::InvalidDiceChain
-    })
-}
-
-fn value_to_byte_array<const N: usize>(v: Value, context: &'static str) -> Result<[u8; N]> {
-    value_to_bytes(v, context)?.try_into().map_err(|e| {
-        error!("The provided value '{context}' is not an array of length {N}: {e:?}");
-        RequestProcessingError::InternalError
-    })
-}
-
 fn to_mode(value: Value) -> Result<DiceMode> {
     let mode = match value {
         // Mode is supposed to be encoded as a 1-byte bstr, but some implementations instead
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 9f1d7d1..a2a88d8 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -74,11 +74,7 @@
     // Create new POSIX socketpair, suitable for use with RpcBinder UDS bootstrap
     // transport. Make it O_CLOEXEC to align with how Rust creates file
     // descriptors (expected by SharedChild).
-    let (raw1, raw2) =
-        socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::SOCK_CLOEXEC)?;
-
-    // SAFETY: Taking ownership of brand new FDs.
-    unsafe { Ok((OwnedFd::from_raw_fd(raw1), OwnedFd::from_raw_fd(raw2))) }
+    Ok(socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::SOCK_CLOEXEC)?)
 }
 
 /// A running instance of virtmgr which is hosting a VirtualizationService