Skip to main content

tor_netdoc/types/
embedded_cert.rs

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