Improve ROM_EXT image signer CLI and some cleanups

This change:
- Removes
  - `config.hjson` along with related code and dependencies,
  - `sw/host/rom_ext_image_tools/signer/dev` folder,
  - Most of the methods from the image struct, essentially making it a
thin wrapper around a `Vec<[u8]>`,
- Exposes input image, private key, and output image paths as command
line arguments (required for signing an image with multiple keys during
build),
- Adds `thiserror` and `anyhow` dependencies to remove `expect`s,
- Modifies the ROM_EXT manifest generator rust template to generate
structs instead of individual constants, which enables some basic checks
in `Image::set_manifest_field`.

CLI is pretty basic because the functions in will eventually be
integrated into the opentitan tool.

Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/rom_exts/manifest.rs.tpl b/sw/device/silicon_creator/rom_exts/manifest.rs.tpl
index 6b71f8e..34bd284 100644
--- a/sw/device/silicon_creator/rom_exts/manifest.rs.tpl
+++ b/sw/device/silicon_creator/rom_exts/manifest.rs.tpl
@@ -10,20 +10,21 @@
 //     --output-dir=<destination dir>
 //     --output-files=rust
 
+pub struct ManifestField {
+    pub offset: usize,
+    pub size_bytes: usize,
+}
+
 % for name, region in regions:
-/// Manifest field ${name} offset from the base.
-pub const ${region.offset_name().as_c_define()}:u32 = ${region.base_addr};
-
-/// Manifest field ${name} size in bytes.
-pub const ${region.size_bytes_name().as_c_define()}:u32 = ${region.size_bytes};
-
-/// Manifest field ${name} size in words.
-pub const ${region.size_words_name().as_c_define()}:u32 = ${region.size_words};
+pub const ${region.name.as_c_define()}: ManifestField = ManifestField {
+    offset: ${region.base_addr},
+    size_bytes: ${region.size_bytes},
+};
 
 % endfor
 
 % for name, offset in offsets:
 /// Manifest offset ${name} from the base.
-pub const ${offset.offset_name().as_c_define()}:u32 = ${offset.offset};
+pub const ${offset.offset_name().as_c_define()}:usize = ${offset.offset};
 
 %endfor
diff --git a/sw/host/rom_ext_image_tools/signer/Cargo.lock b/sw/host/rom_ext_image_tools/signer/Cargo.lock
index 483c0b1..775f594 100644
--- a/sw/host/rom_ext_image_tools/signer/Cargo.lock
+++ b/sw/host/rom_ext_image_tools/signer/Cargo.lock
@@ -1,19 +1,10 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
 [[package]]
-name = "aho-corasick"
-version = "0.7.15"
+name = "anyhow"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
 
 [[package]]
 name = "cfg-if"
@@ -33,28 +24,6 @@
 ]
 
 [[package]]
-name = "lazy_static"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "linked-hash-map"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
-dependencies = [
- "serde",
- "serde_test",
-]
-
-[[package]]
 name = "log"
 version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -64,12 +33,6 @@
 ]
 
 [[package]]
-name = "memchr"
-version = "2.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
-
-[[package]]
 name = "mundane"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -79,24 +42,6 @@
 ]
 
 [[package]]
-name = "num-traits"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
-dependencies = [
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
 name = "plain"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -112,10 +57,13 @@
 ]
 
 [[package]]
-name = "quote"
-version = "0.3.15"
+name = "proc-macro2"
+version = "1.0.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
+checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
+dependencies = [
+ "unicode-xid 0.2.2",
+]
 
 [[package]]
 name = "quote"
@@ -123,49 +71,31 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
 dependencies = [
- "proc-macro2",
+ "proc-macro2 0.4.30",
 ]
 
 [[package]]
-name = "regex"
-version = "1.4.2"
+name = "quote"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
 dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
- "thread_local",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
-
-[[package]]
-name = "rom_ext_config"
-version = "0.1.0"
-dependencies = [
- "serde",
- "serde-hjson",
- "serde_derive",
+ "proc-macro2 1.0.26",
 ]
 
 [[package]]
 name = "rom_ext_image"
 version = "0.1.0"
 dependencies = [
- "rom_ext_config",
+ "thiserror",
 ]
 
 [[package]]
 name = "rom_ext_signer"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "mundane",
- "rom_ext_config",
  "rom_ext_image",
 ]
 
@@ -194,7 +124,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb"
 dependencies = [
- "proc-macro2",
+ "proc-macro2 0.4.30",
  "quote 0.6.13",
  "syn 0.15.44",
 ]
@@ -215,100 +145,55 @@
 checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 
 [[package]]
-name = "serde"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8"
-
-[[package]]
-name = "serde-hjson"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153"
-dependencies = [
- "lazy_static 0.2.11",
- "linked-hash-map",
- "num-traits 0.1.43",
- "regex",
- "serde",
-]
-
-[[package]]
-name = "serde_codegen"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c5d8a33087d8984f9535daa62a6498a08f6476050b00ab9339dd847e4c25cc"
-dependencies = [
- "quote 0.3.15",
- "serde_codegen_internals",
- "syn 0.10.8",
-]
-
-[[package]]
-name = "serde_codegen_internals"
-version = "0.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afad7924a009f859f380e4a2e3a509a845c2ac66435fcead74a4d983b21ae806"
-dependencies = [
- "syn 0.10.8",
-]
-
-[[package]]
-name = "serde_derive"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce44e5f4264b39e9d29c875357b7cc3ebdfb967bb9e22bfb5e44ffa400af5306"
-dependencies = [
- "serde_codegen",
-]
-
-[[package]]
-name = "serde_test"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "syn"
-version = "0.10.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58fd09df59565db3399efbba34ba8a2fec1307511ebd245d0061ff9d42691673"
-dependencies = [
- "quote 0.3.15",
- "unicode-xid 0.0.4",
-]
-
-[[package]]
 name = "syn"
 version = "0.15.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
 dependencies = [
- "proc-macro2",
+ "proc-macro2 0.4.30",
  "quote 0.6.13",
  "unicode-xid 0.1.0",
 ]
 
 [[package]]
-name = "thread_local"
-version = "1.0.1"
+name = "syn"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
+checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
 dependencies = [
- "lazy_static 1.4.0",
+ "proc-macro2 1.0.26",
+ "quote 1.0.9",
+ "unicode-xid 0.2.2",
 ]
 
 [[package]]
-name = "unicode-xid"
-version = "0.0.4"
+name = "thiserror"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
+checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
+dependencies = [
+ "proc-macro2 1.0.26",
+ "quote 1.0.9",
+ "syn 1.0.72",
+]
 
 [[package]]
 name = "unicode-xid"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
diff --git a/sw/host/rom_ext_image_tools/signer/Cargo.toml b/sw/host/rom_ext_image_tools/signer/Cargo.toml
index 9ed2af8..4aeeb3e 100644
--- a/sw/host/rom_ext_image_tools/signer/Cargo.toml
+++ b/sw/host/rom_ext_image_tools/signer/Cargo.toml
@@ -10,7 +10,6 @@
 
 [workspace]
 members = [
-    "config",
     "image"
 ]
 
@@ -25,8 +24,8 @@
 debug = true
 
 [dependencies]
-rom_ext_config = { path = "config" }
 rom_ext_image = { path = "image" }
+anyhow = "1.0.40"
 
 [dependencies.mundane]
 version = "0.4.4"
diff --git a/sw/host/rom_ext_image_tools/signer/config/Cargo.toml b/sw/host/rom_ext_image_tools/signer/config/Cargo.toml
deleted file mode 100644
index 7e45593..0000000
--- a/sw/host/rom_ext_image_tools/signer/config/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-[package]
-name = "rom_ext_config"
-version = "0.1.0"
-authors = ["lowRISC contributors"]
-edition = "2018"
-
-[dependencies]
-serde = "0.8"
-serde_derive = "0.8"
-serde-hjson = "0.8"
diff --git a/sw/host/rom_ext_image_tools/signer/config/src/lib.rs b/sw/host/rom_ext_image_tools/signer/config/src/lib.rs
deleted file mode 100644
index c062489..0000000
--- a/sw/host/rom_ext_image_tools/signer/config/src/lib.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-pub mod parser;
diff --git a/sw/host/rom_ext_image_tools/signer/config/src/parser.rs b/sw/host/rom_ext_image_tools/signer/config/src/parser.rs
deleted file mode 100644
index c2cab1f..0000000
--- a/sw/host/rom_ext_image_tools/signer/config/src/parser.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-
-use std::fs;
-use std::path::Path;
-use std::path::PathBuf;
-
-use serde_derive::Deserialize;
-use serde_hjson::Value;
-
-/// Configuration used for the manifest initialisation.
-#[derive(Deserialize, Debug)]
-pub struct ParsedConfig {
-    pub input_files: InputFiles,
-    pub peripheral_lockdown_info: PeripheralLockdownInfo,
-    pub manifest_identifier: String,
-    pub image_version: String,
-    pub extensions: [Extension; 4],
-}
-
-/// Input files that are required for signing.
-#[derive(Deserialize, Debug)]
-pub struct InputFiles {
-    pub image_path: PathBuf,
-    pub private_key_der_path: PathBuf,
-    pub usage_constraints_path: PathBuf,
-    pub system_state_value_path: PathBuf,
-}
-
-/// Peripheral Lockdown Information configuration data.
-///
-/// This data is used to produce 128-bit encoded manifest field.
-#[derive(Deserialize, Debug)]
-pub struct PeripheralLockdownInfo {
-    pub value: u32,
-}
-
-/// ROM_EXT.
-#[derive(Deserialize, Debug)]
-pub struct Extension {
-    pub offset: String,
-    pub checksum: String,
-}
-
-impl ParsedConfig {
-    pub fn new(config: &Path) -> Self {
-        // Read the entire configuration file.
-        let config_data = fs::read_to_string(config)
-            .expect("Failed to read the config file!");
-
-        let data: Value = serde_hjson::from_str(&config_data)
-            .expect("Failed to parse the hjson config file!");
-
-        let deserialized: ParsedConfig = serde_hjson::from_value(data)
-            .expect("Failed to deserialize hjson config data!");
-
-        deserialized
-    }
-}
diff --git a/sw/host/rom_ext_image_tools/signer/dev/config.hjson b/sw/host/rom_ext_image_tools/signer/dev/config.hjson
deleted file mode 100644
index 17d25f7..0000000
--- a/sw/host/rom_ext_image_tools/signer/dev/config.hjson
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright lowRISC contributors.
-// Licensed under the Apache License, Version 2.0, see LICENSE for details.
-// SPDX-License-Identifier: Apache-2.0
-{
-  input_files: {
-    image_path: "sw/host/rom_ext_image_tools/signer/dev/rom_ext_blank_image.bin",
-    private_key_der_path: "sw/host/rom_ext_image_tools/signer/dev/test_key_private.der",
-    usage_constraints_path: "sw/host/rom_ext_image_tools/signer/dev/usage_constraints.bin",
-    system_state_value_path: "sw/host/rom_ext_image_tools/signer/dev/system_state_value.bin",
-  },
-  // TBD
-  peripheral_lockdown_info: {
-    value: 0,
-  },
-  manifest_identifier: "43981",
-  image_version: "0xdeadbeef",
-  extensions: [
-    {
-      offset: "0xdeadbeef",
-      checksum: "0xdeadbeef",
-    },
-    {
-      offset: "0xdeadbeef",
-      checksum: "0xdeadbeef",
-    },
-    {
-      offset: "0xdeadbeef",
-      checksum: "0xdeadbeef",
-    },
-    {
-      offset: "0xdeadbeef",
-      checksum: "0xdeadbeef",
-    },
-  ],
-}
diff --git a/sw/host/rom_ext_image_tools/signer/dev/rom_ext_blank_image.bin b/sw/host/rom_ext_image_tools/signer/dev/rom_ext_blank_image.bin
deleted file mode 100644
index 06d7405..0000000
--- a/sw/host/rom_ext_image_tools/signer/dev/rom_ext_blank_image.bin
+++ /dev/null
Binary files differ
diff --git a/sw/host/rom_ext_image_tools/signer/dev/system_state_value.bin b/sw/host/rom_ext_image_tools/signer/dev/system_state_value.bin
deleted file mode 100644
index 9648fd7..0000000
--- a/sw/host/rom_ext_image_tools/signer/dev/system_state_value.bin
+++ /dev/null
@@ -1 +0,0 @@
-¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
\ No newline at end of file
diff --git a/sw/host/rom_ext_image_tools/signer/dev/test_key_private.der b/sw/host/rom_ext_image_tools/signer/dev/test_key_private.der
deleted file mode 100644
index 184f3bc..0000000
--- a/sw/host/rom_ext_image_tools/signer/dev/test_key_private.der
+++ /dev/null
Binary files differ
diff --git a/sw/host/rom_ext_image_tools/signer/dev/usage_constraints.bin b/sw/host/rom_ext_image_tools/signer/dev/usage_constraints.bin
deleted file mode 100644
index 9648fd7..0000000
--- a/sw/host/rom_ext_image_tools/signer/dev/usage_constraints.bin
+++ /dev/null
@@ -1 +0,0 @@
-¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
\ No newline at end of file
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 5810531..35cb230 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,4 @@
 edition = "2018"
 
 [dependencies]
-rom_ext_config = { path = "../config" }
\ No newline at end of file
+thiserror = "1.0.24"
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 ffec48c..2eac8ad 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,204 +6,67 @@
 #![deny(unused)]
 #![deny(unsafe_code)]
 
-use std;
-use std::ffi::OsString;
-use std::fs;
-use std::path::Path;
-use std::path::PathBuf;
-use std::time::SystemTime;
-use std::time::UNIX_EPOCH;
-
 use crate::manifest;
-use rom_ext_config::parser::ParsedConfig;
-use rom_ext_config::parser::PeripheralLockdownInfo;
+use thiserror::Error;
 
-/// Stripped binary image buffer.
-pub struct RawImage {
+#[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,
+    },
+}
+
+/// A thin wrapper around a Vec to help with setting ROM_EXT manifest fields.
+#[derive(Debug)]
+pub struct Image {
     data: Vec<u8>,
-    path: PathBuf,
 }
 
-/// Buffer manipulation API.
-impl RawImage {
-    /// Creates the new image buffer.
-    ///
-    /// The data is read from the requested raw binary file.
-    pub fn new(image_path: &Path) -> Self {
-        let data = fs::read(image_path).expect("Failed to read the image!");
-
-        RawImage {
-            data: data,
-            path: image_path.to_path_buf(),
-        }
-    }
-
-    /// Updates the fields from the configuration file.
-    ///
-    /// This function updates the image manifest data with values parsed from
-    /// the configuration file (known ahead of time). Some of the other fields
-    /// like signature key public exponent and modulus, are obtained at
-    /// run-time.
-    pub fn update_static_fields(&mut self, config: &ParsedConfig) {
-        // TODO: checks to make sure that the config values (strings) are not
-        //       bigger than the actual field size.
-
-        let mut update = |value, offset| {
-            let bytes = str_to_vec_u8(value);
-            self.update_field(&bytes, offset);
-        };
-
-        update(
-            &config.manifest_identifier,
-            manifest::ROM_EXT_MANIFEST_IDENTIFIER_OFFSET,
-        );
-        update(
-            &config.image_version,
-            manifest::ROM_EXT_IMAGE_VERSION_OFFSET,
-        );
-
-        let offsets = [
-            (
-                manifest::ROM_EXT_EXTENSION0_OFFSET_OFFSET,
-                manifest::ROM_EXT_EXTENSION0_CHECKSUM_OFFSET,
-            ),
-            (
-                manifest::ROM_EXT_EXTENSION1_OFFSET_OFFSET,
-                manifest::ROM_EXT_EXTENSION1_CHECKSUM_OFFSET,
-            ),
-            (
-                manifest::ROM_EXT_EXTENSION2_OFFSET_OFFSET,
-                manifest::ROM_EXT_EXTENSION2_CHECKSUM_OFFSET,
-            ),
-            (
-                manifest::ROM_EXT_EXTENSION3_OFFSET_OFFSET,
-                manifest::ROM_EXT_EXTENSION3_CHECKSUM_OFFSET,
-            ),
-        ];
-        for (i, offset) in offsets.iter().enumerate() {
-            update(&config.extensions[i].offset, offset.0);
-            update(&config.extensions[i].checksum, offset.1);
-        }
-
-        let usage_constraints_path = &config.input_files.usage_constraints_path;
-        self.update_usage_constraints_field(usage_constraints_path);
-
-        let lockdown_info = &config.peripheral_lockdown_info;
-        self.update_peripheral_lockdown_info_field(lockdown_info);
-    }
-
-    /// Updates ROM_EXT manifest timestamp field.
-    ///
-    /// The generated time stamp is u64. Normally time stamp is a signed
-    /// integer, however there is no risk of overflow into a "negative".
-    pub fn update_timestamp_field(&mut self) {
-        let duration = SystemTime::now()
-            .duration_since(UNIX_EPOCH)
-            .expect("Failed to obtain the current time!");
-
-        let bytes = duration.as_secs().to_le_bytes();
-        self.update_field(&bytes, manifest::ROM_EXT_IMAGE_TIMESTAMP_OFFSET);
-    }
-
-    /// Updates ROM_EXT manifest signature key public exponent field.
-    pub fn update_exponent_field(&mut self, exponent: &[u8]) {
-        self.update_field(
-            exponent,
-            manifest::ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT_OFFSET,
-        );
-    }
-
-    /// Updates ROM_EXT manifest signature key modulus field.
-    pub fn update_modulus_field(&mut self, modulus: &[u8]) {
-        self.update_field(
-            modulus,
-            manifest::ROM_EXT_SIGNATURE_KEY_MODULUS_OFFSET,
-        );
-    }
-
-    /// Updates ROM_EXT manifest signature field.
-    pub fn update_signature_field(&mut self, signature: &[u8]) {
-        self.update_field(signature, manifest::ROM_EXT_IMAGE_SIGNATURE_OFFSET);
-    }
-
-    /// Returns the portion of the image used for signing.
-    ///
-    /// Manifest identifier and the signature itself are not signed. The rest
-    /// of the image, including all the manifest fields that follow the
-    /// signature field.
-    pub fn data_to_sign(&self) -> &[u8] {
-        let offset = manifest::ROM_EXT_SIGNED_AREA_START_OFFSET as usize;
-        &self.data[offset..]
-    }
-
-    /// Writes the image buffer contents into a file.
-    ///
-    /// Places the new file alongside the original, with a "new_" prefix.
-    pub fn write_file(&self) {
-        let file_name =
-            self.path.file_name().expect("Failed to get file stem!");
-
-        let mut new_file_name = OsString::from("new_");
-        new_file_name.push(file_name);
-
-        let output_file = self.path.with_file_name(new_file_name);
-
-        fs::write(output_file, &self.data)
-            .expect("Failed to write the new binary file!");
-    }
-
-    /// Updates ROM_EXT manifest usage constraints field.
-    fn update_usage_constraints_field(&mut self, path: &Path) {
-        // Update fields from config.
-        let usage_constraints =
-            fs::read(path).expect("Failed to read usage constraints!");
-        self.update_field(
-            &usage_constraints,
-            manifest::ROM_EXT_USAGE_CONSTRAINTS_OFFSET,
-        );
-    }
-
-    /// Updates ROM_EXT manifest peripheral lockdown info field.
-    ///
-    /// The information is encoded into the 128-bit binary blob.
-    fn update_peripheral_lockdown_info_field(
+impl Image {
+    /// Sets the value of a ROM_EXT manifest field.
+    pub fn set_manifest_field<I>(
         &mut self,
-        _info: &PeripheralLockdownInfo,
-    ) {
-        // TODO: generate the peripheral_lockdown_blob from
-        //       PeripheralLockdownInfo, meanwhile use a hard-coded vector.
-
-        self.update_field(
-            &[0xA5; 16],
-            manifest::ROM_EXT_PERIPHERAL_LOCKDOWN_INFO_OFFSET,
-        );
+        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(())
+        }
     }
 
-    /// Updates a ROM_EXT manifest field.
-    ///
-    /// Generic function, is used by the specific field update counterparts.
-    fn update_field(&mut self, field_data: &[u8], field_offset: u32) {
-        let begin = field_offset as usize;
-        let end = begin + field_data.len();
-        self.data.splice(begin..end, field_data.iter().cloned());
+    /// Returns the signed bytes of the image.
+    pub fn signed_bytes(&self) -> &[u8] {
+        &self.data[manifest::ROM_EXT_SIGNED_AREA_START_OFFSET..]
     }
 }
 
-fn str_to_u64(val: &str) -> u64 {
-    match val.strip_prefix("0x") {
-        Some(s) => u64::from_str_radix(s, 16),
-        None => u64::from_str_radix(val, 10),
+impl AsRef<[u8]> for Image {
+    fn as_ref(&self) -> &[u8] {
+        &self.data
     }
-    .expect("Failed to parse string to u64!")
 }
 
-/// Converts hex/decimal uint string into a little endian byte vector.
-///
-/// Note: only understands values up to u64::MAX.
-fn str_to_vec_u8(s: &str) -> Vec<u8> {
-    match str_to_u64(s).to_le_bytes() {
-        [a, b, c, d, 0, 0, 0, 0] => vec![a, b, c, d],
-        bytes => bytes.to_vec(),
+impl From<Vec<u8>> for Image {
+    fn from(data: Vec<u8>) -> Self {
+        Image { data }
     }
 }
 
@@ -211,149 +74,45 @@
 mod tests {
     use super::*;
 
-    use std::collections::HashMap;
-    use std::env;
-
-    use rom_ext_config::parser::Extension;
-    use rom_ext_config::parser::InputFiles;
-
-    static DEV_RELATIVE_PATH: &str = "sw/host/rom_ext_image_tools/signer/dev";
+    #[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
+        );
+        Ok(())
+    }
 
     #[test]
-    fn test_update_static_fields() {
-        let source_root = env::var("MESON_SOURCE_ROOT")
-            .expect("Failed, ENV variable not set!");
+    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
+            })
+        );
+    }
 
-        let dev_path = Path::new(&source_root).join(DEV_RELATIVE_PATH);
-        let full_path = |path| dev_path.join(path);
-
-        let config = ParsedConfig {
-            input_files: InputFiles {
-                image_path: full_path("rom_ext_blank_image.bin"),
-                private_key_der_path: PathBuf::from(""),
-                usage_constraints_path: full_path("usage_constraints.bin"),
-                system_state_value_path: PathBuf::from(""),
-            },
-            peripheral_lockdown_info: PeripheralLockdownInfo { value: 0 },
-            manifest_identifier: String::from("0x11111111"),
-            image_version: String::from("0x22222222"),
-            extensions: [
-                Extension {
-                    offset: String::from("0x33333333"),
-                    checksum: String::from("0x44444444"),
-                },
-                Extension {
-                    offset: String::from("0x55555555"),
-                    checksum: String::from("0x55555555"),
-                },
-                Extension {
-                    offset: String::from("0x66666666"),
-                    checksum: String::from("0x66666666"),
-                },
-                Extension {
-                    offset: String::from("0x77777777"),
-                    checksum: String::from("0x77777777"),
-                },
-            ],
-        };
-
-        let mut test_items: HashMap<usize, Vec<u8>> = HashMap::new();
-
-        let mut add_test_item = |key, val: Vec<u8>, expected_size| {
-            assert_eq!(val.len(), expected_size as usize);
-            test_items.insert(key as usize, val);
-        };
-
-        add_test_item(
-            manifest::ROM_EXT_MANIFEST_IDENTIFIER_OFFSET,
-            str_to_vec_u8(&config.manifest_identifier),
-            manifest::ROM_EXT_MANIFEST_IDENTIFIER_SIZE_BYTES,
+    #[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
         );
-        add_test_item(
-            manifest::ROM_EXT_IMAGE_VERSION_OFFSET,
-            str_to_vec_u8(&config.image_version),
-            manifest::ROM_EXT_IMAGE_VERSION_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION0_OFFSET_OFFSET,
-            str_to_vec_u8(&config.extensions[0].offset),
-            manifest::ROM_EXT_EXTENSION0_OFFSET_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION1_OFFSET_OFFSET,
-            str_to_vec_u8(&config.extensions[1].offset),
-            manifest::ROM_EXT_EXTENSION1_OFFSET_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION2_OFFSET_OFFSET,
-            str_to_vec_u8(&config.extensions[2].offset),
-            manifest::ROM_EXT_EXTENSION2_OFFSET_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION3_OFFSET_OFFSET,
-            str_to_vec_u8(&config.extensions[3].offset),
-            manifest::ROM_EXT_EXTENSION3_OFFSET_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION0_CHECKSUM_OFFSET,
-            str_to_vec_u8(&config.extensions[0].checksum),
-            manifest::ROM_EXT_EXTENSION0_CHECKSUM_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION1_CHECKSUM_OFFSET,
-            str_to_vec_u8(&config.extensions[1].checksum),
-            manifest::ROM_EXT_EXTENSION1_CHECKSUM_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION2_CHECKSUM_OFFSET,
-            str_to_vec_u8(&config.extensions[2].checksum),
-            manifest::ROM_EXT_EXTENSION2_CHECKSUM_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_EXTENSION3_CHECKSUM_OFFSET,
-            str_to_vec_u8(&config.extensions[3].checksum),
-            manifest::ROM_EXT_EXTENSION3_CHECKSUM_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_USAGE_CONSTRAINTS_OFFSET,
-            fs::read(&config.input_files.usage_constraints_path)
-                .expect("Failed to read usage constraints!"),
-            manifest::ROM_EXT_USAGE_CONSTRAINTS_SIZE_BYTES,
-        );
-        add_test_item(
-            manifest::ROM_EXT_PERIPHERAL_LOCKDOWN_INFO_OFFSET,
-            [0xA5; 16].to_vec(),
-            manifest::ROM_EXT_PERIPHERAL_LOCKDOWN_INFO_SIZE_BYTES,
-        );
-
-        let mut image = RawImage::new(&config.input_files.image_path);
-
-        // Make sure that the image is blank to start with.
-        assert_eq!(image.data, vec![0; image.data.len()]);
-
-        image.update_static_fields(&config);
-
-        // Iterate through every single byte in the image, if it belongs to a
-        // field under test - compare with the respective `test_tuple` field,
-        // otherwise it should be zero.
-        let mut i = 0;
-        while i < image.data.len() {
-            // Check if the byte is part of the fields under test.
-            match test_items.get(&i) {
-                Some(x) => {
-                    // Check that field under test was successfully written
-                    // to the image.
-                    for byte in x {
-                        assert_eq!(*byte, image.data[i], "index = {}", i);
-                        i += 1;
-                    }
-                }
-                _ => {
-                    // Make sure that any other bytes are zero.
-                    assert_eq!(image.data[i], 0, "index = {}", i);
-                    i += 1;
-                }
-            }
-        }
+        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 5c6b473..2959964 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
@@ -10,164 +10,102 @@
 //     --output-dir=<destination dir>
 //     --output-files=rust
 
-/// Manifest field manifest_identifier offset from the base.
-pub const ROM_EXT_MANIFEST_IDENTIFIER_OFFSET: u32 = 0;
+pub struct ManifestField {
+    pub offset: usize,
+    pub size_bytes: usize,
+}
 
-/// Manifest field manifest_identifier size in bytes.
-pub const ROM_EXT_MANIFEST_IDENTIFIER_SIZE_BYTES: u32 = 4;
+pub const ROM_EXT_MANIFEST_IDENTIFIER: ManifestField = ManifestField {
+    offset: 0,
+    size_bytes: 4,
+};
 
-/// Manifest field manifest_identifier size in words.
-pub const ROM_EXT_MANIFEST_IDENTIFIER_SIZE_WORDS: u32 = 1;
+pub const ROM_EXT_IMAGE_SIGNATURE: ManifestField = ManifestField {
+    offset: 8,
+    size_bytes: 384,
+};
 
-/// Manifest field image_signature offset from the base.
-pub const ROM_EXT_IMAGE_SIGNATURE_OFFSET: u32 = 8;
+pub const ROM_EXT_IMAGE_LENGTH: ManifestField = ManifestField {
+    offset: 392,
+    size_bytes: 4,
+};
 
-/// Manifest field image_signature size in bytes.
-pub const ROM_EXT_IMAGE_SIGNATURE_SIZE_BYTES: u32 = 384;
+pub const ROM_EXT_IMAGE_VERSION: ManifestField = ManifestField {
+    offset: 396,
+    size_bytes: 4,
+};
 
-/// Manifest field image_signature size in words.
-pub const ROM_EXT_IMAGE_SIGNATURE_SIZE_WORDS: u32 = 96;
+pub const ROM_EXT_IMAGE_TIMESTAMP: ManifestField = ManifestField {
+    offset: 400,
+    size_bytes: 8,
+};
 
-/// Manifest field image_length offset from the base.
-pub const ROM_EXT_IMAGE_LENGTH_OFFSET: u32 = 392;
+pub const ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT: ManifestField =
+    ManifestField {
+        offset: 408,
+        size_bytes: 4,
+    };
 
-/// Manifest field image_length size in bytes.
-pub const ROM_EXT_IMAGE_LENGTH_SIZE_BYTES: u32 = 4;
+pub const ROM_EXT_USAGE_CONSTRAINTS: ManifestField = ManifestField {
+    offset: 416,
+    size_bytes: 32,
+};
 
-/// Manifest field image_length size in words.
-pub const ROM_EXT_IMAGE_LENGTH_SIZE_WORDS: u32 = 1;
+pub const ROM_EXT_PERIPHERAL_LOCKDOWN_INFO: ManifestField = ManifestField {
+    offset: 448,
+    size_bytes: 16,
+};
 
-/// Manifest field image_version offset from the base.
-pub const ROM_EXT_IMAGE_VERSION_OFFSET: u32 = 396;
+pub const ROM_EXT_SIGNATURE_KEY_MODULUS: ManifestField = ManifestField {
+    offset: 464,
+    size_bytes: 384,
+};
 
-/// Manifest field image_version size in bytes.
-pub const ROM_EXT_IMAGE_VERSION_SIZE_BYTES: u32 = 4;
+pub const ROM_EXT_EXTENSION0_OFFSET: ManifestField = ManifestField {
+    offset: 848,
+    size_bytes: 4,
+};
 
-/// Manifest field image_version size in words.
-pub const ROM_EXT_IMAGE_VERSION_SIZE_WORDS: u32 = 1;
+pub const ROM_EXT_EXTENSION0_CHECKSUM: ManifestField = ManifestField {
+    offset: 852,
+    size_bytes: 4,
+};
 
-/// Manifest field image_timestamp offset from the base.
-pub const ROM_EXT_IMAGE_TIMESTAMP_OFFSET: u32 = 400;
+pub const ROM_EXT_EXTENSION1_OFFSET: ManifestField = ManifestField {
+    offset: 856,
+    size_bytes: 4,
+};
 
-/// Manifest field image_timestamp size in bytes.
-pub const ROM_EXT_IMAGE_TIMESTAMP_SIZE_BYTES: u32 = 8;
+pub const ROM_EXT_EXTENSION1_CHECKSUM: ManifestField = ManifestField {
+    offset: 860,
+    size_bytes: 4,
+};
 
-/// Manifest field image_timestamp size in words.
-pub const ROM_EXT_IMAGE_TIMESTAMP_SIZE_WORDS: u32 = 2;
+pub const ROM_EXT_EXTENSION2_OFFSET: ManifestField = ManifestField {
+    offset: 864,
+    size_bytes: 4,
+};
 
-/// Manifest field signature_key_public_exponent offset from the base.
-pub const ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT_OFFSET: u32 = 408;
+pub const ROM_EXT_EXTENSION2_CHECKSUM: ManifestField = ManifestField {
+    offset: 868,
+    size_bytes: 4,
+};
 
-/// Manifest field signature_key_public_exponent size in bytes.
-pub const ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT_SIZE_BYTES: u32 = 4;
+pub const ROM_EXT_EXTENSION3_OFFSET: ManifestField = ManifestField {
+    offset: 872,
+    size_bytes: 4,
+};
 
-/// Manifest field signature_key_public_exponent size in words.
-pub const ROM_EXT_SIGNATURE_KEY_PUBLIC_EXPONENT_SIZE_WORDS: u32 = 1;
-
-/// Manifest field usage_constraints offset from the base.
-pub const ROM_EXT_USAGE_CONSTRAINTS_OFFSET: u32 = 416;
-
-/// Manifest field usage_constraints size in bytes.
-pub const ROM_EXT_USAGE_CONSTRAINTS_SIZE_BYTES: u32 = 32;
-
-/// Manifest field usage_constraints size in words.
-pub const ROM_EXT_USAGE_CONSTRAINTS_SIZE_WORDS: u32 = 8;
-
-/// Manifest field peripheral_lockdown_info offset from the base.
-pub const ROM_EXT_PERIPHERAL_LOCKDOWN_INFO_OFFSET: u32 = 448;
-
-/// Manifest field peripheral_lockdown_info size in bytes.
-pub const ROM_EXT_PERIPHERAL_LOCKDOWN_INFO_SIZE_BYTES: u32 = 16;
-
-/// Manifest field peripheral_lockdown_info size in words.
-pub const ROM_EXT_PERIPHERAL_LOCKDOWN_INFO_SIZE_WORDS: u32 = 4;
-
-/// Manifest field signature_key_modulus offset from the base.
-pub const ROM_EXT_SIGNATURE_KEY_MODULUS_OFFSET: u32 = 464;
-
-/// Manifest field signature_key_modulus size in bytes.
-pub const ROM_EXT_SIGNATURE_KEY_MODULUS_SIZE_BYTES: u32 = 384;
-
-/// Manifest field signature_key_modulus size in words.
-pub const ROM_EXT_SIGNATURE_KEY_MODULUS_SIZE_WORDS: u32 = 96;
-
-/// Manifest field extension0_offset offset from the base.
-pub const ROM_EXT_EXTENSION0_OFFSET_OFFSET: u32 = 848;
-
-/// Manifest field extension0_offset size in bytes.
-pub const ROM_EXT_EXTENSION0_OFFSET_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension0_offset size in words.
-pub const ROM_EXT_EXTENSION0_OFFSET_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension0_checksum offset from the base.
-pub const ROM_EXT_EXTENSION0_CHECKSUM_OFFSET: u32 = 852;
-
-/// Manifest field extension0_checksum size in bytes.
-pub const ROM_EXT_EXTENSION0_CHECKSUM_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension0_checksum size in words.
-pub const ROM_EXT_EXTENSION0_CHECKSUM_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension1_offset offset from the base.
-pub const ROM_EXT_EXTENSION1_OFFSET_OFFSET: u32 = 856;
-
-/// Manifest field extension1_offset size in bytes.
-pub const ROM_EXT_EXTENSION1_OFFSET_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension1_offset size in words.
-pub const ROM_EXT_EXTENSION1_OFFSET_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension1_checksum offset from the base.
-pub const ROM_EXT_EXTENSION1_CHECKSUM_OFFSET: u32 = 860;
-
-/// Manifest field extension1_checksum size in bytes.
-pub const ROM_EXT_EXTENSION1_CHECKSUM_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension1_checksum size in words.
-pub const ROM_EXT_EXTENSION1_CHECKSUM_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension2_offset offset from the base.
-pub const ROM_EXT_EXTENSION2_OFFSET_OFFSET: u32 = 864;
-
-/// Manifest field extension2_offset size in bytes.
-pub const ROM_EXT_EXTENSION2_OFFSET_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension2_offset size in words.
-pub const ROM_EXT_EXTENSION2_OFFSET_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension2_checksum offset from the base.
-pub const ROM_EXT_EXTENSION2_CHECKSUM_OFFSET: u32 = 868;
-
-/// Manifest field extension2_checksum size in bytes.
-pub const ROM_EXT_EXTENSION2_CHECKSUM_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension2_checksum size in words.
-pub const ROM_EXT_EXTENSION2_CHECKSUM_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension3_offset offset from the base.
-pub const ROM_EXT_EXTENSION3_OFFSET_OFFSET: u32 = 872;
-
-/// Manifest field extension3_offset size in bytes.
-pub const ROM_EXT_EXTENSION3_OFFSET_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension3_offset size in words.
-pub const ROM_EXT_EXTENSION3_OFFSET_SIZE_WORDS: u32 = 1;
-
-/// Manifest field extension3_checksum offset from the base.
-pub const ROM_EXT_EXTENSION3_CHECKSUM_OFFSET: u32 = 876;
-
-/// Manifest field extension3_checksum size in bytes.
-pub const ROM_EXT_EXTENSION3_CHECKSUM_SIZE_BYTES: u32 = 4;
-
-/// Manifest field extension3_checksum size in words.
-pub const ROM_EXT_EXTENSION3_CHECKSUM_SIZE_WORDS: u32 = 1;
+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: u32 = 392;
+pub const ROM_EXT_SIGNED_AREA_START_OFFSET: usize = 392;
 
 /// Manifest offset interrupt_vector from the base.
-pub const ROM_EXT_INTERRUPT_VECTOR_OFFSET: u32 = 1024;
+pub const ROM_EXT_INTERRUPT_VECTOR_OFFSET: usize = 1024;
 
 /// Manifest offset entry_point from the base.
-pub const ROM_EXT_ENTRY_POINT_OFFSET: u32 = 1152;
+pub const ROM_EXT_ENTRY_POINT_OFFSET: usize = 1152;
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 c760d89..023aa37 100644
--- a/sw/host/rom_ext_image_tools/signer/src/main.rs
+++ b/sw/host/rom_ext_image_tools/signer/src/main.rs
@@ -9,91 +9,186 @@
 use std::env;
 use std::fs;
 use std::path::Path;
+use std::path::PathBuf;
 
-use rom_ext_config::parser::ParsedConfig;
-use rom_ext_image::image::RawImage;
+use rom_ext_image::image::Image;
+use rom_ext_image::manifest;
 
 use mundane::hash::Sha256;
 use mundane::public::rsa::RsaPkcs1v15;
-use mundane::public::rsa::RsaPrivKey;
-use mundane::public::rsa::RsaSignature;
 use mundane::public::rsa::B3072;
 use mundane::public::DerPrivateKey;
 use mundane::public::Signature;
 
-fn main() {
-    let arg: String = env::args().nth(1).expect("Config path is missing");
+use anyhow::bail;
+use anyhow::ensure;
+use anyhow::Context;
+use anyhow::Result;
 
-    let config_path = Path::new(&arg);
+// Type aliases for convenience.
+type ImageSignature =
+    mundane::public::rsa::RsaSignature<B3072, RsaPkcs1v15, Sha256>;
+type PrivateKey = mundane::public::rsa::RsaPrivKey<B3072>;
 
-    // Parse the config.
-    let config = ParsedConfig::new(&config_path);
-
-    // Read raw binary.
-    let mut image = RawImage::new(&config.input_files.image_path);
-
-    // Get the private key used for signature generation. It is also used to
-    // extract key public exponent and modulus.
-    let private_key_der = fs::read(&config.input_files.private_key_der_path)
-        .expect("Failed to read the image!");
-
-    // Update "signed" manifest fields.
-    image.update_static_fields(&config);
-
-    // Convert ASN.1 DER private key into Mundane RsaPrivKey.
-    let private_key = RsaPrivKey::parse_from_der(&private_key_der)
-        .expect("Failed to parse private key!");
-
-    let mut exponent = private_key.public_exponent_be();
-    // Encode public key components in little-endian byte order.
-    exponent.reverse();
-    image.update_exponent_field(&exponent);
-
-    let mut modulus = private_key.public_modulus_be();
-    // Encode public key components in little-endian byte order.
-    modulus.reverse();
-    image.update_modulus_field(&modulus);
-
-    // Produce the signature from concatenated system_state_value,
-    // device_usage_value and the portion of the "signed" portion of the image.
-    image.update_timestamp_field();
-    let image_sign_data = image.data_to_sign();
-    let device_usage_value =
-        &device_usage_value(&config.input_files.usage_constraints_path);
-    let system_state_value =
-        &system_state_value(&config.input_files.system_state_value_path);
-
-    let mut message_to_sign = Vec::<u8>::new();
-    message_to_sign.extend_from_slice(system_state_value);
-    message_to_sign.extend_from_slice(device_usage_value);
-    message_to_sign.extend_from_slice(image_sign_data);
-
-    let signature = RsaSignature::<B3072, RsaPkcs1v15, Sha256>::sign(
-        &private_key,
-        &message_to_sign,
-    )
-    .expect("Failed to sign!");
-
-    image.update_signature_field(&signature.bytes());
-
-    // The whole image has been updated and signed, write the result to disk.
-    image.write_file();
+// TODO: Remove this struct when this functionality is integrated into `opentitantool`.
+struct Args {
+    input: PathBuf,
+    priv_key: PathBuf,
+    output: PathBuf,
 }
 
-/// Generates the device usage value.
-///
-/// This value is extrapolated from the ROM_EXT manifest usage_constraints
-/// field, and does not reside in the ROM_EXT manifest directly.
-pub fn device_usage_value(path: &Path) -> Vec<u8> {
-    let _usage_constraints =
-        fs::read(path).expect("Failed to read usage constraints!");
-
-    // TODO: generate the device_usage_value from usage_constraints.
-    //       meanwhile use a "dummy" hard-coded vector.
-    vec![0xA5; 1024]
+impl Args {
+    pub fn new(args: env::ArgsOs) -> Result<Args> {
+        let args = args.skip(1).collect::<Vec<_>>();
+        match args.as_slice() {
+            [input, priv_key, output] => Ok(Args {
+                    input: PathBuf::from(input),
+                    priv_key: PathBuf::from(priv_key),
+                    output: PathBuf::from(output),
+                }),
+            args => bail!("Expected exactly 3 positional arguments: input, private key, and output, got: {}.", args.len()),
+        }
+    }
 }
 
-/// Obtains the system state value from the binary on disk.
-pub fn system_state_value(path: &Path) -> Vec<u8> {
-    fs::read(path).expect("Failed to read system state value!")
+/// Parses an unsigned big-endian hex string into a little-endian byte vector.
+fn parse_hex_str(hex_str: &str) -> Result<Vec<u8>> {
+    ensure!(
+        hex_str.starts_with("0x")
+            && hex_str.len() > 2
+            && hex_str.len() % 2 == 0,
+        "Invalid hex string: {}",
+        hex_str
+    );
+    let bytes = (2..hex_str.len())
+        .step_by(2)
+        .map(|i| u8::from_str_radix(&hex_str[i..i + 2], 16))
+        .rev()
+        .collect::<Result<_, _>>()?;
+    Ok(bytes)
+}
+
+/// 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.")?;
+
+    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()),
+        )?;
+    }
+    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()),
+    )?;
+
+    Ok(())
+}
+
+/// Calculates the signature for the signed portion of an image.
+fn calculate_image_signature(
+    image: &Image,
+    key: impl AsRef<Path>,
+) -> 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)
+}
+
+/// Updates the signature of an image.
+fn update_image_signature(
+    image: &mut Image,
+    signature: ImageSignature,
+) -> Result<()> {
+    image.set_manifest_field(
+        &manifest::ROM_EXT_IMAGE_SIGNATURE,
+        signature.bytes().iter().rev().cloned(),
+    )?;
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    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)?;
+    update_image_signature(&mut image, sig)?;
+    fs::write(&args.output, image).with_context(|| {
+        format!("Failed to write the image to {}", args.output.display())
+    })?;
+    Ok(())
 }