Merge "[attestation] Rename the client VM attestation API" into main
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index 0a05471..b889ee5 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -57,6 +57,7 @@
":fdt_test_tree_multiple_memory_ranges_dtb",
":fdt_test_tree_empty_memory_range_dtb",
":fdt_test_tree_no_memory_node_dtb",
+ ":fdt_test_tree_phandle_dtb",
],
prefer_rlib: true,
rustlibs: [
@@ -91,3 +92,10 @@
srcs: ["tests/data/test_tree_no_memory_node.dts"],
out: ["data/test_tree_no_memory_node.dtb"],
}
+
+genrule {
+ name: "fdt_test_tree_phandle_dtb",
+ defaults: ["dts_to_dtb"],
+ srcs: ["tests/data/test_tree_phandle.dts"],
+ out: ["data/test_tree_phandle.dtb"],
+}
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index 7bffd8c..000f723 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -17,6 +17,7 @@
use crate::Fdt;
use crate::FdtError;
use crate::FdtNode;
+use crate::FdtProperty;
use crate::{AddrCells, SizeCells};
use core::ffi::CStr;
use core::marker::PhantomData;
@@ -321,3 +322,29 @@
res
}
}
+
+/// Iterator over properties
+#[derive(Debug)]
+pub struct PropertyIterator<'a> {
+ prop: Option<FdtProperty<'a>>,
+}
+
+impl<'a> PropertyIterator<'a> {
+ pub(crate) fn new(node: &'a FdtNode) -> Result<Self, FdtError> {
+ let prop = node.first_property()?;
+
+ Ok(Self { prop })
+ }
+}
+
+impl<'a> Iterator for PropertyIterator<'a> {
+ type Item = FdtProperty<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let res = self.prop;
+
+ self.prop = res?.next_property().ok()?;
+
+ res
+ }
+}
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index a6d5739..d800c13 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, RangesIterator, Reg,
- RegIterator, SubnodeIterator,
+ AddressRange, CellIterator, CompatibleIterator, MemRegIterator, PropertyIterator,
+ RangesIterator, Reg, RegIterator, SubnodeIterator,
};
use core::cmp::max;
@@ -29,6 +29,7 @@
use core::fmt;
use core::mem;
use core::ops::Range;
+use core::ptr;
use core::result;
use zerocopy::AsBytes as _;
@@ -194,6 +195,71 @@
}
}
+/// DT property wrapper to abstract endianess changes
+#[repr(transparent)]
+#[derive(Debug)]
+struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
+
+impl FdtPropertyStruct {
+ fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
+ let mut len = 0;
+ let prop =
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
+ if prop.is_null() {
+ fdt_err(len)?;
+ return Err(FdtError::Internal); // shouldn't happen.
+ }
+ // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
+ Ok(unsafe { &*prop.cast::<FdtPropertyStruct>() })
+ }
+
+ fn name_offset(&self) -> c_int {
+ u32::from_be(self.0.nameoff).try_into().unwrap()
+ }
+
+ fn data_len(&self) -> usize {
+ u32::from_be(self.0.len).try_into().unwrap()
+ }
+
+ fn data_ptr(&self) -> *const c_void {
+ self.0.data.as_ptr().cast::<_>()
+ }
+}
+
+/// DT property.
+#[derive(Clone, Copy, Debug)]
+pub struct FdtProperty<'a> {
+ fdt: &'a Fdt,
+ offset: c_int,
+ property: &'a FdtPropertyStruct,
+}
+
+impl<'a> FdtProperty<'a> {
+ fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
+ let property = FdtPropertyStruct::from_offset(fdt, offset)?;
+ Ok(Self { fdt, offset, property })
+ }
+
+ /// Returns the property name
+ pub fn name(&self) -> Result<&'a CStr> {
+ self.fdt.string(self.property.name_offset())
+ }
+
+ /// Returns the property value
+ pub fn value(&self) -> Result<&'a [u8]> {
+ self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
+ }
+
+ fn next_property(&self) -> Result<Option<Self>> {
+ let ret =
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
+
+ fdt_err_or_option(ret)?.map(|offset| Self::new(self.fdt, offset)).transpose()
+ }
+}
+
/// DT node.
#[derive(Clone, Copy, Debug)]
pub struct FdtNode<'a> {
@@ -214,6 +280,21 @@
Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
}
+ /// Returns supernode with depth. Note that root is at depth 0.
+ pub fn supernode_at_depth(&self, depth: usize) -> Result<Self> {
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe {
+ libfdt_bindgen::fdt_supernode_atdepth_offset(
+ self.fdt.as_ptr(),
+ self.offset,
+ depth.try_into().unwrap(),
+ ptr::null_mut(),
+ )
+ };
+
+ Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
+ }
+
/// Returns the standard (deprecated) device_type <string> property.
pub fn device_type(&self) -> Result<Option<&CStr>> {
self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
@@ -403,6 +484,40 @@
Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
}
+
+ /// Returns an iterator of properties
+ pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
+ PropertyIterator::new(self)
+ }
+
+ fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
+ let ret =
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
+
+ fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
+ }
+}
+
+/// Phandle of a FDT node
+#[repr(transparent)]
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct Phandle(u32);
+
+impl Phandle {
+ /// Creates a new Phandle
+ pub fn new(value: u32) -> Result<Self> {
+ if value == 0 || value > libfdt_bindgen::FDT_MAX_PHANDLE {
+ return Err(FdtError::BadPhandle);
+ }
+ Ok(Self(value))
+ }
+}
+
+impl From<Phandle> for u32 {
+ fn from(phandle: Phandle) -> u32 {
+ phandle.0
+ }
}
/// Mutable FDT node.
@@ -512,7 +627,7 @@
fdt_err_expect_zero(ret)
}
- /// Sets the given property with FDT_NOP, effectively removing it from the DT.
+ /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
// SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
// library locates the node's property.
@@ -555,12 +670,49 @@
/// Adds a new subnode to the given node and return it as a FdtNodeMut on success.
pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
+ let offset = self.add_subnode_offset(name.to_bytes())?;
+ Ok(Self { fdt: self.fdt, offset })
+ }
+
+ /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut
+ /// on success.
+ pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
+ let offset = { self.add_subnode_offset(&name.to_bytes()[..namelen])? };
+ Ok(Self { fdt: self.fdt, offset })
+ }
+
+ fn add_subnode_offset(&mut self, name: &[u8]) -> Result<c_int> {
+ let namelen = name.len().try_into().unwrap();
// SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
let ret = unsafe {
- libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
+ libfdt_bindgen::fdt_add_subnode_namelen(
+ self.fdt.as_mut_ptr(),
+ self.offset,
+ name.as_ptr().cast::<_>(),
+ namelen,
+ )
};
+ fdt_err(ret)
+ }
- Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
+ /// Returns the subnode of the given name with len.
+ pub fn subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Option<Self>> {
+ let offset = self.subnode_offset(&name.to_bytes()[..namelen])?;
+ Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
+ }
+
+ fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
+ let namelen = name.len().try_into().unwrap();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe {
+ libfdt_bindgen::fdt_subnode_offset_namelen(
+ self.fdt.as_ptr(),
+ self.offset,
+ name.as_ptr().cast::<_>(),
+ namelen,
+ )
+ };
+ fdt_err_or_option(ret)
}
fn parent(&'a self) -> Result<FdtNode<'a>> {
@@ -596,7 +748,7 @@
// node, and delete the current node, the Rust borrow checker kicks in. The next node has a
// mutable reference to DT, so we can't use current node (which also has a mutable reference to
// DT).
- pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
+ pub fn delete_and_next_compatible(mut self, compatible: &CStr) -> Result<Option<Self>> {
// SAFETY: Accesses (read-only) are constrained to the DT totalsize.
let ret = unsafe {
libfdt_bindgen::fdt_node_offset_by_compatible(
@@ -607,13 +759,33 @@
};
let next_offset = fdt_err_or_option(ret)?;
- // SAFETY: fdt_nop_node alter only the bytes in the blob which contain the node and its
- // properties and subnodes, and will not alter or move any other part of the tree.
- let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
- fdt_err_expect_zero(ret)?;
+ if Some(self.offset) == next_offset {
+ return Err(FdtError::Internal);
+ }
+
+ // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
+ // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
+ unsafe { self.nop_self()? };
Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
}
+
+ /// Deletes this node effectively from DT, by setting it with FDT_NOP
+ pub fn nop(mut self) -> Result<()> {
+ // SAFETY: This consumes self, so invalid node wouldn't be used any further
+ unsafe { self.nop_self() }
+ }
+
+ /// Deletes this node effectively from DT, by setting it with FDT_NOP.
+ /// This only changes bytes of the node and its properties and subnodes, and doesn't alter or
+ /// move any other part of the tree.
+ /// SAFETY: This node is no longer valid.
+ unsafe fn nop_self(&mut self) -> Result<()> {
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
+
+ fdt_err_expect_zero(ret)
+ }
}
/// Wrapper around low-level libfdt functions.
@@ -777,7 +949,7 @@
/// Returns a tree node by its full path.
pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
- Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
+ Ok(self.path_offset(path.to_bytes())?.map(|offset| FdtNode { fdt: self, offset }))
}
/// Iterate over nodes with a given compatible string.
@@ -785,6 +957,23 @@
CompatibleIterator::new(self, compatible)
}
+ /// Returns max phandle in the tree.
+ pub fn max_phandle(&self) -> Result<Phandle> {
+ let mut phandle: u32 = 0;
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
+
+ fdt_err_expect_zero(ret)?;
+ Phandle::new(phandle)
+ }
+
+ /// 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 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 }))
+ }
+
/// Returns the mutable root node of the tree.
pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
@@ -792,7 +981,7 @@
/// Returns a mutable tree node by its full path.
pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
- Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
+ Ok(self.path_offset(path.to_bytes())?.map(|offset| FdtNodeMut { fdt: self, offset }))
}
/// Returns the device tree as a slice (may be smaller than the containing buffer).
@@ -800,13 +989,13 @@
&self.buffer[..self.totalsize()]
}
- fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
- let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
+ fn path_offset(&self, path: &[u8]) -> Result<Option<c_int>> {
+ let len = path.len().try_into().map_err(|_| FdtError::BadPath)?;
// SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
// function respects the passed number of characters.
let ret = unsafe {
// *_namelen functions don't include the trailing nul terminator in 'len'.
- libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
+ libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr().cast::<_>(), len)
};
fdt_err_or_option(ret)
@@ -828,6 +1017,17 @@
self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
}
+ fn string(&self, offset: c_int) -> Result<&CStr> {
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
+ if res.is_null() {
+ return Err(FdtError::Internal);
+ }
+
+ // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
+ Ok(unsafe { CStr::from_ptr(res) })
+ }
+
/// Returns a shared pointer to the device tree.
pub fn as_ptr(&self) -> *const c_void {
self.buffer.as_ptr().cast::<_>()
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index 513ffd8..61503eb 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -16,16 +16,23 @@
//! Integration tests of the library libfdt.
-use libfdt::{Fdt, FdtError};
-use std::ffi::CStr;
+use libfdt::{Fdt, FdtError, Phandle};
+use std::ffi::{CStr, CString};
use std::fs;
use std::ops::Range;
+macro_rules! cstr {
+ ($str:literal) => {{
+ CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
+ }};
+}
+
const TEST_TREE_WITH_ONE_MEMORY_RANGE_PATH: &str = "data/test_tree_one_memory_range.dtb";
const TEST_TREE_WITH_MULTIPLE_MEMORY_RANGES_PATH: &str =
"data/test_tree_multiple_memory_ranges.dtb";
const TEST_TREE_WITH_EMPTY_MEMORY_RANGE_PATH: &str = "data/test_tree_empty_memory_range.dtb";
const TEST_TREE_WITH_NO_MEMORY_NODE_PATH: &str = "data/test_tree_no_memory_node.dtb";
+const TEST_TREE_PHANDLE_PATH: &str = "data/test_tree_phandle.dtb";
#[test]
fn retrieving_memory_from_fdt_with_one_memory_range_succeeds() {
@@ -83,7 +90,7 @@
let chosen = fdt.chosen().unwrap().unwrap();
assert_eq!(chosen.name().unwrap().to_str().unwrap(), "chosen");
- let nested_node_path = CStr::from_bytes_with_nul(b"/cpus/PowerPC,970@0\0").unwrap();
+ let nested_node_path = cstr!("/cpus/PowerPC,970@0");
let nested_node = fdt.node(nested_node_path).unwrap().unwrap();
assert_eq!(nested_node.name().unwrap().to_str().unwrap(), "PowerPC,970@0");
}
@@ -99,3 +106,117 @@
assert_eq!(node.name().unwrap().to_str().unwrap(), name);
}
}
+
+#[test]
+fn node_properties() {
+ 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 one_be = 0x1_u32.to_be_bytes();
+ let expected: Vec<(&str, &[u8])> = vec![
+ ("model", b"MyBoardName\0"),
+ ("compatible", b"MyBoardName\0MyBoardFamilyName\0"),
+ ("#address-cells", &one_be),
+ ("#size-cells", &one_be),
+ ("empty_prop", b""),
+ ];
+
+ for (prop, (name, value)) in root.properties().unwrap().zip(expected) {
+ assert_eq!(prop.name().unwrap().to_str().unwrap(), name);
+ assert_eq!(prop.value().unwrap(), value);
+ }
+}
+
+#[test]
+fn node_supernode_at_depth() {
+ 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 = &["", "cpus", "PowerPC,970@1"];
+
+ for (depth, expect) in expected.iter().enumerate() {
+ let supernode = node.supernode_at_depth(depth).unwrap();
+ assert_eq!(supernode.name().unwrap().to_str().unwrap(), *expect);
+ }
+}
+
+#[test]
+fn phandle_new() {
+ let phandle_u32 = 0x55;
+ let phandle = Phandle::new(phandle_u32).unwrap();
+
+ assert_eq!(u32::from(phandle), phandle_u32);
+}
+
+#[test]
+fn max_phandle() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ assert_eq!(fdt.max_phandle().unwrap(), Phandle::new(0xFF).unwrap());
+}
+
+#[test]
+fn node_with_phandle() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ // Test linux,phandle
+ let node = fdt.node_with_phandle(Phandle::new(0xFF).unwrap()).unwrap().unwrap();
+ assert_eq!(node.name().unwrap().to_str().unwrap(), "node_zz");
+
+ // Test phandle
+ let node = fdt.node_with_phandle(Phandle::new(0x22).unwrap()).unwrap().unwrap();
+ assert_eq!(node.name().unwrap().to_str().unwrap(), "node_abc");
+}
+
+#[test]
+fn node_nop() {
+ let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+ fdt.node_with_phandle(Phandle::new(0xFF).unwrap()).unwrap().unwrap();
+ let node = fdt.node_mut(cstr!("/node_z/node_zz")).unwrap().unwrap();
+
+ node.nop().unwrap();
+
+ assert!(fdt.node_with_phandle(Phandle::new(0xFF).unwrap()).unwrap().is_none());
+ assert!(fdt.node(cstr!("/node_z/node_zz")).unwrap().is_none());
+
+ fdt.unpack().unwrap();
+ fdt.pack().unwrap();
+
+ assert!(fdt.node_with_phandle(Phandle::new(0xFF).unwrap()).unwrap().is_none());
+ assert!(fdt.node(cstr!("/node_z/node_zz")).unwrap().is_none());
+}
+
+#[test]
+fn node_add_subnode_with_namelen() {
+ let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ data.resize(data.len() * 2, 0_u8);
+
+ let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+ fdt.unpack().unwrap();
+
+ let node_path = cstr!("/node_z/node_zz");
+ let subnode_name = cstr!("123456789");
+
+ for len in 0..subnode_name.to_bytes().len() {
+ let mut node = fdt.node_mut(node_path).unwrap().unwrap();
+ assert!(node.subnode_with_namelen(subnode_name, len).unwrap().is_none());
+
+ let mut node = fdt.node_mut(node_path).unwrap().unwrap();
+ node.add_subnode_with_namelen(subnode_name, len).unwrap();
+
+ let mut node = fdt.node_mut(node_path).unwrap().unwrap();
+ assert!(node.subnode_with_namelen(subnode_name, len).unwrap().is_some());
+ }
+
+ let node_path = node_path.to_str().unwrap();
+ for len in 1..subnode_name.to_bytes().len() {
+ let name = String::from_utf8(subnode_name.to_bytes()[..len].to_vec()).unwrap();
+ let path = CString::new(format!("{node_path}/{name}")).unwrap();
+ let subnode = fdt.node(&path).unwrap().unwrap();
+ assert_eq!(subnode.name().unwrap().to_str().unwrap(), name);
+ }
+}
diff --git a/libs/libfdt/tests/data/test_tree_no_memory_node.dts b/libs/libfdt/tests/data/test_tree_no_memory_node.dts
index 35e02cd..73451d7 100644
--- a/libs/libfdt/tests/data/test_tree_no_memory_node.dts
+++ b/libs/libfdt/tests/data/test_tree_no_memory_node.dts
@@ -6,6 +6,7 @@
compatible = "MyBoardName", "MyBoardFamilyName";
#address-cells = <0x1>;
#size-cells = <0x1>;
+ empty_prop;
cpus {
linux,phandle = <0x1>;
diff --git a/libs/libfdt/tests/data/test_tree_phandle.dts b/libs/libfdt/tests/data/test_tree_phandle.dts
new file mode 100644
index 0000000..0438241
--- /dev/null
+++ b/libs/libfdt/tests/data/test_tree_phandle.dts
@@ -0,0 +1,35 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ node_a {
+ phandle = <0x1>;
+ node_ab {
+ node_abc {
+ linux,phandle = <0x22>;
+ };
+ };
+ };
+
+ node_b {
+ };
+
+ node_c {
+ };
+
+ node_z {
+ node_za {
+ };
+
+ node_zb {
+
+ };
+
+ node_zz {
+ phandle = <0xFF>;
+
+ node_zzz {
+ };
+ };
+ };
+};
\ No newline at end of file
diff --git a/secretkeeper/dice_policy/src/lib.rs b/secretkeeper/dice_policy/src/lib.rs
index f5d117c..327b8a4 100644
--- a/secretkeeper/dice_policy/src/lib.rs
+++ b/secretkeeper/dice_policy/src/lib.rs
@@ -174,7 +174,7 @@
payload: Value,
constraint_spec: &[ConstraintSpec],
) -> Result<NodeConstraints> {
- let mut node_constraints: Vec<Constraint> = Vec::new();
+ 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() {
@@ -258,15 +258,98 @@
const COMPONENT_NAME: i64 = -70002;
const KEY_MODE: i64 = -4670551;
- // 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`
- const COMPOS_DICE_CHAIN_SIZE: usize = 5;
- const EXAMPLE_STRING: &str = "testing_dice_policy";
- const EXAMPLE_NUM: i64 = 59765;
+ // 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,
+ }
+
+ 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_STRING: &str = "testing_dice_policy";
+
+ 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();
+
+ // 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 constraint 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),
+ ),
+ ])),
+ ]),
+ };
+ Self { input_dice, constraint_spec, expected_dice_policy }
+ }
+ }
+
+ 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_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(),
@@ -275,66 +358,7 @@
.unwrap(),
];
let policy = DicePolicy::from_dice_chain(input_dice, &constraint_spec).unwrap();
- assert_eq!(policy.node_constraints_list.len(), COMPOS_DICE_CHAIN_SIZE);
- }
-
- test!(policy_structure_check);
- fn policy_structure_check() {
- 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 constraint_spec = [
- ConstraintSpec::new(ConstraintType::ExactMatch, vec![1]).unwrap(),
- ConstraintSpec::new(ConstraintType::GreaterOrEqual, vec![3, 100]).unwrap(),
- ];
- let policy = DicePolicy::from_dice_chain(&input_dice, &constraint_spec).unwrap();
-
- // Assert policy is exactly as expected!
- assert_eq!(
- policy,
- DicePolicy {
- version: 1,
- node_constraints_list: Box::new([
- NodeConstraints(Box::new([Constraint(
- ConstraintType::ExactMatch as u16,
- vec![],
- rot_key
- )])),
- 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)
- )
- ])),
- ])
- }
- );
+ assert_eq!(policy.node_constraints_list.len(), compos_dice_chain_size);
}
/// Encodes a ciborium::Value into bytes.