opentitanlib/image/
manifest.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
5//! Structs for reading and writing manifests of flash boot stage images.
6//!
7//! Note: The structs below must match the definitions in
8//! sw/device/silicon_creator/lib/manifest.h.
9
10#![deny(warnings)]
11#![deny(unused)]
12#![deny(unsafe_code)]
13
14use crate::with_unknown;
15use anyhow::Result;
16use byteorder::{LittleEndian, WriteBytesExt};
17use serde::{Deserialize, Serialize};
18use serde_annotate::Annotate;
19use std::io::Write;
20use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
21
22// Currently, these definitions must be updated manually but they can be
23// generated using the following commands (requires bindgen):
24//   cargo install bindgen
25//   cd "${REPO_TOP}"
26//   bindgen --allowlist-type manifest_t --allowlist-var "MANIFEST_.*" \
27//      --allowlist-var "CHIP_.*" \
28//      --no-doc-comments --no-layout-tests \
29//      sw/device/silicon_creator/lib/manifest.h \
30//      sw/device/silicon_creator/lib/base/chip.h \
31//      -- -I./ -Isw/device/lib/base/freestanding
32// TODO: Generate some constants as hex if possible, replacing manually for now.
33
34pub const CHIP_MANIFEST_SIZE: u32 = 1024;
35
36// TODO(moidx): Update to a valid number once we figure out a manifest
37// versioning scheme.
38pub const CHIP_MANIFEST_VERSION_MAJOR2: u16 = 0x0002;
39pub const CHIP_MANIFEST_VERSION_MINOR1: u16 = 0x6c47;
40pub const CHIP_MANIFEST_VERSION_MAJOR1: u16 = 0x71c3;
41pub const CHIP_MANIFEST_EXT_TABLE_COUNT: usize = 15;
42pub const MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL: u32 = 0xa5a5a5a5;
43pub const MANIFEST_EXT_ID_SPX_KEY: u32 = 0x94ac01ec;
44pub const MANIFEST_EXT_ID_SPX_SIGNATURE: u32 = 0xad77f84a;
45pub const MANIFEST_EXT_ID_IMAGE_TYPE: u32 = 0x494d4754;
46pub const MANIFEST_EXT_ID_SECVER_WRITE: u32 = 0x3f086a41;
47pub const MANIFEST_EXT_ID_ISFB: u32 = 0x42465349;
48pub const MANIFEST_EXT_ID_ISFB_ERASE: u32 = 0x45465349;
49pub const MANIFEST_EXT_NAME_SPX_KEY: u32 = 0x30545845;
50pub const MANIFEST_EXT_NAME_SPX_SIGNATURE: u32 = 0x31545845;
51pub const MANIFEST_EXT_NAME_IMAGE_TYPE: u32 = 0x494d4754;
52pub const MANIFEST_EXT_NAME_SECVER_WRITE: u32 = 0x56434553;
53pub const MANIFEST_EXT_NAME_ISFB: u32 = 0x42465349;
54pub const MANIFEST_EXT_NAME_ISFB_ERASE: u32 = 0x45465349;
55pub const CHIP_ROM_EXT_IDENTIFIER: u32 = 0x4552544f;
56pub const CHIP_BL0_IDENTIFIER: u32 = 0x3042544f;
57pub const CHIP_ROM_EXT_SIZE_MIN: u32 = 8788;
58pub const CHIP_ROM_EXT_SIZE_MAX: u32 = 0x10000;
59pub const CHIP_BL0_SIZE_MIN: u32 = 8788;
60pub const CHIP_BL0_SIZE_MAX: u32 = 0x70000;
61
62with_unknown! {
63    pub enum ManifestKind: u32 {
64        RomExt = CHIP_ROM_EXT_IDENTIFIER,
65        Application = CHIP_BL0_IDENTIFIER,
66    }
67}
68
69/// Manifest for boot stage images stored in flash.
70#[repr(C)]
71#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Default)]
72pub struct Manifest {
73    pub signature: SigverifyBuffer,
74    pub usage_constraints: ManifestUsageConstraints,
75    pub pub_key: SigverifyBuffer,
76    pub address_translation: u32,
77    pub identifier: u32,
78    pub manifest_version: ManifestVersion,
79    pub signed_region_end: u32,
80    pub length: u32,
81    pub version_major: u32,
82    pub version_minor: u32,
83    pub security_version: u32,
84    pub timestamp: Timestamp,
85    pub binding_value: KeymgrBindingValue,
86    pub max_key_version: u32,
87    pub code_start: u32,
88    pub code_end: u32,
89    pub entry_point: u32,
90    pub extensions: ManifestExtTable,
91}
92
93/// A type that holds 2 16-bit values for manifest major and minor format versions.
94#[repr(C)]
95#[derive(Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
96pub struct ManifestVersion {
97    pub minor: u16,
98    pub major: u16,
99}
100
101/// A type that holds 1964 32-bit words for SPHINCS+ signatures.
102#[repr(C)]
103#[derive(Immutable, IntoBytes, FromBytes, Debug, Copy, Clone)]
104pub struct SigverifySpxSignature {
105    pub data: [u32; 1964usize],
106}
107
108impl Default for SigverifySpxSignature {
109    fn default() -> Self {
110        Self {
111            data: [0; 1964usize],
112        }
113    }
114}
115
116/// Extension header.
117#[repr(C)]
118#[derive(Immutable, IntoBytes, FromBytes, Debug, Default, Serialize, Deserialize)]
119pub struct ManifestExtHeader {
120    pub identifier: u32,
121    pub name: u32,
122}
123
124impl ManifestExtHeader {
125    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
126        dest.write_all(self.as_bytes())?;
127        Ok(())
128    }
129}
130
131/// SPHINCS+ signature manifest extension.
132#[repr(C)]
133#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
134pub struct ManifestExtSpxSignature {
135    pub header: ManifestExtHeader,
136    pub signature: SigverifySpxSignature,
137}
138
139/// A type that holds 8 32-bit words for SPHINCS+ public keys.
140#[repr(C)]
141#[derive(Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
142pub struct SigverifySpxKey {
143    pub data: [u32; 8usize],
144}
145
146/// SPHINCS+ public key manifest extension.
147#[repr(C)]
148#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
149pub struct ManifestExtSpxKey {
150    pub header: ManifestExtHeader,
151    pub key: SigverifySpxKey,
152}
153
154/// A type that holds 96 32-bit words for RSA-3072.
155#[repr(C)]
156#[derive(Immutable, IntoBytes, FromBytes, Debug)]
157pub struct SigverifyBuffer {
158    pub data: [u32; 96usize],
159}
160
161impl Default for SigverifyBuffer {
162    fn default() -> Self {
163        Self { data: [0; 96usize] }
164    }
165}
166
167#[repr(C)]
168#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
169pub struct ManifestExtImageType {
170    pub header: ManifestExtHeader,
171    pub image_type: u32,
172}
173
174/// SecVer Write manifest extension
175#[repr(C)]
176#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
177pub struct ManifestExtSecVerWrite {
178    pub header: ManifestExtHeader,
179    pub write: u32,
180}
181
182/// Integrator Specific Firmware Binding product expression.
183#[derive(Debug, Deserialize, Annotate)]
184pub struct ManifestExtIsfbProductExpr {
185    pub mask: u32,
186    pub value: u32,
187}
188
189/// Integrator Specific Firmware Binding manifest extension.
190#[derive(Debug, Serialize, Deserialize)]
191pub struct ManifestExtIsfb {
192    pub header: ManifestExtHeader,
193    pub strike_mask: u128,
194    pub product_expr_count: u32,
195    pub product_expr: Vec<ManifestExtIsfbProductExpr>,
196}
197
198impl ManifestExtIsfb {
199    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
200        self.header.write(dest)?;
201        dest.write_u128::<LittleEndian>(self.strike_mask)?;
202        dest.write_u32::<LittleEndian>(self.product_expr_count)?;
203        for x in &self.product_expr {
204            dest.write_u32::<LittleEndian>(x.mask)?;
205            dest.write_u32::<LittleEndian>(x.value)?;
206        }
207        Ok(())
208    }
209
210    pub fn to_vec(&self) -> Result<Vec<u8>> {
211        let mut buf = Vec::new();
212        self.write(&mut buf)?;
213        Ok(buf)
214    }
215}
216
217#[repr(C)]
218#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
219pub struct ManifestExtIsfbErasePolicy {
220    pub header: ManifestExtHeader,
221    pub erase_allowed: u32,
222}
223
224/// A type that holds the 256-bit device identifier.
225#[repr(C)]
226#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
227pub struct LifecycleDeviceId {
228    pub device_id: [u32; 8usize],
229}
230
231/// Manifest usage constraints.
232#[repr(C)]
233#[derive(Immutable, IntoBytes, FromBytes, Debug)]
234pub struct ManifestUsageConstraints {
235    pub selector_bits: u32,
236    pub device_id: LifecycleDeviceId,
237    pub manuf_state_creator: u32,
238    pub manuf_state_owner: u32,
239    pub life_cycle_state: u32,
240}
241
242impl Default for ManifestUsageConstraints {
243    fn default() -> Self {
244        Self {
245            selector_bits: 0,
246            device_id: LifecycleDeviceId {
247                device_id: [MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL; 8usize],
248            },
249            manuf_state_creator: MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL,
250            manuf_state_owner: MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL,
251            life_cycle_state: MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL,
252        }
253    }
254}
255
256/// Manifest timestamp
257#[repr(C)]
258#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
259pub struct Timestamp {
260    pub timestamp_low: u32,
261    pub timestamp_high: u32,
262}
263
264#[repr(C)]
265#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
266pub struct KeymgrBindingValue {
267    pub data: [u32; 8usize],
268}
269
270#[repr(C)]
271#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
272pub struct ManifestExtTableEntry {
273    pub identifier: u32,
274    pub offset: u32,
275}
276
277#[repr(C)]
278#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
279pub struct ManifestExtTable {
280    pub entries: [ManifestExtTableEntry; CHIP_MANIFEST_EXT_TABLE_COUNT],
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286    use std::mem::{offset_of, size_of};
287
288    /// Checks the layout of the manifest struct.
289    ///
290    /// Implemented as a function because using `offset_of!` at compile-time
291    /// requires a nightly compiler.
292    #[test]
293    pub fn test_manifest_layout() {
294        assert_eq!(offset_of!(Manifest, signature), 0);
295        assert_eq!(offset_of!(Manifest, usage_constraints), 384);
296        assert_eq!(offset_of!(Manifest, pub_key), 432);
297        assert_eq!(offset_of!(Manifest, address_translation), 816);
298        assert_eq!(offset_of!(Manifest, identifier), 820);
299        assert_eq!(offset_of!(Manifest, manifest_version), 824);
300        assert_eq!(offset_of!(Manifest, signed_region_end), 828);
301        assert_eq!(offset_of!(Manifest, length), 832);
302        assert_eq!(offset_of!(Manifest, version_major), 836);
303        assert_eq!(offset_of!(Manifest, version_minor), 840);
304        assert_eq!(offset_of!(Manifest, security_version), 844);
305        assert_eq!(offset_of!(Manifest, timestamp), 848);
306        assert_eq!(offset_of!(Manifest, binding_value), 856);
307        assert_eq!(offset_of!(Manifest, max_key_version), 888);
308        assert_eq!(offset_of!(Manifest, code_start), 892);
309        assert_eq!(offset_of!(Manifest, code_end), 896);
310        assert_eq!(offset_of!(Manifest, entry_point), 900);
311        assert_eq!(offset_of!(Manifest, extensions), 904);
312        assert_eq!(size_of::<Manifest>(), CHIP_MANIFEST_SIZE as usize);
313    }
314}