Skip to main content

tor_netdoc/types/
embedded_cert.rs

1//! Types related to certificates
2
3use crate::encode::{
4    ItemEncoder, ItemObjectEncodable, ItemValueEncodable, NetdocEncodable, NetdocEncoder,
5};
6use crate::parse2::{
7    ErrorProblem as P2EP, IsStructural, ItemObjectParseable, ItemStream, ItemValueParseable,
8    KeywordRef, NetdocParseable, UnparsedItem,
9};
10use tor_bytes::{Writeable, Writer};
11use tor_error::{Bug, internal};
12
13/// One certificate *inside* a netdoc, covering data other than the netdoc itself
14///
15/// # Semantics and value
16///
17/// This type always embodies:
18///
19///  * The encoded form of a certificate or signature
20///    (its actual bytes, for encoding/decoding.
21///
22///    This encoded unverified raw form is the **type parameter `UR`**.
23///    Often `UR` will be [`tor_cert::KeyUnknownCert`].
24///
25/// Additionally, it can and usually does contain the "verified form":
26///
27///  * Interpreted, parsed, data, of whatever was certified.
28///    For example, for a family certificate, the family IDs.
29///
30///    It might or might not include something like a [`tor_cert::Ed25519Cert`],
31///    depending whether downstreams need that information.
32///
33///    This decoded verified data is the **type parameter `VD`**;
34///    `EmbeddedCert` contains `Option<VD>` (or equivalent).
35///
36/// (We call an `EmbeddedCert` without the verified form an "unverified `EmbeddedCert`".)
37///
38/// # Correctness/availability invariant
39///
40/// Whenever an `EmbeddedCert` appears in a parsed and verified network document body,
41/// the `EmbeddedCert` has been verified and the verified form is present.
42///
43/// During parsing of a network document, the document type's verification function
44/// gets access to the unverified `EmbeddedCert`.
45/// It is the verify function which must verify and timecheck the certificate,
46/// and, if it is satisfied, call [`set_verified`](Self::set_verified).
47/// Include fields of this type in documents deriving
48/// [`NetdocParseableUnverified`](derive_deftly_template_NetdocParseableUnverified),
49/// rather than plain `NetdocParseable`.
50///
51/// This invariant is somewhat fuzzy around the edges, and not 100% enforced by the compiler.
52/// If it is relied on inappropriately, or violated, `Bug` is thrown.
53///
54// It is hard to do better than this.  Most alternatives involve some or all of
55// proliferating type parameters, even more complex macrology, and
56// significantly more complex marker types.
57//
58// See https://gitlab.torproject.org/tpo/core/arti/-/work_items/2485.
59// This is Option E from that ticket:
60// https://gitlab.torproject.org/tpo/core/arti/-/work_items/2485#note_3398883
61//
62/// # Security invariant
63///
64/// Presence of the verified form guarantees that, if the document came from outside,
65/// we have verified the signature, and checked that it is timely.
66/// So the interpreted form can safely be used.
67///
68/// This guarantee flows from the caller of [`set_verified`](Self::set_verified),
69/// and may be relied on by users - eg, by callers of [`get`](Self::get).
70///
71/// # Parsing and encoding
72///
73/// This type implements applicable parsing and encoding traits,
74/// if `VD` is [`EmbeddableCertObject<UR>`]
75/// and `UR` is [`Readable`](tor_bytes::Readable) and [`Writable`](tor_bytes::Writeable).
76///
77/// See [`EmbeddableCertObject`] for full details.
78///
79/// # Example
80///
81/// See `crates/tor-netdoc/src/types/embedded_cert/test.rs`.
82#[derive(Clone, Debug, PartialEq, Eq)]
83pub struct EmbeddedCert<VD, UR> {
84    /// The verified form, if this `EmbeddedCert` is verified.
85    verified: Option<VD>,
86    /// The unverified form.
87    unverified: UR,
88}
89
90/// Certificate data whose unverified form `UR` is representable as a netdoc Object
91///
92/// Implement for `VD`.
93///
94/// Enables encoding/decoding traits for `EmbeddableCert<VD, UR>`.
95/// See [`EmbeddedCert`].
96///
97/// # Usage
98///
99///  * implement [`tor_bytes::Writeable`] for `UR`
100///  * implement [`tor_bytes::Readable`] for `UR`
101///  * implement **`EmbeddableCertObject<UR>`** for `VD`
102///
103/// Then `EmbeddableCert<VD, UR>` will implement:
104///
105///  * [`ItemValueEncodable`] and [`ItemValueParseable`]
106///  * [`ItemObjectEncodable`] and [`ItemObjectParseable`]
107///  * [`Writeable`]
108pub trait EmbeddableCertObject<UR> {
109    /// The netdoc Object Label
110    const LABEL: &str;
111}
112
113impl<VD, UR> EmbeddedCert<VD, UR> {
114    /// Make a new (verified) `EmbeddedCert`
115    ///
116    /// # Security
117    ///
118    /// If this certificate originated elsewhere,
119    /// it must have been verified and timechecked.
120    pub fn new(data: VD, raw: UR) -> Self {
121        EmbeddedCert {
122            verified: Some(data),
123            unverified: raw,
124        }
125    }
126
127    /// Obtain the verified data
128    ///
129    /// This function will always succeed on a cert found in a (verified) netdoc.
130    ///
131    /// # Error conditions
132    ///
133    /// `get` will fail only if the correctness/availability invariant
134    /// is violated or relied on inappropriately.
135    /// See the [type-level documentation](EmbeddedCert).
136    ///
137    /// It can fail inside a netdoc verification function,
138    /// or after `EmbeddedCert::new_unverified_hazardous`.
139    /// It could also fail if an `EmbeddedCert` is included in an unsigned netdoc
140    /// (ie one to which derived plain
141    /// [`NetdocParseable`](derive_deftly_template_NetdocParseable)
142    /// rather than
143    /// [`NetdocParseableUnverified`](derive_deftly_template_NetdocParseableUnverified).
144    pub fn get(&self) -> Result<&VD, Bug> {
145        self.verified.as_ref().ok_or_else(|| internal!(
146 "attempted to access verified data of unverified EmbeddedCert; buggy netdoc fn verify?"
147        ))
148    }
149
150    /// Make a new unverified `EmbeddedCert`
151    ///
152    /// # Correctness
153    ///
154    /// It is the caller's responsibility to uphold the correctness/availability invariant.
155    /// See the [type-level documentation](EmbeddedCert).
156    ///
157    /// Carelessly creating a loose unverified `EmbeddedCert`
158    /// could expose it to naive code, which expects [`get`](Self::get) to succeed.
159    pub fn new_unverified_hazardous(unverified: UR) -> Self {
160        EmbeddedCert {
161            unverified,
162            verified: None,
163        }
164    }
165
166    /// Obtain the raw data, for verification or encoding
167    pub fn raw_unverified(&self) -> &UR {
168        &self.unverified
169    }
170
171    /// Set the verified data
172    ///
173    /// Usually called from within a document-specific verify function.
174    ///
175    /// # Security
176    ///
177    /// The signature must have been verified, and timeliness checked.
178    pub fn set_verified(&mut self, verified: VD) {
179        self.verified = Some(verified);
180    }
181}
182
183impl<VD, UR> Writeable for EmbeddedCert<VD, UR>
184where
185    UR: Writeable,
186{
187    fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> Result<(), tor_bytes::EncodeError> {
188        self.unverified.write_onto(b)
189    }
190}
191
192impl<VD, UR> ItemObjectEncodable for EmbeddedCert<VD, UR>
193where
194    VD: EmbeddableCertObject<UR>,
195    UR: Writeable,
196{
197    fn label(&self) -> &str {
198        VD::LABEL
199    }
200    fn write_object_onto(&self, b: &mut Vec<u8>) -> Result<(), Bug> {
201        Ok(self.write_onto(b)?)
202    }
203}
204
205impl<VD, UR> ItemValueEncodable for EmbeddedCert<VD, UR>
206where
207    Self: ItemObjectEncodable,
208{
209    fn write_item_value_onto(&self, out: ItemEncoder) -> Result<(), Bug> {
210        out.object(self);
211        Ok(())
212    }
213}
214
215impl<VD, UR> NetdocEncodable for EmbeddedCert<VD, UR>
216where
217    UR: NetdocEncodable,
218{
219    fn encode_unsigned(&self, out: &mut NetdocEncoder) -> Result<(), Bug> {
220        self.unverified.encode_unsigned(out)
221    }
222}
223
224impl<VD, UR> ItemObjectParseable for EmbeddedCert<VD, UR>
225where
226    VD: EmbeddableCertObject<UR>,
227    UR: tor_bytes::Readable,
228{
229    fn check_label(label: &str) -> Result<(), P2EP> {
230        (label == VD::LABEL)
231            .then_some(())
232            .ok_or(P2EP::ObjectIncorrectLabel)
233    }
234    fn from_bytes(input: &[u8]) -> Result<Self, P2EP> {
235        let unverified = tor_bytes::Reader::from_slice(input)
236            .extract()
237            .map_err(|_| P2EP::ObjectInvalidData)?;
238        Ok(EmbeddedCert::new_unverified_hazardous(unverified))
239    }
240}
241
242impl<VD, UR> ItemValueParseable for EmbeddedCert<VD, UR>
243where
244    VD: EmbeddableCertObject<UR>,
245    UR: tor_bytes::Readable,
246{
247    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, P2EP> {
248        let object = item.object().ok_or(P2EP::MissingObject)?;
249        <Self as ItemObjectParseable>::check_label(object.label())?;
250        <Self as ItemObjectParseable>::from_bytes(&object.decode_data()?)
251    }
252}
253
254impl<VD, UR> NetdocParseable for EmbeddedCert<VD, UR>
255where
256    UR: NetdocParseable,
257{
258    fn doctype_for_error() -> &'static str {
259        UR::doctype_for_error()
260    }
261
262    fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool {
263        UR::is_intro_item_keyword(kw)
264    }
265
266    fn is_structural_keyword(kw: KeywordRef<'_>) -> Option<IsStructural> {
267        UR::is_structural_keyword(kw)
268    }
269
270    fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, P2EP> {
271        let unverified = UR::from_items(input, stop_at)?;
272        Ok(EmbeddedCert::new_unverified_hazardous(unverified))
273    }
274}
275
276#[cfg(test)]
277mod test;