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 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
30pub 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 write_padding(signature_bytes, output)?;
55 copy_zip(input, output).map_err(Error::Copy)?;
56
57 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 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}