wot-network 0.0.6

Data structures for OpenPGP Web of Trust calculations
Documentation
use std::collections::HashMap;

use crate::{Binding, Certificate, Certification, Delegation, Regex, TrustDepth};

/// A "Web of Trust" network consisting of [Certification]s and [Delegation]s.
///
/// A [Network] represents a snapshot of valid nodes and edges in a set of OpenPGP Certificates
/// at a reference time.
///
/// **NOTE**:
///
/// Users of this crate will usually want to create a `wot-network` graph from OpenPGP certificates.
/// This involves applying all relevant OpenPGP semantics and forming a network of exactly
/// those nodes and edges that are valid and active at the reference time.
///
/// One implementation of formation of `wot-network` graphs is available in
/// [wot-network-rpgpie](https://crates.io/crates/wot-network-rpgpie).
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, PartialEq, Clone)]
pub struct Network {
    // Certifications towards a binding
    //
    // In contrast to conventional trust graphs, the certification edges in this network are
    // backwards, i.e. certifications pointing on binding.
    pub certifications: HashMap<Binding, Vec<Certification>>,

    // Delegations towards a cert.
    //
    // In contrast to conventional trust graphs, the delegation edges in this network are
    // backwards, i.e. delegations pointing on a certification.
    pub delegations: HashMap<Certificate, Vec<Delegation>>,
}

impl Default for Network {
    fn default() -> Self {
        Self::new()
    }
}

impl Network {
    pub fn new() -> Self {
        Self {
            certifications: Default::default(),
            delegations: Default::default(),
        }
    }

    pub fn num_edges(&self) -> usize {
        self.certifications.len() + self.delegations.len()
    }

    pub fn add_binding(&mut self, issuer: Certificate, target: Binding) {
        // FIXME: less cloning!?

        let edge = Certification {
            issuer: issuer.clone(),
            target: target.clone(),
        };

        if let Some(edges) = self.certifications.get_mut(&Binding {
            cert: target.cert.clone(),
            identity: target.identity.clone(),
        }) {
            // add edge to pre-existing EdgeSet
            edges.push(edge);
        } else {
            // make a new EdgeSet, put this edge in
            let list = vec![edge];

            self.certifications.insert(
                Binding {
                    cert: target.cert,
                    identity: target.identity,
                },
                list,
            );
        }
    }

    pub fn add_delegation(
        &mut self,
        issuer: Certificate,
        target_cert: Certificate,
        trust_amount: u8,
        trust_depth: TrustDepth,
        regexes: Vec<Regex>,
    ) {
        // FIXME: less Certificate cloning!?

        let edge = Delegation {
            issuer: issuer.clone(),
            target: target_cert.clone(),
            trust_amount,
            trust_depth,
            regexes,
        };

        if let Some(edges) = self.delegations.get_mut(&target_cert) {
            // Add edge to pre-existing EdgeSet
            edges.push(edge);
        } else {
            // Make a new EdgeSet
            let list = vec![edge];

            // Add this EdgeSet to the Network
            self.delegations.insert(target_cert, list);
        }
    }
}

/// A minimalistic representation of a network
///
/// This is effectively a [`Network`], but as a single combined vector instead of a HashMap over
/// vectors. This is used to allow specification of test networks in a format that's easier to read
/// for humans.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
pub struct MinimalNetwork {
    pub certifications: Vec<Certification>,
    pub delegations: Vec<Delegation>,
}

impl From<Network> for MinimalNetwork {
    fn from(network: Network) -> Self {
        let certifications = network.certifications.into_values().flatten().collect();
        let delegations = network.delegations.into_values().flatten().collect();
        MinimalNetwork {
            certifications,
            delegations,
        }
    }
}

impl From<MinimalNetwork> for Network {
    fn from(minimal_network: MinimalNetwork) -> Self {
        let mut certifications: HashMap<Binding, Vec<Certification>> = HashMap::new();
        for cert in minimal_network.certifications {
            certifications
                .entry(cert.target.clone())
                .or_default()
                .push(cert);
        }

        let mut delegations: HashMap<Certificate, Vec<Delegation>> = HashMap::new();
        for delegation in minimal_network.delegations {
            delegations
                .entry(delegation.target.clone())
                .or_default()
                .push(delegation);
        }

        Network {
            certifications,
            delegations,
        }
    }
}