1use crate::{base64, error::TorError};
2use base32::{self, Alphabet};
3use curve25519_dalek::Scalar;
4use ed25519_dalek::{
5 hazmat::{raw_sign, ExpandedSecretKey},
6 Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey,
7};
8use rand::rngs::OsRng;
9use serde::{Deserialize, Serialize};
10use serde_with::serde_as;
11use sha2::Sha512;
12use sha3::{Digest, Sha3_256};
13use std::str::FromStr;
14use zeroize::{Zeroize, ZeroizeOnDrop};
15
16const TOR_VERSION: u8 = 3;
17
18#[derive(
24 Clone,
25 Deserialize,
26 Serialize,
27 Debug,
28 Hash,
29 PartialEq,
30 Eq,
31 PartialOrd,
32 Ord,
33 Zeroize,
34 ZeroizeOnDrop,
35)]
36pub struct TorServiceId(String);
37
38impl From<TorServiceId> for String {
40 fn from(id: TorServiceId) -> String {
41 id.0.clone()
42 }
43}
44
45impl std::fmt::Display for TorServiceId {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 write!(f, "{}", self.0)
48 }
49}
50
51impl std::convert::From<VerifyingKey> for TorServiceId {
54 fn from(verifying_key: VerifyingKey) -> Self {
55 let version = &[TOR_VERSION];
57
58 let verifying_key_bytes = verifying_key.as_bytes();
60 let checksum = TorServiceId::calculate_checksum(verifying_key_bytes.as_ref());
61
62 let mut onion_bytes = verifying_key_bytes.to_vec();
64 onion_bytes.extend_from_slice(&checksum);
65 onion_bytes.extend_from_slice(version);
66
67 Self(base32::encode(Alphabet::RFC4648 { padding: false }, &onion_bytes).to_lowercase())
68 }
69}
70
71impl FromStr for TorServiceId {
74 type Err = TorError;
75
76 fn from_str(service_id: &str) -> Result<Self, Self::Err> {
77 let onion_bytes = match base32::decode(Alphabet::RFC4648 { padding: false }, service_id) {
78 Some(bytes) => bytes,
79 None => return Err(TorError::protocol_error("Error base32 decoding service ID")),
80 };
81 if onion_bytes.len() != 35 {
82 return Err(TorError::protocol_error("Service ID is of wrong length"));
83 }
84 let mut verifying_key_bytes = [0u8; 32];
85 verifying_key_bytes.copy_from_slice(&onion_bytes[..32]);
86 let mut checksum = [0u8; 2];
87 checksum.copy_from_slice(&onion_bytes[32..34]);
88 let verifying_checksum = Self::calculate_checksum(&verifying_key_bytes);
89 if checksum != verifying_checksum {
90 return Err(TorError::protocol_error("Invalid checksum"));
91 }
92
93 Ok(Self(service_id.to_string()))
94 }
95}
96
97impl TorServiceId {
98 pub fn generate() -> Self {
100 let signing_key = SigningKey::generate(&mut OsRng);
101 signing_key.verifying_key().into()
102 }
103
104 fn calculate_checksum(verifying_key_bytes: &[u8]) -> [u8; 2] {
105 let mut checksum_bytes = ".onion checksum".as_bytes().to_vec();
106 checksum_bytes.extend_from_slice(verifying_key_bytes);
107 checksum_bytes.extend_from_slice(&[TOR_VERSION]);
108 let mut checksum = [0u8; 2];
109 checksum
110 .copy_from_slice(&Sha3_256::default().chain_update(&checksum_bytes).finalize()[..2]);
111
112 checksum
113 }
114
115 pub fn verifying_key(&self) -> Result<VerifyingKey, TorError> {
117 let onion_bytes = match base32::decode(Alphabet::RFC4648 { padding: false }, &self.0) {
118 Some(bytes) => bytes,
119 None => return Err(TorError::protocol_error("Error base32 decoding service ID")),
120 };
121 let mut verifying_key_bytes = [0u8; 32];
122 verifying_key_bytes.copy_from_slice(&onion_bytes[..32]);
123 let verifying_key = match VerifyingKey::from_bytes(&verifying_key_bytes) {
124 Ok(key) => key,
125 Err(_) => {
126 return Err(TorError::protocol_error(
127 "Error parsing verifying key from bytes",
128 ))
129 }
130 };
131 Ok(verifying_key)
132 }
133
134 pub fn as_str(&self) -> &str {
135 &self.0
136 }
137
138 pub fn short_id(&self) -> &str {
139 &self.0[..10]
140 }
141
142 pub fn onion_hostname(&self) -> String {
144 format!("{}.onion", self.0)
145 }
146}
147
148pub type TorBlob = [u8; 64];
159
160#[serde_as]
162#[derive(ZeroizeOnDrop)]
163pub struct TorEd25519SigningKey {
164 scalar_bytes: [u8; 32],
170
171 expanded_secret_key: ExpandedSecretKey,
173}
174
175impl TorEd25519SigningKey {
176 fn expanded_secret_key(&self) -> &ExpandedSecretKey {
178 &self.expanded_secret_key
179 }
180
181 pub fn verifying_key(&self) -> VerifyingKey {
183 VerifyingKey::from(self.expanded_secret_key())
184 }
185
186 pub fn scalar(&self) -> Scalar {
188 self.expanded_secret_key.scalar
189 }
190
191 pub fn from_blob(blob: &str) -> Result<Self, TorError> {
194 let blob_bytes: [u8; 64] = match base64::decode(blob) {
196 Ok(bytes) => match bytes.try_into() {
197 Ok(bytes) => bytes,
198 Err(_) => Err(TorError::protocol_error("Wrong number of bytes in blob"))?,
199 },
200 Err(error) => Err(TorError::protocol_error(&format!(
201 "Error decoding blob: {error}"
202 )))?,
203 };
204 Ok(Self::from_bytes(blob_bytes))
205 }
206
207 pub fn from_bytes(bytes: [u8; 64]) -> Self {
209 let mut scalar_bytes = [0u8; 32];
210 scalar_bytes.copy_from_slice(&bytes[..32]);
211 Self {
212 scalar_bytes,
213 expanded_secret_key: ExpandedSecretKey::from_bytes(&bytes),
214 }
215 }
216
217 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
219 self.verifying_key().verify(message, signature)
220 }
221
222 pub fn to_blob(&self) -> String {
224 let mut blob_bytes = [0u8; 64];
225 blob_bytes[..32].copy_from_slice(&self.scalar_bytes);
226 blob_bytes[32..].copy_from_slice(&self.expanded_secret_key.hash_prefix);
227 base64::encode(&blob_bytes)
228 }
229}
230
231impl std::str::FromStr for TorEd25519SigningKey {
233 type Err = TorError;
234
235 fn from_str(key: &str) -> Result<Self, Self::Err> {
236 Self::from_blob(key)
237 }
238}
239
240impl std::fmt::Display for TorEd25519SigningKey {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 write!(f, "{}", self.to_blob())
243 }
244}
245
246impl From<TorBlob> for TorEd25519SigningKey {
247 fn from(blob: TorBlob) -> Self {
248 Self::from_bytes(blob)
249 }
250}
251
252impl From<SigningKey> for TorEd25519SigningKey {
256 fn from(signing_key: SigningKey) -> Self {
257 let expanded_secret_key = ExpandedSecretKey::from(signing_key.as_bytes());
258
259 Self {
260 scalar_bytes: *expanded_secret_key.scalar.as_bytes(),
265 expanded_secret_key,
266 }
267 }
268}
269
270impl Signer<Signature> for TorEd25519SigningKey {
272 fn try_sign(&self, message: &[u8]) -> Result<Signature, SignatureError> {
273 Ok(raw_sign::<Sha512>(
274 self.expanded_secret_key(),
275 message,
276 &self.verifying_key(),
277 ))
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use serde_json;
285 use std::str::FromStr;
286
287 #[test]
290 fn test_ed25519v3_service_id() -> Result<(), anyhow::Error> {
291 let base64_blob = "0H/jnBeWzMoU1MGNRQPnmd8JqlpTNS3UeTiDOMyPTGGXXpLd0KinCtQbcgz2fCYjbzfK3ElJ7x3zGCkB1fAtAA==";
292 let signing_key = TorEd25519SigningKey::from_blob(base64_blob)?;
293 let public_key = signing_key.verifying_key();
294 assert_eq!(
295 "vvqbbaknxi6w44t6rplzh7nmesfzw3rjujdijpqsu5xl3nhlkdscgqad",
296 TorServiceId::from(public_key).as_str(),
297 );
298 Ok(())
299 }
300
301 #[test]
302 fn test_tor_generated_tor_ed25519v3_sign_verify() -> Result<(), anyhow::Error> {
303 let message = b"This is my very secret message";
304 let base64_blob = "0H/jnBeWzMoU1MGNRQPnmd8JqlpTNS3UeTiDOMyPTGGXXpLd0KinCtQbcgz2fCYjbzfK3ElJ7x3zGCkB1fAtAA==";
305 let signing_key = TorEd25519SigningKey::from_blob(base64_blob)?;
306 let signature = signing_key.sign(message);
307 assert!(signing_key.verify(message, &signature).is_ok());
308 Ok(())
309 }
310
311 #[test]
312 fn test_ed25519_tor_ed25519v3_expanded_keys() -> Result<(), anyhow::Error> {
313 let dalek_signing_key = SigningKey::generate(&mut OsRng);
314 let dalek_expanded_key = ExpandedSecretKey::from(dalek_signing_key.as_bytes());
315 let signing_key = TorEd25519SigningKey::from(dalek_signing_key);
316 let expanded_key = signing_key.expanded_secret_key();
317 assert_eq!(dalek_expanded_key.scalar, expanded_key.scalar);
318 assert_eq!(dalek_expanded_key.hash_prefix, expanded_key.hash_prefix);
319 Ok(())
320 }
321
322 #[test]
323 fn test_self_generated_tor_ed25519v3_sign_verify() -> Result<(), anyhow::Error> {
324 let message = b"This is my very secret message";
325 let dalek_signing_key = SigningKey::generate(&mut OsRng);
326 let signing_key = TorEd25519SigningKey::from(dalek_signing_key);
327 let signature = signing_key.sign(message);
328 assert!(signing_key.verify(message, &signature).is_ok());
329 Ok(())
330 }
331
332 #[test]
333 fn test_to_from_blob() -> Result<(), anyhow::Error> {
334 let blob_in = "0H/jnBeWzMoU1MGNRQPnmd8JqlpTNS3UeTiDOMyPTGGXXpLd0KinCtQbcgz2fCYjbzfK3ElJ7x3zGCkB1fAtAA==";
335 let signing_key = TorEd25519SigningKey::from_blob(blob_in)?;
336 let blob_out = signing_key.to_blob();
337 assert_eq!(blob_in, blob_out);
338
339 Ok(())
340 }
341
342 #[test]
343 fn test_blob_reduced() -> Result<(), anyhow::Error> {
344 let blob_in = "0H/jnBeWzMoU1MGNRQPnmd8JqlpTNS3UeTiDOMyPTGGXXpLd0KinCtQbcgz2fCYjbzfK3ElJ7x3zGCkB1fAtAA==";
345 let bytes = base64::decode(blob_in)?;
346 let scalar_bytes: [u8; 32] = bytes[..32].try_into().unwrap();
347 let scalar = Scalar::from_bytes_mod_order(scalar_bytes);
348 let output = scalar.to_bytes();
349 let scalar2 = Scalar::from_bytes_mod_order(output);
350 let output2 = scalar2.to_bytes();
351
352 assert_eq!(output, output2);
353
354 Ok(())
355 }
356
357 #[test]
358 fn test_serialize_service_id() -> Result<(), anyhow::Error> {
359 let service_id =
360 TorServiceId::from_str("vvqbbaknxi6w44t6rplzh7nmesfzw3rjujdijpqsu5xl3nhlkdscgqad")?;
361 let expected = "\"vvqbbaknxi6w44t6rplzh7nmesfzw3rjujdijpqsu5xl3nhlkdscgqad\"";
362 let json_out = serde_json::to_string(&service_id)?;
363 assert_eq!(expected, json_out);
364 let deserialized_service_id: TorServiceId = serde_json::from_str(&json_out)?;
365 assert_eq!(service_id, deserialized_service_id);
366 Ok(())
367 }
368
369 #[test]
370 fn test_ed25519_signing_key_to_tor_signing_key() -> Result<(), Box<dyn std::error::Error>> {
371 let mut csprng = OsRng;
372 let signing_key: SigningKey = SigningKey::generate(&mut csprng);
373 let tor_signing_key: TorEd25519SigningKey = signing_key.clone().into();
374
375 assert_eq!(signing_key.verifying_key(), tor_signing_key.verifying_key());
376
377 Ok(())
378 }
379}