From 75c832c404d8e0256cf2ab018e24e65835c04803 Mon Sep 17 00:00:00 2001 From: Bernhard Schuster Date: Sat, 15 Apr 2023 16:05:23 +0200 Subject: [PATCH 1/4] revert: remove inconsistent use of outcome enums, add another error variant instead --- CHANGELOG.md | 2 ++ src/errors.rs | 3 +++ src/rpm/builder.rs | 4 ++-- src/rpm/package.rs | 29 +++++++++++------------------ 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2312c6b5..26f12680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `get_entry_data_as_u16_array()`, `get_entry_data_as_u32()`, `get_entry_data_as_u32_array()`, `get_entry_data_as_u64()`, `get_entry_data_as_u64_array()`, `get_entry_data_as_string_array()`, `get_entry_data_as_i18n_string()` +- Added `verify_signature()` and `verify_digests()` to `RPMPackage` to enable checking the integrity + and provenance of packages. ### Fixed diff --git a/src/errors.rs b/src/errors.rs index 0088a346..b5984618 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -73,6 +73,9 @@ pub enum RPMError { key_ref: String, }, + #[error("digests from content did not match those in the header")] + DigestMismatchError, + #[error("unable to find key with key-ref: {key_ref}")] KeyNotFoundError { key_ref: String }, diff --git a/src/rpm/builder.rs b/src/rpm/builder.rs index f46caebe..66fdb273 100644 --- a/src/rpm/builder.rs +++ b/src/rpm/builder.rs @@ -501,7 +501,7 @@ impl RPMBuilder { let Digests { header_and_content_digest: header_and_content_digest_md5, header_digest: header_digest_sha1, - } = RPMPackage::create_digests_from_readers( + } = RPMPackage::create_legacy_header_digests( &mut header.as_slice(), header_and_content_cursor, )?; @@ -546,7 +546,7 @@ impl RPMBuilder { let Digests { header_and_content_digest: header_and_content_digest_md5, header_digest: header_digest_sha1, - } = RPMPackage::create_digests_from_readers( + } = RPMPackage::create_legacy_header_digests( &mut header.as_slice(), header_and_content_cursor, )?; diff --git a/src/rpm/package.rs b/src/rpm/package.rs index 6d1d72eb..45617d3d 100644 --- a/src/rpm/package.rs +++ b/src/rpm/package.rs @@ -20,16 +20,6 @@ use crate::sequential_cursor::SeqCursor; #[cfg(feature = "signature-meta")] use crate::signature; -pub enum SignatureVerificationOutcome { - Pass, - Failure, -} - -pub enum DigestVerificationOutcome { - Match, - Mismatch, -} - /// Combined digest of signature header tags `RPMSIGTAG_MD5` and `RPMSIGTAG_SHA1` /// /// Succinct to cover to "verify" the content of the rpm file. Quotes because @@ -111,7 +101,7 @@ impl RPMPackage { } /// Prepare both header and content digests as used by the `SignatureIndex`. - pub(crate) fn create_digests_from_readers( + pub(crate) fn create_legacy_header_digests( header_cursor: HC, header_and_content_cursor: HACC, ) -> Result @@ -160,7 +150,7 @@ impl RPMPackage { let Digests { header_digest, header_and_content_digest, - } = Self::create_digests_from_readers( + } = Self::create_legacy_header_digests( &mut header_bytes.as_slice(), &mut header_and_content_cursor, )?; @@ -187,11 +177,13 @@ impl RPMPackage { Ok(()) } + // @todo: function that returns the key ID of the key used to sign this package + /// Verify the signature as present within the RPM package. /// /// #[cfg(feature = "signature-meta")] - pub fn verify_signature(&self, verifier: V) -> Result + pub fn verify_signature(&self, verifier: V) -> Result<(), RPMError> where V: signature::Verifying>, { @@ -222,24 +214,25 @@ impl RPMPackage { verifier.verify(header_and_content_cursor, signature_header_and_content)?; - Ok(SignatureVerificationOutcome::Pass) + Ok(()) } - pub fn verify_digest(&self) -> Result { + pub fn verify_digest(&self) -> Result<(), RPMError> { let mut header = Vec::::with_capacity(1024); // make sure to not hash any previous signatures in the header self.metadata.header.write(&mut header)?; - let recreated_from_content = Self::create_digests_from_readers( + let recreated_from_content = Self::create_legacy_header_digests( &mut header.as_slice(), SeqCursor::new(&[header.as_slice(), self.content.as_slice()]), )?; let package_contained = self.metadata.get_digests()?; + if recreated_from_content == package_contained { - Ok(DigestVerificationOutcome::Match) + Ok(()) } else { - Ok(DigestVerificationOutcome::Mismatch) + Err(RPMError::DigestMismatchError) } } } From dceecea175106f8d3112e4deafea7a5a13489d8a Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Wed, 22 Mar 2023 19:33:25 -0400 Subject: [PATCH 2/4] Generate PAYLOADDIGEST, PAYLOADDIGESTALT, PAYLOADDIGESTALGO These are checksums of the compressed payload, uncompressed payload, and the algorithm used respectively. --- CHANGELOG.md | 1 + Cargo.toml | 3 +-- src/rpm/builder.rs | 46 ++++++++++++++++++++++++++++++++++++++++------ src/rpm/package.rs | 45 ++++++++++++++++++++++++++++++++++++--------- src/tests.rs | 4 ++++ 5 files changed, 82 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f12680..e9021ea1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 did not have any files associated. - Ensured that digests are always added to built RPMs. Previously they would not be included unless the "signature-meta" (or "signature-pgp") features were enabled. +- Added `PAYLOADDIGEST`, `PAYLOADDIGESTALT`, and `PAYLOADDIGESTALGO` tags to built packages. ### Breaking Changes diff --git a/Cargo.toml b/Cargo.toml index e72fd21e..ee9fca74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ enum-display-derive = "0.1" cpio = "0.2" # consider migrating to flate2 libflate = "1" +digest = "0.10" sha2 = "0.10" md-5 = "0.10" sha1 = "0.10" @@ -60,8 +61,6 @@ async-std = { version = "1.12.0", optional = true } tokio = { version = "1", optional = true } tokio-util = { version = "0.7.4", features = ["compat"], optional = true } -digest = "0.10.6" - [dev-dependencies] rsa = { version = "0.8" } rsa-der = { version = "^0.3.0" } diff --git a/src/rpm/builder.rs b/src/rpm/builder.rs index 66fdb273..79b0b9dd 100644 --- a/src/rpm/builder.rs +++ b/src/rpm/builder.rs @@ -8,6 +8,7 @@ use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::time::{SystemTime, UNIX_EPOCH}; +use digest::Digest; use gethostname::gethostname; use super::compressor::Compressor; @@ -328,8 +329,6 @@ impl RPMBuilder { modified_at: u32, options: RPMFileOptions, ) -> Result<(), RPMError> { - use sha2::Digest; - let dest = options.destination; if !dest.starts_with("./") && !dest.starts_with('/') { return Err(RPMError::InvalidDestinationPath { @@ -592,6 +591,7 @@ impl RPMBuilder { // Lead is not important. just build it here let lead = Lead::new(&self.name); + let mut archive: Vec = Vec::new(); let mut ino_index = 1; @@ -645,13 +645,14 @@ impl RPMBuilder { .ino(ino_index) .uid(self.uid.unwrap_or(0)) .gid(self.gid.unwrap_or(0)) - .write(&mut self.compressor, content.len() as u32); + .write(&mut archive, content.len() as u32); writer.write_all(&content)?; writer.finish()?; ino_index += 1; } + cpio::newc::trailer(&mut archive)?; self.provides .push(Dependency::eq(self.name.clone(), self.version.clone())); @@ -962,6 +963,41 @@ impl RPMBuilder { let possible_compression_details = self.compressor.get_details(); + self.compressor.write_all(&archive)?; + let payload = self.compressor.finish_compression()?; + + // digest of the uncompressed raw archive + let raw_payload_digest_sha256 = { + let mut hasher = sha2::Sha256::default(); + hasher.update(archive.as_slice()); + hex::encode(hasher.finalize()) + }; + + // digest of the compressed archive (payload) + let payload_digest_sha256 = { + let mut hasher = sha2::Sha256::default(); + hasher.update(payload.as_slice()); + hex::encode(hasher.finalize()) + }; + + actual_records.extend([ + IndexEntry::new( + IndexTag::RPMTAG_PAYLOADDIGEST, + offset, + IndexData::StringArray(vec![payload_digest_sha256]), + ), + IndexEntry::new( + IndexTag::RPMTAG_PAYLOADDIGESTALGO, + offset, + IndexData::Int32(vec![FileDigestAlgorithm::Sha2_256 as u32]), + ), + IndexEntry::new( + IndexTag::RPMTAG_PAYLOADDIGESTALT, + offset, + IndexData::StringArray(vec![raw_payload_digest_sha256]), + ), + ]); + if let Some(details) = possible_compression_details { actual_records.push(IndexEntry::new( IndexTag::RPMTAG_PAYLOADCOMPRESSOR, @@ -1184,9 +1220,7 @@ impl RPMBuilder { } let header = Header::from_entries(actual_records, IndexTag::RPMTAG_HEADERIMMUTABLE); - self.compressor = cpio::newc::trailer(self.compressor)?; - let content = self.compressor.finish_compression()?; - Ok((lead, header, content)) + Ok((lead, header, payload)) } } diff --git a/src/rpm/package.rs b/src/rpm/package.rs index 45617d3d..cb004e16 100644 --- a/src/rpm/package.rs +++ b/src/rpm/package.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use chrono::offset::TimeZone; +use digest::Digest; #[cfg(feature = "async-futures")] use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use num_traits::FromPrimitive; @@ -100,6 +101,7 @@ impl RPMPackage { } } + // @todo: delete when EL7 goes EOL, PAYLOADDIGEST has been available for years now. /// Prepare both header and content digests as used by the `SignatureIndex`. pub(crate) fn create_legacy_header_digests( header_cursor: HC, @@ -110,7 +112,6 @@ impl RPMPackage { HACC: std::io::Read, { let digest_md5 = { - use md5::Digest; let mut hasher = md5::Md5::default(); Self::read_and_update_digest_256(&mut hasher, header_and_content_cursor); let hash_result = hasher.finalize(); @@ -118,7 +119,6 @@ impl RPMPackage { }; let digest_sha1 = { - use sha1::Digest; let mut hasher = sha1::Sha1::default(); Self::read_and_update_digest_256(&mut hasher, header_cursor); let digest = hasher.finalize(); @@ -208,6 +208,7 @@ impl RPMPackage { ); verifier.verify(header_bytes.as_slice(), signature_header_only)?; + self.verify_digests()?; let header_and_content_cursor = SeqCursor::new(&[header_bytes.as_slice(), self.content.as_slice()]); @@ -217,7 +218,7 @@ impl RPMPackage { Ok(()) } - pub fn verify_digest(&self) -> Result<(), RPMError> { + pub fn verify_digests(&self) -> Result<(), RPMError> { let mut header = Vec::::with_capacity(1024); // make sure to not hash any previous signatures in the header self.metadata.header.write(&mut header)?; @@ -227,13 +228,39 @@ impl RPMPackage { SeqCursor::new(&[header.as_slice(), self.content.as_slice()]), )?; - let package_contained = self.metadata.get_digests()?; + let sig_header_digests = self.metadata.get_digests()?; - if recreated_from_content == package_contained { - Ok(()) - } else { - Err(RPMError::DigestMismatchError) + let payload_digest_val = self + .metadata + .header + .get_entry_data_as_string_array(IndexTag::RPMTAG_PAYLOADDIGEST); + let payload_digest_algo = self + .metadata + .header + .get_entry_data_as_u32(IndexTag::RPMTAG_PAYLOADDIGESTALGO); + + if let (Ok(payload_digest_val), Ok(payload_digest_algo)) = + (payload_digest_val, payload_digest_algo) + { + let mut hasher = match payload_digest_algo { + a if a == FileDigestAlgorithm::Sha2_256 as u32 => sha2::Sha256::default(), + _ => unimplemented!("An unknown payload digest algorithm was used"), + // At the present moment even rpmbuild only supports sha256 + }; + let payload_digest = { + hasher.update(self.content.as_slice()); + hex::encode(hasher.finalize()) + }; + if payload_digest != payload_digest_val[0] { + return Err(RPMError::DigestMismatchError); + } + } + + if recreated_from_content != sig_header_digests { + return Err(RPMError::DigestMismatchError); } + + Ok(()) } } @@ -287,7 +314,7 @@ impl RPMPackageMetadata { Ok(()) } - pub fn get_digests(&self) -> Result { + fn get_digests(&self) -> Result { let md5 = self .signature .get_entry_data_as_binary(IndexSignatureTag::RPMSIGTAG_MD5)?; diff --git a/src/tests.rs b/src/tests.rs index e1fe48dd..0ec0ddef 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -284,6 +284,8 @@ fn test_rpm_header_base(package: RPMPackage) -> Result<(), Box Result<(), Box> { pkg.write(&mut buff)?; + pkg.verify_digests()?; + Ok(()) } From 0af74e9752efb006a4d0782d03d92b950b5747d6 Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Sun, 16 Apr 2023 19:08:33 -0400 Subject: [PATCH 3/4] Streaming digest calculation of raw archive --- src/rpm/builder.rs | 26 ++++++++++++-------------- src/rpm/headers/types.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/rpm/builder.rs b/src/rpm/builder.rs index 79b0b9dd..8638f5db 100644 --- a/src/rpm/builder.rs +++ b/src/rpm/builder.rs @@ -591,7 +591,13 @@ impl RPMBuilder { // Lead is not important. just build it here let lead = Lead::new(&self.name); - let mut archive: Vec = Vec::new(); + let possible_compression_details = self.compressor.get_details(); + + // Make the borrow checker happy + let is_zstd = matches!(self.compressor, Compressor::Zstd(_)); + // Calculate the sha256 of the archive as we write it into the compressor, so that we don't + // need to keep two copies in memory simultaneously. + let mut archive = Sha256Writer::new(&mut self.compressor); let mut ino_index = 1; @@ -676,7 +682,7 @@ impl RPMBuilder { "4.0-1".to_string(), )); - if matches!(self.compressor, Compressor::Zstd(_)) { + if is_zstd { self.requires.push(Dependency::rpmlib( "rpmlib(PayloadIsZstd)".to_string(), "5.4.18-1".to_string(), @@ -961,19 +967,11 @@ impl RPMBuilder { ), ]); - let possible_compression_details = self.compressor.get_details(); - - self.compressor.write_all(&archive)?; + // digest of the uncompressed raw archive calculated on the inner writer + let raw_archive_digest_sha256 = hex::encode(archive.into_digest()); let payload = self.compressor.finish_compression()?; - // digest of the uncompressed raw archive - let raw_payload_digest_sha256 = { - let mut hasher = sha2::Sha256::default(); - hasher.update(archive.as_slice()); - hex::encode(hasher.finalize()) - }; - - // digest of the compressed archive (payload) + // digest of the post-compression archive (payload) let payload_digest_sha256 = { let mut hasher = sha2::Sha256::default(); hasher.update(payload.as_slice()); @@ -994,7 +992,7 @@ impl RPMBuilder { IndexEntry::new( IndexTag::RPMTAG_PAYLOADDIGESTALT, offset, - IndexData::StringArray(vec![raw_payload_digest_sha256]), + IndexData::StringArray(vec![raw_archive_digest_sha256]), ), ]); diff --git a/src/rpm/headers/types.rs b/src/rpm/headers/types.rs index be494102..91f62297 100644 --- a/src/rpm/headers/types.rs +++ b/src/rpm/headers/types.rs @@ -1,5 +1,6 @@ //! A collection of types used in various header records. use crate::{constants::*, errors}; +use digest::Digest; /// Describes a file present in the rpm file. pub struct RPMFileEntry { @@ -315,6 +316,35 @@ impl Dependency { } } +pub struct Sha256Writer { + writer: W, + hasher: sha2::Sha256, +} + +impl Sha256Writer { + pub fn new(writer: W) -> Self { + Sha256Writer { + writer, + hasher: sha2::Sha256::new(), + } + } + + pub fn into_digest(self) -> impl AsRef<[u8]> { + self.hasher.finalize() + } +} + +impl std::io::Write for Sha256Writer { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.hasher.update(buf); + self.writer.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.writer.flush() + } +} + mod test { #[test] From 92c07767e8f442ea8e45c339fc66befad6322dba Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Mon, 17 Apr 2023 10:47:06 -0400 Subject: [PATCH 4/4] PR Adjustments --- Cargo.toml | 1 - src/constants.rs | 15 +++++---- src/rpm/headers/header.rs | 14 ++++---- src/rpm/headers/types.rs | 1 + src/rpm/package.rs | 70 ++++++++++++++++++++++++++------------- 5 files changed, 63 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee9fca74..804bb9c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,6 @@ digest = "0.10" sha2 = "0.10" md-5 = "0.10" sha1 = "0.10" -rand = { version = "0.8" } pgp = { version = "0.9", optional = true } chrono = "0.4" log = "0.4" diff --git a/src/constants.rs b/src/constants.rs index 5fdeeed9..3b431fa4 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -335,12 +335,12 @@ pub enum IndexTag { RPMTAG_TRANSFILETRIGGERTYPE = 5089, RPMTAG_FILESIGNATURES = 5090, RPMTAG_FILESIGNATURELENGTH = 5091, - RPMTAG_PAYLOADDIGEST = 5092, + RPMTAG_PAYLOADDIGEST = 5092, // hex-encoded string representing the digest of the payload RPMTAG_PAYLOADDIGESTALGO = 5093, RPMTAG_AUTOINSTALLED = 5094, RPMTAG_IDENTITY = 5095, RPMTAG_MODULARITYLABEL = 5096, - RPMTAG_PAYLOADDIGESTALT = 5097, + RPMTAG_PAYLOADDIGESTALT = 5097, // hex-encoded string representing the digest of the payload without compression RPMTAG_ARCHSUFFIX = 5098, RPMTAG_SPEC = 5099, RPMTAG_TRANSLATIONURL = 5100, @@ -375,11 +375,12 @@ pub enum IndexSignatureTag { /// This tag specifies the uncompressed size of the Payload archive, including the cpio headers. RPMSIGTAG_PAYLOADSIZE = HEADER_TAGBASE + 7, - /// This index contains the SHA1 checksum of the entire Header Section, - /// including the Header Record, Index Records and Header store. + /// The SHA1 checksum of the entire Header Section, including the Header Record, Index Records and + /// Header store, stored as a hex-encoded string. RPMSIGTAG_SHA1 = 269, - /// This tag specifies the 128-bit MD5 checksum of the combined Header and Archive sections. + /// This tag specifies the 128-bit MD5 checksum of the combined Header and Archive sections, stored as + /// a binary representation. RPMSIGTAG_MD5 = 1004, /// The tag contains the DSA signature of the Header section. @@ -415,8 +416,8 @@ pub enum IndexSignatureTag { /// The data is formatted as a Version 3 Signature Packet as specified in RFC 2440: OpenPGP Message Format. RPMSIGTAG_GPG = 1005, - /// This index contains the SHA256 checksum of the entire Header Section, - /// including the Header Record, Index Records and Header store. + /// This index contains the SHA256 checksum of the entire Header Section, including the Header Record, + /// Index Records and Header store, stored as a hex-encoded string. RPMSIGTAG_SHA256 = IndexTag::RPMTAG_SHA256HEADER as u32, /// A silly tag for a date. diff --git a/src/rpm/headers/header.rs b/src/rpm/headers/header.rs index 2d886483..578be65d 100644 --- a/src/rpm/headers/header.rs +++ b/src/rpm/headers/header.rs @@ -416,15 +416,15 @@ impl Default for FileCategory { pub enum FileDigestAlgorithm { // broken and very broken Md5 = constants::PGPHASHALGO_MD5, - Sha1 = constants::PGPHASHALGO_SHA1, - Md2 = constants::PGPHASHALGO_MD2, + // Sha1 = constants::PGPHASHALGO_SHA1, + // Md2 = constants::PGPHASHALGO_MD2, - // not proven to be broken, weaker variants broken - #[allow(non_camel_case_types)] - Haval_5_160 = constants::PGPHASHALGO_HAVAL_5_160, // not part of PGP - Ripemd160 = constants::PGPHASHALGO_RIPEMD160, + // // not proven to be broken, weaker variants broken + // #[allow(non_camel_case_types)] + // Haval_5_160 = constants::PGPHASHALGO_HAVAL_5_160, // not part of PGP + // Ripemd160 = constants::PGPHASHALGO_RIPEMD160, - Tiger192 = constants::PGPHASHALGO_TIGER192, // not part of PGP + // Tiger192 = constants::PGPHASHALGO_TIGER192, // not part of PGP Sha2_256 = constants::PGPHASHALGO_SHA256, Sha2_384 = constants::PGPHASHALGO_SHA384, Sha2_512 = constants::PGPHASHALGO_SHA512, diff --git a/src/rpm/headers/types.rs b/src/rpm/headers/types.rs index 91f62297..fafd8384 100644 --- a/src/rpm/headers/types.rs +++ b/src/rpm/headers/types.rs @@ -316,6 +316,7 @@ impl Dependency { } } +/// A wrapper for calculating the sha256 checksum of the contents written to it pub struct Sha256Writer { writer: W, hasher: sha2::Sha256, diff --git a/src/rpm/package.rs b/src/rpm/package.rs index cb004e16..b79749ec 100644 --- a/src/rpm/package.rs +++ b/src/rpm/package.rs @@ -177,11 +177,11 @@ impl RPMPackage { Ok(()) } - // @todo: function that returns the key ID of the key used to sign this package + // @todo: a function that returns the key ID of the key used to sign this package would be useful + // @todo: verify_signature() and verify_digests() don't provide any feedback on whether a signature/digest + // was present and verified or whether it was not present at all. /// Verify the signature as present within the RPM package. - /// - /// #[cfg(feature = "signature-meta")] pub fn verify_signature(&self, verifier: V) -> Result<(), RPMError> where @@ -218,6 +218,7 @@ impl RPMPackage { Ok(()) } + /// Verify any digests which may be present in the RPM headers pub fn verify_digests(&self) -> Result<(), RPMError> { let mut header = Vec::::with_capacity(1024); // make sure to not hash any previous signatures in the header @@ -228,7 +229,41 @@ impl RPMPackage { SeqCursor::new(&[header.as_slice(), self.content.as_slice()]), )?; - let sig_header_digests = self.metadata.get_digests()?; + let md5 = self + .metadata + .signature + .get_entry_data_as_binary(IndexSignatureTag::RPMSIGTAG_MD5); + let sha1 = self + .metadata + .signature + .get_entry_data_as_string(IndexSignatureTag::RPMSIGTAG_SHA1); + let sha256 = self + .metadata + .signature + .get_entry_data_as_string(IndexSignatureTag::RPMSIGTAG_SHA256); + + if let Ok(md5) = md5 { + if md5 != recreated_from_content.header_and_content_digest { + return Err(RPMError::DigestMismatchError); + } + } + + if let Ok(sha1) = sha1 { + if sha1 != recreated_from_content.header_digest { + return Err(RPMError::DigestMismatchError); + } + } + + if let Ok(sha256) = sha256 { + let sha256_calculated = { + let mut hasher = sha2::Sha256::default(); + hasher.update(self.content.as_slice()); + hex::encode(hasher.finalize()) + }; + if sha256 != sha256_calculated { + return Err(RPMError::DigestMismatchError); + } + } let payload_digest_val = self .metadata @@ -242,9 +277,15 @@ impl RPMPackage { if let (Ok(payload_digest_val), Ok(payload_digest_algo)) = (payload_digest_val, payload_digest_algo) { + let payload_digest_algo = FileDigestAlgorithm::from_u32(payload_digest_algo) + .expect("Completely unknown payload digest algorithm"); + + // @todo: UnsupportedFileDigestAlgorithm is awkward, if a number is outside the range of the expected + // variants to begin with, we can't even return it, as it carries a FileDigestAlgorithm + let mut hasher = match payload_digest_algo { - a if a == FileDigestAlgorithm::Sha2_256 as u32 => sha2::Sha256::default(), - _ => unimplemented!("An unknown payload digest algorithm was used"), + FileDigestAlgorithm::Sha2_256 => sha2::Sha256::default(), + a => return Err(RPMError::UnsupportedFileDigestAlgorithm(a)), // At the present moment even rpmbuild only supports sha256 }; let payload_digest = { @@ -256,10 +297,6 @@ impl RPMPackage { } } - if recreated_from_content != sig_header_digests { - return Err(RPMError::DigestMismatchError); - } - Ok(()) } } @@ -314,19 +351,6 @@ impl RPMPackageMetadata { Ok(()) } - fn get_digests(&self) -> Result { - let md5 = self - .signature - .get_entry_data_as_binary(IndexSignatureTag::RPMSIGTAG_MD5)?; - let sha1 = self - .signature - .get_entry_data_as_string(IndexSignatureTag::RPMSIGTAG_SHA1)?; - Ok(Digests { - header_digest: sha1.to_owned(), - header_and_content_digest: Vec::from(md5), - }) - } - #[inline] pub fn is_source_package(&self) -> bool { self.header