Skip to main content

luct_scanner/
lib.rs

1//! Certificate transparency auditing logic used by luCT firefox extension and CLI tool
2
3#![forbid(unsafe_code)]
4
5use crate::log::{ScannerLog, builder::LogImpls};
6use chrono::{DateTime, Utc};
7use futures::future::try_join_all;
8use luct_client::Client;
9use luct_core::{CtLog, Fingerprint, LogId, store::SearchableStore, v1::SignedTreeHead};
10use std::collections::BTreeMap;
11pub use {
12    config::{ScannerConfig, ScannerConfigBuilder},
13    error::ScannerError,
14    report::{Report, SctReport, SthReport},
15    utils::Validated,
16};
17
18type HashOutput = [u8; 32];
19
20mod config;
21mod error;
22mod log;
23mod report;
24mod sth;
25mod utils;
26
27/// Bundle trait for [`Scanner`]
28///
29/// Defines the [`Store`](luct_core::store::Store) and [`Client`] backends to be used by the scanner
30pub trait ScannerImpl {
31    /// [`Client`] implementation to make connections to logs to
32    type Client: Client + Clone;
33    /// The [`Store`](luct_core::store::Store) type used to store cached [`Reports`](Report) of audit results
34    type ReportStore: SearchableStore<Key = Fingerprint, Value = Report>;
35    /// The [`Store`](luct_core::store::Store) use to store [`SignedTreeHeads`](SignedTreeHead)
36    type SthStore: SearchableStore<Key = u64, Value = Validated<SignedTreeHead>>;
37}
38
39/// The scanner holds the state that is necessary to perform audits as well as the auditing logic
40///
41/// It is generic over [`ScannerImpl`], which is a bundle trait containing implementations of [`Stores`](luct_core::store::Store)
42/// and [`Clients`](Client).
43pub struct Scanner<S: ScannerImpl> {
44    config: ScannerConfig,
45    logs: BTreeMap<LogId, ScannerLog<S>>,
46    report_store: S::ReportStore,
47    client: S::Client,
48    time_source: Box<dyn Fn() -> DateTime<Utc>>,
49}
50
51#[allow(clippy::type_complexity)]
52impl<S: ScannerImpl> Scanner<S> {
53    pub fn logs<'a>(&'a self) -> Box<dyn Iterator<Item = &'a CtLog> + 'a> {
54        Box::new(self.logs.values().map(|val| val.client().log()))
55    }
56
57    pub fn new<F: Fn() -> DateTime<Utc> + 'static>(
58        config: ScannerConfig,
59        report_store: S::ReportStore,
60        client: S::Client,
61        time_source: F,
62    ) -> Self {
63        Self {
64            config,
65            logs: BTreeMap::new(),
66            report_store,
67            client,
68            time_source: Box::new(time_source) as _,
69        }
70    }
71
72    pub fn add_log(&mut self, log: &CtLog, sth_store: S::SthStore) -> &mut Self {
73        let impls = LogImpls {
74            client: self.client.clone(),
75            sth_store,
76        };
77        let scanner_log = ScannerLog::new(log, impls);
78        let log_id = scanner_log.client().log().log_id().clone();
79
80        self.logs.insert(log_id, scanner_log);
81        self
82    }
83
84    /// Updates all log's STHs
85    pub async fn refresh_all_logs(&self) -> Result<(), ScannerError> {
86        let updates = self
87            .logs
88            .values()
89            .map(|log| log.update_sth())
90            .collect::<Vec<_>>();
91
92        try_join_all(updates).await?;
93
94        Ok(())
95    }
96}