public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Filip Schauer <f.schauer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH proxmox 1/3] oci: add a digest helper module
Date: Tue,  3 Nov 2026 16:11:44 +0100	[thread overview]
Message-ID: <20261103151153.290754-2-f.schauer@proxmox.com> (raw)
In-Reply-To: <20261103151153.290754-1-f.schauer@proxmox.com>

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 proxmox-oci/src/digest.rs | 94 +++++++++++++++++++++++++++++++++++++++
 proxmox-oci/src/lib.rs    |  2 +
 2 files changed, 96 insertions(+)
 create mode 100644 proxmox-oci/src/digest.rs

diff --git a/proxmox-oci/src/digest.rs b/proxmox-oci/src/digest.rs
new file mode 100644
index 00000000..b2170b87
--- /dev/null
+++ b/proxmox-oci/src/digest.rs
@@ -0,0 +1,94 @@
+use std::io::{self, Read};
+use std::str::FromStr;
+
+use oci_spec::image::{Digest, DigestAlgorithm};
+use sha2::Digest as _;
+use sha2::digest::DynDigest;
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum DigestError {
+    #[error("Unsupported digest algorithm: {0}")]
+    UnsupportedAlgorithm(String),
+    #[error("Digest mismatch. Expected {expected}, got {actual}")]
+    Mismatch { expected: Digest, actual: Digest },
+}
+
+struct Digester(Box<dyn DynDigest>);
+
+impl Digester {
+    fn new(digest_algorithm: &DigestAlgorithm) -> Result<Self, DigestError> {
+        let hasher: Box<dyn DynDigest> = match digest_algorithm {
+            DigestAlgorithm::Sha256 => Box::new(sha2::Sha256::new()),
+            DigestAlgorithm::Sha384 => Box::new(sha2::Sha384::new()),
+            DigestAlgorithm::Sha512 => Box::new(sha2::Sha512::new()),
+            other => return Err(DigestError::UnsupportedAlgorithm(other.to_string())),
+        };
+        Ok(Self(hasher))
+    }
+
+    fn update(&mut self, data: &[u8]) {
+        self.0.update(data);
+    }
+
+    fn finalize(&mut self) -> Digest {
+        let sha_bit_count = self.0.output_size() * 8;
+        let digest_str = self
+            .0
+            .finalize_reset()
+            .iter()
+            .map(|b| format!("{b:02x}"))
+            .collect::<String>();
+        Digest::from_str(&format!("sha{sha_bit_count}:{digest_str}")).expect("valid digest")
+    }
+}
+
+pub struct VerifyingReader<R: Read> {
+    inner: R,
+    digester: Digester,
+    expected_digest: Digest,
+    eof: bool,
+}
+
+impl<R: Read> VerifyingReader<R> {
+    pub fn new(inner: R, expected_digest: Digest) -> Result<Self, DigestError> {
+        Ok(Self {
+            inner,
+            digester: Digester::new(expected_digest.algorithm())?,
+            expected_digest,
+            eof: false,
+        })
+    }
+
+    fn verify(&mut self) -> io::Result<()> {
+        let actual = self.digester.finalize();
+
+        if actual != self.expected_digest {
+            return Err(io::Error::other(DigestError::Mismatch {
+                expected: self.expected_digest.clone(),
+                actual,
+            }));
+        }
+
+        Ok(())
+    }
+}
+
+impl<R: Read> Read for VerifyingReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        if buf.is_empty() {
+            return Ok(0);
+        }
+
+        let n = self.inner.read(buf)?;
+        if n == 0 {
+            if !self.eof {
+                self.eof = true;
+                self.verify()?;
+            }
+            return Ok(0);
+        }
+        self.digester.update(&buf[..n]);
+        Ok(n)
+    }
+}
diff --git a/proxmox-oci/src/lib.rs b/proxmox-oci/src/lib.rs
index 78045f49..71970cd8 100644
--- a/proxmox-oci/src/lib.rs
+++ b/proxmox-oci/src/lib.rs
@@ -15,6 +15,8 @@ use sha2::{Digest, Sha256};
 use tar::{Archive, EntryType};
 use thiserror::Error;
 
+mod digest;
+
 mod oci_tar_image;
 use oci_tar_image::{OciTarImage, OciTarImageBlob};
 
-- 
2.47.3





  reply	other threads:[~2026-11-03 15:13 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 15:11 [PATCH proxmox 0/3] oci: refactor code Filip Schauer
2026-11-03 15:11 ` Filip Schauer [this message]
2026-11-03 15:11 ` [PATCH proxmox 2/3] oci: reduce redundant layer decompression during extraction Filip Schauer
2026-11-03 15:11 ` [PATCH proxmox 3/3] oci: factor out parsing and extraction into modules Filip Schauer

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=20261103151153.290754-2-f.schauer@proxmox.com \
    --to=f.schauer@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