// Copyright © 2019-2020 The Radicle Foundation // // This file is part of radicle-link, distributed under the GPLv3 with Radicle // Linking Exception. For full terms see the included LICENSE file. use std::{ collections::BTreeMap, convert::TryFrom, iter::FromIterator, ops::{Deref, DerefMut}, }; use crypto::{ssh::ExtendedSignature, PublicKey}; use git_commit::{ Commit, Signature::{Pgp, Ssh}, }; pub mod error; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Signature { pub(super) key: PublicKey, pub(super) sig: crypto::Signature, } impl Signature { pub fn verify(&self, payload: &[u8]) -> bool { self.key.verify(payload, &self.sig).is_ok() } } impl From for ExtendedSignature { fn from(sig: Signature) -> Self { Self::new(sig.key, sig.sig) } } impl From for Signature { fn from(ex: ExtendedSignature) -> Self { let (key, sig) = ex.into(); Self { key, sig } } } impl From<(PublicKey, crypto::Signature)> for Signature { fn from((key, sig): (PublicKey, crypto::Signature)) -> Self { Self { key, sig } } } // FIXME(kim): This should really be a HashMap with a no-op Hasher -- PublicKey // collisions are catastrophic #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct Signatures(BTreeMap); impl Deref for Signatures { type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Signatures { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for Signatures { fn from(Signature { key, sig }: Signature) -> Self { let mut map = BTreeMap::new(); map.insert(key, sig); map.into() } } impl From> for Signatures { fn from(map: BTreeMap) -> Self { Self(map) } } impl From for BTreeMap { fn from(s: Signatures) -> Self { s.0 } } impl TryFrom<&Commit> for Signatures { type Error = error::Signatures; fn try_from(value: &Commit) -> Result { value .signatures() .filter_map(|signature| { match signature { // Skip PGP signatures Pgp(_) => None, Ssh(armored) => Some( ExtendedSignature::from_armored(armored.as_bytes()) .map_err(error::Signatures::from), ), } }) .map(|ex| ex.map(|ex| ex.into())) .collect::>() } } impl FromIterator<(PublicKey, crypto::Signature)> for Signatures { fn from_iter(iter: T) -> Self where T: IntoIterator, { Self(BTreeMap::from_iter(iter)) } } impl IntoIterator for Signatures { type Item = (PublicKey, crypto::Signature); type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Extend for Signatures { fn extend(&mut self, iter: T) where T: IntoIterator, { for Signature { key, sig } in iter { self.insert(key, sig); } } } impl Extend<(PublicKey, crypto::Signature)> for Signatures { fn extend(&mut self, iter: T) where T: IntoIterator, { for (key, sig) in iter { self.insert(key, sig); } } }