Repository: chipsalliance/caliptra-sw
Pull request #397 (2023-06-19 10:46:45)

Add owner LMS key sign and verify

ericeilertson: I think we could merge this in and create new work items and PRs to handle a few new things that need to be added.

Integration Tests:
- LMS Revocation Tests
- Tests to optionally require LMS

Verifier changes: 
-Changes to optionally require LMS.
-Need to increase the bundle of LMS vendor keys from 4 to 32 to match what was agreed to.


Patch:
diff --git a/error/src/lib.rs b/error/src/lib.rs
index 15c0f195..f2bf688e 100644
--- a/error/src/lib.rs
+++ b/error/src/lib.rs
@@ -219,11 +219,17 @@ impl CaliptraError {
         CaliptraError::new_const(0x000b0032);
     pub const IMAGE_VERIFIER_ERR_VENDOR_LMS_SIGNATURE_INVALID: CaliptraError =
         CaliptraError::new_const(0x000b0033);
-    pub const IMAGE_VERIFIER_ERR_VEN_LMS_PUB_KEY_INDEX_OUT_OF_BOUNDS: CaliptraError =
-        CaliptraError::new_const(0x000b0034);
     pub const IMAGE_VERIFIER_ERR_FMC_RUNTIME_LOAD_ADDR_OVERLAP: CaliptraError =
+        CaliptraError::new_const(0x000b0034);
+    pub const IMAGE_VERIFIER_ERR_OWNER_LMS_PUB_KEY_INDEX_MISMATCH: CaliptraError =
         CaliptraError::new_const(0x000b0035);
-    pub const RUNTIME_HANDOFF_FHT_NOT_LOADED: CaliptraError = CaliptraError::new_const(0x000b0036);
+    pub const IMAGE_VERIFIER_ERR_OWNER_LMS_VERIFY_FAILURE: CaliptraError =
+        CaliptraError::new_const(0x000b0036);
+    pub const IMAGE_VERIFIER_ERR_OWNER_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS: CaliptraError =
+        CaliptraError::new_const(0x000b0037);
+    pub const IMAGE_VERIFIER_ERR_OWNER_LMS_SIGNATURE_INVALID: CaliptraError =
+        CaliptraError::new_const(0x000b0038);
+    pub const RUNTIME_HANDOFF_FHT_NOT_LOADED: CaliptraError = CaliptraError::new_const(0x000b0039);
 
     /// Driver Error: LMS
     pub const DRIVER_LMS_INVALID_LMS_ALGO_TYPE: CaliptraError =
diff --git a/fmc/Makefile b/fmc/Makefile
index f3315154..b1bdff30 100644
--- a/fmc/Makefile
+++ b/fmc/Makefile
@@ -71,6 +71,7 @@ build-fw-image: gen-certs build-emu build-test-rt
 		--key-config $(TARGET_DIR)/keys.toml \
 		--ecc-pk-idx 3 \
 		--lms-pk-idx 3 \
+		--owner-lms-pk-idx 0 \
 		--fmc $(TARGET_DIR)/caliptra-fmc \
 		--fmc-svn 0 \
 		--fmc-min-svn 0 \
diff --git a/image/app/src/create/config.rs b/image/app/src/create/config.rs
index dcb7d79b..6ac7e8a7 100644
--- a/image/app/src/create/config.rs
+++ b/image/app/src/create/config.rs
@@ -13,7 +13,7 @@ Abstract:
 --*/
 
 use anyhow::Context;
-use caliptra_image_types::{VENDOR_ECC_KEY_COUNT, VENDOR_LMS_KEY_COUNT};
+use caliptra_image_types::{OWNER_LMS_KEY_COUNT, VENDOR_ECC_KEY_COUNT, VENDOR_LMS_KEY_COUNT};
 use serde_derive::{Deserialize, Serialize};
 use std::path::PathBuf;
 
@@ -35,6 +35,10 @@ pub(crate) struct OwnerKeyConfig {
     pub ecc_pub_key: String,
 
     pub ecc_priv_key: Option<String>,
+
+    pub lms_pub_keys: [String; OWNER_LMS_KEY_COUNT as usize],
+
+    pub lms_priv_keys: Option<[String; OWNER_LMS_KEY_COUNT as usize]>,
 }
 
 //Key Configuration
diff --git a/image/app/src/create/mod.rs b/image/app/src/create/mod.rs
index 9b6ddb4a..c4769116 100644
--- a/image/app/src/create/mod.rs
+++ b/image/app/src/create/mod.rs
@@ -116,6 +116,10 @@ pub(crate) fn run_cmd(args: &ArgMatches) -> anyhow::Result<()> {
         .get_one::<u32>("lms-pk-idx")
         .with_context(|| "lms-pk-idx arg not specified")?;
 
+    let owner_lms_key_idx: &u32 = args
+        .get_one::<u32>("owner-lms-pk-idx")
+        .with_context(|| "owner-lms-pk-idx arg not specified")?;
+
     let out_path: &PathBuf = args
         .get_one::<PathBuf>("out")
         .with_context(|| "out arg not specified")?;
@@ -177,7 +181,13 @@ pub(crate) fn run_cmd(args: &ArgMatches) -> anyhow::Result<()> {
             mfg_from_date,
             mfg_to_date,
         )?,
-        owner_config: owner_config(config_dir, &config.owner, own_from_date, own_to_date)?,
+        owner_config: owner_config(
+            config_dir,
+            &config.owner,
+            *owner_lms_key_idx,
+            own_from_date,
+            own_to_date,
+        )?,
         fmc,
         runtime,
     };
@@ -267,6 +277,7 @@ fn vendor_config(
 fn owner_config(
     path: &Path,
     config: &Option<OwnerKeyConfig>,
+    lms_key_idx: u32,
     from_date: [u8; 15],
     to_date: [u8; 15],
 ) -> anyhow::Result<Option<ImageGeneratorOwnerConfig>> {
@@ -277,12 +288,36 @@ fn owner_config(
         let pub_key_path = path.join(pem_file);
         gen_config.pub_keys.ecc_pub_key = ecc_pub_key_from_pem(&pub_key_path)?;
 
+        let lms_pub_keys = &config.lms_pub_keys;
+
+        for (i, pem_file) in lms_pub_keys
+            .iter()
+            .enumerate()
+            .take(OWNER_LMS_KEY_COUNT as usize)
+        {
+            let pub_key_path = path.join(pem_file);
+            gen_config.pub_keys.lms_pub_keys[i] = lms_pub_key_from_pem(&pub_key_path)?;
+        }
+
+        let mut priv_keys = ImageOwnerPrivKeys::default();
         if let Some(pem_file) = &config.ecc_priv_key {
-            let mut priv_keys = ImageOwnerPrivKeys::default();
             let pub_key_path = path.join(pem_file);
             priv_keys.ecc_priv_key = ecc_priv_key_from_pem(&pub_key_path)?;
             gen_config.priv_keys = Some(priv_keys);
         }
+
+        if let Some(lms_priv_keys) = &config.lms_priv_keys {
+            for (i, pem_file) in lms_priv_keys
+                .iter()
+                .enumerate()
+                .take(OWNER_LMS_KEY_COUNT as usize)
+            {
+                let priv_key_path = path.join(pem_file);
+                priv_keys.lms_priv_keys[i] = lms_priv_key_from_pem(&priv_key_path)?;
+            }
+            gen_config.priv_keys = Some(priv_keys);
+        }
+        gen_config.lms_key_idx = lms_key_idx;
         gen_config.not_before = from_date;
         gen_config.not_after = to_date;
 
diff --git a/image/app/src/main.rs b/image/app/src/main.rs
index 63818e0a..83c721b3 100644
--- a/image/app/src/main.rs
+++ b/image/app/src/main.rs
@@ -36,6 +36,11 @@ fn main() {
                 .required(false)
                 .value_parser(value_parser!(u32)),
         )
+        .arg(
+            arg!(--"owner-lms-pk-idx" <U32> "Owner LMS Public Key Index")
+                .required(false)
+                .value_parser(value_parser!(u32)),
+        )
         .arg(
             arg!(--"fmc" <FILE> "FMC ELF binary")
                 .required(true)
diff --git a/image/fake-keys/src/lib.rs b/image/fake-keys/src/lib.rs
index bebcb697..2fa2d4c0 100644
--- a/image/fake-keys/src/lib.rs
+++ b/image/fake-keys/src/lib.rs
@@ -251,6 +251,10 @@ pub const VENDOR_PUBLIC_KEYS: ImageVendorPubKeys = ImageVendorPubKeys {
     ],
 };
 
+pub const OWNER_PUBLIC_KEYS: ImageOwnerPubKeys = ImageOwnerPubKeys {
+    ecc_pub_key: OWNER_KEY_PUBLIC,
+    lms_pub_keys: [OWNER_LMS_KEY0_PUBLIC],
+};
 pub const VENDOR_PRIVATE_KEYS: ImageVendorPrivKeys = ImageVendorPrivKeys {
     ecc_priv_keys: [
         VENDOR_KEY_0_PRIVATE,
@@ -266,6 +270,11 @@ pub const VENDOR_PRIVATE_KEYS: ImageVendorPrivKeys = ImageVendorPrivKeys {
     ],
 };
 
+pub const OWNER_PRIVATE_KEYS: ImageOwnerPrivKeys = ImageOwnerPrivKeys {
+    ecc_priv_key: OWNER_KEY_PRIVATE,
+    lms_priv_keys: [OWNER_LMS_KEY0_PRIVATE],
+};
+
 pub const VENDOR_CONFIG_KEY_0: ImageGeneratorVendorConfig = ImageGeneratorVendorConfig {
     pub_keys: VENDOR_PUBLIC_KEYS,
     ecc_key_idx: 0,
@@ -296,10 +305,13 @@ pub const VENDOR_CONFIG_KEY_3: ImageGeneratorVendorConfig = ImageGeneratorVendor
 pub const OWNER_CONFIG: ImageGeneratorOwnerConfig = ImageGeneratorOwnerConfig {
     pub_keys: ImageOwnerPubKeys {
         ecc_pub_key: OWNER_KEY_PUBLIC,
+        lms_pub_keys: [OWNER_LMS_KEY0_PUBLIC],
     },
     priv_keys: Some(ImageOwnerPrivKeys {
         ecc_priv_key: OWNER_KEY_PRIVATE,
+        lms_priv_keys: [OWNER_LMS_KEY0_PRIVATE],
     }),
+    lms_key_idx: 0,
     not_before: [0u8; 15],
     not_after: [0u8; 15],
 };
@@ -333,4 +345,30 @@ fn test_write_lms_keys() {
         file.write_all(VENDOR_PUBLIC_KEYS.lms_pub_keys[i].as_bytes())
             .unwrap();
     }
+    for i in 0..OWNER_PRIVATE_KEYS.lms_priv_keys.len() {
+        let mut file = fs::OpenOptions::new()
+            .create(true)
+            .write(true)
+            .truncate(true)
+            .open(format!(
+                "../../target/riscv32imc-unknown-none-elf/firmware/own-lms-priv-key-{}.pem",
+                i
+            ))
+            .unwrap();
+        file.write_all(OWNER_PRIVATE_KEYS.lms_priv_keys[i].as_bytes())
+            .unwrap();
+    }
+    for i in 0..OWNER_PUBLIC_KEYS.lms_pub_keys.len() {
+        let mut file = fs::OpenOptions::new()
+            .create(true)
+            .write(true)
+            .truncate(true)
+            .open(format!(
+                "../../target/riscv32imc-unknown-none-elf/firmware/own-lms-pub-key-{}.pem",
+                i
+            ))
+            .unwrap();
+        file.write_all(OWNER_PUBLIC_KEYS.lms_pub_keys[i].as_bytes())
+            .unwrap();
+    }
 }
diff --git a/image/gen/src/generator.rs b/image/gen/src/generator.rs
index d7c7fad9..dcd8919c 100644
--- a/image/gen/src/generator.rs
+++ b/image/gen/src/generator.rs
@@ -73,6 +73,11 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
 
         let ecc_key_idx = config.vendor_config.ecc_key_idx;
         let lms_key_idx = config.vendor_config.lms_key_idx;
+        let owner_lms_key_idx = if let Some(owner_config) = &config.owner_config {
+            owner_config.lms_key_idx
+        } else {
+            OWNER_LMS_KEY_COUNT
+        };
 
         // Create Header
         let toc_digest = self.toc_digest(&fmc_toc, &runtime_toc)?;
@@ -80,6 +85,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
             config,
             ecc_key_idx,
             lms_key_idx,
+            owner_lms_key_idx,
             Self::DEFAULT_FLAGS,
             toc_digest,
         )?;
@@ -91,6 +97,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
             config,
             ecc_key_idx,
             lms_key_idx,
+            owner_lms_key_idx,
             &header_digest_vendor,
             &header_digest_owner,
         )?;
@@ -121,6 +128,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
         config: &ImageGeneratorConfig<E>,
         ecc_key_idx: u32,
         lms_key_idx: u32,
+        owner_lms_key_idx: u32,
         digest_vendor: &ImageDigest,
         digest_owner: &ImageDigest,
     ) -> anyhow::Result<ImagePreamble>
@@ -152,6 +160,11 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
                     &owner_config.pub_keys.ecc_pub_key,
                 )?;
                 owner_sigs.ecc_sig = sig;
+                let lms_sig = self.crypto.lms_sign(
+                    digest_owner,
+                    &priv_keys.lms_priv_keys[owner_lms_key_idx as usize],
+                )?;
+                owner_sigs.lms_sig = lms_sig;
             }
         }
 
@@ -159,6 +172,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
             vendor_pub_keys: config.vendor_config.pub_keys,
             vendor_ecc_pub_key_idx: ecc_key_idx,
             vendor_lms_pub_key_idx: lms_key_idx,
+            owner_lms_pub_key_idx: owner_lms_key_idx,
             vendor_sigs,
             owner_sigs,
             ..Default::default()
@@ -177,6 +191,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
         config: &ImageGeneratorConfig<E>,
         ecc_key_idx: u32,
         lms_key_idx: u32,
+        owner_lms_key_idx: u32,
         flags: u32,
         digest: ImageDigest,
     ) -> anyhow::Result<ImageHeader>
@@ -186,6 +201,7 @@ impl<Crypto: ImageGeneratorCrypto> ImageGenerator<Crypto> {
         let mut header = ImageHeader {
             vendor_ecc_pub_key_idx: ecc_key_idx,
             vendor_lms_pub_key_idx: lms_key_idx,
+            owner_lms_pub_key_idx: owner_lms_key_idx,
             flags,
             toc_len: MAX_TOC_ENTRY_COUNT,
             toc_digest: digest,
diff --git a/image/gen/src/lib.rs b/image/gen/src/lib.rs
index d58db9b3..e9ab511a 100644
--- a/image/gen/src/lib.rs
+++ b/image/gen/src/lib.rs
@@ -82,6 +82,8 @@ pub struct ImageGeneratorVendorConfig {
 /// Image Generator Owner Configuration
 #[derive(Default)]
 pub struct ImageGeneratorOwnerConfig {
+    pub lms_key_idx: u32,
+
     pub pub_keys: ImageOwnerPubKeys,
 
     pub priv_keys: Option<ImageOwnerPrivKeys>,
diff --git a/image/types/src/lib.rs b/image/types/src/lib.rs
index 3316bff9..3628f2d2 100644
--- a/image/types/src/lib.rs
+++ b/image/types/src/lib.rs
@@ -26,6 +26,7 @@ use zerocopy::{AsBytes, FromBytes};
 pub const MANIFEST_MARKER: u32 = 0x4E414D43;
 pub const VENDOR_ECC_KEY_COUNT: u32 = 4;
 pub const VENDOR_LMS_KEY_COUNT: u32 = 4;
+pub const OWNER_LMS_KEY_COUNT: u32 = 1;
 pub const MAX_TOC_ENTRY_COUNT: u32 = 2;
 pub const IMAGE_REVISION_BYTE_SIZE: usize = 20;
 pub const ECC384_SCALAR_WORD_SIZE: usize = 12;
@@ -227,12 +228,14 @@ pub struct ImageVendorPrivKeys {
 #[derive(AsBytes, FromBytes, Default, Debug, Clone, Copy)]
 pub struct ImageOwnerPubKeys {
     pub ecc_pub_key: ImageEccPubKey,
+    pub lms_pub_keys: [ImageLmsPublicKey; OWNER_LMS_KEY_COUNT as usize],
 }
 
 #[repr(C)]
-#[derive(AsBytes, FromBytes, Default, Debug)]
+#[derive(AsBytes, FromBytes, Default, Debug, Clone, Copy)]
 pub struct ImageOwnerPrivKeys {
     pub ecc_priv_key: ImageEccPrivKey,
+    pub lms_priv_keys: [ImageLmsPrivKey; OWNER_LMS_KEY_COUNT as usize],
 }
 
 #[repr(C)]
@@ -261,6 +264,9 @@ pub struct ImagePreamble {
     /// Owner Public Key
     pub owner_pub_keys: ImageOwnerPubKeys,
 
+    /// Owner LMS Public Key Index
+    pub owner_lms_pub_key_idx: u32,
+
     /// Owner Signatures
     pub owner_sigs: ImageSignatures,
 
@@ -304,6 +310,9 @@ pub struct ImageHeader {
     /// Vendor LMS Public Key Index
     pub vendor_lms_pub_key_idx: u32,
 
+    /// Owner LMS Public Key Index
+    pub owner_lms_pub_key_idx: u32,
+
     /// Flags
     pub flags: u32,
 
diff --git a/image/verify/src/verifier.rs b/image/verify/src/verifier.rs
index 5148bc2d..f0dd1c98 100644
--- a/image/verify/src/verifier.rs
+++ b/image/verify/src/verifier.rs
@@ -25,10 +25,12 @@ const ZERO_DIGEST: ImageDigest = [0u32; SHA384_DIGEST_WORD_SIZE];
 struct HeaderInfo<'a> {
     vendor_ecc_pub_key_idx: u32,
     vendor_lms_pub_key_idx: u32,
+    owner_lms_pub_key_idx: u32,
     vendor_pub_key_revocation: VendorPubKeyRevocation,
     vendor_info: (&'a ImageEccPubKey, &'a ImageEccSignature),
     vendor_lms_info: (&'a ImageLmsPublicKey, &'a ImageLmsSignature),
     owner_info: Option<(&'a ImageEccPubKey, &'a ImageEccSignature)>,
+    owner_lms_info: Option<(&'a ImageLmsPublicKey, &'a ImageLmsSignature)>,
     owner_pub_keys_digest: ImageDigest,
 }
 
@@ -140,6 +142,10 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
         let (vendor_ecc_pub_key_idx, vendor_pub_key_revocation) =
             self.verify_vendor_ecc_pk_idx(preamble, reason)?;
 
+        if vendor_ecc_pub_key_idx >= VENDOR_ECC_KEY_COUNT {
+            Err(CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_INDEX_OUT_OF_BOUNDS)?;
+        }
+
         // Vendor Information
         let vendor_info = (
             &preamble.vendor_pub_keys.ecc_pub_keys[vendor_ecc_pub_key_idx as usize],
@@ -148,7 +154,7 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
 
         let vendor_lms_pub_key_idx = preamble.vendor_lms_pub_key_idx;
         if vendor_lms_pub_key_idx >= VENDOR_LMS_KEY_COUNT {
-            return Err(CaliptraError::IMAGE_VERIFIER_ERR_VEN_LMS_PUB_KEY_INDEX_OUT_OF_BOUNDS);
+            return Err(CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS);
         }
 
         let vendor_lms_info = (
@@ -157,23 +163,38 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
         );
 
         // Owner Information
-        let (owner_pub_keys_digest, owner_info) = if let Some(digest) = owner_pk_digest {
-            (
-                digest,
-                Some((
-                    &preamble.owner_pub_keys.ecc_pub_key,
-                    &preamble.owner_sigs.ecc_sig,
-                )),
-            )
-        } else {
-            (ZERO_DIGEST, None)
-        };
+        let (owner_lms_pub_key_idx, owner_pub_keys_digest, owner_info, owner_lms_info) =
+            if let Some(digest) = owner_pk_digest {
+                if preamble.owner_lms_pub_key_idx >= OWNER_LMS_KEY_COUNT {
+                    return Err(
+                        CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS,
+                    );
+                }
+
+                (
+                    preamble.owner_lms_pub_key_idx,
+                    digest,
+                    Some((
+                        &preamble.owner_pub_keys.ecc_pub_key,
+                        &preamble.owner_sigs.ecc_sig,
+                    )),
+                    Some((
+                        &preamble.owner_pub_keys.lms_pub_keys
+                            [preamble.owner_lms_pub_key_idx as usize],
+                        &preamble.owner_sigs.lms_sig,
+                    )),
+                )
+            } else {
+                (OWNER_LMS_KEY_COUNT, ZERO_DIGEST, None, None)
+            };
 
         let info = HeaderInfo {
             vendor_ecc_pub_key_idx,
             vendor_lms_pub_key_idx,
             vendor_info,
             vendor_lms_info,
+            owner_lms_pub_key_idx,
+            owner_lms_info,
             owner_pub_keys_digest,
             owner_info,
             vendor_pub_key_revocation,
@@ -310,6 +331,14 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
             return Err(CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_INDEX_MISMATCH);
         }
 
+        if let Some((owner_lms_pub_key, owner_lms_sig)) = info.owner_lms_info {
+            if header.owner_lms_pub_key_idx != info.owner_lms_pub_key_idx {
+                return Err(CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_PUB_KEY_INDEX_MISMATCH);
+            }
+
+            self.verify_owner_lms_sig(&digest_owner, owner_lms_pub_key, owner_lms_sig)?;
+        }
+
         // Verify owner signature
         if let Some((pub_key, sig)) = info.owner_info {
             self.verify_owner_sig(&digest_owner, pub_key, sig)?;
@@ -385,6 +414,24 @@ impl<Env: ImageVerificationEnv> ImageVerifier<Env> {
         Ok(())
     }
 
+    /// Verify owner LMS Signature
+    fn verify_owner_lms_sig(
+        &mut self,
+        digest: &ImageDigest,
+        lms_pub_key: &ImageLmsPublicKey,
+        lms_sig: &ImageLmsSignature,
+    ) -> CaliptraResult<()> {
+        let result = self
+            .env
+            .lms_verify(digest, lms_pub_key, lms_sig)
+            .map_err(|_| CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_VERIFY_FAILURE)?;
+        if !result {
+            return Err(CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_SIGNATURE_INVALID);
+        }
+
+        Ok(())
+    }
+
     /// Verify Table of Contents
     fn verify_toc<'a>(
         &mut self,
@@ -857,12 +904,16 @@ mod tests {
         let header = ImageHeader::default();
         let ecc_pubkey = ImageEccPubKey::default();
         let ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&ImageEccPubKey::default(), &ImageEccSignature::default()),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&ecc_pubkey, &ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -880,12 +931,16 @@ mod tests {
         let header = ImageHeader::default();
         let owner_ecc_pubkey = ImageEccPubKey::default();
         let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &ImageEccSignature::default()),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -909,12 +964,16 @@ mod tests {
         let header = ImageHeader::default();
         let owner_ecc_pubkey = ImageEccPubKey::default();
         let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -938,13 +997,17 @@ mod tests {
         let header = ImageHeader::default();
         let owner_ecc_pubkey = ImageEccPubKey::default();
         let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
             vendor_pub_key_revocation: Default::default(),
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
         };
         let result = verifier.verify_header(&header, &header_info);
@@ -965,12 +1028,16 @@ mod tests {
         let header = ImageHeader::default();
         let owner_ecc_pubkey = ImageEccPubKey::default();
         let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 1,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -981,6 +1048,68 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_header_incorrect_lms_pubkey_index() {
+        let test_env = TestEnv {
+            verify_result: true,
+            verify_lms_result: true,
+            ..Default::default()
+        };
+        let mut verifier = ImageVerifier::new(test_env);
+        let header = ImageHeader::default();
+        let owner_ecc_pubkey = ImageEccPubKey::default();
+        let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
+        let header_info: HeaderInfo = HeaderInfo {
+            vendor_ecc_pub_key_idx: 0,
+            vendor_lms_pub_key_idx: 1,
+            owner_lms_pub_key_idx: 0,
+            vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
+            vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
+            owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
+            owner_pub_keys_digest: ImageDigest::default(),
+            vendor_pub_key_revocation: Default::default(),
+        };
+        let result = verifier.verify_header(&header, &header_info);
+        assert_eq!(
+            result.err(),
+            Some(CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_INDEX_MISMATCH)
+        );
+    }
+
+    #[test]
+    fn test_header_incorrect_owner_lms_pubkey_index() {
+        let test_env = TestEnv {
+            verify_result: true,
+            verify_lms_result: true,
+            ..Default::default()
+        };
+        let mut verifier = ImageVerifier::new(test_env);
+        let header = ImageHeader::default();
+        let owner_ecc_pubkey = ImageEccPubKey::default();
+        let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
+        let header_info: HeaderInfo = HeaderInfo {
+            vendor_ecc_pub_key_idx: 0,
+            vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 1,
+            vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
+            vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
+            owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
+            owner_pub_keys_digest: ImageDigest::default(),
+            vendor_pub_key_revocation: Default::default(),
+        };
+        let result = verifier.verify_header(&header, &header_info);
+        assert_eq!(
+            result.err(),
+            Some(CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_PUB_KEY_INDEX_MISMATCH)
+        );
+    }
+
     #[test]
     fn test_header_owner_pubkey_invalid_arg() {
         let test_env = TestEnv {
@@ -992,12 +1121,16 @@ mod tests {
         let header = ImageHeader::default();
         let owner_ecc_pubkey = ImageEccPubKey::default();
         let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&owner_ecc_pubkey, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -1018,12 +1151,16 @@ mod tests {
         let mut verifier = ImageVerifier::new(test_env);
         let header = ImageHeader::default();
         let owner_ecc_sig = ImageEccSignature::default();
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&OWNER_ECC_PUBKEY, &owner_ecc_sig)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -1047,12 +1184,16 @@ mod tests {
             toc_digest: DUMMY_DATA,
             ..Default::default()
         };
+        let owner_lms_pubkey = ImageLmsPublicKey::default();
+        let owner_lms_sig = ImageLmsSignature::default();
         let header_info: HeaderInfo = HeaderInfo {
             vendor_ecc_pub_key_idx: 0,
             vendor_lms_pub_key_idx: 0,
+            owner_lms_pub_key_idx: 0,
             vendor_info: (&VENDOR_ECC_PUBKEY, &VENDOR_ECC_SIG),
             vendor_lms_info: (&vendor_lms_pubkey(), &vendor_lms_sig()),
             owner_info: Some((&OWNER_ECC_PUBKEY, &OWNER_ECC_SIG)),
+            owner_lms_info: Some((&owner_lms_pubkey, &owner_lms_sig)),
             owner_pub_keys_digest: ImageDigest::default(),
             vendor_pub_key_revocation: Default::default(),
         };
@@ -1149,7 +1290,6 @@ mod tests {
             &toc_info,
             manifest.size + manifest.fmc.image_size() + manifest.runtime.image_size(),
         );
-
         assert_eq!(
             result.err(),
             Some(CaliptraError::IMAGE_VERIFIER_ERR_FMC_RUNTIME_OVERLAP)
diff --git a/rom/dev/Makefile b/rom/dev/Makefile
index 6b16bb2a..2ead775d 100644
--- a/rom/dev/Makefile
+++ b/rom/dev/Makefile
@@ -70,6 +70,7 @@ build-fw-image: gen-certs build-test-fmc build-test-rt
 		--key-config $(TARGET_DIR)/keys.toml \
 		--ecc-pk-idx 3 \
 		--lms-pk-idx 3 \
+		--owner-lms-pk-idx 0 \
 		--fmc $(TARGET_DIR)/caliptra-rom-test-fmc \
 		--fmc-svn 0 \
 		--fmc-min-svn 0 \
diff --git a/rom/dev/tools/keys.toml b/rom/dev/tools/keys.toml
index 5754fbf4..4c11024d 100644
--- a/rom/dev/tools/keys.toml
+++ b/rom/dev/tools/keys.toml
@@ -29,3 +29,15 @@ lms_priv_keys = [
 [owner]
 ecc_pub_key = "own-pub-key.pem"
 ecc_priv_key = "own-priv-key.pem"
+lms_pub_keys = [
+  "own-lms-pub-key-0.pem",
+  "own-lms-pub-key-1.pem",
+  "own-lms-pub-key-2.pem",
+  "own-lms-pub-key-3.pem",
+]
+lms_priv_keys = [
+  "own-lms-priv-key-0.pem",
+  "own-lms-priv-key-1.pem",
+  "own-lms-priv-key-2.pem",
+  "own-lms-priv-key-3.pem",
+]


Test patch:
diff --git a/rom/dev/doc/test-coverage/test-coverage.md b/rom/dev/doc/test-coverage/test-coverage.md
index aa8857e4..c7e222b5 100644
--- a/rom/dev/doc/test-coverage/test-coverage.md
+++ b/rom/dev/doc/test-coverage/test-coverage.md
@@ -8,10 +8,17 @@ Test Name | ROM Stage | Description | ROM Error Code
 **test_preamble_owner_pubkey_digest_mismatch** | Image Verification - Preamble | Checks if the owner public key hash from fuse is not zero and matches the hash of the owner public key in the Preamble | IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_MISMATCH
 **test_preamble_vendor_pubkey_revocation** | Image Verification - Preamble | * Checks revoking of key idx 0/1/2 <br> * Checks if last key (idx = 3) is revocable | IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_REVOKED
 **test_preamble_vendor_pubkey_out_of_bounds** | Image Verification - Preamble | Checks if key idx is >= 4 | IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_INDEX_OUT_OF_BOUNDS
+**test_preamble_vendor_lms_pubkey_out_of_bounds** | Image Verification - Preamble | Checks if LMS key idx is >= 4 | IMAGE_VERIFIER_ERR_VENDOR_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS
+**test_preamble_owner_pubkey_out_of_bounds** | Image Verification - Preamble | Checks if key idx is >= 4 | IMAGE_VERIFIER_ERR_OWNER_ECC_PUB_KEY_INDEX_OUT_OF_BOUNDS
+**test_preamble_owner_lms_pubkey_out_of_bounds** | Image Verification - Preamble | Checks if LMS key idx is >= 4 | IMAGE_VERIFIER_ERR_OWNER_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS
 **test_header_verify_vendor_sig_zero_pubkey** | Image Verification - Header | Checks if vendor ECC public key is non-zero | IMAGE_VERIFIER_ERR_VENDOR_PUB_KEY_DIGEST_INVALID_ARG
 **test_header_verify_vendor_sig_zero_signature** | Image Verification - Header | Checks if vendor signature is non-zero | IMAGE_VERIFIER_ERR_VENDOR_ECC_SIGNATURE_INVALID_ARG
 **test_header_verify_vendor_sig_mismatch** | Image Verification - Header | Checks if vendor ECC signature from Preamble and computed header signature match | IMAGE_VERIFIER_ERR_VENDOR_ECC_SIGNATURE_INVALID
+**test_header_verify_vendor_lms_sig_mismatch** | Image Verification - Header | Checks if vendor LMS signature from Preamble and computed header signature match | IMAGE_VERIFIER_ERR_VENDOR_LMS_SIGNATURE_INVALID
+**test_header_verify_owner_lms_sig_mismatch** | Image Verification - Header | Checks if owner LMS signature from Preamble and computed header signature match | IMAGE_VERIFIER_ERR_OWNER_LMS_SIGNATURE_INVALID
 **test_header_verify_vendor_pub_key_in_preamble_and_header** | Image Verification - Header | Checks if the vendor ECC public key index in Preamble and Header match | IMAGE_VERIFIER_ERR_VENDOR_ECC_PUB_KEY_INDEX_MISMATCH
+**test_header_verify_vendor_lms_pub_key_in_preamble_and_header** | Image Verification - Header | Checks if the vendor LMS public key index in Preamble and Header match | IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_INDEX_MISMATCH
+**test_header_verify_owner_lms_pub_key_in_preamble_and_header** | Image Verification - Header | Checks if the owner LMS public key index in Preamble and Header match | IMAGE_VERIFIER_ERR_OWNER_LMS_PUB_KEY_INDEX_MISMATCH
 **test_header_verify_owner_sig_zero_fuses_zero_pubkey_x** | Image Verification - Header | Checks if the owner ECC public key in Preamble is zero | IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_INVALID_ARG
 **test_header_verify_owner_sig_zero_pubkey_x** | Image Verification - Header | Checks if the owner ECC public key in Preamble is zero | IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_INVALID_ARG
 **test_header_verify_owner_sig_zero_pubkey_y** | Image Verification - Header | Checks if the owner ECC public key in Preamble is zero | IMAGE_VERIFIER_ERR_OWNER_PUB_KEY_DIGEST_INVALID_ARG
diff --git a/rom/dev/tests/test_image_validation.rs b/rom/dev/tests/test_image_validation.rs
index dfb6863b..a4d21f9d 100644
--- a/rom/dev/tests/test_image_validation.rs
+++ b/rom/dev/tests/test_image_validation.rs
@@ -14,7 +14,9 @@ use caliptra_image_fake_keys::{
 };
 use caliptra_image_gen::{ImageGenerator, ImageGeneratorConfig, ImageGeneratorVendorConfig};
 use caliptra_image_openssl::OsslCrypto;
-use caliptra_image_types::{ImageBundle, ImageManifest, VENDOR_ECC_KEY_COUNT};
+use caliptra_image_types::{
+    ImageBundle, ImageManifest, OWNER_LMS_KEY_COUNT, VENDOR_ECC_KEY_COUNT, VENDOR_LMS_KEY_COUNT,
+};
 use openssl::asn1::Asn1Integer;
 use openssl::asn1::Asn1Time;
 use openssl::bn::BigNum;
@@ -213,6 +215,36 @@ fn test_preamble_vendor_pubkey_out_of_bounds() {
     );
 }
 
+#[test]
+fn test_preamble_vendor_lms_pubkey_out_of_bounds() {
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+    image_bundle.manifest.preamble.vendor_lms_pub_key_idx = VENDOR_LMS_KEY_COUNT;
+
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+}
+
+#[test]
+fn test_preamble_owner_lms_pubkey_out_of_bounds() {
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+    image_bundle.manifest.preamble.owner_lms_pub_key_idx = OWNER_LMS_KEY_COUNT;
+
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_PUBKEY_INDEX_OUT_OF_BOUNDS.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+}
+
 #[test]
 fn test_header_verify_vendor_sig_zero_pubkey() {
     let (mut hw, mut image_bundle) =
@@ -341,6 +373,80 @@ fn test_header_verify_vendor_sig_mismatch() {
     );
 }
 
+#[test]
+fn test_header_verify_vendor_lms_sig_mismatch() {
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+    let vendor_lms_pub_key_idx = image_bundle.manifest.preamble.vendor_lms_pub_key_idx as usize;
+
+    // Modify the vendor public key.
+    let lms_pub_key_backup =
+        image_bundle.manifest.preamble.vendor_pub_keys.lms_pub_keys[vendor_lms_pub_key_idx];
+
+    image_bundle.manifest.preamble.vendor_pub_keys.lms_pub_keys[vendor_lms_pub_key_idx].digest =
+        [Default::default(); 6];
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_SIGNATURE_INVALID.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+
+    // Modify the vendor signature.
+    image_bundle.manifest.preamble.vendor_pub_keys.lms_pub_keys[vendor_lms_pub_key_idx] =
+        lms_pub_key_backup;
+    image_bundle.manifest.preamble.vendor_sigs.lms_sig.tree_path[0] = [Default::default(); 6];
+
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_SIGNATURE_INVALID.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+}
+
+#[test]
+fn test_header_verify_owner_lms_sig_mismatch() {
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+    let owner_lms_pub_key_idx = image_bundle.manifest.preamble.owner_lms_pub_key_idx as usize;
+
+    // Modify the owner public key.
+    let lms_pub_key_backup =
+        image_bundle.manifest.preamble.owner_pub_keys.lms_pub_keys[owner_lms_pub_key_idx];
+
+    image_bundle.manifest.preamble.owner_pub_keys.lms_pub_keys[owner_lms_pub_key_idx].digest =
+        [Default::default(); 6];
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_SIGNATURE_INVALID.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+
+    // Modify the owner signature.
+    image_bundle.manifest.preamble.owner_pub_keys.lms_pub_keys[owner_lms_pub_key_idx] =
+        lms_pub_key_backup;
+    image_bundle.manifest.preamble.owner_sigs.lms_sig.tree_path[0] = [Default::default(); 6];
+
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_SIGNATURE_INVALID.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+}
+
 #[test]
 fn test_header_verify_vendor_pub_key_in_preamble_and_header() {
     let (mut hw, mut image_bundle) =
@@ -360,6 +466,44 @@ fn test_header_verify_vendor_pub_key_in_preamble_and_header() {
     );
 }
 
+#[test]
+fn test_header_verify_vendor_lms_pub_key_in_preamble_and_header() {
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+
+    // Change vendor pubkey index.
+    image_bundle.manifest.header.vendor_lms_pub_key_idx =
+        image_bundle.manifest.preamble.vendor_lms_pub_key_idx + 1;
+    update_header(&mut image_bundle);
+
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_VENDOR_LMS_PUB_KEY_INDEX_MISMATCH.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+}
+
+#[test]
+fn test_header_verify_owner_lms_pub_key_in_preamble_and_header() {
+    let (mut hw, mut image_bundle) =
+        helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
+
+    // Change vendor pubkey index.
+    image_bundle.manifest.header.owner_lms_pub_key_idx =
+        image_bundle.manifest.preamble.owner_lms_pub_key_idx + 1;
+    update_header(&mut image_bundle);
+
+    assert_eq!(
+        ModelError::MailboxCmdFailed(
+            CaliptraError::IMAGE_VERIFIER_ERR_OWNER_LMS_PUB_KEY_INDEX_MISMATCH.into()
+        ),
+        hw.upload_firmware(&image_bundle.to_bytes().unwrap())
+            .unwrap_err()
+    );
+}
+
 #[test]
 fn test_header_verify_owner_sig_zero_fuses() {
     let image_bundle = caliptra_builder::build_and_sign_image(
@@ -1448,6 +1592,7 @@ fn update_header(image_bundle: &mut ImageBundle) {
             &config,
             image_bundle.manifest.preamble.vendor_ecc_pub_key_idx,
             image_bundle.manifest.preamble.vendor_lms_pub_key_idx,
+            image_bundle.manifest.preamble.owner_lms_pub_key_idx,
             &header_digest_vendor,
             &header_digest_owner,
         )