Merge "[test] Test attestation request in e2e test rialto_test" into main
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 8257aae..029ac76 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -27,6 +27,7 @@
- lpmake, lpunpack, simg2img, img2simg, initrd_bootconfig
"""
import argparse
+import builtins
import hashlib
import os
import re
@@ -282,7 +283,7 @@
avb_version_bc = re.search(
r"androidboot.vbmeta.avb_version = \"([^\"]*)\"", bootconfigs).group(1)
if avb_version_curr != avb_version_bc:
- raise Exception(f'AVB version mismatch between current & one & \
+ raise builtins.Exception(f'AVB version mismatch between current & one & \
used to build bootconfigs:{avb_version_curr}&{avb_version_bc}')
def calc_vbmeta_digest():
@@ -430,21 +431,32 @@
# unpacked files (will be unpacked from super.img below)
system_a_img = os.path.join(unpack_dir.name, 'system_a.img')
+ vendor_a_img = os.path.join(unpack_dir.name, 'vendor_a.img')
# re-sign super.img
# 1. unpack super.img
- # 2. resign system
- # 3. repack super.img out of resigned system
+ # 2. resign system and vendor (if exists)
+ # 3. repack super.img out of resigned system and vendor (if exists)
UnpackSuperImg(args, files['super.img'], unpack_dir.name)
system_a_f = Async(AddHashTreeFooter, args, key, system_a_img)
partitions = {"system_a": system_a_img}
+ images = [system_a_img]
+ images_f = [system_a_f]
+
+ # if vendor_a.img exists, resign it
+ if os.path.exists(vendor_a_img):
+ partitions.update({'vendor_a': vendor_a_img})
+ images.append(vendor_a_img)
+ vendor_a_f = Async(AddHashTreeFooter, args, key, vendor_a_img)
+ images_f.append(vendor_a_f)
+
Async(MakeSuperImage, args, partitions,
- files['super.img'], wait=[system_a_f])
+ files['super.img'], wait=images_f)
# re-generate vbmeta from re-signed system_a.img
vbmeta_f = Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
- images=[system_a_img],
- wait=[system_a_f])
+ images=images,
+ wait=images_f)
vbmeta_bc_f = None
if not args.do_not_update_bootconfigs:
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index b0fc323..128d581 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -54,10 +54,7 @@
// SAFETY: We hold a strong pointer, so the raw pointer remains valid. The bindgen AIBinder
// is the same type as sys::AIBinder. It is safe for on_ready to be invoked at any time, with
// any parameter.
- unsafe {
- AVmPayload_runVsockRpcServer(service, COMPOS_VSOCK_PORT, Some(on_ready), param);
- }
- Ok(())
+ unsafe { AVmPayload_runVsockRpcServer(service, COMPOS_VSOCK_PORT, Some(on_ready), param) }
}
extern "C" fn on_ready(_param: *mut c_void) {
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index b811730..b369390 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -504,6 +504,18 @@
fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
}
+
+ /// Returns the phandle
+ pub fn get_phandle(&self) -> Result<Option<Phandle>> {
+ // This rewrites the fdt_get_phandle() because it doesn't return error code.
+ if let Some(prop) = self.getprop_u32(cstr!("phandle"))? {
+ Ok(Some(prop.try_into()?))
+ } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? {
+ Ok(Some(prop.try_into()?))
+ } else {
+ Ok(None)
+ }
+ }
}
impl<'a> PartialEq for FdtNode<'a> {
@@ -1012,9 +1024,20 @@
/// Returns a node with the phandle
pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
- // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let offset = self.node_offset_with_phandle(phandle)?;
+ Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
+ }
+
+ /// Returns a mutable node with the phandle
+ pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
+ let offset = self.node_offset_with_phandle(phandle)?;
+ Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
+ }
+
+ fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
+ // SAFETY: Accesses are constrained to the DT totalsize.
let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
- Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self, offset }))
+ fdt_err_or_option(ret)
}
/// Returns the mutable root node of the tree.
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index bc306ad..d76b1a4 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -16,6 +16,7 @@
//! Integration tests of the library libfdt.
+use core::ffi::CStr;
use libfdt::{Fdt, FdtError, FdtNodeMut, Phandle};
use std::ffi::CString;
use std::fs;
@@ -106,11 +107,11 @@
let data = fs::read(TEST_TREE_WITH_NO_MEMORY_NODE_PATH).unwrap();
let fdt = Fdt::from_slice(&data).unwrap();
let root = fdt.root().unwrap();
- let expected = [cstr!("cpus"), cstr!("randomnode"), cstr!("chosen")];
+ let expected = [Ok(cstr!("cpus")), Ok(cstr!("randomnode")), Ok(cstr!("chosen"))];
- for (node, name) in root.subnodes().unwrap().zip(expected) {
- assert_eq!(node.name(), Ok(name));
- }
+ let root_subnodes = root.subnodes().unwrap();
+ let subnode_names: Vec<_> = root_subnodes.map(|node| node.name()).collect();
+ assert_eq!(subnode_names, expected);
}
#[test]
@@ -119,18 +120,19 @@
let fdt = Fdt::from_slice(&data).unwrap();
let root = fdt.root().unwrap();
let one_be = 0x1_u32.to_be_bytes();
- let expected = [
- (cstr!("model"), b"MyBoardName\0".as_ref()),
- (cstr!("compatible"), b"MyBoardName\0MyBoardFamilyName\0".as_ref()),
- (cstr!("#address-cells"), &one_be),
- (cstr!("#size-cells"), &one_be),
- (cstr!("empty_prop"), &[]),
+ type Result<T> = core::result::Result<T, FdtError>;
+ let expected: Vec<(Result<&CStr>, Result<&[u8]>)> = vec![
+ (Ok(cstr!("model")), Ok(b"MyBoardName\0".as_ref())),
+ (Ok(cstr!("compatible")), Ok(b"MyBoardName\0MyBoardFamilyName\0".as_ref())),
+ (Ok(cstr!("#address-cells")), Ok(&one_be)),
+ (Ok(cstr!("#size-cells")), Ok(&one_be)),
+ (Ok(cstr!("empty_prop")), Ok(&[])),
];
let properties = root.properties().unwrap();
- for (prop, (name, value)) in properties.zip(expected.into_iter()) {
- assert_eq!((prop.name(), prop.value()), (Ok(name), Ok(value)));
- }
+ let subnode_properties: Vec<_> = properties.map(|prop| (prop.name(), prop.value())).collect();
+
+ assert_eq!(subnode_properties, expected);
}
#[test]
@@ -138,12 +140,16 @@
let data = fs::read(TEST_TREE_WITH_NO_MEMORY_NODE_PATH).unwrap();
let fdt = Fdt::from_slice(&data).unwrap();
let node = fdt.node(cstr!("/cpus/PowerPC,970@1")).unwrap().unwrap();
- let expected = [cstr!(""), cstr!("cpus"), cstr!("PowerPC,970@1")];
+ let expected = vec![Ok(cstr!("")), Ok(cstr!("cpus")), Ok(cstr!("PowerPC,970@1"))];
- for (depth, name) in expected.into_iter().enumerate() {
- let supernode = node.supernode_at_depth(depth).unwrap();
- assert_eq!(supernode.name(), Ok(name));
+ let mut supernode_names = vec![];
+ let mut depth = 0;
+ while let Ok(supernode) = node.supernode_at_depth(depth) {
+ supernode_names.push(supernode.name());
+ depth += 1;
}
+
+ assert_eq!(supernode_names, expected);
}
#[test]
@@ -200,6 +206,40 @@
}
#[test]
+fn node_mut_with_phandle() {
+ let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+ // Test linux,phandle
+ let phandle = Phandle::new(0xFF).unwrap();
+ let node: FdtNodeMut = fdt.node_mut_with_phandle(phandle).unwrap().unwrap();
+ assert_eq!(node.as_node().name(), Ok(cstr!("node_zz")));
+
+ // Test phandle
+ let phandle = Phandle::new(0x22).unwrap();
+ let node: FdtNodeMut = fdt.node_mut_with_phandle(phandle).unwrap().unwrap();
+ assert_eq!(node.as_node().name(), Ok(cstr!("node_abc")));
+}
+
+#[test]
+fn node_get_phandle() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ // Test linux,phandle
+ let node = fdt.node(cstr!("/node_z/node_zz")).unwrap().unwrap();
+ assert_eq!(node.get_phandle(), Ok(Phandle::new(0xFF)));
+
+ // Test phandle
+ let node = fdt.node(cstr!("/node_a/node_ab/node_abc")).unwrap().unwrap();
+ assert_eq!(node.get_phandle(), Ok(Phandle::new(0x22)));
+
+ // Test no phandle
+ let node = fdt.node(cstr!("/node_b")).unwrap().unwrap();
+ assert_eq!(node.get_phandle(), Ok(None));
+}
+
+#[test]
fn node_nop() {
let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
let fdt = Fdt::from_mut_slice(&mut data).unwrap();
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 42ff4b0..4e735e6 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -243,7 +243,35 @@
"echo ro.product.cpu.abi=arm64-v8a) > $(out)",
}
-logical_partition {
+// Need to keep microdroid_vendor for the release configurations that don't
+// have RELEASE_AVF_ENABLE_VENDOR_MODULES build flag enabled.
+android_filesystem {
+ name: "microdroid_vendor",
+ partition_name: "vendor",
+ use_avb: true,
+ avb_private_key: ":microdroid_sign_key",
+ avb_algorithm: "SHA256_RSA4096",
+ avb_hash_algorithm: "sha256",
+ file_contexts: ":microdroid_vendor_file_contexts.gen",
+ // For deterministic output, use fake_timestamp, hard-coded uuid
+ fake_timestamp: "1611569676",
+ // python -c "import uuid; print(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com/avf/microdroid/vendor'))"
+ uuid: "156d40d7-8d8e-5c99-8913-ec82de549a70",
+}
+
+soong_config_module_type {
+ name: "flag_aware_microdroid_super_partition",
+ module_type: "logical_partition",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_avf_enable_vendor_modules",
+ ],
+ properties: [
+ "default_group",
+ ],
+}
+
+flag_aware_microdroid_super_partition {
name: "microdroid_super",
sparse: true,
size: "auto",
@@ -253,6 +281,16 @@
filesystem: ":microdroid",
},
],
+ soong_config_variables: {
+ release_avf_enable_vendor_modules: {
+ default_group: [
+ {
+ name: "vendor_a",
+ filesystem: ":microdroid_vendor",
+ },
+ ],
+ },
+ },
}
android_filesystem {
@@ -330,13 +368,30 @@
srcs: [":avb_testkey_rsa4096"],
}
-vbmeta {
+soong_config_module_type {
+ name: "flag_aware_microdroid_vbmeta",
+ module_type: "vbmeta",
+ config_namespace: "ANDROID",
+ bool_variables: [
+ "release_avf_enable_vendor_modules",
+ ],
+ properties: [
+ "partitions",
+ ],
+}
+
+flag_aware_microdroid_vbmeta {
name: "microdroid_vbmeta",
partition_name: "vbmeta",
private_key: ":microdroid_sign_key",
partitions: [
"microdroid",
],
+ soong_config_variables: {
+ release_avf_enable_vendor_modules: {
+ partitions: ["microdroid_vendor"],
+ },
+ },
}
prebuilt_etc {
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 946ed85..b7b5900 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -224,7 +224,7 @@
srcs: [":pvmfw_platform.dts.preprocessed"],
out: ["lib.rs"],
tools: ["dtc"],
- cmd: "$(location dtc) -I dts -O dtb -o $(genDir)/compiled.dtbo $(in) && " +
+ cmd: "$(location dtc) -@ -I dts -O dtb -o $(genDir)/compiled.dtbo $(in) && " +
"(" +
" echo '#![no_std]';" +
" echo '#![allow(missing_docs)]';" +
diff --git a/pvmfw/platform.dts b/pvmfw/platform.dts
index cb8e30d..4a269c3 100644
--- a/pvmfw/platform.dts
+++ b/pvmfw/platform.dts
@@ -261,4 +261,64 @@
clock-frequency = <10>;
timeout-sec = <8>;
};
+
+ pviommu_0: pviommu0 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_1: pviommu1 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_2: pviommu2 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_3: pviommu3 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_4: pviommu4 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_5: pviommu5 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_6: pviommu6 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_7: pviommu7 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_8: pviommu8 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_9: pviommu9 {
+ compatible = "pkvm,pviommu";
+ id = <PLACEHOLDER>;
+ #iommu-cells = <0>;
+ };
};
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index 7eae09f..a92b418 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -401,17 +401,28 @@
unsafe {
platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
}
+ device_info.patch(platform_dt).unwrap();
- let rng_node = platform_dt.node(cstr!("/rng")).unwrap().unwrap();
- let expected: Vec<(&CStr, Vec<u8>)> = vec![
- (cstr!("android,rng,ignore-gctrl-reset"), Vec::<u8>::new()),
- (cstr!("compatible"), b"android,rng\0".to_vec()),
- (cstr!("reg"), into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF])),
- (cstr!("interrupts"), into_fdt_prop(vec![0x0, 0xF, 0x4])),
+ type FdtResult<T> = libfdt::Result<T>;
+ let expected: Vec<(FdtResult<&CStr>, FdtResult<Vec<u8>>)> = vec![
+ (Ok(cstr!("android,rng,ignore-gctrl-reset")), Ok(Vec::new())),
+ (Ok(cstr!("compatible")), Ok(Vec::from(*b"android,rng\0"))),
+ (Ok(cstr!("interrupts")), Ok(into_fdt_prop(vec![0x0, 0xF, 0x4]))),
+ (Ok(cstr!("reg")), Ok(into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]))),
];
- for (prop, (prop_name, prop_value)) in rng_node.properties().unwrap().zip(expected) {
- assert_eq!((prop.name(), prop.value()), (Ok(prop_name), Ok(prop_value.as_slice())));
- }
+ let rng_node = platform_dt.node(cstr!("/rng")).unwrap().unwrap();
+ let mut properties: Vec<_> = rng_node
+ .properties()
+ .unwrap()
+ .map(|prop| (prop.name(), prop.value().map(|x| x.into())))
+ .collect();
+ properties.sort_by(|a, b| {
+ let lhs = a.0.unwrap_or_default();
+ let rhs = b.0.unwrap_or_default();
+ lhs.partial_cmp(rhs).unwrap()
+ });
+
+ assert_eq!(properties, expected);
}
}
diff --git a/secretkeeper/dice_policy/Android.bp b/secretkeeper/dice_policy/Android.bp
index a7ac5b9..4f1e8b6 100644
--- a/secretkeeper/dice_policy/Android.bp
+++ b/secretkeeper/dice_policy/Android.bp
@@ -13,7 +13,9 @@
"libanyhow",
"libciborium",
"libcoset",
+ "libnum_traits",
],
+ proc_macros: ["libnum_derive"],
}
rust_library {
diff --git a/secretkeeper/dice_policy/src/lib.rs b/secretkeeper/dice_policy/src/lib.rs
index 327b8a4..2e91305 100644
--- a/secretkeeper/dice_policy/src/lib.rs
+++ b/secretkeeper/dice_policy/src/lib.rs
@@ -57,16 +57,20 @@
//!
//! value = bool / int / tstr / bstr
-use anyhow::{anyhow, bail, Context, Result};
+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, PartialEq)]
+#[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.
@@ -133,6 +137,7 @@
/// ];
///
/// 2. For hypothetical (and highly simplified) dice chain:
+ ///
/// [ROT_KEY, [{1 : 'a', 2 : {200 : 5, 201 : 'b'}}]]
/// The following can be used
/// constraint_spec =[
@@ -140,13 +145,7 @@
/// ConstraintSpec(ConstraintType::GreaterOrEqual, vec![2, 200]),// matches any value >= 5
/// ];
pub fn from_dice_chain(dice_chain: &[u8], constraint_spec: &[ConstraintSpec]) -> Result<Self> {
- // TODO(b/298217847): Check if the given dice chain adheres to Explicit-key DiceCertChain
- // format and if not, convert it before policy construction.
- let dice_chain = value_from_bytes(dice_chain).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),
- };
+ 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();
@@ -167,6 +166,61 @@
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.
@@ -231,6 +285,17 @@
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.
@@ -266,38 +331,29 @@
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: i64 = 59765;
+ 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 nested_payload = cbor!({
- 100 => EXAMPLE_NUM
- })
- .unwrap();
- let payload = cbor!({
- 1 => EXAMPLE_STRING,
- 2 => "some_other_example_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());
-
- let input_dice = value_to_bytes(&input_dice).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.
@@ -305,7 +361,7 @@
let constraint_spec = vec![
ConstraintSpec::new(ConstraintType::ExactMatch, vec![1]).unwrap(),
// Notice how key "2" is (deliberately) absent in ConstraintSpec
- // so policy should not constraint it.
+ // so policy should not constrain it.
ConstraintSpec::new(ConstraintType::GreaterOrEqual, vec![3, 100]).unwrap(),
];
let expected_dice_policy = DicePolicy {
@@ -325,12 +381,53 @@
Constraint(
ConstraintType::GreaterOrEqual as u16,
vec![3, 100],
- Value::from(EXAMPLE_NUM),
+ Value::from(EXAMPLE_NUM_1),
),
])),
]),
};
- Self { input_dice, constraint_spec, expected_dice_policy }
+
+ 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()
}
}
@@ -344,6 +441,43 @@
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)
diff --git a/vm_payload/include/vm_payload.h b/vm_payload/include/vm_payload.h
index 951b57f..78cd80d 100644
--- a/vm_payload/include/vm_payload.h
+++ b/vm_payload/include/vm_payload.h
@@ -19,7 +19,6 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-#include <stdnoreturn.h>
#include <sys/cdefs.h>
#include "vm_main.h"
@@ -81,9 +80,9 @@
* callback will be called at most once.
* \param param parameter to be passed to the `on_ready` callback.
*/
-noreturn void AVmPayload_runVsockRpcServer(AIBinder* _Nonnull service, uint32_t port,
- void (*_Nullable on_ready)(void* _Nullable param),
- void* _Nullable param);
+__attribute__((noreturn)) void AVmPayload_runVsockRpcServer(
+ AIBinder* _Nonnull service, uint32_t port,
+ void (*_Nullable on_ready)(void* _Nullable param), void* _Nullable param);
/**
* Returns all or part of a 32-byte secret that is bound to this unique VM