tor_netdoc/parse2/signatures.rs
1//! Handling of netdoc signatures
2//
3// TODO use tor_checkable to provide a generic .verify function.
4//
5// But the tor_checkable API might need some updates and this seems nontrivial.
6// Each verification function seems to take different inputs.
7
8use super::*;
9
10/// A signature item that can appear in a netdoc
11///
12/// This is the type `T` of a field `item: T` in a netdoc signatures section type.
13///
14/// Types that implement this embody both:
15///
16/// * The item, parameters, and signature data, provided in the document.
17/// * The hash of the document body, which will needed during verification.
18///
19/// Typically derived with
20/// [`#[derive_deftly(ItemValueParseable)]`](derive_deftly_template_ItemValueParseable).
21///
22/// Normal (non-signature) items implement [`ItemValueParseable`].
23pub trait SignatureItemParseable: Sized {
24 /// Parse the item's value
25 fn from_unparsed_and_body(
26 item: UnparsedItem<'_>,
27 document_body: &SignatureHashInputs<'_>,
28 ) -> Result<Self, ErrorProblem>;
29}
30
31/// The part of a network document before the first signature item
32///
33/// This is used for both Regular signatures
34/// where the hash does not contain any part of the signature Item
35/// (of which there are none yet)
36/// and Irregular signatures
37/// where the hash contains part of the signature Item.
38///
39/// See <https://gitlab.torproject.org/tpo/core/torspec/-/issues/322>.
40//
41// This type exists as a separate newtype mostly to avoid mistakes inside
42// parser implementations, where lots of different strings are floating about.
43// In particular, the parser must save this value when it starts parsing
44// signatures and must then reuse it for later ones.
45#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, amplify::Getters)]
46pub struct SignedDocumentBody<'s> {
47 /// The actual body as a string
48 #[getter(as_copy)]
49 pub(crate) body: &'s str,
50}
51
52/// Inputs needed to calculate a specific signature hash for a specific Item
53///
54/// Embodies:
55///
56/// * `&str` for the body, as for `SignedDocumentBody`.
57/// For calculating Regular signatures.
58///
59/// * Extra information for calculating Irregular signatures.
60/// Irregular signature Items can only be implemented within this crate.
61#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash, amplify::Getters)]
62pub struct SignatureHashInputs<'s> {
63 /// The Regular body
64 #[getter(as_copy)]
65 pub(crate) body: SignedDocumentBody<'s>,
66 /// The signature item keyword and the following space
67 #[getter(skip)]
68 pub(crate) signature_item_kw_spc: &'s str,
69 /// The whole signature item keyword line not including the final newline
70 #[getter(skip)]
71 pub(crate) signature_item_line: &'s str,
72}
73
74impl<'s> SignatureHashInputs<'s> {
75 /// Hash into `h` the body and the whole of the signature item's keyword line
76 pub(crate) fn hash_whole_keyword_line(&self, h: &mut impl Digest) {
77 h.update(self.body().body());
78 h.update(self.signature_item_line);
79 h.update("\n");
80 }
81}
82
83/// Methods suitable for use with `#[deftly(netdoc(sig_hash = "METHOD"))]`
84///
85/// See
86/// [`#[derive_deftly(ItemValueParseable)]`](derive_deftly_template_ItemValueParseable).
87pub mod sig_hash_methods {
88 use super::*;
89
90 /// SHA-1 including the whole keyword line
91 ///
92 /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
93 pub fn whole_keyword_line_sha1(body: &SignatureHashInputs) -> [u8; 20] {
94 let mut h = tor_llcrypto::d::Sha1::new();
95 body.hash_whole_keyword_line(&mut h);
96 h.finalize().into()
97 }
98}
99
100/// Utility function to check that a time is within a validity period
101pub fn check_validity_time(
102 now: SystemTime,
103 validity: std::ops::RangeInclusive<SystemTime>,
104) -> Result<(), VF> {
105 if now < *validity.start() {
106 Err(VF::TooNew)
107 } else if now > *validity.end() {
108 Err(VF::TooOld)
109 } else {
110 Ok(())
111 }
112}