blob: 91929612b82886f2fedea02649faf8a51d461580 [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!(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());
}
}