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;