zipsign_api/
verify_unsign_tar.rs1use std::io::{Read, Seek, SeekFrom};
2use std::mem::size_of;
3
4use base64::Engine;
5use base64::prelude::BASE64_STANDARD;
6use ed25519_dalek::{SIGNATURE_LENGTH, Signature, SignatureError};
7
8use crate::constants::{
9 BUF_LIMIT, GZIP_END, GZIP_START, HEADER_SIZE, MAGIC_HEADER, SignatureCountLeInt,
10};
11
12#[derive(Debug, thiserror::Error)]
13pub(crate) enum TarFindDataStartAndLenError {
14 #[error("the expected last GZIP block was missing or corrupted")]
15 Gzip,
16 #[error("could not read input")]
17 Read(#[source] std::io::Error),
18 #[error("could not seek inside the input")]
19 Seek(#[source] std::io::Error),
20 #[error("too many signatures in input")]
21 TooManySignatures,
22}
23
24pub(crate) fn tar_find_data_start_and_len<I>(
25 input: &mut I,
26) -> Result<(u64, usize), TarFindDataStartAndLenError>
27where
28 I: ?Sized + Read + Seek,
29{
30 let mut tail = [0; u64::BITS as usize / 4 + GZIP_END.len()];
31 let data_end = input
32 .seek(SeekFrom::End(-(tail.len() as i64)))
33 .map_err(TarFindDataStartAndLenError::Seek)?;
34
35 input
36 .read_exact(&mut tail)
37 .map_err(TarFindDataStartAndLenError::Read)?;
38 if tail[u64::BITS as usize / 4..] != *GZIP_END {
39 return Err(TarFindDataStartAndLenError::Gzip);
40 }
41 let Ok(gzip_start) = std::str::from_utf8(&tail[..16]) else {
42 return Err(TarFindDataStartAndLenError::Gzip);
43 };
44 let Ok(gzip_start) = u64::from_str_radix(gzip_start, 16) else {
45 return Err(TarFindDataStartAndLenError::Gzip);
46 };
47 let Some(data_start) = gzip_start.checked_add(10) else {
48 return Err(TarFindDataStartAndLenError::Gzip);
49 };
50 let Some(data_len) = data_end.checked_sub(data_start) else {
51 return Err(TarFindDataStartAndLenError::Gzip);
52 };
53 let Ok(data_len) = usize::try_from(data_len) else {
54 return Err(TarFindDataStartAndLenError::Gzip);
55 };
56 if data_len > BUF_LIMIT {
57 return Err(TarFindDataStartAndLenError::TooManySignatures);
58 }
59
60 Ok((gzip_start, data_len + GZIP_START.len()))
61}
62
63#[derive(Debug, thiserror::Error)]
64pub(crate) enum TarReadSignaturesError {
65 #[error("the input contained invalid base64 encoded data")]
66 Base64,
67 #[error("the input contained no signatures")]
68 Empty,
69 #[error("the expected last GZIP block was missing or corrupted")]
70 Gzip,
71 #[error("the encoded length did not fit the expected length")]
72 LengthMismatch,
73 #[error("the expected magic header was missing or corrupted")]
74 MagicHeader,
75 #[error("could not read input")]
76 Read(#[source] std::io::Error),
77 #[error("the input contained an illegal signature at index #{1}")]
78 Signature(#[source] SignatureError, usize),
79}
80
81pub(crate) fn tar_read_signatures<I>(
82 data_start: u64,
83 data_len: usize,
84 input: &mut I,
85) -> Result<Vec<Signature>, TarReadSignaturesError>
86where
87 I: ?Sized + Read + Seek,
88{
89 let _: u64 = input
90 .seek(SeekFrom::Start(data_start))
91 .map_err(TarReadSignaturesError::Read)?;
92
93 let mut data = vec![0; data_len];
94 input
95 .read_exact(&mut data)
96 .map_err(TarReadSignaturesError::Read)?;
97
98 if data[..GZIP_START.len()] != *GZIP_START {
99 return Err(TarReadSignaturesError::Gzip);
100 }
101 let Ok(data) = BASE64_STANDARD.decode(&data[GZIP_START.len()..]) else {
102 return Err(TarReadSignaturesError::Base64);
103 };
104 if data.len() < HEADER_SIZE {
105 return Err(TarReadSignaturesError::MagicHeader);
106 }
107 if data[..MAGIC_HEADER.len()] != *MAGIC_HEADER {
108 return Err(TarReadSignaturesError::MagicHeader);
109 }
110
111 let signature_count = data[MAGIC_HEADER.len()..][..size_of::<SignatureCountLeInt>()]
112 .try_into()
113 .unwrap();
114 let signature_count = SignatureCountLeInt::from_le_bytes(signature_count) as usize;
115 if signature_count == 0 {
116 return Err(TarReadSignaturesError::Empty);
117 }
118 if data.len() != HEADER_SIZE + signature_count * SIGNATURE_LENGTH {
119 return Err(TarReadSignaturesError::LengthMismatch);
120 }
121
122 let signatures = data[HEADER_SIZE..]
123 .chunks_exact(SIGNATURE_LENGTH)
124 .enumerate()
125 .map(|(idx, bytes)| {
126 Signature::from_slice(bytes).map_err(|err| TarReadSignaturesError::Signature(err, idx))
127 })
128 .collect::<Result<Vec<_>, _>>()?;
129 Ok(signatures)
130}