|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | use crate::image::manifest::*; | 
|  | use crate::util::bigint::fixed_size_bigint; | 
|  | use crate::util::num_de::HexEncoded; | 
|  | use crate::util::parse_int::ParseInt; | 
|  |  | 
|  | use anyhow::{bail, Result}; | 
|  | use serde::{Deserialize, Serialize}; | 
|  | use std::convert::{TryFrom, TryInto}; | 
|  | use std::fmt; | 
|  | use std::iter::IntoIterator; | 
|  | use std::path::Path; | 
|  | use thiserror::Error; | 
|  |  | 
|  | use zerocopy::AsBytes; | 
|  |  | 
|  | #[derive(Debug, Error)] | 
|  | pub enum ManifestError { | 
|  | #[error("Manifest is missing field \"{0}\".")] | 
|  | MissingField(&'static str), | 
|  | } | 
|  |  | 
|  | fixed_size_bigint!(ManifestRsaBuffer, at_most 3072); | 
|  |  | 
|  | #[derive(Clone, Default, Debug, Deserialize, Serialize)] | 
|  | struct ManifestBigInt(Option<HexEncoded<ManifestRsaBuffer>>); | 
|  |  | 
|  | #[derive(Clone, Default, Debug, Deserialize, Serialize)] | 
|  | struct ManifestSmallInt<T: ParseInt + fmt::LowerHex>(Option<HexEncoded<T>>); | 
|  |  | 
|  | impl fmt::LowerHex for ManifestRsaBuffer { | 
|  | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { | 
|  | fmt::LowerHex::fmt(&self.as_biguint(), f) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// A macro for wrapping manifest struct definitions that parse from HJSON. | 
|  | /// | 
|  | /// The #[repr(C)] version of `Manifest` can only be built when the fields in `ManifestSpec` are | 
|  | /// present. This macro sets up the field by field conversion and provides the field names for | 
|  | /// purposes of error reporting. | 
|  | macro_rules! manifest_def { | 
|  | ($access:vis struct $name:ident { | 
|  | $( | 
|  | $(#[$doc:meta])? | 
|  | $field_name:ident: $field_type:ty, | 
|  | )* | 
|  | }, $out_type:ident) => { | 
|  | #[derive(Clone, Default, Deserialize, Serialize, Debug)] | 
|  | $access struct $name { | 
|  | $( | 
|  | $(#[$doc])? | 
|  | #[serde(default)] | 
|  | $field_name: $field_type, | 
|  | )* | 
|  | } | 
|  |  | 
|  | impl ManifestPacked<$out_type> for $name { | 
|  | fn unpack(self, _name: &'static str) -> Result<$out_type> { | 
|  | Ok($out_type { | 
|  | // Call `unpack()` on each field with the field's name included for use in | 
|  | // error messages. | 
|  | $($field_name: self.$field_name | 
|  | .unpack(stringify!($field_name))?.try_into()?,)* | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn overwrite(&mut self, o: $name) { | 
|  | $(self.$field_name.overwrite(o.$field_name);)* | 
|  | } | 
|  | } | 
|  |  | 
|  | impl TryInto<$out_type> for $name { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_into(self) -> Result<$out_type> { | 
|  | self.unpack("") | 
|  | } | 
|  | } | 
|  |  | 
|  | impl TryFrom<&$out_type> for $name { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(o: &$out_type) -> Result<Self> { | 
|  | Ok($name { | 
|  | $($field_name: (&o.$field_name).try_into()?,)* | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl ManifestSpec { | 
|  | pub fn read_from_file(path: &Path) -> Result<ManifestSpec> { | 
|  | Ok(deser_hjson::from_str(&std::fs::read_to_string(path)?)?) | 
|  | } | 
|  |  | 
|  | pub fn overwrite_fields(&mut self, other: ManifestSpec) { | 
|  | self.overwrite(other) | 
|  | } | 
|  |  | 
|  | pub fn update_signature(&mut self, signature: ManifestRsaBuffer) { | 
|  | self.signature.0 = Some(HexEncoded(signature)) | 
|  | } | 
|  |  | 
|  | pub fn update_modulus(&mut self, modulus: ManifestRsaBuffer) { | 
|  | self.modulus.0 = Some(HexEncoded(modulus)) | 
|  | } | 
|  |  | 
|  | pub fn signature(&self) -> Option<&ManifestRsaBuffer> { | 
|  | self.signature.0.as_ref().map(|v| &v.0) | 
|  | } | 
|  |  | 
|  | pub fn modulus(&self) -> Option<&ManifestRsaBuffer> { | 
|  | self.modulus.0.as_ref().map(|v| &v.0) | 
|  | } | 
|  | } | 
|  |  | 
|  | trait ManifestPacked<T> { | 
|  | /// The default error for missing fields. | 
|  | fn unpack_err(&self, name: &'static str) -> Result<T> { | 
|  | bail!(ManifestError::MissingField(name)) | 
|  | } | 
|  |  | 
|  | /// Unpack optional fields in the manifest, and error if the field isn't defined. | 
|  | fn unpack(self, name: &'static str) -> Result<T>; | 
|  |  | 
|  | /// Overwrite manifest field. | 
|  | fn overwrite(&mut self, o: Self); | 
|  | } | 
|  |  | 
|  | impl ManifestPacked<ManifestRsaBuffer> for ManifestBigInt { | 
|  | fn unpack(self, name: &'static str) -> Result<ManifestRsaBuffer> { | 
|  | match self.0 { | 
|  | Some(v) => Ok(v.0), | 
|  | None => self.unpack_err(name), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn overwrite(&mut self, o: Self) { | 
|  | if o.0.is_some() { | 
|  | *self = o; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T: ParseInt + fmt::LowerHex> ManifestPacked<T> for ManifestSmallInt<T> { | 
|  | fn unpack(self, name: &'static str) -> Result<T> { | 
|  | match self.0 { | 
|  | Some(v) => Ok(v.0), | 
|  | None => self.unpack_err(name), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn overwrite(&mut self, o: Self) { | 
|  | if o.0.is_some() { | 
|  | *self = o; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T: ParseInt + fmt::LowerHex, const N: usize> ManifestPacked<[T; N]> | 
|  | for [ManifestSmallInt<T>; N] | 
|  | { | 
|  | fn unpack(self, name: &'static str) -> Result<[T; N]> { | 
|  | let results = self.map(|e| e.unpack(name)); | 
|  | if let Some(err_idx) = results.iter().position(Result::is_err) { | 
|  | IntoIterator::into_iter(results).nth(err_idx).unwrap()?; | 
|  | unreachable!(); | 
|  | } else { | 
|  | Ok(results.map(|x| x.unwrap())) | 
|  | } | 
|  | } | 
|  |  | 
|  | fn overwrite(&mut self, o: Self) { | 
|  | // Only perform the overwrite if all elements of `o` are present. | 
|  | if o.iter().all(|v| v.0.is_some()) { | 
|  | *self = o; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | manifest_def! { | 
|  | pub struct ManifestSpec { | 
|  | signature: ManifestBigInt, | 
|  | usage_constraints: ManifestUsageConstraintsDef, | 
|  | modulus: ManifestBigInt, | 
|  | address_translation: ManifestSmallInt<u32>, | 
|  | identifier: ManifestSmallInt<u32>, | 
|  | length: ManifestSmallInt<u32>, | 
|  | version_major: ManifestSmallInt<u32>, | 
|  | version_minor: ManifestSmallInt<u32>, | 
|  | security_version: ManifestSmallInt<u32>, | 
|  | timestamp: ManifestSmallInt<u64>, | 
|  | binding_value: [ManifestSmallInt<u32>; 8], | 
|  | max_key_version: ManifestSmallInt<u32>, | 
|  | code_start: ManifestSmallInt<u32>, | 
|  | code_end: ManifestSmallInt<u32>, | 
|  | entry_point: ManifestSmallInt<u32>, | 
|  | }, Manifest | 
|  | } | 
|  |  | 
|  | manifest_def! { | 
|  | pub struct ManifestUsageConstraintsDef { | 
|  | selector_bits: ManifestSmallInt<u32>, | 
|  | device_id: [ManifestSmallInt<u32>; 8], | 
|  | manuf_state_creator: ManifestSmallInt<u32>, | 
|  | manuf_state_owner: ManifestSmallInt<u32>, | 
|  | life_cycle_state: ManifestSmallInt<u32>, | 
|  | }, ManifestUsageConstraints | 
|  | } | 
|  |  | 
|  | impl TryFrom<ManifestRsaBuffer> for SigverifyRsaBuffer { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(rsa: ManifestRsaBuffer) -> Result<SigverifyRsaBuffer> { | 
|  | if rsa.eq(&ManifestRsaBuffer::from_le_bytes([0])?) { | 
|  | // In the case where the BigInt fields are defined but == 0 we should just keep it 0. | 
|  | // Without this the conversion to [u32; 96] would fail. | 
|  | Ok(SigverifyRsaBuffer { | 
|  | data: le_slice_to_arr(&[0]), | 
|  | }) | 
|  | } else { | 
|  | // Convert between the BigInt byte representation and the manifest word representation. | 
|  | Ok(SigverifyRsaBuffer { | 
|  | data: le_slice_to_arr( | 
|  | rsa.to_le_bytes() | 
|  | .chunks(4) | 
|  | .map(|v| Ok(u32::from_le_bytes(le_slice_to_arr(v)))) | 
|  | .collect::<Result<Vec<u32>>>()? | 
|  | .as_slice(), | 
|  | ), | 
|  | }) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Takes a slice with LE element ordering and pads the MSBs with 0 to produce a fixed length array | 
|  | /// | 
|  | /// This is similar to using `try_into()` but does not have the requirement that the slice has | 
|  | /// exactly the correct length. | 
|  | fn le_slice_to_arr<T: Default + Copy, const N: usize>(slice: &[T]) -> [T; N] { | 
|  | let mut arr = [T::default(); N]; | 
|  | arr[..slice.len()].copy_from_slice(slice); | 
|  | arr | 
|  | } | 
|  |  | 
|  | impl TryFrom<[u32; 96]> for SigverifyRsaBuffer { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(words: [u32; 96]) -> Result<SigverifyRsaBuffer> { | 
|  | Ok(SigverifyRsaBuffer { data: words }) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl TryFrom<[u32; 8]> for KeymgrBindingValue { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(words: [u32; 8]) -> Result<KeymgrBindingValue> { | 
|  | Ok(KeymgrBindingValue { data: words }) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl TryFrom<[u32; 8]> for LifecycleDeviceId { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(words: [u32; 8]) -> Result<LifecycleDeviceId> { | 
|  | Ok(LifecycleDeviceId { device_id: words }) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl TryFrom<SigverifyRsaBuffer> for ManifestBigInt { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(o: SigverifyRsaBuffer) -> Result<ManifestBigInt> { | 
|  | (&o).try_into() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl TryFrom<&SigverifyRsaBuffer> for ManifestBigInt { | 
|  | type Error = anyhow::Error; | 
|  |  | 
|  | fn try_from(o: &SigverifyRsaBuffer) -> Result<ManifestBigInt> { | 
|  | let rsa = ManifestRsaBuffer::from_le_bytes(o.data.as_bytes())?; | 
|  | Ok(ManifestBigInt(Some(HexEncoded(rsa)))) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<T> From<&T> for ManifestSmallInt<T> | 
|  | where | 
|  | T: ParseInt + fmt::LowerHex + Copy, | 
|  | { | 
|  | fn from(o: &T) -> ManifestSmallInt<T> { | 
|  | ManifestSmallInt(Some(HexEncoded(*o))) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<&KeymgrBindingValue> for [ManifestSmallInt<u32>; 8] { | 
|  | fn from(o: &KeymgrBindingValue) -> [ManifestSmallInt<u32>; 8] { | 
|  | o.data.map(|v| ManifestSmallInt(Some(HexEncoded(v)))) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<&LifecycleDeviceId> for [ManifestSmallInt<u32>; 8] { | 
|  | fn from(o: &LifecycleDeviceId) -> [ManifestSmallInt<u32>; 8] { | 
|  | o.device_id.map(|v| ManifestSmallInt(Some(HexEncoded(v)))) | 
|  | } | 
|  | } | 
|  |  | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | use super::*; | 
|  | use crate::testdata; | 
|  | use deser_hjson::from_str; | 
|  |  | 
|  | #[test] | 
|  | fn test_manifest_from_hjson() { | 
|  | let def: ManifestSpec = | 
|  | from_str(&std::fs::read_to_string(testdata!("manifest.hjson")).unwrap()).unwrap(); | 
|  |  | 
|  | let _: Manifest = def.try_into().unwrap(); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_manifest_from_hjson_missing() { | 
|  | let def: ManifestSpec = | 
|  | from_str(&std::fs::read_to_string(testdata!("manifest_missing.hjson")).unwrap()) | 
|  | .unwrap(); | 
|  |  | 
|  | let res: Result<Manifest> = def.try_into(); | 
|  | assert!(res.is_err()) | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_manifest_overwrite() { | 
|  | let mut base: ManifestSpec = | 
|  | from_str(&std::fs::read_to_string(testdata!("manifest.hjson")).unwrap()).unwrap(); | 
|  | let other = ManifestSpec { | 
|  | identifier: from_str("0xabcd").unwrap(), | 
|  | binding_value: from_str(stringify!(["0", "1", "2", "3", "4", "5", "6", "7"])).unwrap(), | 
|  | ..Default::default() | 
|  | }; | 
|  | base.overwrite(other); | 
|  | assert_eq!(base.identifier.0.unwrap().0, 0xabcd); | 
|  | assert_eq!( | 
|  | base.binding_value.map(|v| v.0.unwrap().0)[..], | 
|  | [0, 1, 2, 3, 4, 5, 6, 7] | 
|  | ); | 
|  |  | 
|  | // Ensure unspecified fields are not overwritten. | 
|  | assert_eq!(base.address_translation.0.unwrap().0, 0x739); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_manifest_convert() { | 
|  | let def1: ManifestSpec = | 
|  | from_str(&std::fs::read_to_string(testdata!("manifest.hjson")).unwrap()).unwrap(); | 
|  | let def2 = def1.clone(); | 
|  |  | 
|  | let bin1: Manifest = def1.try_into().unwrap(); | 
|  | let bin2: Manifest = def2.try_into().unwrap(); | 
|  |  | 
|  | let redef: ManifestSpec = (&bin1).try_into().unwrap(); | 
|  | let rebin: Manifest = redef.try_into().unwrap(); | 
|  | assert_eq!(bin2.as_bytes(), rebin.as_bytes()); | 
|  | } | 
|  | } |