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}