opentitanlib/image/
manifest_def.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::image::manifest::*;
6use crate::image::manifest_ext::ManifestExtId;
7use crate::util::bigint::fixed_size_bigint;
8use crate::util::num_de::HexEncoded;
9use crate::util::parse_int::ParseInt;
10
11use anyhow::{Context, Result, bail};
12use serde::{Deserialize, Serialize};
13use std::convert::{TryFrom, TryInto};
14use std::fmt;
15use std::iter::IntoIterator;
16use std::path::Path;
17use thiserror::Error;
18
19use zerocopy::IntoBytes;
20
21#[derive(Debug, Error)]
22pub enum ManifestError {
23    #[error("Manifest is missing field \"{0}\".")]
24    MissingField(&'static str),
25}
26
27fixed_size_bigint!(ManifestSigverifyBuffer, at_most 3072);
28
29#[derive(Clone, Default, Debug, Deserialize, Serialize)]
30struct ManifestSigverifyBigInt(Option<HexEncoded<ManifestSigverifyBuffer>>);
31
32#[derive(Clone, Default, Debug, Deserialize, Serialize)]
33struct ManifestSmallInt<T: ParseInt + fmt::LowerHex>(Option<HexEncoded<T>>);
34
35impl fmt::LowerHex for ManifestSigverifyBuffer {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
37        fmt::LowerHex::fmt(&self.as_biguint(), f)
38    }
39}
40
41/// A macro for wrapping manifest struct definitions that parse from HJSON.
42///
43/// The #[repr(C)] version of `Manifest` can only be built when the fields in `ManifestSpec` are
44/// present. This macro sets up the field by field conversion and provides the field names for
45/// purposes of error reporting.
46macro_rules! manifest_def {
47    ($access:vis struct $name:ident {
48        $(
49            $(#[$doc:meta])?
50            $field_name:ident: $field_type:ty,
51        )*
52    }, $out_type:ident) => {
53        #[derive(Clone, Default, Deserialize, Serialize, Debug)]
54        $access struct $name {
55            $(
56                $(#[$doc])?
57                #[serde(default)]
58                $field_name: $field_type,
59            )*
60        }
61
62        impl ManifestPacked<$out_type> for $name {
63            fn unpack(self, _name: &'static str) -> Result<$out_type> {
64                Ok($out_type {
65                    // Call `unpack()` on each field with the field's name included for use in
66                    // error messages.
67                    $($field_name: self.$field_name
68                        .unpack(stringify!($field_name))?.try_into()?,)*
69                })
70            }
71
72            fn overwrite(&mut self, o: $name) {
73                $(self.$field_name.overwrite(o.$field_name);)*
74            }
75        }
76
77        impl TryInto<$out_type> for $name {
78            type Error = anyhow::Error;
79
80            fn try_into(self) -> Result<$out_type> {
81                self.unpack("")
82            }
83        }
84
85        impl TryFrom<&$out_type> for $name {
86            type Error = anyhow::Error;
87
88            fn try_from(o: &$out_type) -> Result<Self> {
89                Ok($name {
90                    $($field_name: (&o.$field_name).try_into()?,)*
91                })
92            }
93        }
94    }
95}
96
97impl ManifestSpec {
98    pub fn read_from_file(path: &Path) -> Result<ManifestSpec> {
99        Ok(deser_hjson::from_str(
100            &std::fs::read_to_string(path).with_context(|| format!("Failed to open {path:?}"))?,
101        )?)
102    }
103
104    pub fn overwrite_fields(&mut self, other: ManifestSpec) {
105        self.overwrite(other)
106    }
107
108    pub fn update_signature(&mut self, signature: ManifestSigverifyBuffer) {
109        self.signature.0 = Some(HexEncoded(signature))
110    }
111
112    pub fn update_pub_key(&mut self, pub_key: ManifestSigverifyBuffer) {
113        self.pub_key.0 = Some(HexEncoded(pub_key))
114    }
115
116    pub fn signature(&self) -> Option<&ManifestSigverifyBuffer> {
117        self.signature.0.as_ref().map(|v| &v.0)
118    }
119
120    pub fn pub_key(&self) -> Option<&ManifestSigverifyBuffer> {
121        self.pub_key.0.as_ref().map(|v| &v.0)
122    }
123
124    pub fn has_length(&self) -> bool {
125        self.length.0.is_some()
126    }
127}
128
129trait ManifestPacked<T> {
130    /// The default error for missing fields.
131    fn unpack_err(&self, name: &'static str) -> Result<T> {
132        bail!(ManifestError::MissingField(name))
133    }
134
135    /// Unpack optional fields in the manifest, and error if the field isn't defined.
136    fn unpack(self, name: &'static str) -> Result<T>;
137
138    /// Overwrite manifest field.
139    fn overwrite(&mut self, o: Self);
140}
141
142impl ManifestPacked<ManifestSigverifyBuffer> for ManifestSigverifyBigInt {
143    fn unpack(self, name: &'static str) -> Result<ManifestSigverifyBuffer> {
144        match self.0 {
145            Some(v) => Ok(v.0),
146            None => self.unpack_err(name),
147        }
148    }
149
150    fn overwrite(&mut self, o: Self) {
151        if o.0.is_some() {
152            *self = o;
153        }
154    }
155}
156
157impl ManifestPacked<[ManifestExtTableEntry; CHIP_MANIFEST_EXT_TABLE_COUNT]>
158    for [ManifestExtTableEntryDef; CHIP_MANIFEST_EXT_TABLE_COUNT]
159{
160    fn unpack(
161        self,
162        _name: &'static str,
163    ) -> Result<[ManifestExtTableEntry; CHIP_MANIFEST_EXT_TABLE_COUNT]> {
164        Ok(self.map(|v| match v.0 {
165            ManifestExtEntryVar::Name(name) => ManifestExtTableEntry {
166                identifier: name.into(),
167                offset: 0,
168            },
169            ManifestExtEntryVar::IdOffset { identifier, offset } => ManifestExtTableEntry {
170                identifier: identifier.into(),
171                offset,
172            },
173            _ => ManifestExtTableEntry {
174                identifier: 0,
175                offset: 0,
176            },
177        }))
178    }
179
180    fn overwrite(&mut self, o: Self) {
181        for i in 0..self.len() {
182            match o[i].0 {
183                ManifestExtEntryVar::Name(other_id) => match self[i].0 {
184                    ManifestExtEntryVar::IdOffset {
185                        identifier: self_id,
186                        offset: _,
187                    } => {
188                        if self_id == other_id {
189                            // Do not overwrite existing entries with matching IDs.
190                            continue;
191                        } else {
192                            self[i].0 = o[i].0.clone()
193                        }
194                    }
195                    _ => self[i].0 = o[i].0.clone(),
196                },
197                ManifestExtEntryVar::None => (),
198                _ => self[i].0 = o[i].0.clone(),
199            }
200        }
201    }
202}
203
204impl<T: ParseInt + fmt::LowerHex> ManifestPacked<T> for ManifestSmallInt<T> {
205    fn unpack(self, name: &'static str) -> Result<T> {
206        match self.0 {
207            Some(v) => Ok(v.0),
208            None => self.unpack_err(name),
209        }
210    }
211
212    fn overwrite(&mut self, o: Self) {
213        if o.0.is_some() {
214            *self = o;
215        }
216    }
217}
218
219impl<T: ParseInt + fmt::LowerHex, const N: usize> ManifestPacked<[T; N]>
220    for [ManifestSmallInt<T>; N]
221{
222    fn unpack(self, name: &'static str) -> Result<[T; N]> {
223        let results = self.map(|e| e.unpack(name));
224        if let Some(err_idx) = results.iter().position(Result::is_err) {
225            IntoIterator::into_iter(results).nth(err_idx).unwrap()?;
226            unreachable!();
227        } else {
228            Ok(results.map(|x| x.unwrap()))
229        }
230    }
231
232    fn overwrite(&mut self, o: Self) {
233        // Only perform the overwrite if all elements of `o` are present.
234        if o.iter().all(|v| v.0.is_some()) {
235            *self = o;
236        }
237    }
238}
239
240manifest_def! {
241    pub struct ManifestSpec {
242        signature: ManifestSigverifyBigInt,
243        usage_constraints: ManifestUsageConstraintsDef,
244        pub_key: ManifestSigverifyBigInt,
245        address_translation: ManifestSmallInt<u32>,
246        identifier: ManifestSmallInt<u32>,
247        manifest_version: ManifestVersionDef,
248        signed_region_end: ManifestSmallInt<u32>,
249        length: ManifestSmallInt<u32>,
250        version_major: ManifestSmallInt<u32>,
251        version_minor: ManifestSmallInt<u32>,
252        security_version: ManifestSmallInt<u32>,
253        timestamp: [ManifestSmallInt<u32>; 2],
254        binding_value: [ManifestSmallInt<u32>; 8],
255        max_key_version: ManifestSmallInt<u32>,
256        code_start: ManifestSmallInt<u32>,
257        code_end: ManifestSmallInt<u32>,
258        entry_point: ManifestSmallInt<u32>,
259        extensions: [ManifestExtTableEntryDef; CHIP_MANIFEST_EXT_TABLE_COUNT],
260    }, Manifest
261}
262
263manifest_def! {
264    pub struct ManifestUsageConstraintsDef {
265        selector_bits: ManifestSmallInt<u32>,
266        device_id: [ManifestSmallInt<u32>; 8],
267        manuf_state_creator: ManifestSmallInt<u32>,
268        manuf_state_owner: ManifestSmallInt<u32>,
269        life_cycle_state: ManifestSmallInt<u32>,
270    }, ManifestUsageConstraints
271}
272
273manifest_def! {
274    pub struct ManifestVersionDef {
275        minor: ManifestSmallInt<u16>,
276        major: ManifestSmallInt<u16>,
277    }, ManifestVersion
278}
279
280#[derive(Clone, Default, Deserialize, Serialize, Debug)]
281#[serde(untagged)]
282enum ManifestExtEntryVar {
283    #[default]
284    None,
285    Name(ManifestExtId),
286    IdOffset {
287        identifier: ManifestExtId,
288        offset: u32,
289    },
290}
291
292#[derive(Clone, Default, Deserialize, Serialize, Debug)]
293pub struct ManifestExtTableEntryDef(ManifestExtEntryVar);
294
295impl TryFrom<ManifestSigverifyBuffer> for SigverifyBuffer {
296    type Error = anyhow::Error;
297
298    fn try_from(buffer: ManifestSigverifyBuffer) -> Result<SigverifyBuffer> {
299        if buffer.eq(&ManifestSigverifyBuffer::from_le_bytes([0])?) {
300            // In the case where the BigInt fields are defined but == 0 we should just keep it 0.
301            // Without this the conversion to [u32; 96] would fail.
302            Ok(SigverifyBuffer {
303                data: le_slice_to_arr(&[0]),
304            })
305        } else {
306            // Convert between the BigInt byte representation and the manifest word representation.
307            Ok(SigverifyBuffer {
308                data: le_bytes_to_word_arr(&buffer.to_le_bytes())?,
309            })
310        }
311    }
312}
313
314pub(crate) fn le_bytes_to_word_arr<const N: usize>(bytes: &[u8]) -> Result<[u32; N]> {
315    Ok(le_slice_to_arr(
316        bytes
317            .chunks(4)
318            .map(|v| Ok(u32::from_le_bytes(le_slice_to_arr(v))))
319            .collect::<Result<Vec<u32>>>()?
320            .as_slice(),
321    ))
322}
323
324/// Takes a slice with LE element ordering and pads the MSBs with 0 to produce a fixed length array
325///
326/// This is similar to using `try_into()` but does not have the requirement that the slice has
327/// exactly the correct length.
328fn le_slice_to_arr<T: Default + Copy, const N: usize>(slice: &[T]) -> [T; N] {
329    let mut arr = [T::default(); N];
330    arr[..slice.len()].copy_from_slice(slice);
331    arr
332}
333
334impl TryFrom<[u32; 96]> for SigverifyBuffer {
335    type Error = anyhow::Error;
336
337    fn try_from(words: [u32; 96]) -> Result<SigverifyBuffer> {
338        Ok(SigverifyBuffer { data: words })
339    }
340}
341
342impl TryFrom<[u32; 8]> for KeymgrBindingValue {
343    type Error = anyhow::Error;
344
345    fn try_from(words: [u32; 8]) -> Result<KeymgrBindingValue> {
346        Ok(KeymgrBindingValue { data: words })
347    }
348}
349
350impl TryFrom<[u32; 2]> for Timestamp {
351    type Error = anyhow::Error;
352
353    fn try_from(words: [u32; 2]) -> Result<Timestamp> {
354        Ok(Timestamp {
355            timestamp_low: words[0],
356            timestamp_high: words[1],
357        })
358    }
359}
360
361impl TryFrom<[u32; 8]> for LifecycleDeviceId {
362    type Error = anyhow::Error;
363
364    fn try_from(words: [u32; 8]) -> Result<LifecycleDeviceId> {
365        Ok(LifecycleDeviceId { device_id: words })
366    }
367}
368
369impl TryFrom<SigverifyBuffer> for ManifestSigverifyBigInt {
370    type Error = anyhow::Error;
371
372    fn try_from(o: SigverifyBuffer) -> Result<ManifestSigverifyBigInt> {
373        (&o).try_into()
374    }
375}
376
377impl TryFrom<&SigverifyBuffer> for ManifestSigverifyBigInt {
378    type Error = anyhow::Error;
379
380    fn try_from(o: &SigverifyBuffer) -> Result<ManifestSigverifyBigInt> {
381        let rsa = ManifestSigverifyBuffer::from_le_bytes(o.data.as_bytes())?;
382        Ok(ManifestSigverifyBigInt(Some(HexEncoded(rsa))))
383    }
384}
385
386impl<T> From<&T> for ManifestSmallInt<T>
387where
388    T: ParseInt + fmt::LowerHex + Copy,
389{
390    fn from(o: &T) -> ManifestSmallInt<T> {
391        ManifestSmallInt(Some(HexEncoded(*o)))
392    }
393}
394
395impl From<&KeymgrBindingValue> for [ManifestSmallInt<u32>; 8] {
396    fn from(o: &KeymgrBindingValue) -> [ManifestSmallInt<u32>; 8] {
397        o.data.map(|v| ManifestSmallInt(Some(HexEncoded(v))))
398    }
399}
400impl From<&Timestamp> for [ManifestSmallInt<u32>; 2] {
401    fn from(o: &Timestamp) -> [ManifestSmallInt<u32>; 2] {
402        [
403            ManifestSmallInt(Some(HexEncoded(o.timestamp_low))),
404            ManifestSmallInt(Some(HexEncoded(o.timestamp_high))),
405        ]
406    }
407}
408
409impl From<&LifecycleDeviceId> for [ManifestSmallInt<u32>; 8] {
410    fn from(o: &LifecycleDeviceId) -> [ManifestSmallInt<u32>; 8] {
411        o.device_id.map(|v| ManifestSmallInt(Some(HexEncoded(v))))
412    }
413}
414
415impl From<&ManifestExtTableEntry> for ManifestExtTableEntryDef {
416    fn from(o: &ManifestExtTableEntry) -> ManifestExtTableEntryDef {
417        ManifestExtTableEntryDef(ManifestExtEntryVar::IdOffset {
418            identifier: ManifestExtId(o.identifier),
419            offset: o.offset,
420        })
421    }
422}
423
424impl From<[ManifestExtTableEntry; CHIP_MANIFEST_EXT_TABLE_COUNT]> for ManifestExtTable {
425    fn from(o: [ManifestExtTableEntry; CHIP_MANIFEST_EXT_TABLE_COUNT]) -> ManifestExtTable {
426        ManifestExtTable { entries: o }
427    }
428}
429
430impl From<&ManifestExtTable> for [ManifestExtTableEntryDef; CHIP_MANIFEST_EXT_TABLE_COUNT] {
431    fn from(o: &ManifestExtTable) -> [ManifestExtTableEntryDef; CHIP_MANIFEST_EXT_TABLE_COUNT] {
432        o.entries.map(|v| (&v).into())
433    }
434}
435
436#[cfg(test)]
437mod tests {
438    use super::*;
439    use crate::util::testdata;
440    use deser_hjson::from_str;
441
442    #[test]
443    fn test_manifest_from_hjson() {
444        let def: ManifestSpec =
445            from_str(&std::fs::read_to_string(testdata("image/manifest.hjson")).unwrap()).unwrap();
446
447        let _: Manifest = def.try_into().unwrap();
448    }
449
450    #[test]
451    fn test_manifest_from_hjson_missing() {
452        let def: ManifestSpec =
453            from_str(&std::fs::read_to_string(testdata("image/manifest_missing.hjson")).unwrap())
454                .unwrap();
455
456        let res: Result<Manifest> = def.try_into();
457        assert!(res.is_err())
458    }
459
460    #[test]
461    fn test_manifest_overwrite() {
462        let mut base: ManifestSpec =
463            from_str(&std::fs::read_to_string(testdata("image/manifest.hjson")).unwrap()).unwrap();
464        let other = ManifestSpec {
465            identifier: from_str("0xabcd").unwrap(),
466            binding_value: from_str(stringify!(["0", "1", "2", "3", "4", "5", "6", "7"])).unwrap(),
467            ..Default::default()
468        };
469        base.overwrite(other);
470        assert_eq!(base.identifier.0.unwrap().0, 0xabcd);
471        assert_eq!(
472            base.binding_value.map(|v| v.0.unwrap().0)[..],
473            [0, 1, 2, 3, 4, 5, 6, 7]
474        );
475
476        // Ensure unspecified fields are not overwritten.
477        assert_eq!(base.address_translation.0.unwrap().0, 0x739);
478    }
479
480    #[test]
481    fn test_manifest_convert() {
482        let def1: ManifestSpec =
483            from_str(&std::fs::read_to_string(testdata("image/manifest.hjson")).unwrap()).unwrap();
484        let def2 = def1.clone();
485
486        let bin1: Manifest = def1.try_into().unwrap();
487        let bin2: Manifest = def2.try_into().unwrap();
488
489        let redef: ManifestSpec = (&bin1).try_into().unwrap();
490        let rebin: Manifest = redef.try_into().unwrap();
491        assert_eq!(bin2.as_bytes(), rebin.as_bytes());
492    }
493}