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 = SIGNATURE_LENGTH * keys.len() + HEADER_SIZE;
45 if signature_bytes > BUF_LIMIT {
46 return Err(Error::TooManyKeys.into());
47 }
48
49 write_padding(signature_bytes, output)?;
51 copy_zip(input, output).map_err(Error::Copy)?;
52
53 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 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}