trust_graph/
trust_graph.rs

1/*
2 * Copyright 2020 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::certificate::CertificateError::CertificateLengthError;
18use crate::certificate::{Certificate, CertificateError};
19use crate::chain::Chain;
20use crate::public_key_hashable::PublicKeyHashable as PK;
21use crate::revoke::Revocation;
22use crate::revoke::RevokeError;
23use crate::trust::Trust;
24use crate::trust_graph::TrustGraphError::{
25    CertificateCheckError, EmptyChain, InternalStorageError, NoRoot,
26};
27use crate::trust_graph_storage::Storage;
28use crate::trust_relation::Auth;
29use crate::{StorageError, TrustError};
30use fluence_keypair::public_key::PublicKey;
31use nonempty::NonEmpty;
32use std::borrow::Borrow;
33use std::collections::{HashSet, VecDeque};
34use std::convert::{From, Into};
35use std::result::Result;
36use std::time::Duration;
37use thiserror::Error as ThisError;
38
39/// for simplicity, we store `n` where Weight = 1/n^2
40pub type WeightFactor = u32;
41
42pub static MAX_WEIGHT_FACTOR: u32 = 16;
43
44/// Graph to efficiently calculate weights of certificates and get chains of certificates.
45/// TODO serialization/deserialization
46/// TODO export a certificate from graph
47#[allow(dead_code)]
48pub struct TrustGraph<S>
49where
50    S: Storage,
51{
52    storage: S,
53}
54
55#[derive(ThisError, Debug)]
56pub enum TrustGraphError {
57    #[error("Internal storage error: {0}")]
58    InternalStorageError(Box<dyn StorageError>),
59    #[error("There is no root for this certificate.")]
60    NoRoot,
61    #[error("Chain is empty")]
62    EmptyChain,
63    #[error("Certificate check error: {0}")]
64    CertificateCheckError(
65        #[from]
66        #[source]
67        CertificateError,
68    ),
69    #[error("Error on revoking a trust: {0}")]
70    RevokeCheckError(
71        #[from]
72        #[source]
73        RevokeError,
74    ),
75    #[error("Path to {0} not found")]
76    AddTrustError(String),
77    #[error("Trust verification error: {0}")]
78    TrustVerificationError(
79        #[from]
80        #[source]
81        TrustError,
82    ),
83}
84
85impl<T: StorageError + 'static> From<T> for TrustGraphError {
86    fn from(err: T) -> Self {
87        InternalStorageError(Box::new(err))
88    }
89}
90
91impl From<TrustGraphError> for String {
92    fn from(err: TrustGraphError) -> Self {
93        format!("{err}")
94    }
95}
96
97fn get_weight_factor(max_chain_len: u32) -> u32 {
98    MAX_WEIGHT_FACTOR.saturating_sub(max_chain_len)
99}
100
101pub fn get_weight_from_factor(wf: WeightFactor) -> u32 {
102    2u32.pow(MAX_WEIGHT_FACTOR.saturating_sub(wf))
103}
104
105impl<S> TrustGraph<S>
106where
107    S: Storage,
108{
109    pub fn new(storage: S) -> Self {
110        Self { storage }
111    }
112
113    /// Insert new root weight
114    pub fn set_root(&mut self, pk: PublicKey, max_chain_len: u32) -> Result<(), TrustGraphError> {
115        Ok(self
116            .storage
117            .set_root_weight_factor(pk.into(), get_weight_factor(max_chain_len))?)
118    }
119
120    pub fn add_trust<T, P>(
121        &mut self,
122        trust: T,
123        issued_by: P,
124        cur_time: Duration,
125    ) -> Result<u32, TrustGraphError>
126    where
127        T: Borrow<Trust>,
128        P: Borrow<PublicKey>,
129    {
130        Trust::verify(trust.borrow(), issued_by.borrow(), cur_time)?;
131        let next_weight = self.get_next_weight(
132            issued_by.borrow().as_ref(),
133            trust.borrow().issued_for.as_ref(),
134            cur_time,
135        )?;
136
137        if next_weight == 0u32 {
138            return Ok(0u32);
139        }
140
141        let auth = Auth {
142            trust: trust.borrow().clone(),
143            issued_by: issued_by.borrow().clone(),
144        };
145
146        self.storage.update_auth(auth, cur_time)?;
147
148        Ok(next_weight)
149    }
150
151    /// Certificate is a chain of trusts, add this chain to graph
152    pub fn add<C>(&mut self, cert: C, cur_time: Duration) -> Result<(), TrustGraphError>
153    where
154        C: Borrow<Certificate>,
155    {
156        let chain = &cert.borrow().chain;
157        let mut issued_by = chain.first().ok_or(EmptyChain)?.issued_for.clone();
158
159        // TODO: optimize to check only root weight
160        for trust in chain {
161            self.add_trust(trust, issued_by, cur_time)?;
162            issued_by = trust.issued_for.clone();
163        }
164
165        Ok(())
166    }
167
168    fn get_next_weight(
169        &mut self,
170        issued_by: &PK,
171        issued_for: &PK,
172        cur_time: Duration,
173    ) -> Result<u32, TrustGraphError> {
174        let issued_by_weight = self.weight(issued_by.as_ref(), cur_time)?;
175
176        // self-signed trust has same weight as max weight of issuer
177        if issued_by.eq(issued_for) {
178            Ok(issued_by_weight)
179        } else {
180            Ok(issued_by_weight / 2)
181        }
182    }
183
184    /// Get the maximum weight of trust for one public key.
185    pub fn weight<P>(&mut self, pk: P, cur_time: Duration) -> Result<u32, TrustGraphError>
186    where
187        P: Borrow<PublicKey>,
188    {
189        let mut max_weight = 0;
190        if let Some(weight_factor) = self.storage.get_root_weight_factor(pk.borrow().as_ref())? {
191            max_weight = get_weight_from_factor(weight_factor);
192        }
193
194        // get all possible certificates from the given public key to all roots in the graph
195        let certs = self.get_all_certs(pk, cur_time)?;
196        if let Some(weight_factor) = self.certificates_weight_factor(certs)? {
197            max_weight = std::cmp::max(max_weight, get_weight_from_factor(weight_factor))
198        }
199
200        Ok(max_weight)
201    }
202
203    /// Get the maximum weight of trust for one public key.
204    /// for all chains which contain `issuer`
205    pub fn weight_from<P>(
206        &mut self,
207        issued_for: P,
208        issuer: P,
209        cur_time: Duration,
210    ) -> Result<u32, TrustGraphError>
211    where
212        P: Borrow<PublicKey>,
213    {
214        let mut max_weight = 0;
215
216        // get all possible certificates from the given public key to all roots in the graph
217        // which contain `issuer`
218        let certs = self.get_all_certs_from(issued_for, issuer, cur_time)?;
219        if let Some(weight_factor) = self.certificates_weight_factor(certs)? {
220            max_weight = std::cmp::max(max_weight, get_weight_from_factor(weight_factor))
221        }
222
223        Ok(max_weight)
224    }
225
226    /// Calculate weight from given certificates
227    /// Returns None if there is no such public key
228    /// or some trust between this key and a root key is revoked.
229    pub fn certificates_weight_factor<C, I>(
230        &self,
231        certs: I,
232    ) -> Result<Option<WeightFactor>, TrustGraphError>
233    where
234        C: Borrow<Certificate>,
235        I: IntoIterator<Item = C>,
236    {
237        let mut certs = certs.into_iter().peekable();
238        // if there are no certificates for the given public key, there is no info about this public key
239        // or some elements of possible certificate chains was revoked
240        if certs.peek().is_none() {
241            return Ok(None);
242        }
243
244        let mut weight_factor = u32::MAX;
245
246        for cert in certs {
247            let c = cert.borrow();
248
249            let first = c
250                .chain
251                .first()
252                .ok_or(CertificateCheckError(CertificateLengthError))?;
253
254            let root_weight_factor = self
255                .storage
256                .get_root_weight_factor(first.issued_for.as_ref())?
257                .ok_or(NoRoot)?;
258
259            // certificate weight_factor = root weight factor + 1 * every other element in the chain
260            // (except root, so the formula is `root weight factor + chain length - 1`)
261            weight_factor =
262                std::cmp::min(weight_factor, root_weight_factor + c.chain.len() as u32 - 1)
263        }
264
265        Ok(Some(weight_factor))
266    }
267
268    /// BF search for all converging paths (chains) in the graph
269    fn bf_search_paths(
270        &self,
271        pk: &PK,
272        roots: HashSet<&PK>,
273    ) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
274        // queue to collect all chains in the trust graph (each chain is a path in the trust graph)
275        let mut chains_queue: VecDeque<Chain> = VecDeque::new();
276
277        let node_auths: Vec<Auth> = self.storage.get_authorizations(pk)?;
278        let node_revocations = self.storage.get_revocations(pk)?;
279
280        // put all auth in the queue as the first possible paths through the graph
281        for auth in node_auths {
282            chains_queue.push_back(Chain::new(NonEmpty::new(auth), node_revocations.clone()));
283        }
284
285        // List of all chains that converge (terminate) to known roots
286        let mut terminated_chains: Vec<Vec<Auth>> = Vec::new();
287
288        while !chains_queue.is_empty() {
289            let cur_chain = chains_queue
290                .pop_front()
291                .expect("`chains_queue` always has at least one element");
292
293            let last = cur_chain.auths.last();
294
295            let auths = self
296                .storage
297                .get_authorizations(&last.issued_by.clone().into())?;
298
299            for auth in auths {
300                // if there is auth, that we not visited in the current chain and no revocations to any chain member --  copy chain and append this auth
301                if cur_chain.can_be_extended_by(&auth.issued_by) {
302                    let mut new_chain = cur_chain.clone();
303                    new_chain.add_revocations(
304                        self.storage
305                            .get_revocations(&auth.issued_by.clone().into())?,
306                    );
307                    new_chain.auths.push(auth);
308                    chains_queue.push_back(new_chain);
309                }
310            }
311
312            // to be considered a valid chain, the chain must:
313            // - end with a self-signed trust
314            // - that trust must converge to one of the roots
315            // - there should be more than 1 trust in the chain
316            let self_signed = last.issued_by == last.trust.issued_for;
317            let issued_by: &PK = last.issued_by.as_ref();
318            let converges_to_root = roots.contains(issued_by);
319
320            if self_signed && converges_to_root && cur_chain.auths.len() > 1 {
321                terminated_chains.push(cur_chain.auths.into());
322            }
323        }
324
325        Ok(terminated_chains)
326    }
327
328    /// Get all possible certificates where `issued_for` will be the last element of the chain,
329    /// all certificates contain `issuer`
330    /// and one of the destinations is the root of this chain.
331    pub fn get_all_certs_from<P>(
332        &mut self,
333        issued_for: P,
334        issuer: P,
335        cur_time: Duration,
336    ) -> Result<Vec<Certificate>, TrustGraphError>
337    where
338        P: Borrow<PublicKey>,
339    {
340        self.get_all_certs(issued_for, cur_time).map(|c| {
341            c.into_iter()
342                .filter(|cert: &Certificate| {
343                    cert.chain.iter().any(|t| t.issued_for.eq(issuer.borrow()))
344                })
345                .collect()
346        })
347    }
348
349    /// Get all possible certificates where `issued_for` will be the last element of the chain
350    /// and one of the destinations is the root of this chain.
351    pub fn get_all_certs<P>(
352        &mut self,
353        issued_for: P,
354        cur_time: Duration,
355    ) -> Result<Vec<Certificate>, TrustGraphError>
356    where
357        P: Borrow<PublicKey>,
358    {
359        // garbage collect
360        self.storage.remove_expired(cur_time)?;
361
362        // maybe later we should retrieve root keys lazily
363        let keys = self.storage.root_keys()?;
364        let roots = keys.iter().collect();
365
366        Ok(self
367            .bf_search_paths(issued_for.borrow().as_ref(), roots)?
368            .into_iter()
369            .map(|auths| {
370                let trusts: Vec<Trust> = auths.into_iter().map(|auth| auth.trust).rev().collect();
371                Certificate::new_unverified(trusts)
372            })
373            .filter(|c| {
374                // Certificate with one trust means nothing, gotta be a bug. Checking for it here.
375                debug_assert!(
376                    c.chain.len() > 1,
377                    "certificate with chain of len 1 arose: {c:#?}",
378                );
379                c.chain.len() > 1
380            })
381            .collect())
382    }
383
384    /// Mark public key as revoked.
385    /// Every chain that contains path from `revoked_by` to revoked `pk`
386    /// will be excluded from valid certificates until revocation canceled by giving trust
387    pub fn revoke(&mut self, revocation: Revocation) -> Result<(), TrustGraphError> {
388        Revocation::verify(&revocation)?;
389
390        Ok(self.storage.revoke(revocation)?)
391    }
392
393    pub fn get_revocations<P>(&self, issued_for: P) -> Result<Vec<Revocation>, TrustGraphError>
394    where
395        P: Borrow<PublicKey>,
396    {
397        Ok(self.storage.get_revocations(issued_for.borrow().as_ref())?)
398    }
399}