rpm/rpm/
package.rs

1#[cfg(feature = "async-tokio")]
2use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt};
3
4use super::headers::*;
5
6use crate::constants::*;
7
8use crate::errors::*;
9
10use super::Lead;
11
12#[cfg(feature = "signature-meta")]
13use crate::sequential_cursor::SeqCursor;
14#[cfg(feature = "signature-meta")]
15use crate::signature;
16
17use std::io::Read;
18#[cfg(feature = "signature-meta")]
19use std::io::{Seek, SeekFrom};
20
21/// A complete rpm file.
22///
23/// Can either be created using the [`RPMPackageBuilder`](super::builder::RPMPackageBuilder)
24/// or used with [`parse`](`self::RPMPackage::parse`) to obtain from a file.
25#[derive(Debug)]
26pub struct RPMPackage {
27    /// Header and metadata structures.
28    ///
29    /// Contains the constant lead as well as the metadata store.
30    pub metadata: RPMPackageMetadata,
31    /// The compressed or uncompressed files.
32    pub content: Vec<u8>,
33}
34
35impl RPMPackage {
36    #[cfg(feature = "async-tokio")]
37    pub async fn parse_async<I: AsyncRead + Unpin>(input: &mut I) -> Result<Self, RPMError> {
38        let metadata = RPMPackageMetadata::parse_async(input).await?;
39        let mut content = Vec::new();
40        input.read_to_end(&mut content).await?;
41        Ok(RPMPackage { metadata, content })
42    }
43
44    pub fn parse<T: std::io::BufRead>(input: &mut T) -> Result<Self, RPMError> {
45        let metadata = RPMPackageMetadata::parse(input)?;
46        let mut content = Vec::new();
47        input.read_to_end(&mut content)?;
48        Ok(RPMPackage { metadata, content })
49    }
50
51    pub fn write<W: std::io::Write>(&self, out: &mut W) -> Result<(), RPMError> {
52        self.metadata.write(out)?;
53        out.write_all(&self.content)?;
54        Ok(())
55    }
56
57    #[cfg(feature = "async-tokio")]
58    pub async fn write_async<W: tokio::io::AsyncWrite + Unpin>(
59        &self,
60        out: &mut W,
61    ) -> Result<(), RPMError> {
62        self.metadata.write_async(out).await?;
63        out.write_all(&self.content).await?;
64        Ok(())
65    }
66
67    // TODO allow passing an external signer/verifier
68
69    /// sign all headers (except for the lead) using an external key and store it as the initial header
70    #[cfg(feature = "signature-meta")]
71    pub fn sign<S>(&mut self, signer: S) -> Result<(), RPMError>
72    where
73        S: signature::Signing<signature::algorithm::RSA, Signature = Vec<u8>>,
74    {
75        // create a temporary byte repr of the header
76        // and re-create all hashes
77        let mut header_bytes = Vec::<u8>::with_capacity(1024);
78        self.metadata.header.write(&mut header_bytes)?;
79
80        let mut header_and_content_cursor =
81            SeqCursor::new(&[header_bytes.as_slice(), self.content.as_slice()]);
82
83        let digest_md5 = {
84            use md5::Digest;
85            let mut hasher = md5::Md5::default();
86            {
87                // avoid loading it into memory all at once
88                // since the content could be multiple 100s of MBs
89                let mut buf = [0u8; 256];
90                while let Ok(n) = header_and_content_cursor.read(&mut buf[..]) {
91                    hasher.update(&buf[0..n]);
92                }
93            }
94            let hash_result = hasher.finalize();
95            hash_result.to_vec()
96        };
97
98        header_and_content_cursor.seek(SeekFrom::Start(0))?;
99
100        let digest_sha1 = {
101            use sha1::Digest;
102            let mut hasher = sha1::Sha1::default();
103            hasher.update(&header_bytes);
104            let digest = hasher.finalize();
105            hex::encode(digest)
106        };
107
108        let rsa_signature_spanning_header_only = signer.sign(header_bytes.as_slice())?;
109
110        let rsa_signature_spanning_header_and_archive =
111            signer.sign(&mut header_and_content_cursor)?;
112
113        // TODO FIXME verify this is the size we want, I don't think it is
114        // TODO maybe use signature_size instead of size
115        self.metadata.signature = Header::<IndexSignatureTag>::new_signature_header(
116            header_and_content_cursor.len() as i32,
117            &digest_md5,
118            digest_sha1,
119            rsa_signature_spanning_header_only.as_slice(),
120            rsa_signature_spanning_header_and_archive.as_slice(),
121        );
122
123        Ok(())
124    }
125
126    /// Verify the signature as present within the RPM package.
127    ///
128    ///
129    #[cfg(feature = "signature-meta")]
130    pub fn verify_signature<V>(&self, verifier: V) -> Result<(), RPMError>
131    where
132        V: signature::Verifying<signature::algorithm::RSA, Signature = Vec<u8>>,
133    {
134        // TODO retval should be SIGNATURE_VERIFIED or MISMATCH, not just an error
135
136        let mut header_bytes = Vec::<u8>::with_capacity(1024);
137        self.metadata.header.write(&mut header_bytes)?;
138
139        let signature_header_only = self
140            .metadata
141            .signature
142            .get_entry_binary_data(IndexSignatureTag::RPMSIGTAG_RSA)?;
143
144        crate::signature::echo_signature("signature_header(header only)", signature_header_only);
145
146        let signature_header_and_content = self
147            .metadata
148            .signature
149            .get_entry_binary_data(IndexSignatureTag::RPMSIGTAG_PGP)?;
150
151        crate::signature::echo_signature(
152            "signature_header(header and content)",
153            signature_header_and_content,
154        );
155
156        verifier.verify(header_bytes.as_slice(), signature_header_only)?;
157
158        let header_and_content_cursor =
159            SeqCursor::new(&[header_bytes.as_slice(), self.content.as_slice()]);
160
161        verifier.verify(header_and_content_cursor, signature_header_and_content)?;
162
163        Ok(())
164    }
165}
166
167#[derive(PartialEq, Debug)]
168pub struct RPMPackageMetadata {
169    pub lead: Lead,
170    pub signature: Header<IndexSignatureTag>,
171    pub header: Header<IndexTag>,
172}
173
174impl RPMPackageMetadata {
175    #[cfg(feature = "async-tokio")]
176    pub async fn parse_async<T: AsyncRead + Unpin>(input: &mut T) -> Result<Self, RPMError> {
177        let mut lead_buffer = [0; LEAD_SIZE];
178        input.read_exact(&mut lead_buffer).await?;
179        let lead = Lead::parse(&lead_buffer)?;
180        let signature_header = Header::parse_signature_async(input).await?;
181        let header = Header::parse_async(input).await?;
182        Ok(RPMPackageMetadata {
183            lead,
184            signature: signature_header,
185            header,
186        })
187    }
188
189    pub(crate) fn parse<T: std::io::BufRead>(input: &mut T) -> Result<Self, RPMError> {
190        let mut lead_buffer = [0; LEAD_SIZE];
191        input.read_exact(&mut lead_buffer)?;
192        let lead = Lead::parse(&lead_buffer)?;
193        let signature_header = Header::parse_signature(input)?;
194        let header = Header::parse(input)?;
195        Ok(RPMPackageMetadata {
196            lead,
197            signature: signature_header,
198            header,
199        })
200    }
201
202    pub(crate) fn write<W: std::io::Write>(&self, out: &mut W) -> Result<(), RPMError> {
203        self.lead.write(out)?;
204        self.signature.write_signature(out)?;
205        self.header.write(out)?;
206        Ok(())
207    }
208
209    #[cfg(feature = "async-tokio")]
210    pub async fn write_async<W: tokio::io::AsyncWrite + Unpin>(
211        &self,
212        out: &mut W,
213    ) -> Result<(), RPMError> {
214        self.lead.write_async(out).await?;
215        self.signature.write_signature_async(out).await?;
216        self.header.write_async(out).await?;
217        Ok(())
218    }
219}