tor_netdoc/parse2/poc/
authcert.rs

1//! Directory Authority Key Certificates
2
3use super::*;
4
5use crate::types;
6
7/// SHA1 hash as used in directory authority certificates
8//
9// We don't have a better name for this!
10type DirKeyCertificateHash = [u8; 20];
11
12/// A directory authority key certificate (body)
13///
14/// <https://spec.torproject.org/dir-spec/creating-key-certificates.html>
15#[derive(Clone, Debug, Deftly)]
16#[derive_deftly(NetdocParseable, NetdocSigned)]
17#[non_exhaustive]
18pub struct DirAuthKeyCert {
19    /// Heading line
20    pub dir_key_certificate_version: (NdaDirKeyCertificateVersion,),
21
22    /// H(KP_auth_id_rsa)
23    #[deftly(netdoc(keyword = "fingerprint"))]
24    pub h_kp_auth_id_rsa: (types::Fingerprint,),
25
26    /// KP_auth_id_rsa
27    #[deftly(netdoc(keyword = "dir-identity-key"))]
28    pub kp_auth_id_rsa: pk::rsa::PublicKey,
29
30    /// cert generation time
31    pub dir_key_published: (NdaSystemTimeDeprecatedSyntax,),
32
33    /// cert expiration time
34    pub dir_key_expires: (NdaSystemTimeDeprecatedSyntax,),
35
36    /// KP_auth_sign_rsa
37    #[deftly(netdoc(keyword = "dir-signing-key"))]
38    pub kp_auth_sign_rsa: pk::rsa::PublicKey,
39
40    /// Reverse certificate ("cross certificate"), by KP_auth_sign_rsa on KP_auth_id_rsa
41    pub dir_key_crosscert: DirAuthCrossCert,
42}
43
44/// Signature section of a directory authority key certificate
45#[derive(Clone, Debug, Deftly)]
46#[derive_deftly(NetdocParseable)]
47#[deftly(netdoc(signatures))]
48#[non_exhaustive]
49pub struct DirAuthKeyCertSignatures {
50    /// Signature by KP_auth_id_rsa
51    pub dir_key_certification: DirAuthCertRsaSignature,
52}
53
54/// `network-status-version` version value
55#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, strum::EnumString)]
56#[non_exhaustive]
57pub enum NdaDirKeyCertificateVersion {
58    /// The currently supported version, `3`
59    #[strum(serialize = "3")]
60    V3,
61}
62
63/// RSA signature
64///
65/// Used for `dir-key-certification`
66///
67/// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
68#[derive(Deftly)]
69#[derive_deftly(ItemValueParseable)]
70#[deftly(netdoc(no_extra_args))]
71#[derive(Debug, Clone, Hash, Eq, PartialEq)]
72#[non_exhaustive]
73pub struct DirAuthCertRsaSignature {
74    /// The bytes of the signature (base64-decoded)
75    #[deftly(netdoc(object(label = "SIGNATURE")))]
76    pub signature: Vec<u8>,
77
78    /// The hash of the document
79    #[deftly(netdoc(sig_hash = "whole_keyword_line_sha1"))]
80    pub hash: DirKeyCertificateHash,
81}
82
83impl DirAuthKeyCertSigned {
84    /// Verify the signatures (and check validity times)
85    ///
86    /// # Security considerations
87    ///
88    /// The caller must check that the KP_auth_id is correct/relevant.
89    pub fn verify_selfcert(self, now: SystemTime) -> Result<DirAuthKeyCert, VF> {
90        // verify main document signature (and timestamp)
91        let hash = self.signatures.dir_key_certification.hash;
92
93        let validity = *self.body.dir_key_published.0..=*self.body.dir_key_expires.0;
94        check_validity_time(now, validity)?;
95        self.body
96            .kp_auth_id_rsa
97            .verify(&hash, &self.signatures.dir_key_certification.signature)?;
98
99        // double-check the id hash
100        if *self.body.h_kp_auth_id_rsa.0 != self.body.kp_auth_id_rsa.to_rsa_identity() {
101            return Err(VF::Inconsistent);
102        }
103
104        // verify cross-cert
105        let h_kp_auth_id_rsa: DirKeyCertificateHash =
106            tor_llcrypto::d::Sha1::digest(self.body.kp_auth_id_rsa.to_der()).into();
107        // Cross-cert has no timestamp.  Whatever.
108        self.body
109            .kp_auth_sign_rsa
110            .verify(&h_kp_auth_id_rsa, &self.body.dir_key_crosscert.signature)?;
111
112        Ok(self.body)
113    }
114}
115
116/// RSA signature of subset of the document data, with anomalous label
117///
118/// Used for `dir-key-crosscert`
119#[derive(Debug, Clone, Hash, Eq, PartialEq, derive_more::Deref)]
120#[non_exhaustive]
121pub struct DirAuthCrossCert {
122    /// The bytes of the signature (base64-decoded)
123    pub signature: Vec<u8>,
124}
125
126impl ItemValueParseable for DirAuthCrossCert {
127    fn from_unparsed(mut item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
128        item.args_mut().reject_extra_args()?;
129
130        let object = item.object().ok_or(EP::MissingObject)?;
131        match object.label() {
132            "SIGNATURE" | "ID SIGNATURE" => Ok(()),
133            _other => Err(EP::ObjectIncorrectLabel),
134        }?;
135        let signature = object.decode_data()?;
136        Ok(DirAuthCrossCert { signature })
137    }
138}