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, MetadataPath, RawSignedMetadata};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Verified<T> {
value: T,
}
impl<T> Verified<T> {
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
}
}
pub fn verify_signatures<'a, D, M, I>(
role: &MetadataPath,
raw_metadata: &RawSignedMetadata<D, M>,
threshold: u32,
authorized_keys: I,
) -> Result<Verified<M>, Error>
where
D: DataInterchange,
M: Metadata,
I: IntoIterator<Item = &'a PublicKey>,
{
if threshold < 1 {
return Err(Error::MetadataThresholdMustBeGreaterThanZero(role.clone()));
}
let authorized_keys = authorized_keys
.into_iter()
.map(|k| (k.key_id(), k))
.collect::<HashMap<&KeyId, &PublicKey>>();
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())?;
let canonical_bytes = D::canonicalize(&unverified.signed)?;
(unverified.signatures, canonical_bytes)
};
let mut signatures_needed = threshold;
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(role, &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::MetadataMissingSignatures {
role: role.clone(),
number_of_valid_signatures: threshold - signatures_needed,
threshold,
});
}
let verified_metadata = D::from_slice(&canonical_bytes)?;
Ok(Verified::new(verified_metadata))
}