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