[sw, rom_ext_signer] Add signing and remaining image update API

- Retrieving exponent is work-in-progress, and at the moment
  dummy hardcoded vector of `0xA5` is used.
- Retrieving modulus is work-in-progress, and at the moment
  dummy hardcoded vector of `0xA5` is used.
- Peripheral Lockdown Info encodingis work in progress, and at
  the moment dummy hardcoded vector of `0xA5` is used.
- It is not clear yet how the device_usage_value is encoded in
  the 256-bit usage_constraints blob, and at the moment dummy
  hardcoded vector of `0xA5` is used.

This change adds image signing functionality through Mundane.

* There was some refactoring done, missing fields
  `extension{0,1,2,3}_offset` have been added.

* `image_length` is no longer passed in the config, as it is
  set automatically in the manifest assembly file.

* `image_timestamp` is no longer passed in the config, and is
  calculated at runtime.

Signed-off-by: Silvestrs Timofejevs <silvestrst@lowrisc.org>
diff --git a/sw/host/rom_ext_image_tools/signer/Cargo.toml b/sw/host/rom_ext_image_tools/signer/Cargo.toml
index e4fcfc0..f21464a 100644
--- a/sw/host/rom_ext_image_tools/signer/Cargo.toml
+++ b/sw/host/rom_ext_image_tools/signer/Cargo.toml
@@ -25,6 +25,9 @@
 debug = true
 
 [dependencies]
-mundane = "0.4.3"
 rom_ext_config = { path = "config" }
 rom_ext_image = { path = "image" }
+
+[dependencies.mundane]
+version = "0.4.3"
+features = ["rsa-pkcs1v15"]
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
index 634db71..af22d67 100644
--- a/sw/host/rom_ext_image_tools/signer/config/src/parser.rs
+++ b/sw/host/rom_ext_image_tools/signer/config/src/parser.rs
@@ -4,6 +4,7 @@
 
 use std::fs;
 use std::path::Path;
+use std::path::PathBuf;
 
 use serde_derive::Deserialize;
 use serde_hjson::Value;
@@ -12,37 +13,36 @@
 #[derive(Deserialize, Debug)]
 pub struct ParsedConfig {
     pub input_files: InputFiles,
-    pub usage_constraints: UsageConstraints,
     pub peripheral_lockdown_info: PeripheralLockdownInfo,
     pub manifest_identifier: String,
-    pub image_length: String,
     pub image_version: String,
-    pub image_timestamp: String,
-    pub extension0_checksum: String,
-    pub extension1_checksum: String,
-    pub extension2_checksum: String,
-    pub extension3_checksum: String,
+    pub extensions: [Extension; 4],
 }
 
 /// Input files that are required for signing.
 #[derive(Deserialize, Debug)]
 pub struct InputFiles {
-    pub image_path: String,
-    pub private_key_der_path: String,
+    pub image_path: PathBuf,
+    pub private_key_der_path: PathBuf,
+    pub usage_constraints_path: PathBuf,
+    pub system_state_value_path: PathBuf,
 }
 
-/// TODO - possibly should be a binary file.
-#[derive(Deserialize, Debug)]
-pub struct UsageConstraints {
-    pub value: u32,
-}
-
-/// TODO
+/// 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.
diff --git a/sw/host/rom_ext_image_tools/signer/dev/config.hjson b/sw/host/rom_ext_image_tools/signer/dev/config.hjson
index 2e2c98e..17d25f7 100644
--- a/sw/host/rom_ext_image_tools/signer/dev/config.hjson
+++ b/sw/host/rom_ext_image_tools/signer/dev/config.hjson
@@ -5,21 +5,31 @@
   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",
-  },
-  // TBD
-  usage_constraints: {
-    value: 0,
+    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_length: "0xdeadbeef",
   image_version: "0xdeadbeef",
-  image_timestamp: "0xaabbccddeeff1122",
-  extension0_checksum: "0xdeadbeef",
-  extension1_checksum: "0xdeadbeef",
-  extension2_checksum: "0xdeadbeef",
-  extension3_checksum: "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/image/src/image.rs b/sw/host/rom_ext_image_tools/signer/image/src/image.rs
index ef261c4..cb43aa5 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
@@ -2,14 +2,21 @@
 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
 // SPDX-License-Identifier: Apache-2.0
 
+#![deny(warnings)]
+#![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;
 
 /// Stripped binary image buffer.
 pub struct RawImage {
@@ -31,51 +38,90 @@
         }
     }
 
-    /// Updates the manifest portion of the image buffer.
+    /// Updates the fields from the configuration file.
     ///
-    /// This function updates the image manifest data with values parsed
-    /// from configuration file.
-    pub fn update_generic_fields(&mut self, config: &ParsedConfig) {
-        // TODO checks to make sure that the config values (strings) are not
-        // bigger than the actual field size.
+    /// 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 myclosure = |value, offset| {
+        let mut update = |value, offset| {
             let bytes = str_to_vec_u8(value);
-            let data = &mut self.data;
-            let begin = offset as usize;
-            let end = begin + bytes.len();
-            data.splice(begin..end, bytes.iter().cloned());
+            self.update_field(&bytes, offset);
         };
 
-        myclosure(
+        update(
             &config.manifest_identifier,
             manifest::ROM_EXT_MANIFEST_IDENTIFIER_OFFSET,
         );
-        myclosure(&config.image_length, manifest::ROM_EXT_IMAGE_LENGTH_OFFSET);
-        myclosure(
+        update(
             &config.image_version,
             manifest::ROM_EXT_IMAGE_VERSION_OFFSET,
         );
-        myclosure(
-            &config.image_timestamp,
-            manifest::ROM_EXT_IMAGE_TIMESTAMP_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);
+
+        // TODO: calculated at runtime, so we should probably rename this
+        //       function.
+        self.update_timestamp_field();
+    }
+
+    /// 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,
         );
-        myclosure(
-            &config.extension0_checksum,
-            manifest::ROM_EXT_EXTENSION0_CHECKSUM_OFFSET,
-        );
-        myclosure(
-            &config.extension1_checksum,
-            manifest::ROM_EXT_EXTENSION1_CHECKSUM_OFFSET,
-        );
-        myclosure(
-            &config.extension2_checksum,
-            manifest::ROM_EXT_EXTENSION2_CHECKSUM_OFFSET,
-        );
-        myclosure(
-            &config.extension3_checksum,
-            manifest::ROM_EXT_EXTENSION3_CHECKSUM_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.
@@ -91,6 +137,51 @@
 
         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(&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,
+        );
+    }
+
+    /// 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".
+    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 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());
+    }
 }
 
 /// Converts hex/decimal uint string into a little endian byte vector.
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 6c8de02..32a83bf 100644
--- a/sw/host/rom_ext_image_tools/signer/src/main.rs
+++ b/sw/host/rom_ext_image_tools/signer/src/main.rs
@@ -7,11 +7,20 @@
 #![deny(unsafe_code)]
 
 use std::env;
+use std::fs;
 use std::path::Path;
 
 use rom_ext_config::parser::ParsedConfig;
 use rom_ext_image::image::RawImage;
 
+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");
 
@@ -21,10 +30,74 @@
     let config = ParsedConfig::new(&config_path);
 
     // Read raw binary.
-    let image_path = Path::new(&config.input_files.image_path);
-    let mut raw_image = RawImage::new(&image_path);
+    let mut image = RawImage::new(&config.input_files.image_path);
 
-    // Modify raw binary.
-    raw_image.update_generic_fields(&config);
-    raw_image.write_file();
+    // 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);
+
+    let exponent = &signature_key_public_exponent_le();
+    image.update_exponent_field(exponent);
+
+    let modulus = &signature_key_modulus_le();
+    image.update_modulus_field(modulus);
+
+    // 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!");
+
+    // Produce the signature from concatenated system_state_value,
+    // device_usage_value and the portion of the "signed" portion of the image.
+    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();
+}
+
+/// Generate a dummy signature key public exponent.
+///
+/// Eventually this value will be obtained from the private key.
+fn signature_key_public_exponent_le() -> Vec<u8> {
+    vec![0xA5; 1]
+}
+
+/// Generate a dummy signature key modulus.
+///
+/// Eventually this value will be obtained from the private key.
+fn signature_key_modulus_le() -> Vec<u8> {
+    vec![0xA5; 384]
+}
+
+/// 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]
+}
+
+/// 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!")
 }