Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rust-keylime for openSUSE:Factory 
checked in at 2026-10-28 17:44:24
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rust-keylime (Old)
 and      /work/SRC/openSUSE:Factory/.rust-keylime.new.2252680 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rust-keylime"

Wed Oct 28 17:44:24 2026 rev:36 rq:1393019 version:0.2.10+12

Changes:
--------
--- /work/SRC/openSUSE:Factory/rust-keylime/rust-keylime.changes        
2026-07-08 17:37:35.799691077 +0200
+++ /work/SRC/openSUSE:Factory/.rust-keylime.new.2252680/rust-keylime.changes   
2026-10-28 17:44:44.443536800 +0100
@@ -1,0 +2,23 @@
+Tue Oct 27 08:38:43 UTC 2026 - [email protected]
+
+- Add Cargo_toml.patch
+- Update zip to 9.1.2
+  bsc#1287921, CVE-2026-115905, CVE-2026-115906, CVE-2026-115907,
+  CVE-2026-115908, CVE-2026-115909
+- Update to version 0.2.10+12:
+  * log Agent UUID at push agent startup
+  * Limit actix-web workers
+  * Fix clippy needless_late_init warnings
+  * Unwrap OCTET STRING wrapped EK certificates
+  * build(deps): bump rand from 0.9.4 to 0.10.1
+  * build(deps): bump glob from 0.3.3 to 0.3.4
+  * build(deps): bump reqwest from 0.13.3 to 0.13.4
+  * Check shutdown marker inside ml_extend loop
+  * Fix clippy useless_borrows_in_formatting warnings
+  * build(deps): bump tokio from 1.52.2 to 1.53.1
+  * Fix payload key upgrade regression by falling back to mTLS key
+  * Accept any RSA key size for payload encryption
+  * Bump version to 0.2.10
+  * keylime: use runtime OpenSSL algorithm detection instead of compile-time 
symbols
+
+-------------------------------------------------------------------

Old:
----
  rust-keylime-0.2.9+49.tar.zst

New:
----
  Cargo_toml.patch
  rust-keylime-0.2.10+12.tar.zst

----------(New B)----------
  New:
- Add Cargo_toml.patch
- Update zip to 9.1.2
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rust-keylime.spec ++++++
--- /var/tmp/diff_new_pack.i4q2m4/_old  2026-10-28 17:44:51.885847191 +0100
+++ /var/tmp/diff_new_pack.i4q2m4/_new  2026-10-28 17:44:51.888847316 +0100
@@ -25,7 +25,7 @@
   %define _config_norepl %config(noreplace)
 %endif
 Name:           rust-keylime
-Version:        0.2.9+49
+Version:        0.2.10+12
 Release:        0
 Summary:        Rust implementation of the keylime agent
 License:        (Apache-2.0 OR MIT) AND BSD-3-Clause AND (Apache-2.0 OR MIT) 
AND Unicode-DFS-2016 AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR ISC OR MIT) 
AND (Apache-2.0 OR MIT) AND (Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR 
MIT) AND (Apache-2.0 OR MIT OR Zlib) AND (MIT OR Unlicense) AND (Apache-2.0 OR 
Zlib OR MIT) AND Apache-2.0 AND Apache-2.0 WITH LLVM-exception AND BSD-3-Clause 
AND ISC AND MIT
@@ -42,6 +42,7 @@
 # PATCH-FIX-OPENSUSE keylime-agent.conf.diff
 Patch1:         keylime-agent.conf.diff
 Patch2:         Cargo_lock.patch
+Patch3:         Cargo_toml.patch
 BuildRequires:  cargo-packaging
 BuildRequires:  clang
 BuildRequires:  firewall-macros

++++++ Cargo_lock.patch ++++++
++++ 2752 lines (skipped)
++++ between /work/SRC/openSUSE:Factory/rust-keylime/Cargo_lock.patch
++++ and /work/SRC/openSUSE:Factory/.rust-keylime.new.2252680/Cargo_lock.patch

++++++ Cargo_toml.patch ++++++
diff --git i/Cargo.toml w/Cargo.toml
index d006826..ee7bbbe 100644
--- i/Cargo.toml
+++ w/Cargo.toml
@@ -60,4 +60,4 @@ tokio = {version = "1", features = ["rt", "sync", "macros"]}
 tss-esapi = {version = "7.7.0", features = ["generate-bindings"]}
 url = "2.5.4"
 uuid = {version = "1.3", features = ["v4"]}
-zip = {version = "0.6", default-features = false, features= ["deflate"]}
+zip = {version = "*", default-features = false, features= ["deflate"]}

++++++ _service ++++++
--- /var/tmp/diff_new_pack.i4q2m4/_old  2026-10-28 17:44:52.083855414 +0100
+++ /var/tmp/diff_new_pack.i4q2m4/_new  2026-10-28 17:44:52.092855788 +0100
@@ -1,29 +1,29 @@
 <services>
-  <service mode="disabled" name="obs_scm">
+  <service mode="manual" name="tar_scm">
     <param name="url">https://github.com/keylime/rust-keylime.git</param>
-    <!-- <param name="versionformat">@PARENT_TAG@</param> -->
-    <param name="versionformat">@PARENT_TAG@+@TAG_OFFSET@</param>
     <param name="scm">git</param>
-    <param name="revision">v0.2.9</param>
+    <!-- Use a release tag, or master for a snapshot -->
+    <!-- <param name="revision">v0.2.10</param> -->
     <param name="revision">master</param>
-    <param name="match-tag">*</param>
-    <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param>
+    <!-- <param name="versionformat">@PARENT_TAG@</param> -->
+    <param name="versionformat">@PARENT_TAG@+@TAG_OFFSET@</param>
+    <param name="match-tag">v*</param>
+    <param name="versionrewrite-pattern">v(.*)</param>
     <param name="versionrewrite-replacement">\1</param>
     <param name="changesgenerate">enable</param>
     <param name="changesauthor">[email protected]</param>
   </service>
-  <service mode="disabled" name="tar" />
-  <service mode="disabled" name="recompress">
+  <service mode="manual" name="recompress">
     <param name="file">*.tar</param>
     <param name="compression">zst</param>
   </service>
-  <service mode="disabled" name="set_version"/>
-  <!-- <service name="cargo_vendor" mode="disabled"> -->
-  <!--    <param name="src">rust-keylime</param> -->
-  <!--    <param name="compression">zst</param> -->
-  <!--    <param name="update">true</param> -->
+  <service mode="manual" name="set_version"/>
+  <!-- <service mode="manual" name="cargo_vendor"> -->
+  <!--   <param name="srcdir">rust-keylime</param> -->
+  <!--   <param name="compression">zst</param> -->
+  <!--   <param name="update">true</param> -->
   <!-- </service> -->
-  <service name="cargo_audit" mode="disabled">
+  <service mode="manual" name="cargo_audit">
     <param name="srcdir">rust-keylime</param>
   </service>
 </services>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.i4q2m4/_old  2026-10-28 17:44:52.143857906 +0100
+++ /var/tmp/diff_new_pack.i4q2m4/_new  2026-10-28 17:44:52.156858446 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/keylime/rust-keylime.git</param>
-              <param 
name="changesrevision">28a2e1dec001b29ea417ea152bfbd9a8b06f6e14</param></service></servicedata>
+              <param 
name="changesrevision">54c62facf656a499a21809486c300d52572444d7</param></service></servicedata>
 (No newline at EOF)
 

++++++ rust-keylime-0.2.9+49.tar.zst -> rust-keylime-0.2.10+12.tar.zst ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/Cargo.lock 
new/rust-keylime-0.2.10+12/Cargo.lock
--- old/rust-keylime-0.2.9+49/Cargo.lock        2026-07-03 13:52:37.000000000 
+0200
+++ new/rust-keylime-0.2.10+12/Cargo.lock       2026-09-25 13:29:29.000000000 
+0200
@@ -931,9 +931,9 @@
 
 [[package]]
 name = "glob"
-version = "0.3.3"
+version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
 
 [[package]]
 name = "h2"
@@ -1338,7 +1338,7 @@
 
 [[package]]
 name = "keylime"
-version = "0.2.9"
+version = "0.2.10"
 dependencies = [
  "actix-rt",
  "actix-web",
@@ -1360,11 +1360,12 @@
  "pest_derive",
  "picky-asn1-der",
  "picky-asn1-x509",
- "rand 0.9.4",
+ "rand 0.9.5",
  "reqwest",
  "reqwest-middleware",
  "reqwest-retry",
  "serde",
+ "serde_bytes",
  "serde_json",
  "static_assertions",
  "tempfile",
@@ -1379,7 +1380,7 @@
 
 [[package]]
 name = "keylime-macros"
-version = "0.2.9"
+version = "0.2.10"
 dependencies = [
  "quote",
  "syn",
@@ -1389,7 +1390,7 @@
 
 [[package]]
 name = "keylime_agent"
-version = "0.2.9"
+version = "0.2.10"
 dependencies = [
  "actix-rt",
  "actix-web",
@@ -1417,7 +1418,7 @@
 
 [[package]]
 name = "keylime_ima_emulator"
-version = "0.2.9"
+version = "0.2.10"
 dependencies = [
  "clap",
  "hex",
@@ -1431,7 +1432,7 @@
 
 [[package]]
 name = "keylime_push_model_agent"
-version = "0.2.9"
+version = "0.2.10"
 dependencies = [
  "actix-rt",
  "actix-web",
@@ -1923,9 +1924,9 @@
 
 [[package]]
 name = "rand"
-version = "0.9.4"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
+checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
 dependencies = [
  "rand_chacha",
  "rand_core 0.9.5",
@@ -2013,9 +2014,9 @@
 
 [[package]]
 name = "reqwest"
-version = "0.13.3"
+version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0"
+checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
 dependencies = [
  "base64",
  "bytes",
@@ -2409,7 +2410,7 @@
 checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
 dependencies = [
  "fastrand",
- "getrandom 0.3.4",
+ "getrandom 0.4.2",
  "once_cell",
  "rustix",
  "windows-sys 0.61.2",
@@ -2496,9 +2497,9 @@
 
 [[package]]
 name = "tokio"
-version = "1.52.2"
+version = "1.53.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386"
+checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
 dependencies = [
  "bytes",
  "libc",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/Cargo.toml 
new/rust-keylime-0.2.10+12/Cargo.toml
--- old/rust-keylime-0.2.9+49/Cargo.toml        2026-07-03 13:52:37.000000000 
+0200
+++ new/rust-keylime-0.2.10+12/Cargo.toml       2026-09-25 13:29:29.000000000 
+0200
@@ -12,7 +12,7 @@
 edition = "2021"
 license = "Apache-2.0"
 repository = "https://github.com/keylime/rust-keylime";
-version = "0.2.9"
+version = "0.2.10"
 
 [workspace.dependencies]
 actix-rt = "2"
@@ -31,8 +31,8 @@
 hex = "0.4"
 http = "1.1.0"
 httpdate = "1.0.3"
-keylime = { version = "=0.2.9", path = "keylime" }
-keylime-macros = { version = "=0.2.9", path = "keylime-macros" }
+keylime = { version = "=0.2.10", path = "keylime" }
+keylime-macros = { version = "=0.2.10", path = "keylime-macros" }
 libc = "0.2.43"
 log = "0.4"
 once_cell = "1.19.0"
@@ -49,6 +49,7 @@
 reqwest-retry = "0.9.1"
 retry-policies = "0.5.0"
 serde = "1.0.80"
+serde_bytes = "0.11"
 serde_derive = "1.0.80"
 serde_json = { version = "1.0", features = ["raw_value"] }
 signal-hook = "0.3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/Cargo.toml 
new/rust-keylime-0.2.10+12/keylime/Cargo.toml
--- old/rust-keylime-0.2.9+49/keylime/Cargo.toml        2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/Cargo.toml       2026-09-25 
13:29:29.000000000 +0200
@@ -30,6 +30,7 @@
 reqwest-middleware.workspace = true
 reqwest-retry.workspace = true
 serde.workspace = true
+serde_bytes.workspace = true
 serde_json.workspace = true
 static_assertions.workspace = true
 tempfile.workspace = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime/src/agent_registration.rs 
new/rust-keylime-0.2.10+12/keylime/src/agent_registration.rs
--- old/rust-keylime-0.2.9+49/keylime/src/agent_registration.rs 2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/agent_registration.rs        
2026-09-25 13:29:29.000000000 +0200
@@ -168,7 +168,7 @@
     // Request keyblob material
     let keyblob = registrar_client.register_agent(&ai).await?;
 
-    info!("SUCCESS: Agent {} registered", &aa.agent_uuid);
+    info!("SUCCESS: Agent {} registered", aa.agent_uuid);
 
     let key = ctx.activate_credential(
         keyblob,
@@ -188,6 +188,6 @@
 
     registrar_client.activate_agent(&ai, &auth_tag).await?;
 
-    info!("SUCCESS: Agent {} activated", &aa.agent_uuid);
+    info!("SUCCESS: Agent {} activated", aa.agent_uuid);
     Ok(())
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/src/algorithms.rs 
new/rust-keylime-0.2.10+12/keylime/src/algorithms.rs
--- old/rust-keylime-0.2.9+49/keylime/src/algorithms.rs 2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/algorithms.rs        2026-09-25 
13:29:29.000000000 +0200
@@ -113,16 +113,33 @@
     }
 }
 
-impl From<HashAlgorithm> for MessageDigest {
-    fn from(hash_algorithm: HashAlgorithm) -> Self {
-        match hash_algorithm {
-            HashAlgorithm::Sha1 => MessageDigest::sha1(),
-            HashAlgorithm::Sha256 => MessageDigest::sha256(),
-            HashAlgorithm::Sha384 => MessageDigest::sha384(),
-            HashAlgorithm::Sha512 => MessageDigest::sha512(),
-            HashAlgorithm::Sm3_256 => MessageDigest::sm3(),
+impl HashAlgorithm {
+    fn openssl_name(&self) -> &'static str {
+        match self {
+            HashAlgorithm::Sha1 => "sha1",
+            HashAlgorithm::Sha256 => "sha256",
+            HashAlgorithm::Sha384 => "sha384",
+            HashAlgorithm::Sha512 => "sha512",
+            HashAlgorithm::Sm3_256 => "sm3",
         }
     }
+
+    pub fn is_supported(&self) -> bool {
+        MessageDigest::from_name(self.openssl_name()).is_some()
+    }
+}
+
+impl TryFrom<HashAlgorithm> for MessageDigest {
+    type Error = AlgorithmError;
+
+    fn try_from(hash_algorithm: HashAlgorithm) -> Result<Self, Self::Error> {
+        let name = hash_algorithm.openssl_name();
+        MessageDigest::from_name(name).ok_or_else(|| {
+            AlgorithmError::UnsupportedHashingAlgorithm(format!(
+                "{name} (not supported by this OpenSSL build)"
+            ))
+        })
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -474,6 +491,17 @@
     }
 
     #[test]
+    fn test_hash_to_message_digest() {
+        assert!(MessageDigest::try_from(HashAlgorithm::Sha1).is_ok());
+        assert!(MessageDigest::try_from(HashAlgorithm::Sha256).is_ok());
+        assert!(MessageDigest::try_from(HashAlgorithm::Sha384).is_ok());
+        assert!(MessageDigest::try_from(HashAlgorithm::Sha512).is_ok());
+        // SM3 may or may not be available depending on OpenSSL build;
+        // just verify it does not panic
+        let _ = MessageDigest::try_from(HashAlgorithm::Sm3_256);
+    }
+
+    #[test]
     fn test_hash_to_hashing_algorithm() {
         let cases = [
             (HashAlgorithm::Sha1, HashingAlgorithm::Sha1),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/src/config/base.rs 
new/rust-keylime-0.2.10+12/keylime/src/config/base.rs
--- old/rust-keylime-0.2.9+49/keylime/src/config/base.rs        2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/config/base.rs       2026-09-25 
13:29:29.000000000 +0200
@@ -726,15 +726,15 @@
         }
         "generate" => {
             let agent_uuid = Uuid::new_v4();
-            info!("Generated a new UUID: {}", &agent_uuid);
+            info!("Generated a new UUID: {}", agent_uuid);
             agent_uuid.to_string()
         }
         uuid_config => match Uuid::parse_str(uuid_config) {
             Ok(uuid_config) => uuid_config.to_string(),
             Err(_) => {
-                warn!("Misformatted UUID: {}", &uuid_config);
+                warn!("Misformatted UUID: {}", uuid_config);
                 let agent_uuid = Uuid::new_v4();
-                info!("Using generated UUID: {}", &agent_uuid);
+                info!("Using generated UUID: {}", agent_uuid);
                 agent_uuid.to_string()
             }
         },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/src/context_info.rs 
new/rust-keylime-0.2.10+12/keylime/src/context_info.rs
--- old/rust-keylime-0.2.9+49/keylime/src/context_info.rs       2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/context_info.rs      2026-09-25 
13:29:29.000000000 +0200
@@ -160,6 +160,16 @@
         let tpm_hash_alg = config.tpm_hash_alg;
         let tpm_signing_alg = config.tpm_signing_alg;
 
+        if !tpm_hash_alg.is_supported() {
+            return Err(ContextInfoError::InvalidAlgorithm(
+                algorithms::AlgorithmError::UnsupportedHashingAlgorithm(
+                    format!(
+                        "{tpm_hash_alg} (not supported by this OpenSSL build)"
+                    ),
+                ),
+            ));
+        }
+
         let ek_result = tpm_context.create_ek(tpm_encryption_alg, None)?;
         let ek_handle = ek_result.key_handle;
         let ek_hash = hash_ek::hash_ek_pubkey(ek_result.public.clone())
@@ -372,7 +382,8 @@
         let keylime_hash_alg: KeylimeInternalHashAlgorithm =
             name_h_alg_tss.try_into()?;
         let name_alg_id_value: u16 = name_h_alg_tss.into();
-        let openssl_message_digest: MessageDigest = keylime_hash_alg.into();
+        let openssl_message_digest: MessageDigest =
+            MessageDigest::try_from(keylime_hash_alg)?;
         let mut hasher = Hasher::new(openssl_message_digest)?;
         hasher.update(&marshalled_tpmt_public)?;
         let digest_bytes_vec = hasher.finish()?;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/src/ima/entry.rs 
new/rust-keylime-0.2.10+12/keylime/src/ima/entry.rs
--- old/rust-keylime-0.2.9+49/keylime/src/ima/entry.rs  2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/ima/entry.rs 2026-09-25 
13:29:29.000000000 +0200
@@ -32,7 +32,8 @@
 impl Digest {
     /// Creates a new `Digest` with `algorithm` and `value`.
     pub fn new(algorithm: HashAlgorithm, value: &[u8]) -> Result<Self> {
-        let digest: MessageDigest = algorithm.into();
+        let digest: MessageDigest =
+            MessageDigest::try_from(algorithm).map_err(Error::other)?;
         if value.len() != digest.size() {
             return Err(Error::new(
                 ErrorKind::InvalidInput,
@@ -54,22 +55,24 @@
 
     /// Returns a pre-defined digest value used to indicate the start
     /// of the IMA measurement list.
-    pub fn start(algorithm: HashAlgorithm) -> Self {
-        let digest: MessageDigest = algorithm.into();
-        Self {
+    pub fn start(algorithm: HashAlgorithm) -> Result<Self> {
+        let digest: MessageDigest =
+            MessageDigest::try_from(algorithm).map_err(Error::other)?;
+        Ok(Self {
             algorithm,
             value: vec![0x00u8; digest.size()],
-        }
+        })
     }
 
     /// Returns a pre-defined digest value used to indicate the ToMToU
     /// error in the IMA measurement list.
-    pub fn ff(algorithm: HashAlgorithm) -> Self {
-        let digest: MessageDigest = algorithm.into();
-        Self {
+    pub fn ff(algorithm: HashAlgorithm) -> Result<Self> {
+        let digest: MessageDigest =
+            MessageDigest::try_from(algorithm).map_err(Error::other)?;
+        Ok(Self {
             algorithm,
             value: vec![0xffu8; digest.size()],
-        }
+        })
     }
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime/src/registrar_client.rs 
new/rust-keylime-0.2.10+12/keylime/src/registrar_client.rs
--- old/rust-keylime-0.2.9+49/keylime/src/registrar_client.rs   2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/registrar_client.rs  2026-09-25 
13:29:29.000000000 +0200
@@ -441,7 +441,7 @@
 
         info!(
             "Requesting agent registration from {} for {}",
-            &addr, &ai.uuid
+            addr, ai.uuid
         );
 
         let resp = self
@@ -596,10 +596,7 @@
             
"{scheme}://{registrar_ip}:{registrar_port}/v{api_version}/agents/{uuid}",
         );
 
-        info!(
-            "Requesting agent activation from {} for {}",
-            &addr, &ai.uuid
-        );
+        info!("Requesting agent activation from {} for {}", addr, ai.uuid);
 
         let resp = self
             .resilient_client
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime/src/resilient_client.rs 
new/rust-keylime-0.2.10+12/keylime/src/resilient_client.rs
--- old/rust-keylime-0.2.9+49/keylime/src/resilient_client.rs   2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/resilient_client.rs  2026-09-25 
13:29:29.000000000 +0200
@@ -635,7 +635,7 @@
         // Make a request. The test will pass only if the default header is 
sent correctly.
         let response = resilient_client
             .client
-            .get(format!("{}/test", &mock_server.uri()))
+            .get(format!("{}/test", mock_server.uri()))
             .send()
             .await
             .unwrap(); //#[allow_ci]
@@ -663,7 +663,7 @@
         let response = client
             .get_json_request_from_struct(
                 Method::POST,
-                &format!("{}/submit", &mock_server.uri()),
+                &format!("{}/submit", mock_server.uri()),
                 &json!({}),
                 Some("application/vnd.api+json".to_string()),
             )
@@ -704,7 +704,7 @@
         let response = client
             .get_json_request_from_struct(
                 Method::POST,
-                &format!("{}/submit", &mock_server.uri()),
+                &format!("{}/submit", mock_server.uri()),
                 &json!({}),
                 None,
             )
@@ -740,7 +740,7 @@
         let response = client
             .get_json_request_from_struct(
                 Method::POST,
-                &format!("{}/submit", &mock_server.uri()),
+                &format!("{}/submit", mock_server.uri()),
                 &json!({}),
                 None,
             )
@@ -777,7 +777,7 @@
         let response = client
             .get_json_request_from_struct(
                 Method::POST,
-                &format!("{}/submit", &mock_server.uri()),
+                &format!("{}/submit", mock_server.uri()),
                 &json!({}),
                 None,
             )
@@ -860,7 +860,7 @@
         let response = client
             .get_request(
                 Method::GET,
-                &format!("{}/health", &mock_server.uri()),
+                &format!("{}/health", mock_server.uri()),
             )
             .send()
             .await;
@@ -906,7 +906,7 @@
 
         let start_time = std::time::Instant::now();
         let response = client
-            .get_request(Method::GET, &format!("{}/test", &mock_server.uri()))
+            .get_request(Method::GET, &format!("{}/test", mock_server.uri()))
             .send()
             .await
             .unwrap(); //#[allow_ci]
@@ -957,7 +957,7 @@
 
         let start_time = std::time::Instant::now();
         let response = client
-            .get_request(Method::GET, &format!("{}/test", &mock_server.uri()))
+            .get_request(Method::GET, &format!("{}/test", mock_server.uri()))
             .send()
             .await
             .unwrap(); //#[allow_ci]
@@ -995,7 +995,7 @@
 
         let start_time = std::time::Instant::now();
         let response = client
-            .get_request(Method::GET, &format!("{}/test", &mock_server.uri()))
+            .get_request(Method::GET, &format!("{}/test", mock_server.uri()))
             .send()
             .await
             .unwrap(); //#[allow_ci]
@@ -1087,7 +1087,7 @@
 
         let start_time = std::time::Instant::now();
         let response = client
-            .get_request(Method::GET, &format!("{}/test", &mock_server.uri()))
+            .get_request(Method::GET, &format!("{}/test", mock_server.uri()))
             .send()
             .await
             .unwrap(); //#[allow_ci]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/src/secure_mount.rs 
new/rust-keylime-0.2.10+12/keylime/src/secure_mount.rs
--- old/rust-keylime-0.2.9+49/keylime/src/secure_mount.rs       2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/secure_mount.rs      2026-09-25 
13:29:29.000000000 +0200
@@ -107,7 +107,7 @@
 
         info!(
             "Mounting secure storage location {:?} on tmpfs.",
-            &secure_dir_path
+            secure_dir_path
         );
 
         // mount tmpfs with secure directory
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime/src/tpm.rs 
new/rust-keylime-0.2.10+12/keylime/src/tpm.rs
--- old/rust-keylime-0.2.9+49/keylime/src/tpm.rs        2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime/src/tpm.rs       2026-09-25 
13:29:29.000000000 +0200
@@ -611,6 +611,34 @@
 
 static TPM_CTX: OnceLock<Arc<Mutex<tss_esapi::Context>>> = OnceLock::new();
 
+// Some TPMs store the EK cert in NVRAM wrapped in an ASN.1 OCTET STRING
+// (tag 0x04) instead of raw DER (tag 0x30). Unwrap it here so it can be
+// parsed as a normal certificate.
+fn unwrap_octet_string_ek_cert(cert: &[u8]) -> Vec<u8> {
+    if cert.first() != Some(&0x04) {
+        return cert.to_vec();
+    }
+
+    match picky_asn1_der::from_bytes::<serde_bytes::ByteBuf>(cert) {
+        Ok(inner) if inner.first() == Some(&0x30) => inner.into_vec(),
+        Ok(_) => {
+            warn!(
+                "EK certificate is OCTET-STRING-wrapped, but the inner \
+                 content does not start with a SEQUENCE tag; using it \
+                 as-is"
+            );
+            cert.to_vec()
+        }
+        Err(e) => {
+            warn!(
+                "EK certificate looks OCTET-STRING-wrapped, but could \
+                 not be unwrapped: {e}"
+            );
+            cert.to_vec()
+        }
+    }
+}
+
 impl Context<'_> {
     /// Creates a connection context.
     pub fn new() -> Result<Self> {
@@ -722,13 +750,16 @@
         };
 
         let cert = match ek::retrieve_ek_pubcert(&mut ctx, alg.into()) {
-            Ok(cert) => match self.check_ek_cert(&cert) {
-                Ok(cert_checked) => Some(cert_checked),
-                Err(_) => {
-                    warn!("EK certificate in TPM NVRAM is not ASN.1 DER 
encoded");
-                    Some(cert)
+            Ok(cert) => {
+                let cert = unwrap_octet_string_ek_cert(&cert);
+                match self.check_ek_cert(&cert) {
+                    Ok(cert_checked) => Some(cert_checked),
+                    Err(_) => {
+                        warn!("EK certificate in TPM NVRAM is not ASN.1 DER 
encoded");
+                        Some(cert) // now uses the unwrapped cert
+                    }
                 }
-            },
+            }
             Err(_) => {
                 warn!("No EK certificate found in TPM NVRAM");
                 None
@@ -1818,6 +1849,16 @@
                 }
             }
         }
+        usable_algs.retain(|alg| {
+            let supported = alg.is_supported();
+            if !supported {
+                log::warn!(
+                    "Hash algorithm {alg} supported by TPM but not by \
+                     this OpenSSL build; excluding"
+                );
+            }
+            supported
+        });
         Ok(usable_algs)
     }
 
@@ -2329,14 +2370,14 @@
 fn hash_alg_to_message_digest(
     hash_alg: HashingAlgorithm,
 ) -> Result<MessageDigest> {
-    match hash_alg {
-        HashingAlgorithm::Sha256 => Ok(MessageDigest::sha256()),
-        HashingAlgorithm::Sha1 => Ok(MessageDigest::sha1()),
-        HashingAlgorithm::Sha384 => Ok(MessageDigest::sha384()),
-        HashingAlgorithm::Sha512 => Ok(MessageDigest::sha512()),
-        HashingAlgorithm::Sm3_256 => Ok(MessageDigest::sm3()),
-        other => Err(TpmError::UnsupportedHashingAlgorithm { alg: other }),
-    }
+    let keylime_alg = KeylimeInternalHashAlgorithm::try_from(hash_alg)
+        .map_err(|_| TpmError::UnsupportedHashingAlgorithm {
+            alg: hash_alg,
+        })?;
+    MessageDigest::try_from(keylime_alg).map_err(|e| {
+        log::debug!("OpenSSL digest lookup failed for {hash_alg:?}: {e}");
+        TpmError::UnsupportedHashingAlgorithm { alg: hash_alg }
+    })
 }
 
 /// Check if the data attested in the quote matches the data read from the TPM 
PCRs
@@ -3034,6 +3075,52 @@
         }
     }
 
+    fn test_cert_der() -> Vec<u8> {
+        use std::path::Path;
+
+        let cert_path = Path::new(env!("CARGO_MANIFEST_DIR"))
+            .join("test-data")
+            .join("test-cert.pem");
+        let pem =
+            std::fs::read(cert_path).expect("unable to read test-cert.pem");
+        X509::from_pem(&pem)
+            .expect("unable to parse test-cert.pem")
+            .to_der()
+            .expect("unable to re-encode test cert as DER")
+    }
+
+    #[test]
+    fn test_unwrap_octet_string_ek_cert_wrapped() {
+        let cert_der = test_cert_der();
+        assert_eq!(cert_der[0], 0x30);
+
+        let wrapped = picky_asn1_der::to_vec(&serde_bytes::ByteBuf::from(
+            cert_der.clone(),
+        ))
+        .expect("failed to wrap cert in OCTET STRING");
+        assert_eq!(wrapped[0], 0x04);
+
+        assert_eq!(unwrap_octet_string_ek_cert(&wrapped), cert_der);
+    }
+
+    #[test]
+    fn test_unwrap_octet_string_ek_cert_not_wrapped() {
+        let cert_der = test_cert_der();
+        assert_eq!(unwrap_octet_string_ek_cert(&cert_der), cert_der);
+    }
+
+    #[test]
+    fn test_unwrap_octet_string_ek_cert_invalid_inner() {
+        // wrapped, but inner content isn't a cert
+        let not_a_cert = b"not a certificate".to_vec();
+        let wrapped =
+            picky_asn1_der::to_vec(&serde_bytes::ByteBuf::from(not_a_cert))
+                .expect("failed to wrap non-cert data in OCTET STRING");
+        assert_eq!(wrapped[0], 0x04);
+
+        assert_eq!(unwrap_octet_string_ek_cert(&wrapped), wrapped);
+    }
+
     #[test]
     #[cfg(feature = "testing")]
     fn test_quote_encode_decode() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-agent/src/agent_handler.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/agent_handler.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/agent_handler.rs        
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/agent_handler.rs       
2026-09-25 13:29:29.000000000 +0200
@@ -46,23 +46,21 @@
 
 /// Configure the endpoints for the /agent scope
 async fn agent_default(req: HttpRequest) -> impl Responder {
-    let error;
-    let response;
-    let message;
-
-    match req.head().method {
+    let (error, message, response) = match req.head().method {
         http::Method::GET => {
-            error = 400;
-            message = "URI not supported, only /info is supported for GET in 
/agent interface";
-            response = HttpResponse::BadRequest()
+            let error = 400;
+            let message = "URI not supported, only /info is supported for GET 
in /agent interface";
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         _ => {
-            error = 405;
-            message = "Method is not supported in /agent interface";
-            response = HttpResponse::MethodNotAllowed()
+            let error = 405;
+            let message = "Method is not supported in /agent interface";
+            let response = HttpResponse::MethodNotAllowed()
                 .insert_header(http::header::Allow(vec![http::Method::GET]))
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
     };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime-agent/src/api.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/api.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/api.rs  2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/api.rs 2026-09-25 
13:29:29.000000000 +0200
@@ -64,34 +64,33 @@
 
 /// Handles the default case for the API version scope
 async fn api_default(req: HttpRequest) -> impl Responder {
-    let error;
-    let response;
-    let message;
-
-    match req.head().method {
+    let (error, message, response) = match req.head().method {
         http::Method::GET => {
-            error = 400;
-            message =
+            let error = 400;
+            let message =
                 "Not Implemented: Use /agent, /keys, or /quotes interfaces";
-            response = HttpResponse::BadRequest()
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         http::Method::POST => {
-            error = 400;
-            message =
+            let error = 400;
+            let message =
                 "Not Implemented: Use /keys or /notifications interfaces";
-            response = HttpResponse::BadRequest()
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         _ => {
-            error = 405;
-            message = "Method is not supported";
-            response = HttpResponse::MethodNotAllowed()
+            let error = 405;
+            let message = "Method is not supported";
+            let response = HttpResponse::MethodNotAllowed()
                 .insert_header(http::header::Allow(vec![
                     http::Method::GET,
                     http::Method::POST,
                 ]))
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
     };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-agent/src/errors_handler.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/errors_handler.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/errors_handler.rs       
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/errors_handler.rs      
2026-09-25 13:29:29.000000000 +0200
@@ -16,10 +16,6 @@
     req: HttpRequest,
     quote_data: web::Data<QuoteData<'_>>,
 ) -> impl Responder {
-    let error;
-    let response;
-    let message;
-
     let api_versions = quote_data
         .api_versions
         .iter()
@@ -27,32 +23,35 @@
         .collect::<Vec<_>>()
         .join(", ");
 
-    match req.head().method {
+    let (error, message, response) = match req.head().method {
         http::Method::GET => {
-            error = 400;
-            message = format!(
+            let error = 400;
+            let message = format!(
                 "Not Implemented: Use {api_versions} or /version interfaces"
             );
-            response = HttpResponse::BadRequest()
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, &message));
+            (error, message, response)
         }
         http::Method::POST => {
-            error = 400;
-            message = format!(
+            let error = 400;
+            let message = format!(
                 "Not Implemented: Use {api_versions} or /version interfaces"
             );
-            response = HttpResponse::BadRequest()
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, &message));
+            (error, message, response)
         }
         _ => {
-            error = 405;
-            message = "Method is not supported".to_string();
-            response = HttpResponse::MethodNotAllowed()
+            let error = 405;
+            let message = "Method is not supported".to_string();
+            let response = HttpResponse::MethodNotAllowed()
                 .insert_header(http::header::Allow(vec![
                     http::Method::GET,
                     http::Method::POST,
                 ]))
                 .json(JsonWrapper::error(error, &message));
+            (error, message, response)
         }
     };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-agent/src/keys_handler.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/keys_handler.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/keys_handler.rs 2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/keys_handler.rs        
2026-09-25 13:29:29.000000000 +0200
@@ -550,32 +550,31 @@
 
 /// Handles the default case for the /keys scope
 async fn keys_default(req: HttpRequest) -> impl Responder {
-    let error;
-    let response;
-    let message;
-
-    match req.head().method {
+    let (error, message, response) = match req.head().method {
         http::Method::GET => {
-            error = 400;
-            message = "URI not supported, only /pubkey and /verify are 
supported for GET in /keys interface";
-            response = HttpResponse::BadRequest()
+            let error = 400;
+            let message = "URI not supported, only /pubkey and /verify are 
supported for GET in /keys interface";
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         http::Method::POST => {
-            error = 400;
-            message = "URI not supported, only /ukey and /vkey are supported 
for POST in /keys interface";
-            response = HttpResponse::BadRequest()
+            let error = 400;
+            let message = "URI not supported, only /ukey and /vkey are 
supported for POST in /keys interface";
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         _ => {
-            error = 405;
-            message = "Method is not supported in /keys interface";
-            response = HttpResponse::MethodNotAllowed()
+            let error = 405;
+            let message = "Method is not supported in /keys interface";
+            let response = HttpResponse::MethodNotAllowed()
                 .insert_header(http::header::Allow(vec![
                     http::Method::GET,
                     http::Method::POST,
                 ]))
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
     };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime-agent/src/main.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/main.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/main.rs 2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/main.rs        2026-09-25 
13:29:29.000000000 +0200
@@ -66,7 +66,7 @@
 };
 use log::*;
 use openssl::{
-    pkey::{PKey, Private, Public},
+    pkey::{Id, PKey, Private, Public},
     x509::X509,
 };
 use std::{
@@ -118,6 +118,118 @@
     }
 }
 
+/// On upgrade from a pre-key-separation agent, the payload key file does not
+/// exist yet. If the mTLS server key is RSA, copy it as the payload key so
+/// that PCR 16 stays consistent with what the verifier has cached.
+///
+/// Returns `Ok(())` when the copy succeeds or when a copy is not needed
+/// (key already exists, server key missing, or server key is not RSA).
+/// Returns `Err` if the server key is a valid RSA key but writing the
+/// payload key file fails — continuing in that state could leave a
+/// corrupt or insecure file behind.
+fn ensure_payload_key(
+    payload_key_path: &Path,
+    payload_key_password: &str,
+    server_key_path: &Path,
+    server_key_password: &str,
+) -> std::result::Result<(), Error> {
+    if payload_key_path.exists() {
+        return Ok(());
+    }
+    if !server_key_path.exists() {
+        return Ok(());
+    }
+
+    match crypto::load_key_pair(server_key_path, Some(server_key_password)) {
+        Ok((_, priv_key))
+            if priv_key.id() == Id::RSA && priv_key.bits() >= 2048 =>
+        {
+            crypto::write_key_pair(
+                &priv_key,
+                payload_key_path,
+                Some(payload_key_password),
+            )
+            .map_err(|e| {
+                if matches!(e, crypto::CryptoError::IOSetPermissionError(_)) {
+                    // The key was written successfully but chmod failed.
+                    // Remove the file so the next startup retries the copy
+                    // rather than loading a key with insecure permissions.
+                    // The file did not exist before this call (checked above),
+                    // so removal cannot delete pre-existing user data.
+                    match fs::remove_file(payload_key_path) {
+                        Ok(()) => {
+                            error!(
+                                "Wrote mTLS key to {} but failed to set \
+                                 file permissions: {e}. The file has been \
+                                 removed; the copy will be retried on next \
+                                 startup.",
+                                payload_key_path.display()
+                            );
+                        }
+                        Err(rm_err) => {
+                            error!(
+                                "Wrote mTLS key to {} but failed to set \
+                                 file permissions: {e}. Cleanup also failed: \
+                                 {rm_err}. Remove the file manually before \
+                                 restarting the agent to prevent loading a key 
\
+                                 with insecure permissions.",
+                                payload_key_path.display()
+                            );
+                        }
+                    }
+                } else {
+                    error!(
+                        "Failed to write mTLS key to {}: {e}. \
+                         If a partial file was left behind, remove \
+                         it manually before restarting the agent.",
+                        payload_key_path.display()
+                    );
+                }
+                Error::Crypto(e)
+            })?;
+
+            warn!(
+                "Payload key not found; wrote mTLS key from {} to {} \
+                 for backward compatibility. The same RSA key will \
+                 be used for both mTLS and payload encryption.",
+                server_key_path.display(),
+                payload_key_path.display()
+            );
+            if !server_key_password.is_empty()
+                && payload_key_password.is_empty()
+            {
+                warn!(
+                    "The mTLS key was encrypted at rest but the \
+                     payload key copy at {} is unencrypted. To \
+                     encrypt it, re-encrypt the file manually and \
+                     set 'payload_key_password' in the agent \
+                     configuration.",
+                    payload_key_path.display()
+                );
+            }
+        }
+        Ok(_) => {
+            warn!(
+                "mTLS key {} is not a supported RSA key (at least 2048 \
+                 bits); a new RSA payload key will be generated, which \
+                 will change PCR 16 and may cause attestation failures \
+                 until the agent is re-enrolled.",
+                server_key_path.display()
+            );
+        }
+        Err(e) => {
+            warn!(
+                "Failed to load mTLS key {}: {e}. \
+                 A new payload key will be generated, which will change \
+                 PCR 16 and may cause attestation failures until the \
+                 agent is re-enrolled.",
+                server_key_path.display()
+            );
+        }
+    }
+    Ok(())
+}
+
 // This data is passed in to the actix httpserver threads that
 // handle quotes.
 #[derive(Debug)]
@@ -254,7 +366,7 @@
     {
         let message = "The agent mTLS is disabled and 'payload_script' is not 
empty. To allow the agent to run, 'enable_insecure_payload' has to be set to 
'True'".to_string();
 
-        error!("Configuration error: {}", &message);
+        error!("Configuration error: {}", message);
         return Err(Error::Configuration(
             config::KeylimeConfigError::Generic(message),
         ));
@@ -284,7 +396,7 @@
         if let Err(e) = permissions::run_as(user_group) {
             let message = "The user running the Keylime agent should be set in 
keylime-agent.conf, using the parameter `run_as`, with the format 
`user:group`".to_string();
 
-            error!("Configuration error: {}", &message);
+            error!("Configuration error: {}", message);
             return Err(Error::Configuration(
                 config::KeylimeConfigError::Generic(message),
             ));
@@ -298,10 +410,7 @@
         .map(|s| s.to_string())
         .collect::<Vec<_>>();
 
-    info!(
-        "Starting server with API versions: {}",
-        &config.api_versions
-    );
+    info!("Starting server with API versions: {}", config.api_versions);
 
     let mut ctx = tpm::Context::new()?;
 
@@ -500,11 +609,18 @@
     // by the Tenant and Cloud Verifier, respectively.
     // The payload key is always persistent, stored at the configured path.
     let key_path = Path::new(&config.payload_key);
+    ensure_payload_key(
+        key_path,
+        config.payload_key_password.as_ref(),
+        Path::new(&config.server_key),
+        config.server_key_password.as_ref(),
+    )?;
+
     let (payload_pub_key, payload_priv_key) = crypto::load_or_generate_key(
         key_path,
         Some(config.payload_key_password.as_ref()),
         keylime::algorithms::EncryptionAlgorithm::Rsa2048,
-        true, // Validate that loaded keys are RSA 2048
+        false, // Don't validate key size (accept any RSA for backward 
compatibility)
     )
     .map_err(|e| {
         error!(
@@ -517,6 +633,25 @@
         )))
     })?;
 
+    if payload_priv_key.id() != Id::RSA || payload_priv_key.bits() < 2048 {
+        error!(
+            "Payload key {} must be an RSA key of at least 2048 bits, \
+             found {:?} with {} bits",
+            key_path.display(),
+            payload_priv_key.id(),
+            payload_priv_key.bits()
+        );
+        return Err(Error::Configuration(
+            config::KeylimeConfigError::Generic(format!(
+                "Payload key {} must be an RSA key of at least 2048 \
+                 bits, found {:?} with {} bits",
+                key_path.display(),
+                payload_priv_key.id(),
+                payload_priv_key.bits()
+            )),
+        ));
+    }
+
     // Load or generate mTLS key pair (separate from payload keys)
     // The mTLS key is always persistent, stored at the configured path.
     // Uses ECC P-256 by default for better security and performance
@@ -769,6 +904,10 @@
         )
         .default_service(web::to(errors_handler::app_default))
     })
+    // Limit the number of spawned workers, see:
+    // https://github.com/keylime/rust-keylime/issues/1276
+    // for details
+    .workers(4)
     // Disable default signal handlers.  See:
     // https://github.com/actix/actix-web/issues/2739
     // for details.
@@ -1186,4 +1325,201 @@
             Some(config::DEFAULT_EXP_BACKOFF_MAX_DELAY as u64)
         );
     }
+
+    mod ensure_payload_key_tests {
+        use super::*;
+        use openssl::ec::{EcGroup, EcKey};
+        use openssl::nid::Nid;
+        use openssl::pkey::PKey;
+        use openssl::rsa::Rsa;
+        use tempfile::TempDir;
+
+        type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
+
+        fn write_rsa_key(
+            path: &Path,
+            bits: u32,
+            password: &str,
+        ) -> Result<()> {
+            let rsa = Rsa::generate(bits)?;
+            let key = PKey::from_rsa(rsa)?;
+            crypto::write_key_pair(&key, path, Some(password))?;
+            Ok(())
+        }
+
+        fn write_ec_key(path: &Path, password: &str) -> Result<()> {
+            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
+            let ec = EcKey::generate(&group)?;
+            let key = PKey::from_ec_key(ec)?;
+            crypto::write_key_pair(&key, path, Some(password))?;
+            Ok(())
+        }
+
+        #[test]
+        fn copies_rsa2048_server_key_as_payload_key() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 2048, "")?;
+
+            ensure_payload_key(&payload, "", &server, "")?;
+
+            assert!(payload.exists());
+            let (_, loaded) = crypto::load_key_pair(&payload, Some(""))?;
+            assert_eq!(loaded.id(), Id::RSA);
+            assert_eq!(loaded.bits(), 2048);
+            Ok(())
+        }
+
+        #[test]
+        fn copies_rsa4096_server_key_as_payload_key() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 4096, "")?;
+
+            ensure_payload_key(&payload, "", &server, "")?;
+
+            assert!(payload.exists());
+            let (_, loaded) = crypto::load_key_pair(&payload, Some(""))?;
+            assert_eq!(loaded.id(), Id::RSA);
+            assert_eq!(loaded.bits(), 4096);
+            Ok(())
+        }
+
+        #[test]
+        fn skips_when_payload_key_already_exists() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 2048, "")?;
+            write_rsa_key(&payload, 2048, "")?;
+
+            let before = fs::read(&payload)?;
+            ensure_payload_key(&payload, "", &server, "")?;
+            let after = fs::read(&payload)?;
+
+            assert_eq!(before, after);
+            Ok(())
+        }
+
+        #[test]
+        fn skips_when_no_server_key() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            ensure_payload_key(&payload, "", &server, "")?;
+
+            assert!(!payload.exists());
+            Ok(())
+        }
+
+        #[test]
+        fn skips_non_rsa_server_key() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_ec_key(&server, "")?;
+
+            ensure_payload_key(&payload, "", &server, "")?;
+
+            assert!(!payload.exists());
+            Ok(())
+        }
+
+        #[test]
+        fn skips_rsa_key_below_minimum_size() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 1024, "")?;
+
+            ensure_payload_key(&payload, "", &server, "")?;
+
+            assert!(!payload.exists());
+            Ok(())
+        }
+
+        #[test]
+        fn preserves_key_material_on_copy() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 2048, "")?;
+            let (_, original) = crypto::load_key_pair(&server, Some(""))?;
+
+            ensure_payload_key(&payload, "", &server, "")?;
+
+            let (_, copied) = crypto::load_key_pair(&payload, Some(""))?;
+            assert_eq!(
+                original.private_key_to_pem_pkcs8()?,
+                copied.private_key_to_pem_pkcs8()?,
+            );
+            Ok(())
+        }
+
+        #[test]
+        fn handles_password_mismatch_gracefully() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 2048, "server-pass")?;
+
+            ensure_payload_key(&payload, "payload-pass", &server, "wrong")?;
+
+            assert!(!payload.exists());
+            Ok(())
+        }
+
+        #[test]
+        fn copies_with_different_passwords() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            let payload = dir.path().join("payload-private.pem");
+
+            write_rsa_key(&server, 2048, "server-pass")?;
+            let (_, original) =
+                crypto::load_key_pair(&server, Some("server-pass"))?;
+
+            ensure_payload_key(
+                &payload,
+                "payload-pass",
+                &server,
+                "server-pass",
+            )?;
+
+            let (_, copied) =
+                crypto::load_key_pair(&payload, Some("payload-pass"))?;
+            assert_eq!(
+                original.private_key_to_pem_pkcs8()?,
+                copied.private_key_to_pem_pkcs8()?,
+            );
+            Ok(())
+        }
+
+        #[test]
+        fn returns_err_when_write_fails() -> Result<()> {
+            let dir = TempDir::new()?;
+            let server = dir.path().join("server-private.pem");
+            // Nonexistent parent directory forces FSCreateError in 
write_key_pair.
+            let payload = dir
+                .path()
+                .join("nonexistent-subdir")
+                .join("payload-private.pem");
+
+            write_rsa_key(&server, 2048, "")?;
+
+            assert!(ensure_payload_key(&payload, "", &server, "").is_err());
+            assert!(!payload.exists());
+            Ok(())
+        }
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-agent/src/notifications_handler.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/notifications_handler.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/notifications_handler.rs        
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/notifications_handler.rs       
2026-09-25 13:29:29.000000000 +0200
@@ -36,23 +36,22 @@
 }
 
 async fn notifications_default(req: HttpRequest) -> impl Responder {
-    let error;
-    let response;
-    let message;
-
-    match req.head().method {
+    let (error, message, response) = match req.head().method {
         http::Method::POST => {
-            error = 400;
-            message = "URI not supported, only /revocation is supported for 
POST in /notifications/ interface";
-            response = HttpResponse::BadRequest()
+            let error = 400;
+            let message = "URI not supported, only /revocation is supported 
for POST in /notifications/ interface";
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         _ => {
-            error = 405;
-            message = "Method is not supported in /notifications/ interface";
-            response = HttpResponse::MethodNotAllowed()
+            let error = 405;
+            let message =
+                "Method is not supported in /notifications/ interface";
+            let response = HttpResponse::MethodNotAllowed()
                 .insert_header(http::header::Allow(vec![http::Method::POST]))
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
     };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rust-keylime-0.2.9+49/keylime-agent/src/payloads.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/payloads.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/payloads.rs     2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/payloads.rs    2026-09-25 
13:29:29.000000000 +0200
@@ -143,7 +143,7 @@
     {
         return Err(Error::Other(format!(
             "unable to set {:?} as executable",
-            &script_path
+            script_path
         )));
     }
 
@@ -159,12 +159,12 @@
         .status()
     {
         Ok(_) => {
-            info!("{:?} ran successfully", &script_path);
+            info!("{:?} ran successfully", script_path);
             Ok(())
         }
         Err(e) => Err(Error::Other(format!(
             "{:?} failed during run: {}",
-            &script_path, e
+            script_path, e
         ))),
     }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-agent/src/quotes_handler.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/quotes_handler.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/quotes_handler.rs       
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/quotes_handler.rs      
2026-09-25 13:29:29.000000000 +0200
@@ -330,23 +330,21 @@
 
 /// Handles the default case for the /quotes scope
 async fn quotes_default(req: HttpRequest) -> impl Responder {
-    let error;
-    let response;
-    let message;
-
-    match req.head().method {
+    let (error, message, response) = match req.head().method {
         http::Method::GET => {
-            error = 400;
-            message = "URI not supported, only /identity and /integrity are 
supported for GET in /quotes/ interface";
-            response = HttpResponse::BadRequest()
+            let error = 400;
+            let message = "URI not supported, only /identity and /integrity 
are supported for GET in /quotes/ interface";
+            let response = HttpResponse::BadRequest()
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
         _ => {
-            error = 405;
-            message = "Method is not supported in /quotes/ interface";
-            response = HttpResponse::MethodNotAllowed()
+            let error = 405;
+            let message = "Method is not supported in /quotes/ interface";
+            let response = HttpResponse::MethodNotAllowed()
                 .insert_header(http::header::Allow(vec![http::Method::GET]))
                 .json(JsonWrapper::error(error, message));
+            (error, message, response)
         }
     };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-agent/src/revocation.rs 
new/rust-keylime-0.2.10+12/keylime-agent/src/revocation.rs
--- old/rust-keylime-0.2.9+49/keylime-agent/src/revocation.rs   2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-agent/src/revocation.rs  2026-09-25 
13:29:29.000000000 +0200
@@ -64,7 +64,7 @@
     if !py_action.set_extension("py") {
         return Err(Error::Other(format!(
             "unable to set action {} extension",
-            &action
+            action
         )));
     }
 
@@ -655,7 +655,7 @@
             if #[cfg(feature = "legacy-python-actions")] {
                 // Test local python action
                 let expected =
-                    format!("{}", &actions_dir.join("shim.py").display(),);
+                    format!("{}", actions_dir.join("shim.py").display(),);
 
                 assert_eq!(
                     lookup_action(
@@ -673,7 +673,7 @@
         // Test local non-python action
         let expected = format!(
             "{}",
-            &actions_dir.join("local_action_hello_shell.sh").display()
+            actions_dir.join("local_action_hello_shell.sh").display()
         );
 
         assert_eq!(
@@ -691,7 +691,7 @@
             if #[cfg(feature = "legacy-python-actions")] {
                 // Test payload python action
                 let expected =
-                    format!("{}", &actions_dir.join("shim.py").display(),);
+                    format!("{}", actions_dir.join("shim.py").display(),);
 
                 assert_eq!(
                     lookup_action(
@@ -709,7 +709,7 @@
         // Test payload non-python action
         let expected = format!(
             "{}",
-            &payload_dir.join("local_action_payload_shell.sh").display()
+            payload_dir.join("local_action_payload_shell.sh").display()
         );
 
         assert_eq!(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-ima-emulator/src/main.rs 
new/rust-keylime-0.2.10+12/keylime-ima-emulator/src/main.rs
--- old/rust-keylime-0.2.9+49/keylime-ima-emulator/src/main.rs  2026-07-03 
13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-ima-emulator/src/main.rs 2026-09-25 
13:29:29.000000000 +0200
@@ -59,15 +59,20 @@
     ima_hash_alg: HashAlgorithm,
     pcr_hash_alg: HashAlgorithm,
     search_hash: Option<&Digest>,
+    shutdown: &AtomicBool,
 ) -> Result<usize> {
     let f = File::open(ml)?;
     let mut reader = BufReader::new(f);
-    let ima_digest: MessageDigest = ima_hash_alg.into();
-    let ima_start_hash = ima::Digest::start(ima_hash_alg);
-    let pcr_digest: MessageDigest = pcr_hash_alg.into();
-    let mut running_hash = ima::Digest::start(pcr_hash_alg);
-    let ff_hash = ima::Digest::ff(pcr_hash_alg);
+    let ima_digest: MessageDigest = MessageDigest::try_from(ima_hash_alg)?;
+    let ima_start_hash = ima::Digest::start(ima_hash_alg)?;
+    let pcr_digest: MessageDigest = MessageDigest::try_from(pcr_hash_alg)?;
+    let mut running_hash = ima::Digest::start(pcr_hash_alg)?;
+    let ff_hash = ima::Digest::ff(pcr_hash_alg)?;
     for line in reader.by_ref().lines().skip(position) {
+        if shutdown.load(Ordering::SeqCst) {
+            println!("Shutdown requested, interrupting IMA measurement list 
replay");
+            break;
+        }
         let line = line?;
         if line.is_empty() {
             continue;
@@ -129,7 +134,7 @@
         }
     }
 
-    if search_hash.is_some() {
+    if search_hash.is_some() && !shutdown.load(Ordering::SeqCst) {
         return Err(ImaEmulatorError::Other(
             "Unable to find current measurement list position, Resetting the 
TPM emulator may be neccesary".to_string()));
     }
@@ -176,6 +181,10 @@
         positions.insert(pcr_hash_alg, 0usize);
     }
 
+    let shutdown_marker = Arc::new(AtomicBool::new(false));
+    signal_hook::flag::register(SIGINT, Arc::clone(&shutdown_marker))?;
+    signal_hook::flag::register(SIGTERM, Arc::clone(&shutdown_marker))?;
+
     for (pcr_hash_alg, position) in positions.iter_mut() {
         // check if pcr is clean
         let pcr_list = PcrSelectionListBuilder::new()
@@ -198,7 +207,8 @@
                 )
             })?;
 
-        let pcr_digest: MessageDigest = (*pcr_hash_alg).into();
+        let pcr_digest: MessageDigest =
+            MessageDigest::try_from(*pcr_hash_alg)?;
         let pcr_start_hash = vec![0x00u8; pcr_digest.size()];
         if digest.value() != pcr_start_hash {
             log::warn!("IMA PCR is not empty, trying to find the last updated 
file in the measurement list...");
@@ -209,13 +219,11 @@
                 ima_hash_alg,
                 *pcr_hash_alg,
                 Some(digest),
+                &shutdown_marker,
             )?;
         }
     }
 
-    let shutdown_marker = Arc::new(AtomicBool::new(false));
-    signal_hook::flag::register(SIGINT, Arc::clone(&shutdown_marker))?;
-    signal_hook::flag::register(SIGTERM, Arc::clone(&shutdown_marker))?;
     println!("Monitoring {}", args.ima_log.display());
     while !shutdown_marker.load(Ordering::SeqCst) {
         for (pcr_hash_alg, position) in positions.iter_mut() {
@@ -226,6 +234,7 @@
                 ima_hash_alg,
                 *pcr_hash_alg,
                 None,
+                &shutdown_marker,
                 ).expect("Error extending position {position} on PCR bank 
{pcr_hash_alg}");
         }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/keylime-push-model-agent/src/main.rs 
new/rust-keylime-0.2.10+12/keylime-push-model-agent/src/main.rs
--- old/rust-keylime-0.2.9+49/keylime-push-model-agent/src/main.rs      
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/keylime-push-model-agent/src/main.rs     
2026-09-25 13:29:29.000000000 +0200
@@ -192,6 +192,7 @@
             }
         }
     };
+    info!("Agent UUID: {agent_identifier}");
     let verifier_url = match args.verifier_url {
         Some(ref url) => url.clone(),
         _ => config.verifier_url().to_string(),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/rpm/centos/keylime-agent-rust.spec 
new/rust-keylime-0.2.10+12/rpm/centos/keylime-agent-rust.spec
--- old/rust-keylime-0.2.9+49/rpm/centos/keylime-agent-rust.spec        
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/rpm/centos/keylime-agent-rust.spec       
2026-09-25 13:29:29.000000000 +0200
@@ -9,7 +9,7 @@
 %global __brp_mangle_shebangs_exclude_from ^/usr/src/debug/.*$
 
 Name:           keylime-agent-rust
-Version:        0.2.9
+Version:        0.2.10
 Release:        %{?autorelease}%{!?autorelease:1%{?dist}}
 Summary:        Rust agent for Keylime
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/rust-keylime-0.2.9+49/rpm/fedora/keylime-agent-rust.spec 
new/rust-keylime-0.2.10+12/rpm/fedora/keylime-agent-rust.spec
--- old/rust-keylime-0.2.9+49/rpm/fedora/keylime-agent-rust.spec        
2026-07-03 13:52:37.000000000 +0200
+++ new/rust-keylime-0.2.10+12/rpm/fedora/keylime-agent-rust.spec       
2026-09-25 13:29:29.000000000 +0200
@@ -10,7 +10,7 @@
 %global __brp_mangle_shebangs_exclude_from ^/usr/src/debug/.*$
 
 Name:           keylime-agent-rust
-Version:        0.2.9
+Version:        0.2.10
 Release:        %{?autorelease}%{!?autorelease:1%{?dist}}
 Summary:        Rust agent for Keylime
 

++++++ rust-keylime.obsinfo ++++++
--- /var/tmp/diff_new_pack.i4q2m4/_old  2026-10-28 17:44:52.630878131 +0100
+++ /var/tmp/diff_new_pack.i4q2m4/_new  2026-10-28 17:44:52.638878463 +0100
@@ -1,5 +1,5 @@
 name: rust-keylime
-version: 0.2.9+49
-mtime: 1783079557
-commit: 28a2e1dec001b29ea417ea152bfbd9a8b06f6e14
+version: 0.2.10+12
+mtime: 1790335769
+commit: 54c62facf656a499a21809486c300d52572444d7
 

++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/rust-keylime/vendor.tar.zst 
/work/SRC/openSUSE:Factory/.rust-keylime.new.2252680/vendor.tar.zst differ: 
char 1, line 1

Reply via email to