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;