Use repr(C) structs instead of offsets and lengths in the signer.

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/lib/manifest.h b/sw/device/silicon_creator/lib/manifest.h
index 0271a62..93174c0 100644
--- a/sw/device/silicon_creator/lib/manifest.h
+++ b/sw/device/silicon_creator/lib/manifest.h
@@ -27,6 +27,10 @@
  * integrity and authenticity of the next stage before handing over execution.
  *
  * Use of this struct for stages following BL0 is optional.
+ *
+ * Note: The definitions in
+ * sw/host/rom_ext_image_tools/signer/image/src/manifest.rs must be updated if
+ * this struct is modified. Please see the instructions in that file.
  */
 typedef struct manifest {
   /**
diff --git a/sw/host/rom_ext_image_tools/signer/Cargo.lock b/sw/host/rom_ext_image_tools/signer/Cargo.lock
index 775f594..2655a0f 100644
--- a/sw/host/rom_ext_image_tools/signer/Cargo.lock
+++ b/sw/host/rom_ext_image_tools/signer/Cargo.lock
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "anyhow"
 version = "1.0.40"
@@ -7,6 +9,18 @@
 checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
 
 [[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
 name = "cfg-if"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -33,6 +47,15 @@
 ]
 
 [[package]]
+name = "memoffset"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
 name = "mundane"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -87,7 +110,9 @@
 name = "rom_ext_image"
 version = "0.1.0"
 dependencies = [
+ "memoffset",
  "thiserror",
+ "zerocopy",
 ]
 
 [[package]]
@@ -97,6 +122,7 @@
  "anyhow",
  "mundane",
  "rom_ext_image",
+ "zerocopy",
 ]
 
 [[package]]
@@ -167,19 +193,31 @@
 ]
 
 [[package]]
-name = "thiserror"
-version = "1.0.24"
+name = "synstructure"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
+checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
+dependencies = [
+ "proc-macro2 1.0.26",
+ "quote 1.0.9",
+ "syn 1.0.72",
+ "unicode-xid 0.2.2",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.24"
+version = "1.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
+checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
 dependencies = [
  "proc-macro2 1.0.26",
  "quote 1.0.9",
@@ -197,3 +235,24 @@
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "zerocopy"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e59ec1d2457bd6c0dd89b50e7d9d6b0b647809bf3f0a59ac85557046950b7b2"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0af017aca1fa6181f5dd7a802456fe6f7666ecdcc18d0910431f0fc89d474e51"
+dependencies = [
+ "proc-macro2 1.0.26",
+ "syn 1.0.72",
+ "synstructure",
+]
diff --git a/sw/host/rom_ext_image_tools/signer/Cargo.toml b/sw/host/rom_ext_image_tools/signer/Cargo.toml
index 4aeeb3e..4510720 100644
--- a/sw/host/rom_ext_image_tools/signer/Cargo.toml
+++ b/sw/host/rom_ext_image_tools/signer/Cargo.toml
@@ -24,8 +24,9 @@
 debug = true
 
 [dependencies]
-rom_ext_image = { path = "image" }
 anyhow = "1.0.40"
+rom_ext_image = { path = "image" }
+zerocopy = "0.5.0"
 
 [dependencies.mundane]
 version = "0.4.4"
diff --git a/sw/host/rom_ext_image_tools/signer/image/Cargo.toml b/sw/host/rom_ext_image_tools/signer/image/Cargo.toml
index 35cb230..14b5f31 100644
--- a/sw/host/rom_ext_image_tools/signer/image/Cargo.toml
+++ b/sw/host/rom_ext_image_tools/signer/image/Cargo.toml
@@ -9,4 +9,7 @@
 edition = "2018"
 
 [dependencies]
+memoffset = "0.6.0"
 thiserror = "1.0.24"
+zerocopy = "0.5.0"
+
diff --git a/sw/host/rom_ext_image_tools/signer/image/src/image.rs b/sw/host/rom_ext_image_tools/signer/image/src/image.rs
index 01c3355..e55e9ee 100644
--- a/sw/host/rom_ext_image_tools/signer/image/src/image.rs
+++ b/sw/host/rom_ext_image_tools/signer/image/src/image.rs
@@ -6,72 +6,83 @@
 #![deny(unused)]
 #![deny(unsafe_code)]
 
-use crate::manifest;
+use std::mem::size_of;
+use std::ops::Deref;
+use std::ops::DerefMut;
+
+use memoffset::offset_of;
 use thiserror::Error;
+use zerocopy::AsBytes;
+use zerocopy::LayoutVerified;
+
+use crate::manifest::Manifest;
 
 #[derive(Error, Debug, PartialEq)]
 pub enum ImageError {
-    #[error("Expected at most {len} bytes for offset {offset}, received {data_len}.")]
-    FieldData {
-        offset: usize,
-        len: usize,
-        data_len: usize,
-    },
+    #[error("Failed to parse image manifest.")]
+    Parse,
 }
 
-/// A thin wrapper around a Vec to help with setting ROM_EXT manifest fields.
+/// A thin wrapper around manifest and payload buffers to help with setting manifest fields and
+/// signing the image.
 #[derive(Debug)]
-pub struct Image {
-    data: Vec<u8>,
+pub struct Image<'a> {
+    pub manifest: LayoutVerified<&'a mut [u8], Manifest>,
+    pub payload: &'a [u8],
 }
 
-impl Image {
-    /// Sets the value of a ROM_EXT manifest field.
-    pub fn set_manifest_field<I>(
-        &mut self,
-        field: &manifest::ManifestField,
-        field_data: I,
-    ) -> Result<(), ImageError>
-    where
-        I: IntoIterator<Item = u8>,
-        I::IntoIter: ExactSizeIterator,
-    {
-        let field_data = field_data.into_iter();
-        if field_data.len() > field.size_bytes {
-            Err(ImageError::FieldData {
-                offset: field.offset,
-                len: field.size_bytes,
-                data_len: field_data.len(),
-            })
-        } else {
-            self.data.splice(
-                field.offset..(field.offset + field_data.len()),
-                field_data,
-            );
-            Ok(())
+impl<'a> Image<'a> {
+    pub fn new(
+        manifest_buffer: &'a mut ManifestBuffer,
+        payload: &'a [u8],
+    ) -> Result<Image<'a>, ImageError> {
+        let manifest = LayoutVerified::new(&mut **manifest_buffer)
+            .ok_or(ImageError::Parse)?;
+        Ok(Self { manifest, payload })
+    }
+
+    pub fn bytes(&self) -> Vec<u8> {
+        [self.manifest.as_bytes(), self.payload].concat()
+    }
+
+    pub fn signed_bytes(&self) -> Vec<u8> {
+        self.bytes().split_off(offset_of!(Manifest, image_length))
+    }
+}
+
+/// A buffer with the same size and alignment as `Manifest`.
+pub struct ManifestBuffer {
+    buf: [u8; size_of::<Manifest>()],
+    // This forces this struct to have the same alignment as `Manifest`.
+    _align: [Manifest; 0],
+}
+
+impl ManifestBuffer {
+    pub fn new() -> Self {
+        Self {
+            buf: [0u8; size_of::<Manifest>()],
+            _align: [],
         }
     }
+}
 
-    /// Returns the signed bytes of the image.
-    pub fn signed_bytes(&self) -> &[u8] {
-        &self.data[manifest::ROM_EXT_SIGNED_AREA_START_OFFSET..]
-    }
-
-    /// Returns the size of the image in number of bytes.
-    pub fn len(&self) -> usize {
-        self.data.len()
+impl Default for ManifestBuffer {
+    fn default() -> Self {
+        Self::new()
     }
 }
 
-impl AsRef<[u8]> for Image {
-    fn as_ref(&self) -> &[u8] {
-        &self.data
+impl Deref for ManifestBuffer {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &self.buf
     }
 }
 
-impl From<Vec<u8>> for Image {
-    fn from(data: Vec<u8>) -> Self {
-        Image { data }
+impl DerefMut for ManifestBuffer {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.buf
     }
 }
 
@@ -81,43 +92,29 @@
 
     #[test]
     fn test_set_manifest_field() -> Result<(), ImageError> {
-        let mut image = Image::from(vec![0; 1024]);
-        let field = manifest::ROM_EXT_IMAGE_LENGTH;
-        let val: [u8; 4] = [0xA5; 4];
-        image.set_manifest_field(&field, val.iter().cloned())?;
-        assert_eq!(
-            image.as_ref()[field.offset..field.offset + field.size_bytes],
-            val
-        );
+        let mut manifest_buffer = ManifestBuffer::new();
+        let payload = [0u8; 0];
+        let mut image = Image::new(&mut manifest_buffer, &payload)?;
+        let identifier: u32 = 0x01020304;
+        image.manifest.identifier = identifier;
+        for i in 0..4 {
+            assert_eq!(manifest_buffer[i], identifier.to_le_bytes()[i]);
+        }
         Ok(())
     }
 
     #[test]
-    fn test_set_manifest_field_error() {
-        let mut image = Image::from(vec![0; 1024]);
-        let field = manifest::ROM_EXT_IMAGE_LENGTH;
-        let val: [u8; 6] = [0xA5; 6];
-        assert_eq!(
-            image.set_manifest_field(&field, val.iter().cloned()),
-            Err(ImageError::FieldData {
-                offset: field.offset,
-                len: field.size_bytes,
-                data_len: 6
-            })
-        );
-    }
-
-    #[test]
-    fn test_signed_bytes() -> Result<(), ImageError> {
-        let mut image = Image::from(vec![0; 1024]);
-        let field = manifest::ROM_EXT_IMAGE_SIGNATURE;
-        let val: [u8; manifest::ROM_EXT_IMAGE_SIGNATURE.size_bytes] =
-            [0xA5; manifest::ROM_EXT_IMAGE_SIGNATURE.size_bytes];
-        image.set_manifest_field(&field, val.iter().cloned())?;
-        assert_eq!(
-            image.as_ref()[field.offset..field.offset + field.size_bytes],
-            val
-        );
+    fn test_bytes() -> Result<(), ImageError> {
+        let mut manifest_buffer = ManifestBuffer::new();
+        let payload = [5u8, 6u8, 7u8, 8u8];
+        let mut image = Image::new(&mut manifest_buffer, &payload)?;
+        let identifier: u32 = 0x01020304;
+        image.manifest.identifier = identifier;
+        let bytes = image.bytes();
+        for i in 0..4 {
+            assert_eq!(bytes[i], manifest_buffer[i]);
+            assert_eq!(bytes[size_of::<Manifest>() + i], payload[i]);
+        }
         Ok(())
     }
 }
diff --git a/sw/host/rom_ext_image_tools/signer/image/src/manifest.rs b/sw/host/rom_ext_image_tools/signer/image/src/manifest.rs
index 2959964..8e6c6a9 100644
--- a/sw/host/rom_ext_image_tools/signer/image/src/manifest.rs
+++ b/sw/host/rom_ext_image_tools/signer/image/src/manifest.rs
@@ -1,111 +1,94 @@
 // Copyright lowRISC contributors.
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
-//
-// ---------- W A R N I N G: A U T O - G E N E R A T E D   C O D E !! ----------
-// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE
-// FOLLOWING COMMAND:
-// util/rom-ext-manifest-generator.py
-//     --input-dir=sw/device/silicon_creator/rom_exts
-//     --output-dir=<destination dir>
-//     --output-files=rust
 
-pub struct ManifestField {
-    pub offset: usize,
-    pub size_bytes: usize,
+//! Structs for reading and writing manifests of flash boot stage images.
+//!
+//! Note: The structs below must match the definitions in
+//! sw/device/silicon_creator/lib/manifest.h.
+
+#![deny(warnings)]
+#![deny(unused)]
+#![deny(unsafe_code)]
+
+use std::mem::size_of;
+
+use memoffset::offset_of;
+use zerocopy::AsBytes;
+use zerocopy::FromBytes;
+
+// Currently, these definitions must be updated manually but they can be
+// generated using the following commands (requires bindgen):
+//   cargo install bindgen
+//   cd "${REPO_TOP}"
+//   bindgen --allowlist-type manifest_t --allowlist-var "MANIFEST_.*" \
+//      --no-doc-comments --no-layout-tests \
+//      sw/device/silicon_creator/lib/manifest.h \
+//      -- -I./ -Isw/device/lib/base/freestanding
+
+pub const MANIFEST_SIZE: u32 = 880;
+
+/// Manifest for boot stage images stored in flash.
+#[repr(C)]
+#[derive(FromBytes, AsBytes, Debug, Default)]
+pub struct Manifest {
+    pub identifier: u32,
+    pub reserved0: u32,
+    pub signature: SigverifyRsaBuffer,
+    pub image_length: u32,
+    pub image_version: u32,
+    pub image_timestamp: u64,
+    pub exponent: u32,
+    pub reserved1: u32,
+    pub usage_constraints: [u32; 8usize],
+    pub lockdown_info: [u32; 4usize],
+    pub modulus: SigverifyRsaBuffer,
+    pub extension0_offset: u32,
+    pub extension0_checksum: u32,
+    pub extension1_offset: u32,
+    pub extension1_checksum: u32,
+    pub extension2_offset: u32,
+    pub extension2_checksum: u32,
+    pub extension3_offset: u32,
+    pub extension3_checksum: u32,
 }
 
-pub const ROM_EXT_MANIFEST_IDENTIFIER: ManifestField = ManifestField {
-    offset: 0,
-    size_bytes: 4,
-};
+/// A type that holds 96 32-bit words for RSA-3072.
+#[repr(C)]
+#[derive(FromBytes, AsBytes, Debug)]
+pub struct SigverifyRsaBuffer {
+    pub data: [u32; 96usize],
+}
 
-pub const ROM_EXT_IMAGE_SIGNATURE: ManifestField = ManifestField {
-    offset: 8,
-    size_bytes: 384,
-};
+impl Default for SigverifyRsaBuffer {
+    fn default() -> Self {
+        Self { data: [0; 96usize] }
+    }
+}
 
-pub const ROM_EXT_IMAGE_LENGTH: ManifestField = ManifestField {
-    offset: 392,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_IMAGE_VERSION: ManifestField = ManifestField {
-    offset: 396,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_IMAGE_TIMESTAMP: ManifestField = ManifestField {
-    offset: 400,
-    size_bytes: 8,
-};
-
-pub const ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT: ManifestField =
-    ManifestField {
-        offset: 408,
-        size_bytes: 4,
-    };
-
-pub const ROM_EXT_USAGE_CONSTRAINTS: ManifestField = ManifestField {
-    offset: 416,
-    size_bytes: 32,
-};
-
-pub const ROM_EXT_PERIPHERAL_LOCKDOWN_INFO: ManifestField = ManifestField {
-    offset: 448,
-    size_bytes: 16,
-};
-
-pub const ROM_EXT_SIGNATURE_KEY_MODULUS: ManifestField = ManifestField {
-    offset: 464,
-    size_bytes: 384,
-};
-
-pub const ROM_EXT_EXTENSION0_OFFSET: ManifestField = ManifestField {
-    offset: 848,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION0_CHECKSUM: ManifestField = ManifestField {
-    offset: 852,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION1_OFFSET: ManifestField = ManifestField {
-    offset: 856,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION1_CHECKSUM: ManifestField = ManifestField {
-    offset: 860,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION2_OFFSET: ManifestField = ManifestField {
-    offset: 864,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION2_CHECKSUM: ManifestField = ManifestField {
-    offset: 868,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION3_OFFSET: ManifestField = ManifestField {
-    offset: 872,
-    size_bytes: 4,
-};
-
-pub const ROM_EXT_EXTENSION3_CHECKSUM: ManifestField = ManifestField {
-    offset: 876,
-    size_bytes: 4,
-};
-
-/// Manifest offset signed_area_start from the base.
-pub const ROM_EXT_SIGNED_AREA_START_OFFSET: usize = 392;
-
-/// Manifest offset interrupt_vector from the base.
-pub const ROM_EXT_INTERRUPT_VECTOR_OFFSET: usize = 1024;
-
-/// Manifest offset entry_point from the base.
-pub const ROM_EXT_ENTRY_POINT_OFFSET: usize = 1152;
+/// Checks the layout of the manifest struct.
+///
+/// Implemented as a function because using `offset_of!` at compile-time
+/// requires a nightly compiler.
+/// TODO(#6915): Convert this to a unit test after we start running rust tests during our builds.
+pub fn check_manifest_layout() {
+    assert_eq!(offset_of!(Manifest, identifier), 0);
+    assert_eq!(offset_of!(Manifest, reserved0), 4);
+    assert_eq!(offset_of!(Manifest, signature), 8);
+    assert_eq!(offset_of!(Manifest, image_length), 392);
+    assert_eq!(offset_of!(Manifest, image_version), 396);
+    assert_eq!(offset_of!(Manifest, image_timestamp), 400);
+    assert_eq!(offset_of!(Manifest, exponent), 408);
+    assert_eq!(offset_of!(Manifest, reserved1), 412);
+    assert_eq!(offset_of!(Manifest, usage_constraints), 416);
+    assert_eq!(offset_of!(Manifest, lockdown_info), 448);
+    assert_eq!(offset_of!(Manifest, modulus), 464);
+    assert_eq!(offset_of!(Manifest, extension0_offset), 848);
+    assert_eq!(offset_of!(Manifest, extension0_checksum), 852);
+    assert_eq!(offset_of!(Manifest, extension1_offset), 856);
+    assert_eq!(offset_of!(Manifest, extension1_checksum), 860);
+    assert_eq!(offset_of!(Manifest, extension2_offset), 864);
+    assert_eq!(offset_of!(Manifest, extension2_checksum), 868);
+    assert_eq!(offset_of!(Manifest, extension3_offset), 872);
+    assert_eq!(size_of::<Manifest>(), MANIFEST_SIZE as usize);
+}
diff --git a/sw/host/rom_ext_image_tools/signer/src/main.rs b/sw/host/rom_ext_image_tools/signer/src/main.rs
index 5fd3f23..7982d7a 100644
--- a/sw/host/rom_ext_image_tools/signer/src/main.rs
+++ b/sw/host/rom_ext_image_tools/signer/src/main.rs
@@ -6,14 +6,11 @@
 #![deny(unused)]
 #![deny(unsafe_code)]
 
+use std::convert::TryFrom;
 use std::env;
 use std::fs;
-use std::path::Path;
+use std::mem::size_of;
 use std::path::PathBuf;
-use std::convert::TryFrom;
-
-use rom_ext_image::image::Image;
-use rom_ext_image::manifest;
 
 use mundane::hash::Sha256;
 use mundane::public::rsa::RsaPkcs1v15;
@@ -26,6 +23,13 @@
 use anyhow::Context;
 use anyhow::Result;
 
+use zerocopy::AsBytes;
+
+use rom_ext_image::image::Image;
+use rom_ext_image::image::ManifestBuffer;
+use rom_ext_image::manifest;
+use rom_ext_image::manifest::Manifest;
+
 // Type aliases for convenience.
 type ImageSignature =
     mundane::public::rsa::RsaSignature<B3072, RsaPkcs1v15, Sha256>;
@@ -52,8 +56,9 @@
     }
 }
 
+// FIXME: Keeping for now, can be removed if not used in opentitantool.
 /// Parses an unsigned big-endian hex string into a little-endian byte vector.
-fn parse_hex_str(hex_str: &str) -> Result<Vec<u8>> {
+fn _parse_hex_str(hex_str: &str) -> Result<Vec<u8>> {
     ensure!(
         hex_str.starts_with("0x")
             && hex_str.len() > 2
@@ -71,84 +76,28 @@
 
 /// Updates the manifest of an image.
 // TODO: This function must use a public key.
-fn update_image_manifest(
-    image: &mut Image,
-    key: impl AsRef<Path>,
-) -> Result<()> {
-    let key = fs::read(&key).with_context(|| {
-        format!("Failed to read the key from `{}`.", key.as_ref().display())
-    })?;
-    let key =
-        PrivateKey::parse_from_der(&key).context("Failed to parse the key.")?;
+fn update_image_manifest(image: &mut Image, key: &PrivateKey) -> Result<()> {
+    *image.manifest = Manifest {
+        identifier: 0x4552544f,
+        image_length: u32::try_from(image.bytes().len())?,
+        ..Default::default()
+    };
 
-    image.set_manifest_field(
-        &manifest::ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT,
-        key.public_exponent_be().iter().rev().cloned(),
-    )?;
-    image.set_manifest_field(
-        &manifest::ROM_EXT_SIGNATURE_KEY_MODULUS,
-        key.public_modulus_be().iter().rev().cloned(),
-    )?;
-    image.set_manifest_field(
-        &manifest::ROM_EXT_MANIFEST_IDENTIFIER,
-        parse_hex_str("0x4552544f")?,
-    )?;
-
-    image.set_manifest_field(
-        &manifest::ROM_EXT_IMAGE_VERSION,
-        // TODO: Use into_iter once it's stabilized (for all usages in this file).
-        std::array::IntoIter::new(0_u32.to_le_bytes()),
-    )?;
-
-    // TODO: Do we need these fields?
-    let extensions = [
-        (
-            &manifest::ROM_EXT_EXTENSION0_OFFSET,
-            &manifest::ROM_EXT_EXTENSION0_CHECKSUM,
-        ),
-        (
-            &manifest::ROM_EXT_EXTENSION1_OFFSET,
-            &manifest::ROM_EXT_EXTENSION1_CHECKSUM,
-        ),
-        (
-            &manifest::ROM_EXT_EXTENSION2_OFFSET,
-            &manifest::ROM_EXT_EXTENSION2_CHECKSUM,
-        ),
-        (
-            &manifest::ROM_EXT_EXTENSION3_OFFSET,
-            &manifest::ROM_EXT_EXTENSION3_CHECKSUM,
-        ),
-    ];
-    for fields in &extensions {
-        image.set_manifest_field(
-            fields.0,
-            std::array::IntoIter::new(0_u32.to_le_bytes()),
-        )?;
-        image.set_manifest_field(
-            fields.1,
-            std::array::IntoIter::new(0_u32.to_le_bytes()),
-        )?;
+    let exponent_be = key.public_exponent_be();
+    let dest = image.manifest.exponent.as_bytes_mut().iter_mut();
+    let src = exponent_be.iter().rev().copied();
+    ensure!(dest.len() >= src.len(), "Unexpected exponent length.");
+    for (d, s) in Iterator::zip(dest, src) {
+        *d = s;
     }
-    image.set_manifest_field(
-        &manifest::ROM_EXT_USAGE_CONSTRAINTS,
-        std::array::IntoIter::new(
-            [0; manifest::ROM_EXT_USAGE_CONSTRAINTS.size_bytes],
-        ),
-    )?;
-    image.set_manifest_field(
-        &manifest::ROM_EXT_PERIPHERAL_LOCKDOWN_INFO,
-        std::array::IntoIter::new(
-            [0; manifest::ROM_EXT_PERIPHERAL_LOCKDOWN_INFO.size_bytes],
-        ),
-    )?;
-    image.set_manifest_field(
-        &manifest::ROM_EXT_IMAGE_TIMESTAMP,
-        std::array::IntoIter::new(0_u64.to_le_bytes()),
-    )?;
-    image.set_manifest_field(
-        &manifest::ROM_EXT_IMAGE_LENGTH,
-        std::array::IntoIter::new(u32::try_from(image.len())?.to_le_bytes()),
-    )?;
+
+    let modulus_be = key.public_modulus_be();
+    let dest = image.manifest.modulus.as_bytes_mut().iter_mut();
+    let src = modulus_be.iter().rev().copied();
+    ensure!(dest.len() == src.len(), "Unexpected modulus length.");
+    for (d, s) in Iterator::zip(dest, src) {
+        *d = s;
+    }
 
     Ok(())
 }
@@ -156,43 +105,52 @@
 /// Calculates the signature for the signed portion of an image.
 fn calculate_image_signature(
     image: &Image,
-    key: impl AsRef<Path>,
+    private_key: &PrivateKey,
 ) -> Result<ImageSignature> {
-    let key = fs::read(&key).with_context(|| {
-        format!("Failed to read key from `{}`.", key.as_ref().display())
-    })?;
-    let key =
-        PrivateKey::parse_from_der(&key).context("Failed to parse the key.")?;
-    let sig = ImageSignature::sign(&key, image.signed_bytes())
-        .context("Failed to sign the image.")?;
-    Ok(sig)
+    ImageSignature::sign(&private_key, &image.signed_bytes())
+        .context("Failed to calculate image signature.")
 }
 
 /// Updates the signature of an image.
 fn update_image_signature(
     image: &mut Image,
-    signature: ImageSignature,
+    sig: ImageSignature,
 ) -> Result<()> {
-    image.set_manifest_field(
-        &manifest::ROM_EXT_IMAGE_SIGNATURE,
-        signature.bytes().iter().rev().cloned(),
-    )?;
+    let dest = image.manifest.signature.as_bytes_mut().iter_mut();
+    let src = sig.bytes().iter().rev().copied();
+    ensure!(dest.len() == src.len(), "Unexpected signature length.");
+    for (d, s) in Iterator::zip(dest, src) {
+        *d = s;
+    }
     Ok(())
 }
 
 fn main() -> Result<()> {
+    // TODO(#6915): Convert this to a unit test after we start running rust tests during our
+    // builds.
+    manifest::check_manifest_layout();
     let args = Args::new(env::args_os())?;
-    let mut image = Image::from(fs::read(&args.input).with_context(|| {
-        format!("Failed to read the image from {}", args.input.display())
-    })?);
 
-    // TODO for a future refactor into opentitantool: These functions probably should belong to a
-    // Signer struct and should move into the image crate.
-    // TODO: This must use the public key.
-    update_image_manifest(&mut image, &args.priv_key)?;
-    let sig = calculate_image_signature(&image, &args.priv_key)?;
+    // We use a separate buffer for manifest because it must have the same alignment as `Manifest`
+    // to be able to use `LayoutVerified::new()` and the approach we use to ensure this requires
+    // its size to be known at compile time.
+    let payload = &fs::read(&args.input)
+        .with_context(|| format!("Failed to read {}", args.input.display()))?
+        [size_of::<Manifest>()..];
+    let mut manifest_buffer = ManifestBuffer::new();
+    let mut image = Image::new(&mut manifest_buffer, payload)?;
+
+    let key = fs::read(&args.priv_key).with_context(|| {
+        format!("Failed to read the key from `{}`.", args.priv_key.display())
+    })?;
+    let key =
+        PrivateKey::parse_from_der(&key).context("Failed to parse the key.")?;
+    update_image_manifest(&mut image, &key)?;
+
+    let sig = calculate_image_signature(&image, &key)?;
     update_image_signature(&mut image, sig)?;
-    fs::write(&args.output, image).with_context(|| {
+
+    fs::write(&args.output, image.bytes()).with_context(|| {
         format!("Failed to write the image to {}", args.output.display())
     })?;
     Ok(())