blob: e369226d18c183df3de5c74331f9acbc78a884c5 [file] [log] [blame]
// 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!(ManifestRsa, at_most 3072);
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
struct ManifestBigInt(Option<HexEncoded<ManifestRsa>>);
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
struct ManifestSmallInt<T: ParseInt + fmt::UpperHex>(Option<HexEncoded<T>>);
impl fmt::UpperHex for ManifestRsa {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::UpperHex::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 `ManifestDef` 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])?
$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 ManifestDef {
pub fn read_from_file(path: &Path) -> Result<ManifestDef> {
Ok(deser_hjson::from_str(&std::fs::read_to_string(path)?)?)
}
pub fn overwrite_fields(&mut self, other: ManifestDef) {
self.overwrite(other)
}
}
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<ManifestRsa> for ManifestBigInt {
fn unpack(self, name: &'static str) -> Result<ManifestRsa> {
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::UpperHex> 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::UpperHex, 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) {
match 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().fold(true, |prev, v| prev && v.0.is_some()) {
*self = o;
}
}
}
manifest_def! {
pub struct ManifestDef {
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<ManifestRsa> for SigverifyRsaBuffer {
type Error = anyhow::Error;
fn try_from(rsa: ManifestRsa) -> Result<SigverifyRsaBuffer> {
if rsa.eq(&ManifestRsa::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: [0; 96] })
} else {
// Convert between the BigInt byte representation and the manifest word representation.
Ok(SigverifyRsaBuffer {
data: rsa
.to_le_bytes()
.chunks(4)
.map(|v| Ok(u32::from_le_bytes(v.try_into()?)))
.collect::<Result<Vec<u32>>>()?
.as_slice()
.try_into()?,
})
}
}
}
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 = ManifestRsa::from_le_bytes(o.data.as_bytes())?;
Ok(ManifestBigInt(Some(HexEncoded(rsa))))
}
}
impl<T> From<&T> for ManifestSmallInt<T>
where
T: ParseInt + fmt::UpperHex + 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: ManifestDef =
from_str(&std::fs::read_to_string(testdata!("manifest.hjson")).unwrap()).unwrap();
let _: Manifest = def.try_into().unwrap();
}
#[test]
fn test_manifest_overwrite() {
let mut base: ManifestDef =
from_str(&std::fs::read_to_string(testdata!("manifest.hjson")).unwrap()).unwrap();
let other = ManifestDef {
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: ManifestDef =
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: ManifestDef = (&bin1).try_into().unwrap();
let rebin: Manifest = redef.try_into().unwrap();
assert_eq!(bin2.as_bytes(), rebin.as_bytes());
}
}