1#![cfg_attr(docsrs, doc(cfg(feature = "sign-tar")))]
2
3use std::io::{Read, Seek, Write, copy};
4
5use base64::Engine;
6use base64::prelude::BASE64_STANDARD;
7use ed25519_dalek::SIGNATURE_LENGTH;
8
9use super::{GatherSignatureDataError, gather_signature_data};
10use crate::constants::{
11 BUF_LIMIT, GZIP_END, GZIP_EXTRA, GZIP_START, HEADER_SIZE, SignatureCountLeInt,
12};
13use crate::{Prehash, SigningKey};
14
15crate::Error! {
16 pub struct SignTarError(Error) {
18 #[error("could not copy input to output")]
19 Copy(#[source] std::io::Error),
20 #[error("could not read input")]
21 InputRead(#[source] std::io::Error),
22 #[error("could not seek in input")]
23 InputSeek(#[source] std::io::Error),
24 #[error("could not seek in output")]
25 OutputSeek(#[source] std::io::Error),
26 #[error("could not write output")]
27 OutputWrite(#[source] std::io::Error),
28 #[error("could not sign pre-hashed message")]
29 Sign(#[source] GatherSignatureDataError),
30 #[error("too many keys")]
31 TooManyKeys,
32 }
33}
34
35pub fn copy_and_sign_tar<I, O>(
37 input: &mut I,
38 output: &mut O,
39 keys: &[SigningKey],
40 context: Option<&[u8]>,
41) -> Result<(), SignTarError>
42where
43 I: ?Sized + Seek + Read,
44 O: ?Sized + Seek + Write,
45{
46 if keys.len() > SignatureCountLeInt::MAX as usize {
47 return Err(Error::TooManyKeys.into());
48 }
49 let signature_bytes = SIGNATURE_LENGTH * keys.len() + HEADER_SIZE;
50 if (signature_bytes.saturating_add(2) / 3).saturating_mul(4) > BUF_LIMIT {
51 return Err(Error::TooManyKeys.into());
52 }
53
54 let prehashed_message = Prehash::calculate(input).map_err(Error::InputRead)?;
56 let buf = gather_signature_data(keys, &prehashed_message, context).map_err(Error::Sign)?;
57 let buf = BASE64_STANDARD.encode(buf);
58 if buf.len() > BUF_LIMIT {
59 return Err(Error::TooManyKeys.into());
60 }
61
62 input.rewind().map_err(Error::InputSeek)?;
64 let _: u64 = copy(input, output).map_err(Error::Copy)?;
65
66 let start = output.stream_position().map_err(Error::OutputSeek)?;
68 let mut start_buf = [0u8; 16];
69 write!(&mut start_buf[..], "{start:016x}").unwrap();
70
71 let mut tail = Vec::with_capacity(GZIP_EXTRA + buf.len());
72 tail.extend(GZIP_START);
73 tail.extend(buf.into_bytes()); tail.extend(start_buf); tail.extend(GZIP_END);
76 output.write_all(&tail).map_err(Error::OutputWrite)?;
77
78 Ok(())
79}