public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Nicolas Frey <n.frey@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox v8 2/5] proxmox-pgp: add unit tests for {de, at}tached signatures
Date: Wed, 14 Jan 2026 10:35:59 +0100	[thread overview]
Message-ID: <20260114093602.33057-3-n.frey@proxmox.com> (raw)
In-Reply-To: <20260114093602.33057-1-n.frey@proxmox.com>

test `verify_signature` using `sequoia_openpgp` to create Certs, these cover:
* detached signatures
* attached signatures
* cryptographically weak (SHA1-signed) signatures

Suggested-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Nicolas Frey <n.frey@proxmox.com>
---
New in v8

 proxmox-pgp/src/verifier.rs | 192 ++++++++++++++++++++++++++++++++++++
 1 file changed, 192 insertions(+)

diff --git a/proxmox-pgp/src/verifier.rs b/proxmox-pgp/src/verifier.rs
index c2eadbc5..9beadea0 100644
--- a/proxmox-pgp/src/verifier.rs
+++ b/proxmox-pgp/src/verifier.rs
@@ -192,3 +192,195 @@ pub fn verify_signature(
     // neither a keyring nor a certificate was detect, so we abort here
     bail!("'key-path' contains neither a keyring nor a certificate, aborting!");
 }
+
+#[cfg(test)]
+mod tests {
+    use super::{verify_signature, WeakCryptoConfig};
+    use anyhow::Result;
+    use sequoia_openpgp::packet::prelude::SignatureBuilder;
+    use sequoia_openpgp::packet::signature::subpacket::NotationDataFlags;
+    use sequoia_openpgp::serialize::MarshalInto;
+    use sequoia_openpgp::types::{HashAlgorithm, SignatureType};
+    use sequoia_openpgp::{cert::prelude::*, policy::StandardPolicy, serialize::stream::*};
+    use std::io::Write;
+
+    const MESSAGE: &[u8] = b"Hello, pgp!";
+
+    fn setup(
+        name: &str,
+        mail: &str,
+        hash: Option<HashAlgorithm>,
+        detached: bool,
+    ) -> Result<(Cert, Vec<u8>)> {
+        let mut policy = StandardPolicy::new();
+
+        if let Some(h) = hash {
+            policy.accept_hash(h);
+        }
+
+        let (cert, _sig) =
+            CertBuilder::general_purpose(Some(format!("{name} <{mail}>"))).generate()?;
+
+        let keypair = cert
+            .keys()
+            .secret()
+            .with_policy(&policy, None)
+            .supported()
+            .alive()
+            .revoked(false)
+            .for_signing()
+            .next()
+            .unwrap()
+            .key()
+            .clone()
+            .into_keypair()?;
+
+        let mut sink = Vec::new();
+
+        {
+            let message = Signer::with_template(
+                Message::new(&mut sink),
+                keypair,
+                SignatureBuilder::new(SignatureType::Text)
+                    .add_notation(
+                        mail,
+                        name,
+                        NotationDataFlags::empty().set_human_readable(),
+                        false,
+                    )?
+                    .set_hash_algo(hash.unwrap_or(HashAlgorithm::SHA256)),
+            )?
+            .hash_algo(hash.unwrap_or(HashAlgorithm::SHA256))?;
+
+            if detached {
+                let mut message = message.detached().build()?;
+                message.write_all(MESSAGE)?;
+                message.finalize()?;
+            } else {
+                let mut message = LiteralWriter::new(message.build()?).build()?;
+                message.write_all(MESSAGE)?;
+                message.finalize()?;
+            }
+        }
+
+        Ok((cert, sink))
+    }
+
+    fn root_cause_no_valid_sig(err: anyhow::Error) -> bool {
+        err.root_cause()
+            .to_string()
+            .contains("No valid signature found.")
+    }
+
+    #[test]
+    fn verify_attached_signature_success() -> Result<()> {
+        // using same signature will work
+        {
+            let (cert, sink) = setup("Nicolas Frey", "n.frey@proxmox.com", None, false)?;
+            let verified =
+                verify_signature(&sink, &cert.to_vec()?, None, &WeakCryptoConfig::default())?;
+
+            assert_eq!(verified, MESSAGE);
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn verify_attached_signature_fail() -> Result<()> {
+        // using different signatures will fail
+        {
+            let (cert1, sink1) = setup("Nicolas Frey", "n.frey@proxmox.com", None, false)?;
+            let (cert2, sink2) = setup("Proxmox Support Team", "support@proxmox.com", None, false)?;
+
+            assert!(
+                verify_signature(&sink1, &cert2.to_vec()?, None, &WeakCryptoConfig::default())
+                    .is_err_and(root_cause_no_valid_sig)
+            );
+            assert!(
+                verify_signature(&sink2, &cert1.to_vec()?, None, &WeakCryptoConfig::default())
+                    .is_err_and(root_cause_no_valid_sig)
+            );
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn verify_detached_signature_success() -> Result<()> {
+        // using same signature will work
+        {
+            let (cert, sink) = setup("Nicolas Frey", "n.frey@proxmox.com", None, true)?;
+            let verified = verify_signature(
+                MESSAGE,
+                &cert.to_vec()?,
+                Some(&sink),
+                &WeakCryptoConfig::default(),
+            )?;
+            assert_eq!(verified, MESSAGE);
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn verify_detached_signature_fail() -> Result<()> {
+        // using different signatures will fail
+        {
+            let (cert1, sink1) = setup("Nicolas Frey", "n.frey@proxmox.com", None, true)?;
+            let (cert2, sink2) = setup("Proxmox Support Team", "support@proxmox.com", None, true)?;
+
+            assert!(verify_signature(
+                MESSAGE,
+                &cert2.to_vec()?,
+                Some(&sink1),
+                &WeakCryptoConfig::default()
+            )
+            .is_err_and(root_cause_no_valid_sig));
+
+            assert!(verify_signature(
+                MESSAGE,
+                &cert1.to_vec()?,
+                Some(&sink2),
+                &WeakCryptoConfig::default()
+            )
+            .is_err_and(root_cause_no_valid_sig));
+        }
+
+        Ok(())
+    }
+
+    #[test]
+    fn weak_crypto_config_allow_sha1() -> Result<()> {
+        let (cert, sink) = setup(
+            "Nicolas Frey",
+            "n.frey@proxmox.com",
+            Some(HashAlgorithm::SHA1),
+            false,
+        )?;
+
+        // allowing sha1 will make the policy accept this signature
+        {
+            let verified = verify_signature(
+                &sink,
+                &cert.to_vec()?,
+                None,
+                &WeakCryptoConfig {
+                    allow_sha1: true,
+                    ..Default::default()
+                },
+            )?;
+            assert_eq!(verified, MESSAGE);
+        }
+
+        // while this will fail
+        {
+            assert!(
+                verify_signature(&sink, &cert.to_vec()?, None, &WeakCryptoConfig::default())
+                    .is_err_and(root_cause_no_valid_sig)
+            );
+        }
+
+        Ok(())
+    }
+}
-- 
2.47.3


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


  parent reply	other threads:[~2026-01-14  9:36 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-14  9:35 [pve-devel] [PATCH proxmox v8 0/5] fix #5207: apt: check signature of repos with proxmox-pgp Nicolas Frey
2026-01-14  9:35 ` [pve-devel] [PATCH proxmox v8 1/5] add proxmox-pgp subcrate, move POM verifier code to it Nicolas Frey
2026-01-14  9:35 ` Nicolas Frey [this message]
2026-01-14  9:36 ` [pve-devel] [PATCH proxmox v8 3/5] fix #5207: apt: check signature of repos with proxmox-pgp Nicolas Frey
2026-01-14  9:36 ` [pve-devel] [PATCH proxmox v8 4/5] apt: add tests for POM release filenames Nicolas Frey
2026-01-14  9:36 ` [pve-devel] [PATCH proxmox v8 5/5] apt: check for local POM InRelease as fallback Nicolas Frey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260114093602.33057-3-n.frey@proxmox.com \
    --to=n.frey@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal