Skip to main content

luct_scanner/
report.rs

1use crate::Validated;
2use chrono::{DateTime, Local, Utc};
3use luct_core::{CertificateChain, LogId, v1::SignedTreeHead};
4use luct_store::StringStoreValue;
5use serde::{Deserialize, Serialize};
6use web_time::UNIX_EPOCH;
7
8mod evaluate;
9mod generate;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Report {
13    pub(crate) ca_issuer: String,
14    pub(crate) ca_subject: String,
15    pub(crate) cert_issuer: String,
16    pub(crate) cert_subject: String,
17    pub(crate) fingerprint: String,
18    pub(crate) ca_fingerprint: String,
19    pub(crate) not_before: DateTime<Utc>,
20    pub(crate) not_after: DateTime<Utc>,
21    // TODO: Precert fingerprint
22    #[serde(skip_serializing_if = "Vec::is_empty", default)]
23    pub(crate) scts: Vec<SctReport>,
24    #[serde(skip_serializing_if = "Option::is_none", default)]
25    pub(crate) error_description: Option<String>,
26}
27
28impl Report {
29    pub fn get_error(&self) -> Option<String> {
30        self.error_description.clone()
31    }
32
33    pub(crate) fn error_description(mut self, err: String) -> Self {
34        self.error_description = Some(err);
35        self
36    }
37}
38
39impl From<&CertificateChain> for Report {
40    fn from(chain: &CertificateChain) -> Self {
41        let (not_before, not_after) = chain.cert().get_validity();
42
43        Self {
44            ca_issuer: chain.root().get_issuer_name(),
45            ca_subject: chain.root().get_subject_name(),
46            cert_issuer: chain.cert().get_issuer_name(),
47            cert_subject: chain.cert().get_subject_name(),
48            fingerprint: chain.cert().fingerprint_sha256().to_string(),
49            ca_fingerprint: chain.root().fingerprint_sha256().to_string(),
50            not_before,
51            not_after,
52            scts: vec![],
53            error_description: None,
54        }
55    }
56}
57
58impl StringStoreValue for Report {
59    fn serialize_value(&self) -> String {
60        serde_json::to_string(self).unwrap()
61    }
62
63    fn deserialize_value(value: &str) -> Option<Self> {
64        serde_json::from_str(value).ok()
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct SctReport {
70    pub(crate) log_id: String,
71    #[serde(skip_serializing_if = "Option::is_none", default)]
72    pub(crate) signature_validation_time: Option<DateTime<Local>>,
73    #[serde(skip_serializing_if = "Option::is_none", default)]
74    pub(crate) log_name: Option<String>,
75    #[serde(skip_serializing_if = "Option::is_none", default)]
76    pub(crate) latest_sth: Option<SthReport>,
77    #[serde(skip_serializing_if = "Option::is_none", default)]
78    pub(crate) index: Option<u64>,
79    #[serde(skip_serializing_if = "Option::is_none", default)]
80    pub(crate) inclusion_proof: Option<SthReport>,
81    #[serde(skip_serializing_if = "Option::is_none", default)]
82    pub(crate) error_description: Option<String>,
83}
84
85impl SctReport {
86    pub(crate) fn new(log_id: LogId) -> Self {
87        Self {
88            log_id: log_id.to_string(),
89            signature_validation_time: None,
90            log_name: None,
91            latest_sth: None,
92            index: None,
93            inclusion_proof: None,
94            error_description: None,
95        }
96    }
97
98    pub(crate) fn signature_validation_time(mut self, time: DateTime<Local>) -> Self {
99        self.signature_validation_time = Some(time);
100        self
101    }
102
103    pub(crate) fn log_name(mut self, name: String) -> Self {
104        self.log_name = Some(name);
105        self
106    }
107
108    pub(crate) fn latest_sth(mut self, sth: SthReport) -> Self {
109        self.latest_sth = Some(sth);
110        self
111    }
112
113    pub(crate) fn index(mut self, index: u64) -> Self {
114        self.index = Some(index);
115        self
116    }
117
118    pub(crate) fn inclusion_proof(mut self, sth: SthReport) -> Self {
119        self.inclusion_proof = Some(sth);
120        self
121    }
122
123    pub(crate) fn error_description(mut self, err: String) -> Self {
124        self.error_description = Some(err);
125        self
126    }
127
128    pub(crate) fn set_error_description(&mut self, err: String) {
129        self.error_description = Some(err);
130    }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct SthReport {
135    height: u64,
136    timestamp: DateTime<Utc>,
137    verification_time: DateTime<Utc>,
138}
139
140impl From<&Validated<SignedTreeHead>> for SthReport {
141    fn from(value: &Validated<SignedTreeHead>) -> Self {
142        Self {
143            height: value.tree_size(),
144            timestamp: DateTime::from_timestamp_millis(value.timestamp() as i64).unwrap(),
145            verification_time: DateTime::from_timestamp_millis(
146                value
147                    .validated_at()
148                    .duration_since(UNIX_EPOCH)
149                    .unwrap()
150                    .as_millis() as i64,
151            )
152            .unwrap(),
153        }
154    }
155}
156
157// TODO: Tests for policy evaluation