blob: 776be5fba448febfc22f35b432ba99e8a83bd211 [file] [log] [blame]
//! The `verify` module performs signature verification.
use log::{debug, warn};
use serde_derive::Deserialize;
use std::collections::HashMap;
use crate::crypto::{KeyId, PublicKey, Signature};
use crate::error::Error;
use crate::interchange::DataInterchange;
use crate::metadata::{Metadata, RawSignedMetadata};
use crate::Result;
/// `Verified` is a wrapper type that signifies the inner type has had it's signature verified.
#[derive(Clone, Debug, PartialEq)]
pub struct Verified<T> {
value: T,
}
impl<T> Verified<T> {
// Create a new `Verified` around some type. This must be kept private to this module in order
// to guarantee the `V` can only be created through signature verification.
fn new(value: T) -> Self {
Verified { value }
}
}
impl<T> std::ops::Deref for Verified<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
/// Verify this metadata.
///
/// ```
/// # use chrono::prelude::*;
/// # use tuf::crypto::{Ed25519PrivateKey, PrivateKey, SignatureScheme, HashAlgorithm};
/// # use tuf::interchange::Json;
/// # use tuf::metadata::{SnapshotMetadataBuilder, SignedMetadata};
/// # use tuf::verify::verify_signatures;
///
/// let key_1: &[u8] = include_bytes!("../tests/ed25519/ed25519-1.pk8.der");
/// let key_1 = Ed25519PrivateKey::from_pkcs8(&key_1).unwrap();
///
/// let key_2: &[u8] = include_bytes!("../tests/ed25519/ed25519-2.pk8.der");
/// let key_2 = Ed25519PrivateKey::from_pkcs8(&key_2).unwrap();
///
/// let raw_snapshot = SnapshotMetadataBuilder::new()
/// .signed::<Json>(&key_1)
/// .unwrap()
/// .to_raw()
/// .unwrap();
///
/// assert!(verify_signatures(&raw_snapshot, 1, vec![key_1.public()]).is_ok());
///
/// // fail with increased threshold
/// assert!(verify_signatures(&raw_snapshot, 2, vec![key_1.public()]).is_err());
///
/// // fail when the keys aren't authorized
/// assert!(verify_signatures(&raw_snapshot, 1, vec![key_2.public()]).is_err());
///
/// // fail when the keys don't exist
/// assert!(verify_signatures(&raw_snapshot, 1, &[]).is_err());
pub fn verify_signatures<'a, D, M, I>(
raw_metadata: &RawSignedMetadata<D, M>,
threshold: u32,
authorized_keys: I,
) -> Result<Verified<M>>
where
D: DataInterchange,
M: Metadata,
I: IntoIterator<Item = &'a PublicKey>,
{
if threshold < 1 {
return Err(Error::VerificationFailure(
"Threshold must be strictly greater than zero".into(),
));
}
let authorized_keys = authorized_keys
.into_iter()
.map(|k| (k.key_id(), k))
.collect::<HashMap<&KeyId, &PublicKey>>();
// Extract the signatures and canonicalize the bytes.
let (signatures, canonical_bytes) = {
#[derive(Deserialize)]
pub struct SignedMetadata<D: DataInterchange> {
signatures: Vec<Signature>,
signed: D::RawData,
}
let unverified: SignedMetadata<D> = D::from_slice(raw_metadata.as_bytes())?;
if unverified.signatures.is_empty() {
return Err(Error::VerificationFailure(
"The metadata was not signed with any authorized keys.".into(),
));
}
let canonical_bytes = D::canonicalize(&unverified.signed)?;
(unverified.signatures, canonical_bytes)
};
let mut signatures_needed = threshold;
// Create a key_id->signature map to deduplicate the key_ids.
let signatures = signatures
.iter()
.map(|sig| (sig.key_id(), sig))
.collect::<HashMap<&KeyId, &Signature>>();
for (key_id, sig) in signatures {
match authorized_keys.get(key_id) {
Some(pub_key) => match pub_key.verify(&canonical_bytes, sig) {
Ok(()) => {
debug!("Good signature from key ID {:?}", pub_key.key_id());
signatures_needed -= 1;
}
Err(e) => {
warn!("Bad signature from key ID {:?}: {:?}", pub_key.key_id(), e);
}
},
None => {
warn!(
"Key ID {:?} was not found in the set of authorized keys.",
sig.key_id()
);
}
}
if signatures_needed == 0 {
break;
}
}
if signatures_needed > 0 {
return Err(Error::VerificationFailure(format!(
"Signature threshold not met: {}/{}",
threshold - signatures_needed,
threshold
)));
}
// Everything looks good so deserialize the metadata.
//
// Note: Canonicalization (or any other transformation of data) could modify or filter out
// information about the data. Therefore, while we've confirmed the canonical bytes are signed,
// we shouldn't interpret this as if the raw bytes were signed. So we deserialize from the
// `canonical_bytes`, rather than from `raw_meta.as_bytes()`.
let verified_metadata = D::from_slice(&canonical_bytes)?;
Ok(Verified::new(verified_metadata))
}