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/// Buffer of reserved bytes.
70///
71/// This struct is used to hold reserved bytes in the manifest to pad fields
72/// with 0xa5 to match layout specifications.
73#[repr(C)]
74#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Clone, Copy)]
75pub struct ReservedBuffer<const COUNT: usize> {
76    a: [u8; COUNT],
77}
78
79impl<const COUNT: usize> Default for ReservedBuffer<COUNT> {
80    fn default() -> Self {
81        Self {
82            a: std::array::from_fn(|_| 0xa5),
83        }
84    }
85}
86
87/// Manifest for boot stage images stored in flash.
88#[repr(C)]
89#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Default)]
90pub struct Manifest {
91    pub signature: SigverifyBuffer,
92    pub reserved_signature: ReservedBuffer<160>,
93    pub reserved_unsigned: ReservedBuffer<160>,
94    pub usage_constraints: ManifestUsageConstraints,
95    pub pub_key: SigverifyBuffer,
96    pub reserved_public_key: ReservedBuffer<160>,
97    pub reserved: ReservedBuffer<160>,
98    pub address_translation: u32,
99    pub identifier: u32,
100    pub manifest_version: ManifestVersion,
101    pub signed_region_end: u32,
102    pub length: u32,
103    pub version_major: u32,
104    pub version_minor: u32,
105    pub security_version: u32,
106    pub timestamp: Timestamp,
107    pub binding_value: KeymgrBindingValue,
108    pub max_key_version: u32,
109    pub code_start: u32,
110    pub code_end: u32,
111    pub entry_point: u32,
112    pub extensions: ManifestExtTable,
113}
114
115/// A type that holds 2 16-bit values for manifest major and minor format versions.
116#[repr(C)]
117#[derive(Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
118pub struct ManifestVersion {
119    pub minor: u16,
120    pub major: u16,
121}
122
123/// A type that holds 1964 32-bit words for SPHINCS+ signatures.
124#[repr(C)]
125#[derive(Immutable, IntoBytes, FromBytes, Debug, Copy, Clone)]
126pub struct SigverifySpxSignature {
127    pub data: [u32; 1964usize],
128}
129
130impl Default for SigverifySpxSignature {
131    fn default() -> Self {
132        Self {
133            data: [0; 1964usize],
134        }
135    }
136}
137
138/// Extension header.
139#[repr(C)]
140#[derive(Immutable, IntoBytes, FromBytes, Debug, Default, Serialize, Deserialize)]
141pub struct ManifestExtHeader {
142    pub identifier: u32,
143    pub name: u32,
144}
145
146impl ManifestExtHeader {
147    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
148        dest.write_all(self.as_bytes())?;
149        Ok(())
150    }
151}
152
153/// SPHINCS+ signature manifest extension.
154#[repr(C)]
155#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
156pub struct ManifestExtSpxSignature {
157    pub header: ManifestExtHeader,
158    pub signature: SigverifySpxSignature,
159}
160
161/// A type that holds 8 32-bit words for SPHINCS+ public keys.
162#[repr(C)]
163#[derive(Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
164pub struct SigverifySpxKey {
165    pub data: [u32; 8usize],
166}
167
168/// SPHINCS+ public key manifest extension.
169#[repr(C)]
170#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
171pub struct ManifestExtSpxKey {
172    pub header: ManifestExtHeader,
173    pub key: SigverifySpxKey,
174}
175
176/// A type that holds 16 32-bit words for ECDSA-P256.
177#[repr(C)]
178#[derive(Immutable, IntoBytes, FromBytes, Debug, Clone, Copy, Default)]
179pub struct SigverifyBuffer {
180    pub data: [u32; 16usize],
181}
182
183#[repr(C)]
184#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
185pub struct ManifestExtImageType {
186    pub header: ManifestExtHeader,
187    pub image_type: u32,
188}
189
190/// SecVer Write manifest extension
191#[repr(C)]
192#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
193pub struct ManifestExtSecVerWrite {
194    pub header: ManifestExtHeader,
195    pub write: u32,
196}
197
198/// Integrator Specific Firmware Binding product expression.
199#[derive(Debug, Deserialize, Annotate)]
200pub struct ManifestExtIsfbProductExpr {
201    pub mask: u32,
202    pub value: u32,
203}
204
205/// Integrator Specific Firmware Binding manifest extension.
206#[derive(Debug, Serialize, Deserialize)]
207pub struct ManifestExtIsfb {
208    pub header: ManifestExtHeader,
209    pub strike_mask: u128,
210    pub product_expr_count: u32,
211    pub product_expr: Vec<ManifestExtIsfbProductExpr>,
212}
213
214impl ManifestExtIsfb {
215    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
216        self.header.write(dest)?;
217        dest.write_u128::<LittleEndian>(self.strike_mask)?;
218        dest.write_u32::<LittleEndian>(self.product_expr_count)?;
219        for x in &self.product_expr {
220            dest.write_u32::<LittleEndian>(x.mask)?;
221            dest.write_u32::<LittleEndian>(x.value)?;
222        }
223        Ok(())
224    }
225
226    pub fn to_vec(&self) -> Result<Vec<u8>> {
227        let mut buf = Vec::new();
228        self.write(&mut buf)?;
229        Ok(buf)
230    }
231}
232
233#[repr(C)]
234#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
235pub struct ManifestExtIsfbErasePolicy {
236    pub header: ManifestExtHeader,
237    pub erase_allowed: u32,
238}
239
240/// A type that holds the 256-bit device identifier.
241#[repr(C)]
242#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
243pub struct LifecycleDeviceId {
244    pub device_id: [u32; 8usize],
245}
246
247/// Manifest usage constraints.
248#[repr(C)]
249#[derive(Immutable, IntoBytes, FromBytes, Debug)]
250pub struct ManifestUsageConstraints {
251    pub selector_bits: u32,
252    pub device_id: LifecycleDeviceId,
253    pub manuf_state_creator: u32,
254    pub manuf_state_owner: u32,
255    pub life_cycle_state: u32,
256}
257
258impl Default for ManifestUsageConstraints {
259    fn default() -> Self {
260        Self {
261            selector_bits: 0,
262            device_id: LifecycleDeviceId {
263                device_id: [MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL; 8usize],
264            },
265            manuf_state_creator: MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL,
266            manuf_state_owner: MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL,
267            life_cycle_state: MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL,
268        }
269    }
270}
271
272/// Manifest timestamp
273#[repr(C)]
274#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
275pub struct Timestamp {
276    pub timestamp_low: u32,
277    pub timestamp_high: u32,
278}
279
280#[repr(C)]
281#[derive(Immutable, IntoBytes, FromBytes, Debug, Default)]
282pub struct KeymgrBindingValue {
283    pub data: [u32; 8usize],
284}
285
286#[repr(C)]
287#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
288pub struct ManifestExtTableEntry {
289    pub identifier: u32,
290    pub offset: u32,
291}
292
293#[repr(C)]
294#[derive(KnownLayout, Immutable, IntoBytes, FromBytes, Debug, Default, Copy, Clone)]
295pub struct ManifestExtTable {
296    pub entries: [ManifestExtTableEntry; CHIP_MANIFEST_EXT_TABLE_COUNT],
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use std::mem::{offset_of, size_of};
303
304    /// Checks the layout of the manifest struct.
305    ///
306    /// Implemented as a function because using `offset_of!` at compile-time
307    /// requires a nightly compiler.
308    #[test]
309    pub fn test_manifest_layout() {
310        assert_eq!(offset_of!(Manifest, signature), 0);
311        assert_eq!(offset_of!(Manifest, reserved_signature), 64);
312        assert_eq!(offset_of!(Manifest, reserved_unsigned), 224);
313        assert_eq!(offset_of!(Manifest, usage_constraints), 384);
314        assert_eq!(offset_of!(Manifest, pub_key), 432);
315        assert_eq!(offset_of!(Manifest, reserved_public_key), 496);
316        assert_eq!(offset_of!(Manifest, reserved), 656);
317        assert_eq!(offset_of!(Manifest, address_translation), 816);
318        assert_eq!(offset_of!(Manifest, identifier), 820);
319        assert_eq!(offset_of!(Manifest, manifest_version), 824);
320        assert_eq!(offset_of!(Manifest, signed_region_end), 828);
321        assert_eq!(offset_of!(Manifest, length), 832);
322        assert_eq!(offset_of!(Manifest, version_major), 836);
323        assert_eq!(offset_of!(Manifest, version_minor), 840);
324        assert_eq!(offset_of!(Manifest, security_version), 844);
325        assert_eq!(offset_of!(Manifest, timestamp), 848);
326        assert_eq!(offset_of!(Manifest, binding_value), 856);
327        assert_eq!(offset_of!(Manifest, max_key_version), 888);
328        assert_eq!(offset_of!(Manifest, code_start), 892);
329        assert_eq!(offset_of!(Manifest, code_end), 896);
330        assert_eq!(offset_of!(Manifest, entry_point), 900);
331        assert_eq!(offset_of!(Manifest, extensions), 904);
332        assert_eq!(size_of::<Manifest>(), CHIP_MANIFEST_SIZE as usize);
333    }
334}