Skip to main content

zipsign_api/sign/
zip.rs

1#![cfg_attr(docsrs, doc(cfg(feature = "sign-zip")))]
2
3use std::io::{IoSlice, Read, Seek, SeekFrom, Write};
4
5use super::{GatherSignatureDataError, gather_signature_data};
6use crate::constants::{BUF_LIMIT, HEADER_SIZE, SignatureCountLeInt};
7use crate::sign_unsign_zip::{CopyZipError, copy_zip};
8use crate::{Prehash, SIGNATURE_LENGTH, SigningKey};
9
10crate::Error! {
11    /// An error returned by [`copy_and_sign_zip()`]
12    pub struct SignZipError(Error) {
13        #[error("could not copy ZIP data")]
14        Copy(#[source] CopyZipError),
15        #[error("could not write to output, device full?")]
16        OutputFull,
17        #[error("could not read output")]
18        OutputRead(#[source] std::io::Error),
19        #[error("could not seek in output")]
20        OutputSeek(#[source] std::io::Error),
21        #[error("could not write to output")]
22        OutputWrite(#[source] std::io::Error),
23        #[error("could not gather signature data")]
24        Sign(#[source] GatherSignatureDataError),
25        #[error("too many keys")]
26        TooManyKeys,
27    }
28}
29
30/// Copy a `.zip` file and sign its content
31pub fn copy_and_sign_zip<I, O>(
32    input: &mut I,
33    output: &mut O,
34    keys: &[SigningKey],
35    context: Option<&[u8]>,
36) -> Result<(), SignZipError>
37where
38    I: ?Sized + Read + Seek,
39    O: ?Sized + Read + Seek + Write,
40{
41    if keys.len() > SignatureCountLeInt::MAX as usize {
42        return Err(Error::TooManyKeys.into());
43    }
44    let signature_bytes = keys
45        .len()
46        .checked_mul(SIGNATURE_LENGTH)
47        .and_then(|s| s.checked_add(HEADER_SIZE))
48        .ok_or(Error::TooManyKeys)?;
49    if signature_bytes > BUF_LIMIT {
50        return Err(Error::TooManyKeys.into());
51    }
52
53    // copy ZIP
54    write_padding(signature_bytes, output)?;
55    copy_zip(input, output).map_err(Error::Copy)?;
56
57    // gather signature
58    let _ = output
59        .seek(SeekFrom::Start(signature_bytes.try_into().unwrap()))
60        .map_err(Error::OutputSeek)?;
61    let prehashed_message = Prehash::calculate(output).map_err(Error::OutputRead)?;
62    let buf = gather_signature_data(keys, &prehashed_message, context).map_err(Error::Sign)?;
63
64    // write signature
65    output.rewind().map_err(Error::OutputSeek)?;
66    output.write_all(&buf).map_err(Error::OutputWrite)?;
67    Ok(())
68}
69
70fn write_padding<O>(mut padding_to_write: usize, output: &mut O) -> Result<(), Error>
71where
72    O: ?Sized + Write,
73{
74    while padding_to_write > 0 {
75        const PADDING: &[u8; 512] = &[0; 512];
76        let result = if padding_to_write > PADDING.len() {
77            let num_slices = padding_to_write.div_ceil(PADDING.len()).min(128);
78            let mut slices = vec![IoSlice::new(PADDING); num_slices];
79            slices[num_slices - 1] = IoSlice::new(&PADDING[..padding_to_write % PADDING.len()]);
80            output.write_vectored(&slices)
81        } else {
82            output.write(&PADDING[..padding_to_write])
83        };
84        let written = result.map_err(Error::OutputWrite)?;
85
86        if written == 0 {
87            return Err(Error::OutputFull);
88        }
89        padding_to_write -= written;
90    }
91    Ok(())
92}