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, strum::Display)]
56#[non_exhaustive]
57pub enum NdaDirKeyCertificateVersion {
58    /// The currently supported version, `3`
59    #[strum(serialize = "3")]
60    V3,
61}
62
63impl NormalItemArgument for NdaDirKeyCertificateVersion {}
64
65/// RSA signature
66///
67/// Used for `dir-key-certification`
68///
69/// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
70#[derive(Deftly)]
71#[derive_deftly(ItemValueParseable)]
72#[deftly(netdoc(no_extra_args))]
73#[derive(Debug, Clone, Hash, Eq, PartialEq)]
74#[non_exhaustive]
75pub struct DirAuthCertRsaSignature {
76    /// The bytes of the signature (base64-decoded)
77    #[deftly(netdoc(object(label = "SIGNATURE"), with = "crate::parse2::raw_data_object"))]
78    pub signature: Vec<u8>,
79
80    /// The hash of the document
81    #[deftly(netdoc(sig_hash = "whole_keyword_line_sha1"))]
82    pub hash: DirKeyCertificateHash,
83}
84
85impl DirAuthKeyCertSigned {
86    /// Verify the signatures (and check validity times)
87    ///
88    /// # Security considerations
89    ///
90    /// The caller must check that the KP_auth_id is correct/relevant.
91    pub fn verify_selfcert(self, now: SystemTime) -> Result<DirAuthKeyCert, VF> {
92        // verify main document signature (and timestamp)
93        let hash = self.signatures.dir_key_certification.hash;
94
95        let validity = *self.body.dir_key_published.0..=*self.body.dir_key_expires.0;
96        check_validity_time(now, validity)?;
97        self.body
98            .kp_auth_id_rsa
99            .verify(&hash, &self.signatures.dir_key_certification.signature)?;
100
101        // double-check the id hash
102        if *self.body.h_kp_auth_id_rsa.0 != self.body.kp_auth_id_rsa.to_rsa_identity() {
103            return Err(VF::Inconsistent);
104        }
105
106        // verify cross-cert
107        let h_kp_auth_id_rsa: DirKeyCertificateHash =
108            tor_llcrypto::d::Sha1::digest(self.body.kp_auth_id_rsa.to_der()).into();
109        // Cross-cert has no timestamp.  Whatever.
110        self.body
111            .kp_auth_sign_rsa
112            .verify(&h_kp_auth_id_rsa, &self.body.dir_key_crosscert.signature)?;
113
114        Ok(self.body)
115    }
116}
117
118/// RSA signature of subset of the document data, with anomalous label
119///
120/// Used for `dir-key-crosscert`
121#[derive(Debug, Clone, Hash, Eq, PartialEq, derive_more::Deref)]
122#[non_exhaustive]
123pub struct DirAuthCrossCert {
124    /// The bytes of the signature (base64-decoded)
125    pub signature: Vec<u8>,
126}
127
128impl ItemValueParseable for DirAuthCrossCert {
129    fn from_unparsed(mut item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
130        item.args_mut().reject_extra_args()?;
131
132        let object = item.object().ok_or(EP::MissingObject)?;
133        match object.label() {
134            "SIGNATURE" | "ID SIGNATURE" => Ok(()),
135            _other => Err(EP::ObjectIncorrectLabel),
136        }?;
137        let signature = object.decode_data()?;
138        Ok(DirAuthCrossCert { signature })
139    }
140}