radicle_cob/
signatures.rs

1// Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation>
2
3use std::{
4    collections::BTreeMap,
5    convert::TryFrom,
6    iter::FromIterator,
7    ops::{Deref, DerefMut},
8};
9
10use crypto::{ssh, PublicKey};
11use git_ext::commit::{
12    headers::Signature::{Pgp, Ssh},
13    Commit,
14};
15
16pub use ssh::ExtendedSignature;
17pub mod error;
18
19// FIXME(kim): This should really be a HashMap with a no-op Hasher -- PublicKey
20// collisions are catastrophic
21#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
22pub struct Signatures(BTreeMap<PublicKey, crypto::Signature>);
23
24impl Deref for Signatures {
25    type Target = BTreeMap<PublicKey, crypto::Signature>;
26
27    fn deref(&self) -> &Self::Target {
28        &self.0
29    }
30}
31
32impl DerefMut for Signatures {
33    fn deref_mut(&mut self) -> &mut Self::Target {
34        &mut self.0
35    }
36}
37
38impl From<ExtendedSignature> for Signatures {
39    fn from(ExtendedSignature { key, sig }: ExtendedSignature) -> Self {
40        let mut map = BTreeMap::new();
41        map.insert(key, sig);
42        map.into()
43    }
44}
45
46impl From<BTreeMap<PublicKey, crypto::Signature>> for Signatures {
47    fn from(map: BTreeMap<PublicKey, crypto::Signature>) -> Self {
48        Self(map)
49    }
50}
51
52impl From<Signatures> for BTreeMap<PublicKey, crypto::Signature> {
53    fn from(s: Signatures) -> Self {
54        s.0
55    }
56}
57
58impl TryFrom<&Commit> for Signatures {
59    type Error = error::Signatures;
60
61    fn try_from(value: &Commit) -> Result<Self, Self::Error> {
62        value
63            .signatures()
64            .filter_map(|signature| {
65                match signature {
66                    // Skip PGP signatures
67                    Pgp(_) => None,
68                    Ssh(pem) => Some(
69                        ExtendedSignature::from_pem(pem.as_bytes())
70                            .map_err(error::Signatures::from),
71                    ),
72                }
73            })
74            .map(|r| r.map(|es| (es.key, es.sig)))
75            .collect::<Result<_, _>>()
76    }
77}
78
79impl FromIterator<(PublicKey, crypto::Signature)> for Signatures {
80    fn from_iter<T>(iter: T) -> Self
81    where
82        T: IntoIterator<Item = (PublicKey, crypto::Signature)>,
83    {
84        Self(BTreeMap::from_iter(iter))
85    }
86}
87
88impl IntoIterator for Signatures {
89    type Item = (PublicKey, crypto::Signature);
90    type IntoIter = <BTreeMap<PublicKey, crypto::Signature> as IntoIterator>::IntoIter;
91
92    fn into_iter(self) -> Self::IntoIter {
93        self.0.into_iter()
94    }
95}
96
97impl Extend<ExtendedSignature> for Signatures {
98    fn extend<T>(&mut self, iter: T)
99    where
100        T: IntoIterator<Item = ExtendedSignature>,
101    {
102        for ExtendedSignature { key, sig } in iter {
103            self.insert(key, sig);
104        }
105    }
106}
107
108impl Extend<(PublicKey, crypto::Signature)> for Signatures {
109    fn extend<T>(&mut self, iter: T)
110    where
111        T: IntoIterator<Item = (PublicKey, crypto::Signature)>,
112    {
113        for (key, sig) in iter {
114            self.insert(key, sig);
115        }
116    }
117}