zipsign_api/verify/
mod.rs

1//! Functions to verify a signed file
2
3#[cfg(feature = "verify-tar")]
4mod tar;
5#[cfg(feature = "verify-zip")]
6mod zip;
7
8use std::io::Read;
9
10#[cfg(feature = "verify-tar")]
11pub use self::tar::{VerifyTarError, verify_tar};
12#[cfg(feature = "verify-zip")]
13pub use self::zip::{VerifyZipError, verify_zip};
14use crate::constants::{BUF_LIMIT, HEADER_SIZE, MAGIC_HEADER, SignatureCountLeInt};
15use crate::{
16    PUBLIC_KEY_LENGTH, Prehash, SIGNATURE_LENGTH, Signature, SignatureError, VerifyingKey,
17};
18
19crate::Error! {
20    /// An error returned by [`collect_keys()`]
21    pub struct CollectKeysError(KeysError) {
22        #[error("the input was empty")]
23        Empty,
24        #[error("could not read key #{1}")]
25        Io(#[source] std::io::Error, usize),
26        #[error("input contained an illegal key at #{1}")]
27        Signature(#[source] SignatureError, usize),
28    }
29}
30
31/// Convert a slice of key bytes into a [`Vec`] of [`VerifyingKey`]s.
32pub fn collect_keys<K>(keys: K) -> Result<Vec<VerifyingKey>, CollectKeysError>
33where
34    K: IntoIterator<Item = std::io::Result<[u8; PUBLIC_KEY_LENGTH]>>,
35{
36    let keys = keys
37        .into_iter()
38        .enumerate()
39        .map(|(idx, key)| {
40            let key = key.map_err(|err| KeysError::Io(err, idx))?;
41            VerifyingKey::from_bytes(&key).map_err(|err| KeysError::Signature(err, idx))
42        })
43        .collect::<Result<Vec<_>, _>>()?;
44    if keys.is_empty() {
45        return Err(KeysError::Empty.into());
46    }
47    Ok(keys)
48}
49
50/// No matching key/signature pair found
51#[derive(Debug, Clone, Copy, thiserror::Error)]
52#[error("no matching key/signature pair found")]
53pub struct NoMatch;
54
55/// Iterate signatures and keys, and return the indices of the first match
56/// match
57pub fn find_match(
58    keys: &[VerifyingKey],
59    signatures: &[Signature],
60    prehashed_message: &Prehash,
61    context: Option<&[u8]>,
62) -> Result<(usize, usize), NoMatch> {
63    for (key_idx, key) in keys.iter().enumerate() {
64        for (sig_idx, signature) in signatures.iter().enumerate() {
65            let is_ok = key
66                .verify_prehashed_strict(prehashed_message.0.clone(), context, signature)
67                .is_ok();
68            if is_ok {
69                return Ok((key_idx, sig_idx));
70            }
71        }
72    }
73    Err(NoMatch)
74}
75
76crate::Error! {
77    /// An error returned by [`read_signatures()`]
78    pub struct ReadSignaturesError(SignaturesError) {
79        #[error("the input contained no signatures")]
80        Empty,
81        #[error("could not read signatures")]
82        Read(#[source] std::io::Error),
83        #[error("the expected magic header was missing or corrupted")]
84        MagicHeader,
85        #[error("input contained an illegal signature at #{1}")]
86        Signature(#[source] SignatureError, usize),
87    }
88}
89
90/// Collect all signatures in a file
91pub fn read_signatures<I>(input: &mut I) -> Result<Vec<Signature>, ReadSignaturesError>
92where
93    I: ?Sized + Read,
94{
95    let mut header = [0; HEADER_SIZE];
96    input
97        .read_exact(&mut header)
98        .map_err(SignaturesError::Read)?;
99    if header[..MAGIC_HEADER.len()] != *MAGIC_HEADER {
100        return Err(SignaturesError::MagicHeader.into());
101    }
102
103    let signature_count = header[MAGIC_HEADER.len()..].try_into().unwrap();
104    let signature_count = SignatureCountLeInt::from_le_bytes(signature_count) as usize;
105    if signature_count == 0 {
106        return Err(SignaturesError::Empty.into());
107    }
108    let signature_bytes = signature_count * SIGNATURE_LENGTH;
109    if signature_bytes > BUF_LIMIT {
110        return Err(SignaturesError::MagicHeader.into());
111    }
112
113    let mut signatures = vec![0; signature_bytes];
114    input
115        .read_exact(&mut signatures)
116        .map_err(SignaturesError::Read)?;
117
118    let signatures = signatures
119        .chunks_exact(SIGNATURE_LENGTH)
120        .enumerate()
121        .map(|(idx, bytes)| {
122            Signature::from_slice(bytes).map_err(|err| SignaturesError::Signature(err, idx))
123        })
124        .collect::<Result<Vec<_>, _>>()?;
125
126    Ok(signatures)
127}