Skip to content

Commit

Permalink
always calculate the digest (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
drahnr authored Apr 14, 2023
1 parent 3802671 commit 815ddb3
Show file tree
Hide file tree
Showing 14 changed files with 339 additions and 388 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `rpmlib()` dependencies to built packages as appropriate
- Fixed an issue where `get_file_paths()` and `get_file_entries()` would fail if the package
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.

### Breaking Changes

Expand Down
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = [
"René Richter <[email protected]>",
"Bernhard Schuster <[email protected]>",
"Max Dymond <[email protected]>",
"Daniel Alley <[email protected]>"
"Daniel Alley <[email protected]>",
]
edition = "2021"
license = "Apache-2.0 OR MIT"
Expand Down Expand Up @@ -35,7 +35,7 @@ sha2 = "0.10"
md-5 = "0.10"
sha1 = "0.10"
rand = { version = "0.8" }
pgp = { version="0.9", optional = true }
pgp = { version = "0.9", optional = true }
chrono = "0.4"
log = "0.4"
itertools = "0.10"
Expand All @@ -46,8 +46,10 @@ gethostname = "0.4.1"

# Libraries required for with_file_async() implementations
async-std = { version = "1.12.0", optional = true }
tokio = {version = "1", optional = true}
tokio-util = { version = "0.7.4", features = ["compat"], 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" }
Expand All @@ -56,8 +58,8 @@ env_logger = "0.10.0"
serial_test = "2.0"

# Use for testing async files when async-futures enabled
tokio = {version = "1", features = ["full"]}
tokio-util = { version = "0.7.4", features = ["compat"]}
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7.4", features = ["compat"] }

[features]
default = ["signature-pgp"]
Expand Down
5 changes: 2 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//!
//! ```rust
//!
//! # #[cfg(feature = "signature-meta")]
//! # #[cfg(feature = "signature-pgp")]
//! use rpm::{
//! signature::pgp::{
//! Signer,
Expand All @@ -17,7 +17,7 @@
//! use std::str::FromStr;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # #[cfg(feature = "signature-meta")]
//! # #[cfg(feature = "signature-pgp")]
//! # {
//! let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
//! let pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
Expand Down Expand Up @@ -70,7 +70,6 @@ pub use crate::errors::*;
pub(crate) mod constants;
pub use crate::constants::*;

#[cfg(feature = "signature-meta")]
mod sequential_cursor;

mod rpm;
Expand Down
62 changes: 22 additions & 40 deletions src/rpm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ use std::time::{SystemTime, UNIX_EPOCH};

use gethostname::gethostname;

use crate::errors::*;

use super::compressor::Compressor;
use super::headers::*;
use super::Lead;
use crate::constants::*;
use crate::errors::*;

#[cfg(feature = "signature-meta")]
use crate::sequential_cursor::SeqCursor;
#[cfg(feature = "signature-meta")]
use crate::signature;

use crate::Digests;
use crate::RPMPackage;
use crate::RPMPackageMetadata;

Expand Down Expand Up @@ -493,12 +492,19 @@ impl RPMBuilder {
let mut header = Vec::with_capacity(128);
header_idx_tag.write(&mut header)?;

#[cfg(feature = "signature-meta")]
let digest_header = {
let header = header;
let (header_digest_sha1, header_and_content_digest_md5) =
Self::derive_hashes(header.as_slice(), content.as_slice())?;
let header_and_content_len = header.len() + content.len();

let header_and_content_cursor =
SeqCursor::new(&[header.as_slice(), content.as_slice()]);
let header_and_content_len = header_and_content_cursor.len();
let Digests {
header_and_content_digest: header_and_content_digest_md5,
header_digest: header_digest_sha1,
} = RPMPackage::create_digests_from_readers(
&mut header.as_slice(),
header_and_content_cursor,
)?;

Header::<IndexSignatureTag>::builder()
.add_digest(
Expand All @@ -511,8 +517,6 @@ impl RPMBuilder {
.expect("signature header + signature length must be <4gb"),
)
};
#[cfg(not(feature = "signature-meta"))]
let digest_header = { Header::<IndexSignatureTag>::new_empty() };

let metadata = RPMPackageMetadata {
lead,
Expand All @@ -537,10 +541,15 @@ impl RPMBuilder {
header_idx_tag.write(&mut header)?;
let header = header;

let (header_digest_sha1, header_and_content_digest_md5) =
Self::derive_hashes(header.as_slice(), content.as_slice())?;

let header_and_content_len = header.len() + content.len();
let header_and_content_cursor = SeqCursor::new(&[header.as_slice(), content.as_slice()]);
let header_and_content_len = header_and_content_cursor.len();
let Digests {
header_and_content_digest: header_and_content_digest_md5,
header_digest: header_digest_sha1,
} = RPMPackage::create_digests_from_readers(
&mut header.as_slice(),
header_and_content_cursor,
)?;

let builder = Header::<IndexSignatureTag>::builder().add_digest(
header_digest_sha1.as_str(),
Expand Down Expand Up @@ -574,33 +583,6 @@ impl RPMBuilder {
Ok(pkg)
}

/// use prepared data but make sure the signatures are
#[cfg(feature = "signature-meta")]
fn derive_hashes(header: &[u8], content: &[u8]) -> Result<(String, Vec<u8>), RPMError> {
let digest_md5 = {
use md5::Digest;

// across header index and content (compressed or uncompressed, depends on configuration)
let mut hasher = md5::Md5::default();
hasher.update(header);
hasher.update(content);
let digest_md5 = hasher.finalize();
digest_md5.to_vec()
};

// header only, not the lead, just the header index
let digest_sha1 = {
use sha1::Digest;

let mut hasher = sha1::Sha1::default();
hasher.update(header);
let digest_sha1 = hasher.finalize();
hex::encode(digest_sha1)
};

Ok((digest_sha1, digest_md5))
}

/// prepare all rpm headers including content
///
/// @todo split this into multiple `fn`s, one per `IndexTag`-group.
Expand Down
33 changes: 16 additions & 17 deletions src/rpm/headers/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,17 @@ where
Ok(())
}

pub(crate) fn find_entry_or_err(&self, tag: &T) -> Result<&IndexEntry<T>, RPMError> {
pub(crate) fn find_entry_or_err(&self, tag: T) -> Result<&IndexEntry<T>, RPMError> {
self.index_entries
.iter()
.find(|entry| &entry.tag == tag)
.find(|entry| entry.tag == tag)
.ok_or_else(|| RPMError::TagNotFound(tag.to_string()))
// @todo: this could be more efficient, if the tag is an integer, we can just pass around
// an integer, and the name of the tag (or "unknown") can be easily derived from that
}

pub fn get_entry_data_as_binary(&self, tag: T) -> Result<&[u8], RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_binary()
Expand All @@ -158,7 +158,7 @@ where
}

pub fn get_entry_data_as_string(&self, tag: T) -> Result<&str, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_str()
Expand All @@ -170,7 +170,7 @@ where
}

pub fn get_entry_data_as_i18n_string(&self, tag: T) -> Result<&str, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_i18n_str()
Expand All @@ -182,7 +182,7 @@ where
}

pub fn get_entry_data_as_u16_array(&self, tag: T) -> Result<Vec<u16>, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_u16_array()
Expand All @@ -194,7 +194,7 @@ where
}

pub fn get_entry_data_as_u32(&self, tag: T) -> Result<u32, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_u32()
Expand All @@ -206,7 +206,7 @@ where
}

pub fn get_entry_data_as_u32_array(&self, tag: T) -> Result<Vec<u32>, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_u32_array()
Expand All @@ -218,7 +218,7 @@ where
}

pub fn get_entry_data_as_u64(&self, tag: T) -> Result<u64, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_u64()
Expand All @@ -230,7 +230,7 @@ where
}

pub fn get_entry_data_as_u64_array(&self, tag: T) -> Result<Vec<u64>, RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_u64_array()
Expand All @@ -242,7 +242,7 @@ where
}

pub fn get_entry_data_as_string_array(&self, tag: T) -> Result<&[String], RPMError> {
let entry = self.find_entry_or_err(&tag)?;
let entry = self.find_entry_or_err(tag)?;
entry
.data
.as_string_array()
Expand Down Expand Up @@ -304,17 +304,16 @@ impl Header<IndexSignatureTag> {
pub(crate) fn new_signature_header(
headers_plus_payload_size: u32,
md5sum: &[u8],
sha1: String,
sha1: &str,
rsa_spanning_header: &[u8],
rsa_spanning_header_and_archive: &[u8],
) -> Self {
SignatureHeaderBuilder::new()
.add_digest(sha1.as_str(), md5sum)
.add_digest(sha1, md5sum)
.add_signature(rsa_spanning_header, rsa_spanning_header_and_archive)
.build(headers_plus_payload_size)
}

#[cfg(feature = "signature-meta")]
pub fn builder() -> SignatureHeaderBuilder<Empty> {
SignatureHeaderBuilder::<Empty>::new()
}
Expand Down Expand Up @@ -513,16 +512,16 @@ where
Ok((input, ()))
}

#[cfg(feature = "signature-meta")]
#[cfg(test)]
mod tests2 {
use super::*;

#[cfg(feature = "signature-meta")]
#[test]
fn signature_header_build() {
let size: u32 = 209_348;
let md5sum: &[u8] = &[22u8; 16];
let sha1: String = "5A884F0CB41EC3DA6D6E7FC2F6AB9DECA8826E8D".to_owned();
let sha1 = "5A884F0CB41EC3DA6D6E7FC2F6AB9DECA8826E8D";
let rsa_spanning_header: &[u8] = b"111222333444";
let rsa_spanning_header_and_archive: &[u8] = b"7777888899990000";

Expand All @@ -543,7 +542,7 @@ mod tests2 {
IndexEntry::new(
IndexSignatureTag::RPMSIGTAG_SHA1,
offset,
IndexData::StringTag(sha1.clone()),
IndexData::StringTag(sha1.to_owned()),
),
IndexEntry::new(
IndexSignatureTag::RPMSIGTAG_RSA,
Expand Down
2 changes: 0 additions & 2 deletions src/rpm/headers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ pub use header::*;
pub(crate) use lead::*;
pub use types::*;

#[cfg(feature = "signature-meta")]
mod signature_builder;

#[cfg(feature = "signature-meta")]
pub use signature_builder::*;
40 changes: 33 additions & 7 deletions src/rpm/headers/signature_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,30 +127,56 @@ impl SignatureHeaderBuilder<WithDigest> {
mod test {
use super::*;
#[test]
fn signature_builder() {
fn signature_builder_w_digest_and_signature() {
let builder = SignatureHeaderBuilder::<Empty>::new();

let rsa_sig_header_only = [0u8; 32];
let rsa_sig_header_and_archive = [0u8; 32];
let _digest_header_only = [0u8; 64];

let digest_header_only = hex::encode(&[0u8; 64]);
let digest_header_and_archive = [0u8; 64];

let header = builder
.add_digest("", &digest_header_and_archive[..])
.add_digest(digest_header_only.as_str(), &digest_header_and_archive[..])
.add_signature(&rsa_sig_header_only[..], &rsa_sig_header_and_archive[..])
.build(32);

assert!(header
.find_entry_or_err(&IndexSignatureTag::RPMSIGTAG_RSA)
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_RSA)
.is_ok());
assert!(header
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_PGP)
.is_ok());
assert!(header
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_MD5)
.is_ok());
assert!(header
.find_entry_or_err(&IndexSignatureTag::RPMSIGTAG_PGP)
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_SHA1)
.is_ok());
}

#[test]
fn signature_builder_digest_only() {
let builder = SignatureHeaderBuilder::<Empty>::new();

let digest_header_only = hex::encode(&[0u8; 64]);
let digest_header_and_archive = [0u8; 64];

let header = builder
.add_digest(digest_header_only.as_str(), &digest_header_and_archive[..])
.build(32);

assert!(header
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_RSA)
.is_err());
assert!(header
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_PGP)
.is_err());
assert!(header
.find_entry_or_err(&IndexSignatureTag::RPMSIGTAG_MD5)
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_MD5)
.is_ok());
assert!(header
.find_entry_or_err(&IndexSignatureTag::RPMSIGTAG_SHA1)
.find_entry_or_err(IndexSignatureTag::RPMSIGTAG_SHA1)
.is_ok());
}
}
Loading

0 comments on commit 815ddb3

Please sign in to comment.