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}