tor_netdoc/doc/hsdesc/build/
outer.rs1use crate::build::{NetdocBuilder, NetdocEncoder};
8use crate::doc::hsdesc::outer::{HsOuterKwd, HS_DESC_SIGNATURE_PREFIX, HS_DESC_VERSION_CURRENT};
9
10use rand::{CryptoRng, RngCore};
11use tor_bytes::EncodeError;
12use tor_cert::EncodedEd25519Cert;
13use tor_hscrypto::RevisionCounter;
14use tor_llcrypto::pk::ed25519;
15use tor_units::IntegerMinutes;
16
17use base64ct::{Base64Unpadded, Encoding};
18
19#[derive(Debug)]
23pub(super) struct HsDescOuter<'a> {
24 pub(super) hs_desc_sign: &'a ed25519::Keypair,
26 pub(super) hs_desc_sign_cert: EncodedEd25519Cert,
28 pub(super) lifetime: IntegerMinutes<u16>,
34 pub(super) revision_counter: RevisionCounter,
37 pub(super) superencrypted: Vec<u8>,
44}
45
46impl<'a> NetdocBuilder for HsDescOuter<'a> {
47 fn build_sign<R: RngCore + CryptoRng>(self, _: &mut R) -> Result<String, EncodeError> {
48 use HsOuterKwd::*;
49
50 let HsDescOuter {
51 hs_desc_sign,
52 hs_desc_sign_cert,
53 lifetime,
54 revision_counter,
55 superencrypted,
56 } = self;
57
58 let mut encoder = NetdocEncoder::new();
59 let beginning = encoder.cursor();
60 encoder.item(HS_DESCRIPTOR).arg(&HS_DESC_VERSION_CURRENT);
61 encoder.item(DESCRIPTOR_LIFETIME).arg(&lifetime.to_string());
62
63 encoder
64 .item(DESCRIPTOR_SIGNING_KEY_CERT)
65 .object("ED25519 CERT", hs_desc_sign_cert.as_ref());
66 encoder.item(REVISION_COUNTER).arg(&*revision_counter);
67 encoder
68 .item(SUPERENCRYPTED)
69 .object("MESSAGE", superencrypted);
70 let end = encoder.cursor();
71
72 let mut text = HS_DESC_SIGNATURE_PREFIX.to_vec();
73 text.extend_from_slice(encoder.slice(beginning, end)?.as_bytes());
74 let signature = hs_desc_sign.sign(&text);
75
76 encoder
79 .item(SIGNATURE)
80 .arg(&Base64Unpadded::encode_string(&signature.to_bytes()));
81
82 encoder.finish().map_err(|e| e.into())
83 }
84}
85
86#[cfg(test)]
87mod test {
88 #![allow(clippy::bool_assert_comparison)]
90 #![allow(clippy::clone_on_copy)]
91 #![allow(clippy::dbg_macro)]
92 #![allow(clippy::mixed_attributes_style)]
93 #![allow(clippy::print_stderr)]
94 #![allow(clippy::print_stdout)]
95 #![allow(clippy::single_char_pattern)]
96 #![allow(clippy::unwrap_used)]
97 #![allow(clippy::unchecked_duration_subtraction)]
98 #![allow(clippy::useless_vec)]
99 #![allow(clippy::needless_pass_by_value)]
100 use std::time::UNIX_EPOCH;
103
104 use crate::doc::hsdesc::create_desc_sign_key_cert;
105
106 use super::*;
107 use tor_basic_utils::test_rng::Config;
108 use tor_hscrypto::pk::HsIdKeypair;
109 use tor_hscrypto::time::TimePeriod;
110 use tor_llcrypto::pk::ed25519::ExpandedKeypair;
111 use tor_units::IntegerMinutes;
112
113 const TEST_SUPERENCRYPTED_VALUE: &[u8] = &[1, 2, 3, 4];
115
116 #[test]
117 fn outer_hsdesc() {
118 let mut rng = Config::Deterministic.into_rng();
119 let hs_id = ed25519::Keypair::generate(&mut rng);
120 let hs_desc_sign = ed25519::Keypair::generate(&mut rng);
121 let period = TimePeriod::new(
122 humantime::parse_duration("24 hours").unwrap(),
123 humantime::parse_rfc3339("2023-02-09T12:00:00Z").unwrap(),
124 humantime::parse_duration("12 hours").unwrap(),
125 )
126 .unwrap();
127 let (_public, blinded_id, _) = HsIdKeypair::from(ExpandedKeypair::from(&hs_id))
128 .compute_blinded_key(period)
129 .unwrap();
130
131 let hs_desc_sign_cert =
132 create_desc_sign_key_cert(&hs_desc_sign.verifying_key(), &blinded_id, UNIX_EPOCH)
133 .unwrap();
134
135 let hs_desc = HsDescOuter {
136 hs_desc_sign: &hs_desc_sign,
137 hs_desc_sign_cert,
138 lifetime: IntegerMinutes::new(20),
139 revision_counter: 9001.into(),
140 superencrypted: TEST_SUPERENCRYPTED_VALUE.into(),
141 }
142 .build_sign(&mut Config::Deterministic.into_rng())
143 .unwrap();
144
145 assert_eq!(
146 hs_desc,
147 r#"hs-descriptor 3
148descriptor-lifetime 20
149descriptor-signing-key-cert
150-----BEGIN ED25519 CERT-----
151AQgAAAAAAZZVJwNlzVw1ZQGO7MTzC5MsySASd+fswAcjdTJJOifXAQAgBACI78JJ
152/MuWPH0T5rQziVMJK/yETbYCVycypjsytCmeA4eiWhcVBG4r6AY/fXqHZnI3ApID
153fsb92Bs45IrOrkQdATb5mk1dlFb0X6+0wIF0P0gCVuAEkGv1kvcR/zpvhww=
154-----END ED25519 CERT-----
155revision-counter 9001
156superencrypted
157-----BEGIN MESSAGE-----
158AQIDBA==
159-----END MESSAGE-----
160signature g6wu776AYYD+BXPBocToRXPF9xob3TB34hkR1/h8tDBGjGMnBWZw03INbiX6Z8FaOXCulccQ309fYEO/BmwyDQ
161"#
162 );
163 }
164}