Skip to main content

macula_rust/station_link/
report.rs

1//! A caller's seal report (macula's DESIGN_E2E_SEAL_REPORT): whether the
2//! exchange that produced a result was sealed, and to which key. It states
3//! that sealing ran on that exchange, nothing more: not that the provider
4//! keeps the payload secret, not that the key is uncompromised.
5
6use std::fmt;
7
8use crate::seal::KEY_ID_SIZE;
9
10use super::Stream;
11
12/// A caller's seal report on one exchange, with the three fields every SDK
13/// names alike (macula's `sealed`, `provider`, `seal_key_id`). `sealed` is 1
14/// when the request that produced the result was sealed and its answer opened
15/// under the same key, whose id `seal_key_id` names, and 0, with no key id,
16/// for a clear exchange. `provider` is the node the request was addressed
17/// to: for a sealed result also the one whose answer opened, for a clear one
18/// the target and no claim about who answered.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct Report {
21    pub sealed: u8,
22    pub provider: [u8; 32],
23    pub seal_key_id: Option<[u8; KEY_ID_SIZE]>,
24}
25
26impl Report {
27    /// The report on an exchange with `provider`: sealed to `key_id`, or
28    /// clear when it is `None`.
29    pub(crate) fn of(provider: [u8; 32], key_id: Option<[u8; KEY_ID_SIZE]>) -> Report {
30        Report {
31            sealed: u8::from(key_id.is_some()),
32            provider,
33            seal_key_id: key_id,
34        }
35    }
36}
37
38/// Why a stream has no seal report.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum ReportError {
41    /// The provider has sent no data or reply opened under the stream's key
42    /// (on a clear stream, no data, reply or end), or the stream ended
43    /// first, an error included.
44    NotSettled,
45    /// A served stream: the report is the caller's evidence, and the
46    /// provider side has none.
47    NotACaller,
48}
49
50impl ReportError {
51    /// The error as macula and libmacula name it.
52    pub fn name(self) -> &'static str {
53        match self {
54            ReportError::NotSettled => "not_settled",
55            ReportError::NotACaller => "not_a_caller",
56        }
57    }
58}
59
60impl fmt::Display for ReportError {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.write_str(self.name())
63    }
64}
65
66impl std::error::Error for ReportError {}
67
68impl Stream {
69    /// This caller stream's seal report. It settles on the provider's first
70    /// STREAM_DATA or STREAM_REPLY opened under the stream's key; on a clear
71    /// stream, on its first STREAM_DATA, STREAM_REPLY or STREAM_END. A sealed
72    /// stream's STREAM_END travels clear and settles nothing, and no error
73    /// settles a stream. Before it settles, and on a stream that ended
74    /// first, it is [`ReportError::NotSettled`]; on a served stream,
75    /// [`ReportError::NotACaller`]. A stream that settled and then ended, an
76    /// error included, keeps its report.
77    pub fn report(&self) -> Result<Report, ReportError> {
78        if !self.inner.caller {
79            return Err(ReportError::NotACaller);
80        }
81        if !self.inner.side().settled {
82            return Err(ReportError::NotSettled);
83        }
84        let key_id = self.inner.sealing.as_ref().map(|s| s.key_id());
85        Ok(Report::of(self.inner.open.target, key_id))
86    }
87}