1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Functions to verify a signed file

#[cfg(feature = "verify-tar")]
mod tar;
#[cfg(feature = "verify-zip")]
mod zip;

use std::io::Read;

#[cfg(feature = "verify-tar")]
pub use self::tar::{verify_tar, VerifyTarError};
#[cfg(feature = "verify-zip")]
pub use self::zip::{verify_zip, VerifyZipError};
use crate::constants::{SignatureCountLeInt, BUF_LIMIT, HEADER_SIZE, MAGIC_HEADER};
use crate::{
    Prehash, Signature, SignatureError, VerifyingKey, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH,
};

crate::Error! {
    /// An error returned by [`collect_keys()`]
    pub struct CollectKeysError(KeysError) {
        #[error("the input was empty")]
        Empty,
        #[error("could not read key #{1}")]
        Io(#[source] std::io::Error, usize),
        #[error("input contained an illegal key at #{1}")]
        Signature(#[source] SignatureError, usize),
    }
}

/// Convert a slice of key bytes into a [`Vec`] of [`VerifyingKey`]s.
pub fn collect_keys<K>(keys: K) -> Result<Vec<VerifyingKey>, CollectKeysError>
where
    K: IntoIterator<Item = std::io::Result<[u8; PUBLIC_KEY_LENGTH]>>,
{
    let keys = keys
        .into_iter()
        .enumerate()
        .map(|(idx, key)| {
            let key = key.map_err(|err| KeysError::Io(err, idx))?;
            VerifyingKey::from_bytes(&key).map_err(|err| KeysError::Signature(err, idx))
        })
        .collect::<Result<Vec<_>, _>>()?;
    if keys.is_empty() {
        return Err(KeysError::Empty.into());
    }
    Ok(keys)
}

/// No matching key/signature pair found
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("no matching key/signature pair found")]
pub struct NoMatch;

/// Iterate signatures and keys, and return the indices of the first match
/// match
pub fn find_match(
    keys: &[VerifyingKey],
    signatures: &[Signature],
    prehashed_message: &Prehash,
    context: Option<&[u8]>,
) -> Result<(usize, usize), NoMatch> {
    for (key_idx, key) in keys.iter().enumerate() {
        for (sig_idx, signature) in signatures.iter().enumerate() {
            let is_ok = key
                .verify_prehashed_strict(prehashed_message.0.clone(), context, signature)
                .is_ok();
            if is_ok {
                return Ok((key_idx, sig_idx));
            }
        }
    }
    Err(NoMatch)
}

crate::Error! {
    /// An error returned by [`read_signatures()`]
    pub struct ReadSignaturesError(SignaturesError) {
        #[error("the input contained no signatures")]
        Empty,
        #[error("could not read signatures")]
        Read(#[source] std::io::Error),
        #[error("the expected magic header was missing or corrupted")]
        MagicHeader,
        #[error("input contained an illegal signature at #{1}")]
        Signature(#[source] SignatureError, usize),
    }
}

/// Collect all signatures in a file
pub fn read_signatures<I>(input: &mut I) -> Result<Vec<Signature>, ReadSignaturesError>
where
    I: ?Sized + Read,
{
    let mut header = [0; HEADER_SIZE];
    input
        .read_exact(&mut header)
        .map_err(SignaturesError::Read)?;
    if header[..MAGIC_HEADER.len()] != *MAGIC_HEADER {
        return Err(SignaturesError::MagicHeader.into());
    }

    let signature_count = header[MAGIC_HEADER.len()..].try_into().unwrap();
    let signature_count = SignatureCountLeInt::from_le_bytes(signature_count) as usize;
    if signature_count == 0 {
        return Err(SignaturesError::Empty.into());
    }
    let signature_bytes = signature_count * SIGNATURE_LENGTH;
    if signature_bytes > BUF_LIMIT {
        return Err(SignaturesError::MagicHeader.into());
    }

    let mut signatures = vec![0; signature_bytes];
    input
        .read_exact(&mut signatures)
        .map_err(SignaturesError::Read)?;

    let signatures = signatures
        .chunks_exact(SIGNATURE_LENGTH)
        .enumerate()
        .map(|(idx, bytes)| {
            Signature::from_slice(bytes).map_err(|err| SignaturesError::Signature(err, idx))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(signatures)
}