wot_network/
search.rs

1//! Data structures and traits for path searches in a Web of Trust
2
3use crate::{Binding, Certificate, Certification, Delegation, Edge, Network};
4
5/// An interface for searching paths that authenticate identity bindings in a Web of Trust network.
6pub trait WotSearch<'a> {
7    fn new(network: &'a Network, roots: &'a [TrustAnchor]) -> Self;
8
9    /// Authenticate the link between `target_id` and `target_userid` with an OpenPGP "Web of Trust" query.
10    ///
11    /// Searches for enough paths to satisfy `target_trust_amount`, if possible.
12    fn search(&self, binding: &Binding, target_trust_amount: usize) -> Paths;
13}
14
15/// A list of [Path]s and their *effective amount* in this search.
16///
17/// NOTE: The effective amount of a [Path] in the context of a [Paths] may be smaller than the
18/// path's standalone amount, because edges shared with other [Path]s may restrict flow capacity.
19#[derive(Debug)]
20pub struct Paths {
21    paths: Vec<(Path, u8)>,
22}
23
24impl Paths {
25    pub fn new() -> Self {
26        Self {
27            paths: Default::default(),
28        }
29    }
30
31    pub fn get(&self) -> &[(Path, u8)] {
32        &self.paths
33    }
34
35    /// Add a [Path] to this list of paths.
36    ///
37    /// The `effective_amount` of the path in this set of paths can be smaller than the paths
38    /// actual amount (it can be limited by the available remaining flow along one of its edges,
39    /// caused by other paths in this `Paths`)
40    pub fn add(&mut self, path: Path, effective_amount: u8) {
41        assert!(
42            effective_amount <= path.amount(),
43            "Effective amount cannot exceed actual amount of the path."
44        );
45
46        self.paths.push((path, effective_amount));
47    }
48
49    /// Sum of the effective amounts of each [Path] in this [Paths] object.
50    ///
51    /// This is the total amount of evidence that the [Network] offers for an identity binding,
52    /// based on the configured [TrustAnchor]s
53    pub fn total_amount(&self) -> usize {
54        self.paths.iter().map(|(_, amount)| *amount as usize).sum()
55    }
56}
57
58impl Default for Paths {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64/// A Path starts from an anchoring [Certificate] and consists of a list of delegation edges which lead
65/// to a target identity binding.
66#[derive(Debug, Clone, Eq, Hash, PartialEq)]
67pub struct Path {
68    start: Certificate,
69    delegations: Vec<Delegation>,
70    binding: Option<Certification>,
71}
72
73impl Path {
74    pub fn new(start: Certificate) -> Self {
75        Self {
76            start,
77            delegations: vec![],
78            binding: None,
79        }
80    }
81
82    /// First cert of the path.
83    pub fn first(&self) -> &Certificate {
84        &self.start
85    }
86
87    /// The list of delegation edges that lead from the anchor to the target binding
88    pub fn delegations(&self) -> &[Delegation] {
89        &self.delegations
90    }
91
92    /// The target binding of this [Path]
93    pub fn binding(&self) -> Option<&Certification> {
94        self.binding.as_ref()
95    }
96
97    /// The mathematical "amount" of this path.
98    ///
99    /// This corresponds to the minimum "amount" of any edge in the path.
100    pub(crate) fn amount(&self) -> u8 {
101        // We start with the trust amount of the final binding edge (always 120) as the ceiling
102        let mut min = 120;
103
104        // ... and lower it to the minimal amount on any delegation edge.
105        if self.delegations().is_empty() {
106            min
107        } else {
108            for edge in &self.delegations {
109                min = u8::min(min, edge.trust_amount());
110            }
111            min
112        }
113    }
114
115    /// A list of certificates in this [Path], from the starting [Certificate], followed by all
116    /// [Delegation] edge target [Certificate]s, and the target [Certificate] in the final [Certification] edge.
117    ///
118    /// This list format is mainly mostly for informal uses, such as comparing test results with
119    /// expected paths.
120    pub fn certificates(&self) -> Vec<Certificate> {
121        let mut certs = vec![self.first().clone()];
122        for edge in self.delegations() {
123            certs.push(edge.target().clone())
124        }
125
126        if let Some(b) = &self.binding {
127            certs.push(b.target.clone())
128        } else {
129            panic!("Incomplete path")
130        }
131
132        certs
133    }
134
135    /// The length of this path, counted in nodes: A path with a single edge from node A to node B
136    /// is of length 2.
137    pub fn length(&self) -> usize {
138        // FIXME: do we need to subtract one if the last edge is a redirection to a different
139        // user id in the certificate we're already at?
140        self.delegations.len() + 2
141    }
142
143    pub fn edges(&self) -> impl Iterator<Item = Edge> + '_ {
144        self.delegations()
145            .iter()
146            .map(|d| Edge::Delegation(d.clone()))
147            .chain(self.binding().map(|b| Edge::Certification(b.clone())))
148    }
149
150    /// Add a delegation edge
151    pub fn push(&mut self, edge: Delegation) {
152        assert!(
153            self.binding.is_none(),
154            "Once the binding is set, no more delegations should be added"
155        );
156
157        self.delegations.push(edge)
158    }
159
160    pub fn set_binding(&mut self, bind: Certification) {
161        assert!(
162            self.binding.is_none(),
163            "A binding should not normally be replaced"
164        );
165
166        self.binding = Some(bind)
167    }
168}
169
170/// A certificate that acts as a root of trust in a Web of Trust search
171#[derive(Debug, Clone, Eq, Hash, PartialEq)]
172pub struct TrustAnchor {
173    id: Certificate,
174    trust_amount: u8,
175}
176
177impl TrustAnchor {
178    /// FIXME: is a `trust_amount` over 120 illegal?
179    pub fn new(cert_id: Certificate, trust_amount: u8) -> Self {
180        Self {
181            id: cert_id,
182            trust_amount,
183        }
184    }
185
186    pub fn id(&self) -> &Certificate {
187        &self.id
188    }
189
190    pub fn amount(&self) -> u8 {
191        self.trust_amount
192    }
193}