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
next prev parent 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