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 = SIGNATURE_LENGTH * keys.len() + HEADER_SIZE;
45    if signature_bytes > BUF_LIMIT {
46        return Err(Error::TooManyKeys.into());
47    }
48
49    // copy ZIP
50    write_padding(signature_bytes, output)?;
51    copy_zip(input, output).map_err(Error::Copy)?;
52
53    // gather signature
54    let _ = output
55        .seek(SeekFrom::Start(signature_bytes.try_into().unwrap()))
56        .map_err(Error::OutputSeek)?;
57    let prehashed_message = Prehash::calculate(output).map_err(Error::OutputRead)?;
58    let buf = gather_signature_data(keys, &prehashed_message, context).map_err(Error::Sign)?;
59
60    // write signature
61    output.rewind().map_err(Error::OutputSeek)?;
62    output.write_all(&buf).map_err(Error::OutputWrite)?;
63    Ok(())
64}
65
66fn write_padding<O>(mut padding_to_write: usize, output: &mut O) -> Result<(), Error>
67where
68    O: ?Sized + Write,
69{
70    while padding_to_write > 0 {
71        const PADDING: &[u8; 512] = &[0; 512];
72        let result = if padding_to_write > PADDING.len() {
73            let num_slices = padding_to_write.div_ceil(PADDING.len()).min(128);
74            let mut slices = vec![IoSlice::new(PADDING); num_slices];
75            slices[num_slices - 1] = IoSlice::new(&PADDING[..padding_to_write % PADDING.len()]);
76            output.write_vectored(&slices)
77        } else {
78            output.write(&PADDING[..padding_to_write])
79        };
80        let written = result.map_err(Error::OutputWrite)?;
81
82        if written == 0 {
83            return Err(Error::OutputFull);
84        }
85        padding_to_write -= written;
86    }
87    Ok(())
88}