Skip to content

Commit

Permalink
Stopped store all content in memory
Browse files Browse the repository at this point in the history
  • Loading branch information
ikrivosheev committed Jun 16, 2023
1 parent 9498e02 commit c1d1735
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 91 deletions.
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
//! let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
//! // It's recommended to use timestamp of last commit in your VCS
//! let source_date = 1_600_000_000;
//! let pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
//! let mut pkg = rpm::RPMBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
//! .compression(rpm::CompressionType::Gzip)
//! .with_file(
//! "./test_assets/awesome.toml",
Expand Down Expand Up @@ -67,7 +67,7 @@
//!
//! // reading
//! let raw_pub_key = std::fs::read("test_assets/public_key.asc")?;
//! let pkg = rpm::RPMPackage::open("/tmp/awesome.rpm")?;
//! let mut pkg = rpm::RPMPackage::open("/tmp/awesome.rpm")?;
//! // verifying
//! pkg.verify_signature(Verifier::load_from_asc_bytes(&raw_pub_key)?)?;
//! # }
Expand Down
23 changes: 13 additions & 10 deletions src/rpm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ use std::collections::{BTreeMap, BTreeSet};
use std::convert::TryInto;

use std::fs;
#[cfg(feature = "signature-meta")]
use std::io;
use std::io::{Read, Write};
use std::io::{self, Read, Seek, Write};
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

Expand Down Expand Up @@ -461,21 +459,23 @@ impl RPMBuilder {
/// build without a signature
///
/// ignores a present key, if any
pub fn build(self) -> Result<RPMPackage, RPMError> {
pub fn build(self) -> Result<RPMPackage<io::Cursor<Vec<u8>>>, RPMError> {
let (lead, header_idx_tag, content) = self.prepare_data()?;

let mut content = io::Cursor::new(content);
let mut header = Vec::with_capacity(128);
header_idx_tag.write(&mut header)?;

let digest_header = {
let header = header;
let header_and_content_len = header.len() + content.len();
let header_and_content_len = header.len() as u64 + content.get_ref().len() as u64;

let Digests {
header_and_content_digest: header_and_content_digest_md5,
header_digest_sha1,
header_digest_sha256,
} = RPMPackage::create_sig_header_digests(header.as_slice(), content.as_slice())?;
} = RPMPackage::create_sig_header_digests(header.as_slice(), &mut content)?;
content.rewind()?;

Header::<IndexSignatureTag>::builder()
.add_digest(
Expand All @@ -499,24 +499,26 @@ impl RPMBuilder {
///
/// See `signature::Signing` for more details.
#[cfg(feature = "signature-meta")]
pub fn build_and_sign<S>(self, signer: S) -> Result<RPMPackage, RPMError>
pub fn build_and_sign<S>(self, signer: S) -> Result<RPMPackage<io::Cursor<Vec<u8>>>, RPMError>
where
S: signature::Signing<signature::algorithm::RSA>,
{
let source_date = self.source_date;
let (lead, header_idx_tag, content) = self.prepare_data()?;

let mut content = io::Cursor::new(content);
let mut header = Vec::with_capacity(128);
header_idx_tag.write(&mut header)?;
let header = header;

let header_and_content_len = header.len() + content.len();
let header_and_content_len = header.len() as u64 + content.get_ref().len() as u64;

let Digests {
header_and_content_digest: header_and_content_digest_md5,
header_digest_sha1,
header_digest_sha256,
} = RPMPackage::create_sig_header_digests(header.as_slice(), content.as_slice())?;
} = RPMPackage::create_sig_header_digests(header.as_slice(), &mut content)?;
content.rewind()?;

let builder = Header::<IndexSignatureTag>::builder().add_digest(
header_digest_sha1.as_str(),
Expand All @@ -532,8 +534,9 @@ impl RPMBuilder {
};
let rsa_sig_header_only = signer.sign(header.as_slice(), t)?;

let cursor = io::Cursor::new(header).chain(io::Cursor::new(&content));
let cursor = io::Cursor::new(header).chain(&mut content);
let rsa_sig_header_and_archive = signer.sign(cursor, t)?;
content.rewind()?;

builder
.add_signature(
Expand Down
4 changes: 2 additions & 2 deletions src/rpm/headers/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ impl Header<IndexSignatureTag> {
/// Please use the [`builder`](Self::builder()) which has modular and safe API.
#[cfg(feature = "signature-meta")]
pub(crate) fn new_signature_header(
headers_plus_payload_size: usize,
headers_plus_payload_size: u64,
md5sum: &[u8],
sha1: &str,
sha256: &str,
Expand Down Expand Up @@ -956,7 +956,7 @@ mod test {
};

let built = Header::<IndexSignatureTag>::new_signature_header(
size as usize,
size as u64,
md5sum,
sha1,
sha256,
Expand Down
4 changes: 2 additions & 2 deletions src/rpm/headers/signature_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ where
T: ConstructionStage,
{
/// Construct the complete signature header.
pub fn build(mut self, headers_plus_payload_size: usize) -> Header<IndexSignatureTag> {
pub fn build(mut self, headers_plus_payload_size: u64) -> Header<IndexSignatureTag> {
let entry = match headers_plus_payload_size.try_into() {
Ok(size) => IndexEntry::new(
IndexSignatureTag::RPMSIGTAG_SIZE,
Expand All @@ -66,7 +66,7 @@ where
Err(_) => IndexEntry::new(
IndexSignatureTag::RPMSIGTAG_LONGSIZE,
0i32,
IndexData::Int64(vec![headers_plus_payload_size as u64]),
IndexData::Int64(vec![headers_plus_payload_size]),
),
};
self.entries.insert(0, entry);
Expand Down
1 change: 1 addition & 0 deletions src/rpm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod package;
mod timestamp;

pub mod signature;
mod skip_reader;

pub use headers::*;

Expand Down
103 changes: 65 additions & 38 deletions src/rpm/package.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#[cfg(feature = "signature-meta")]
use std::io::Read;
use std::{
fs, io,
fmt, fs,
io::{self, Seek},
path::{Path, PathBuf},
str::FromStr,
};

use digest::Digest;
use num_traits::FromPrimitive;

use crate::rpm::skip_reader::SkipReader;
use crate::{constants::*, errors::*, CompressionType};

#[cfg(feature = "signature-meta")]
use crate::{signature, Timestamp};
#[cfg(feature = "signature-meta")]
use std::{fmt::Debug, io::Read};

use super::headers::*;
use super::Lead;
Expand All @@ -36,53 +37,71 @@ pub struct Digests {
///
/// Can either be created using the [`RPMBuilder`](super::builder::RPMBuilder)
/// or used with [`parse`](`self::RPMPackage::parse`) to obtain from a file.
#[derive(Debug)]
pub struct RPMPackage {
pub struct RPMPackage<R> {
/// Header and metadata structures.
///
/// Contains the constant lead as well as the metadata store.
pub metadata: RPMPackageMetadata,
/// The compressed or uncompressed files.
pub content: Vec<u8>,
pub content: R,
}

#[cfg(feature = "signature-meta")]
fn stream_len(mut stream: impl Seek) -> io::Result<u64> {
let old_pos = stream.stream_position()?;
let len = stream.seek(io::SeekFrom::End(0))?;

// Avoid seeking a third time when we were already at the end of the
// stream. The branch is usually way cheaper than a seek operation.
if old_pos != len {
stream.seek(io::SeekFrom::Start(old_pos))?;
}

Ok(len)
}

impl RPMPackage {
impl RPMPackage<()> {
/// Open and parse a file at the provided path as an RPM package
pub fn open(path: impl AsRef<Path>) -> Result<Self, RPMError> {
pub fn open(
path: impl AsRef<Path>,
) -> Result<RPMPackage<SkipReader<io::BufReader<fs::File>>>, RPMError> {
let rpm_file = fs::File::open(path.as_ref())?;
let mut buf_reader = io::BufReader::new(rpm_file);
Self::parse(&mut buf_reader)
let buf_reader = io::BufReader::new(rpm_file);
RPMPackage::parse(buf_reader)
}
}

impl<R: io::BufRead + Seek> RPMPackage<R> {
/// Parse an RPM package from an existing buffer
pub fn parse(input: &mut impl io::BufRead) -> Result<Self, RPMError> {
let metadata = RPMPackageMetadata::parse(input)?;
let mut content = Vec::new();
input.read_to_end(&mut content)?;
pub fn parse(mut input: R) -> Result<RPMPackage<SkipReader<R>>, RPMError> {
let metadata = RPMPackageMetadata::parse(&mut input)?;
let content = SkipReader::new(input)?;
Ok(RPMPackage { metadata, content })
}

/// Write the RPM package to a buffer
pub fn write(&self, out: &mut impl io::Write) -> Result<(), RPMError> {
pub fn write(&mut self, out: &mut impl io::Write) -> Result<(), RPMError> {
self.metadata.write(out)?;
out.write_all(&self.content)?;
io::copy(&mut self.content, out)?;
self.content.rewind()?;
Ok(())
}

/// Write the RPM package to a file
pub fn write_file(&self, path: impl AsRef<Path>) -> Result<(), RPMError> {
pub fn write_file(&mut self, path: impl AsRef<Path>) -> Result<(), RPMError> {
self.write(&mut io::BufWriter::new(fs::File::create(path)?))
}

/// Prepare both header and content digests as used by the `SignatureIndex`.
pub(crate) fn create_sig_header_digests(
header: &[u8],
payload: &[u8],
payload: &mut R,
) -> Result<Digests, RPMError> {
let digest_md5 = {
let mut hasher = md5::Md5::default();
hasher.update(header);
hasher.update(payload);
io::copy(payload, &mut hasher)?;
payload.rewind()?;
let hash_result = hasher.finalize();
hash_result.to_vec()
};
Expand Down Expand Up @@ -127,7 +146,7 @@ impl RPMPackage {
pub fn sign_with_timestamp<S>(
&mut self,
signer: S,
t: impl TryInto<Timestamp, Error = impl Debug>,
t: impl TryInto<Timestamp, Error = impl fmt::Debug>,
) -> Result<(), RPMError>
where
S: signature::Signing<signature::algorithm::RSA, Signature = Vec<u8>>,
Expand All @@ -139,17 +158,17 @@ impl RPMPackage {
// make sure to not hash any previous signatures in the header
self.metadata.header.write(&mut header_bytes)?;

let header_and_content_len = header_bytes.len() + self.content.len();
let content_len = stream_len(&mut self.content)?;
let header_and_content_len = header_bytes.len() as u64 + content_len;

let Digests {
header_digest_sha256,
header_digest_sha1,
header_and_content_digest,
} = Self::create_sig_header_digests(header_bytes.as_slice(), &self.content)?;
} = Self::create_sig_header_digests(header_bytes.as_slice(), &mut self.content)?;

let rsa_signature_spanning_header_only = signer.sign(header_bytes.as_slice(), t)?;
let mut header_and_content_cursor =
io::Cursor::new(header_bytes).chain(io::Cursor::new(&self.content));
let mut header_and_content_cursor = io::Cursor::new(header_bytes).chain(&mut self.content);

let rsa_signature_spanning_header_and_archive =
signer.sign(&mut header_and_content_cursor, t)?;
Expand All @@ -173,7 +192,7 @@ impl RPMPackage {

/// Verify the signature as present within the RPM package.
#[cfg(feature = "signature-meta")]
pub fn verify_signature<V>(&self, verifier: V) -> Result<(), RPMError>
pub fn verify_signature<V>(&mut self, verifier: V) -> Result<(), RPMError>
where
V: signature::Verifying<signature::algorithm::RSA, Signature = Vec<u8>>,
{
Expand All @@ -186,6 +205,9 @@ impl RPMPackage {
.get_entry_data_as_binary(IndexSignatureTag::RPMSIGTAG_RSA)?;

signature::echo_signature("signature_header(header only)", signature_header_only);
verifier.verify(header_bytes.as_slice(), signature_header_only)?;

self.verify_digests()?;

let signature_header_and_content = self
.metadata
Expand All @@ -196,25 +218,21 @@ impl RPMPackage {
"signature_header(header and content)",
signature_header_and_content,
);

verifier.verify(header_bytes.as_slice(), signature_header_only)?;
self.verify_digests()?;

let header_and_content_cursor =
io::Cursor::new(header_bytes).chain(io::Cursor::new(&self.content));
let header_and_content_cursor = io::Cursor::new(header_bytes).chain(&mut self.content);
verifier.verify(header_and_content_cursor, signature_header_and_content)?;
self.content.rewind()?;

Ok(())
}

/// Verify any digests which may be present in the RPM headers
pub fn verify_digests(&self) -> Result<(), RPMError> {
pub fn verify_digests(&mut self) -> Result<(), RPMError> {
let mut header = Vec::<u8>::with_capacity(1024);
// make sure to not hash any previous signatures in the header
self.metadata.header.write(&mut header)?;

let pkg_actual_digests =
Self::create_sig_header_digests(header.as_slice(), self.content.as_slice())?;
Self::create_sig_header_digests(header.as_slice(), &mut self.content)?;

let md5 = self
.metadata
Expand Down Expand Up @@ -273,7 +291,8 @@ impl RPMPackage {
// At the present moment even rpmbuild only supports sha256
};
let payload_digest = {
hasher.update(self.content.as_slice());
io::copy(&mut self.content, &mut hasher)?;
self.content.rewind()?;
hex::encode(hasher.finalize())
};
if payload_digest != payload_digest_val[0] {
Expand All @@ -285,6 +304,14 @@ impl RPMPackage {
}
}

impl<R> fmt::Debug for RPMPackage<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RPMPackage")
.field("metadata", &self.metadata)
.finish()
}
}

#[derive(PartialEq, Debug)]
pub struct RPMPackageMetadata {
pub lead: Lead,
Expand All @@ -301,12 +328,12 @@ impl RPMPackageMetadata {
}

/// Parse RPMPackageMetadata from the provided reader
pub fn parse(input: &mut impl io::BufRead) -> Result<Self, RPMError> {
pub fn parse(mut input: impl io::BufRead) -> Result<Self, RPMError> {
let mut lead_buffer = [0; LEAD_SIZE as usize];
input.read_exact(&mut lead_buffer)?;
let lead = Lead::parse(&lead_buffer)?;
let signature_header = Header::parse_signature(input)?;
let header = Header::parse(input)?;
let signature_header = Header::parse_signature(&mut input)?;
let header = Header::parse(&mut input)?;

Ok(RPMPackageMetadata {
lead,
Expand Down
Loading

0 comments on commit c1d1735

Please sign in to comment.