tor_checkable/
signed.rs

1//! Convenience implementation of a SelfSigned object.
2
3use tor_llcrypto::pk::{self, ValidatableSignature};
4
5/// A SignatureGated object is a self-signed object that's well-signed
6/// when one or more ValidatableSignature objects are correct.
7pub struct SignatureGated<T> {
8    /// The underlying object, which we only want to expose if the
9    /// signature(s) are right.
10    obj: T,
11    /// A list of ValidatableSignature; these all must be valid, or the
12    /// underlying object is incorrect.
13    signatures: Vec<Box<dyn ValidatableSignature>>,
14}
15
16impl<T> SignatureGated<T> {
17    /// Return a new SignatureGated object that will be treated as
18    /// correct if every one of the given set of signatures is valid.
19    pub fn new(obj: T, signatures: Vec<Box<dyn ValidatableSignature>>) -> Self {
20        SignatureGated { obj, signatures }
21    }
22
23    /// Consume this [`SignatureGated`], and return a new one with the same
24    /// bounds, applying `f` to its protected value.
25    ///
26    /// The caller must ensure that `f` does not make any assumptions about the
27    /// well-signedness of the protected value, or leak any of its contents in
28    /// an inappropriate way.
29    #[must_use]
30    pub fn dangerously_map<F, U>(self, f: F) -> SignatureGated<U>
31    where
32        F: FnOnce(T) -> U,
33    {
34        SignatureGated {
35            obj: f(self.obj),
36            signatures: self.signatures,
37        }
38    }
39}
40
41impl<T> super::SelfSigned<T> for SignatureGated<T> {
42    type Error = signature::Error;
43    fn dangerously_assume_wellsigned(self) -> T {
44        self.obj
45    }
46    fn is_well_signed(&self) -> Result<(), Self::Error> {
47        if pk::validate_all_sigs(&self.signatures[..]) {
48            Ok(())
49        } else {
50            Err(signature::Error::new())
51        }
52    }
53}
54
55#[cfg(test)]
56mod test {
57    // @@ begin test lint list maintained by maint/add_warning @@
58    #![allow(clippy::bool_assert_comparison)]
59    #![allow(clippy::clone_on_copy)]
60    #![allow(clippy::dbg_macro)]
61    #![allow(clippy::mixed_attributes_style)]
62    #![allow(clippy::print_stderr)]
63    #![allow(clippy::print_stdout)]
64    #![allow(clippy::single_char_pattern)]
65    #![allow(clippy::unwrap_used)]
66    #![allow(clippy::unchecked_duration_subtraction)]
67    #![allow(clippy::useless_vec)]
68    #![allow(clippy::needless_pass_by_value)]
69    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
70    use super::*;
71    use crate::SelfSigned;
72    use tor_llcrypto::pk::ValidatableSignature;
73
74    struct BadSig;
75    struct GoodSig;
76    impl ValidatableSignature for BadSig {
77        fn is_valid(&self) -> bool {
78            false
79        }
80    }
81    impl ValidatableSignature for GoodSig {
82        fn is_valid(&self) -> bool {
83            true
84        }
85    }
86
87    #[test]
88    fn test_sig_gated() {
89        // no signature objects means it's valid
90        let sg = SignatureGated::new(3_u32, Vec::new());
91        assert_eq!(sg.check_signature().unwrap(), 3_u32);
92
93        // any bad signature means it's bad.
94        let sg = SignatureGated::new(77_u32, vec![Box::new(BadSig)]);
95        assert!(sg.check_signature().is_err());
96        let sg = SignatureGated::new(
97            77_u32,
98            vec![Box::new(GoodSig), Box::new(BadSig), Box::new(GoodSig)],
99        );
100        assert!(sg.check_signature().is_err());
101
102        // All good signatures means it's good.
103        let sg = SignatureGated::new(103_u32, vec![Box::new(GoodSig)]);
104        assert_eq!(sg.check_signature().unwrap(), 103_u32);
105        let sg = SignatureGated::new(
106            104_u32,
107            vec![Box::new(GoodSig), Box::new(GoodSig), Box::new(GoodSig)],
108        );
109        assert_eq!(sg.check_signature().unwrap(), 104_u32);
110    }
111
112    #[test]
113    fn test_map() {
114        let good = SignatureGated::new("hello world...", vec![Box::new(GoodSig)]);
115        let good = good.dangerously_map(|s| &s[..11]);
116        let s = good.check_signature().unwrap();
117        assert_eq!(s, "hello world");
118
119        let bad = SignatureGated::new("hello world...", vec![Box::new(BadSig)]);
120        let still_bad = bad.dangerously_map(|s| &s[..11]);
121        assert!(still_bad.check_signature().is_err());
122    }
123}