Skip to main content

tuf/
verify.rs

1//! The `verify` module performs signature verification.
2
3use log::{debug, warn};
4use serde::Deserialize;
5use std::collections::HashMap;
6
7use crate::crypto::{KeyId, PublicKey, Signature};
8use crate::error::Error;
9use crate::metadata::{Metadata, MetadataPath, RawSignedMetadata};
10use crate::pouf::Pouf;
11
12/// `Verified` is a wrapper type that signifies the inner type has had it's signature verified.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Verified<T> {
15    value: T,
16}
17
18impl<T> Verified<T> {
19    // Create a new `Verified` around some type. This must be kept private to this module in order
20    // to guarantee the `V` can only be created through signature verification.
21    fn new(value: T) -> Self {
22        Verified { value }
23    }
24}
25
26impl<T> std::ops::Deref for Verified<T> {
27    type Target = T;
28
29    fn deref(&self) -> &Self::Target {
30        &self.value
31    }
32}
33
34/// Verify this metadata.
35///
36/// ```
37/// # use chrono::prelude::*;
38/// # use tuf::crypto::{Ed25519PrivateKey, PrivateKey, SignatureScheme, HashAlgorithm};
39/// # use tuf::pouf::Pouf1;
40/// # use tuf::metadata::{MetadataPath, SnapshotMetadataBuilder, SignedMetadata};
41/// # use tuf::verify::verify_signatures;
42///
43/// let key_1: &[u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
44/// let key_1 = Ed25519PrivateKey::from_pkcs8(&key_1).unwrap();
45///
46/// let key_2: &[u8] = include_bytes!("../tests/ed25519/ed25519-2.pk8.der");
47/// let key_2 = Ed25519PrivateKey::from_pkcs8(&key_2).unwrap();
48///
49/// let raw_snapshot = SnapshotMetadataBuilder::new()
50///     .signed::<Pouf1>(&key_1)
51///     .unwrap()
52///     .to_raw()
53///     .unwrap();
54///
55/// assert!(verify_signatures(
56///     &MetadataPath::snapshot(),
57///     &raw_snapshot,
58///     1,
59///     vec![key_1.public()],
60/// ).is_ok());
61///
62/// // fail with increased threshold
63/// assert!(verify_signatures(
64///     &MetadataPath::snapshot(),
65///     &raw_snapshot,
66///     2,
67///     vec![key_1.public()],
68/// ).is_err());
69///
70/// // fail when the keys aren't authorized
71/// assert!(verify_signatures(
72///     &MetadataPath::snapshot(),
73///     &raw_snapshot,
74///     1,
75///     vec![key_2.public()],
76/// ).is_err());
77///
78/// // fail when the keys don't exist
79/// assert!(verify_signatures(
80///     &MetadataPath::snapshot(),
81///     &raw_snapshot,
82///     1,
83///     &[],
84/// ).is_err());
85pub fn verify_signatures<'a, D, M, I>(
86    role: &MetadataPath,
87    raw_metadata: &RawSignedMetadata<D, M>,
88    threshold: u32,
89    authorized_keys: I,
90) -> Result<Verified<M>, Error>
91where
92    D: Pouf,
93    M: Metadata,
94    I: IntoIterator<Item = &'a PublicKey>,
95{
96    if threshold < 1 {
97        return Err(Error::MetadataThresholdMustBeGreaterThanZero(role.clone()));
98    }
99
100    let authorized_keys = authorized_keys
101        .into_iter()
102        .map(|k| (k.key_id(), k))
103        .collect::<HashMap<&KeyId, &PublicKey>>();
104
105    // Extract the signatures and canonicalize the bytes.
106    let (signatures, canonical_bytes) = {
107        #[derive(Deserialize)]
108        pub struct SignedMetadata<D: Pouf> {
109            signatures: Vec<Signature>,
110            signed: D::RawData,
111        }
112
113        let unverified: SignedMetadata<D> = D::from_slice(raw_metadata.as_bytes())?;
114
115        let canonical_bytes = D::canonicalize(&unverified.signed)?;
116        (unverified.signatures, canonical_bytes)
117    };
118
119    let mut signatures_needed = threshold;
120
121    // Create a key_id->signature map to deduplicate the key_ids.
122    let signatures = signatures
123        .iter()
124        .map(|sig| (sig.key_id(), sig))
125        .collect::<HashMap<&KeyId, &Signature>>();
126
127    for (key_id, sig) in signatures {
128        match authorized_keys.get(key_id) {
129            Some(pub_key) => match pub_key.verify(role, &canonical_bytes, sig) {
130                Ok(()) => {
131                    debug!("Good signature from key ID {:?}", pub_key.key_id());
132                    signatures_needed -= 1;
133                }
134                Err(e) => {
135                    warn!("Bad signature from key ID {:?}: {:?}", pub_key.key_id(), e);
136                }
137            },
138            None => {
139                warn!(
140                    "Key ID {:?} was not found in the set of authorized keys.",
141                    sig.key_id()
142                );
143            }
144        }
145        if signatures_needed == 0 {
146            break;
147        }
148    }
149
150    if signatures_needed > 0 {
151        return Err(Error::MetadataMissingSignatures {
152            role: role.clone(),
153            number_of_valid_signatures: threshold - signatures_needed,
154            threshold,
155        });
156    }
157
158    // Everything looks good so deserialize the metadata.
159    //
160    // Note: Canonicalization (or any other transformation of data) could modify or filter out
161    // information about the data. Therefore, while we've confirmed the canonical bytes are signed,
162    // we shouldn't interpret this as if the raw bytes were signed. So we deserialize from the
163    // `canonical_bytes`, rather than from `raw_meta.as_bytes()`.
164    let verified_metadata = D::from_slice(&canonical_bytes)?;
165
166    Ok(Verified::new(verified_metadata))
167}