tor_client_lib/
key.rs

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/// The service ID for the onion service.
19///
20/// Basically the service ID is the part of the onion address
21/// before the ".onion" part. This is an encoding of the onion service's public key into a string.
22/// As such, you can convert from the service ID to the public key, and vice-versa.
23#[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
38/// Convert the service ID to a String
39impl 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
51/// Generate the Tor service ID from an ed25519_dalek verifying (public) key
52/// From section 6 of <https://github.com/torproject/torspec/blob/main/rend-spec-v3.txt>
53impl std::convert::From<VerifyingKey> for TorServiceId {
54    fn from(verifying_key: VerifyingKey) -> Self {
55        // Version number
56        let version = &[TOR_VERSION];
57
58        // Hash the verifying key concatenated with the checksum and the version
59        let verifying_key_bytes = verifying_key.as_bytes();
60        let checksum = TorServiceId::calculate_checksum(verifying_key_bytes.as_ref());
61
62        // Base32 the verifying key concatenated with the checksum and the version
63        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
71/// Parse a String into a service ID. Note that this does a good bit of verification that the
72/// service ID is indeed a service ID
73impl 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    /// Generate a new ED25519 public key, and the corresponding TorServiceId
99    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    /// Retrieve the public ED25519 verifying key from the TorServiceId
116    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    /// Generate the corresponding onion hostname (by tacking on the ".onion" part)
143    pub fn onion_hostname(&self) -> String {
144        format!("{}.onion", self.0)
145    }
146}
147
148/// Type definition for the blob data returned by Tor
149/// The blob is the byte representation of the Ed25519 signing key as used by Tor.
150/// It's the output of doing a SHA-512 hash of the original secret key,
151/// followed by clamping, so it's basically the "ExpandedSecretKey".
152/// This yields the scalar used to multiply the base point, and the "hash prefix"
153/// which is also used in the signature.
154///
155/// Note that the Tor implementation, unlike many (including ed25519_dalek),
156/// don't reduce the scalar modulo the group order \\( \ell \\). This doesn't
157/// really matter, though, since the group operation will be the same regardless
158pub type TorBlob = [u8; 64];
159
160/// Tor Ed25519 Signing (private) key
161#[serde_as]
162#[derive(ZeroizeOnDrop)]
163pub struct TorEd25519SigningKey {
164    // Used to recreate the original blob
165    // Because the key returned in the blob is not reduced,
166    // to recreate the original blob (for instance, to return it to Tor)
167    // we need to hold onto the original unreduced bytes to recreate
168    // the blob
169    scalar_bytes: [u8; 32],
170
171    // Actual key data
172    expanded_secret_key: ExpandedSecretKey,
173}
174
175impl TorEd25519SigningKey {
176    /// Expanded secret key, used for signing
177    fn expanded_secret_key(&self) -> &ExpandedSecretKey {
178        &self.expanded_secret_key
179    }
180
181    /// Retrieve the public verifying key from the secret key
182    pub fn verifying_key(&self) -> VerifyingKey {
183        VerifyingKey::from(self.expanded_secret_key())
184    }
185
186    /// Return the ED25519 scalar
187    pub fn scalar(&self) -> Scalar {
188        self.expanded_secret_key.scalar
189    }
190
191    /// Create the signing key from the key blob returned by the `ADD_ONION` call
192    /// (see <https://github.com/torproject/torspec/blob/main/control-spec.txt#L1862-L1864>)
193    pub fn from_blob(blob: &str) -> Result<Self, TorError> {
194        // Decode the blob and turn it into the Dalek ExpandedSecretKey
195        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    /// Convert from raw bytes to the SigningKey
208    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    /// Verify a message against a signature
218    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
219        self.verifying_key().verify(message, signature)
220    }
221
222    /// Convert the key to a Tor blob value
223    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
231/// Convert from a Base64-encoded Tor blob to our signing key
232impl 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
252/// Convert from an ED25519 signing key to our signing key.
253/// This takes the 32-byte secret key and hashes, clamps, and reduces it
254/// to the 64-byte expanded key.
255impl 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            // Hold onto the top 32 bytes for the blob
261            // Note that we don't really have to do that in this case, since the
262            // bytes are the same as the expanded_secret_key, but it makes
263            // the code cleaner
264            scalar_bytes: *expanded_secret_key.scalar.as_bytes(),
265            expanded_secret_key,
266        }
267    }
268}
269
270/// Implement signing for the Tor key
271impl 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    // Signing key is (base64) 0H/jnBeWzMoU1MGNRQPnmd8JqlpTNS3UeTiDOMyPTGGXXpLd0KinCtQbcgz2fCYjbzfK3ElJ7x3zGCkB1fAtAA==
288    // Service id is vvqbbaknxi6w44t6rplzh7nmesfzw3rjujdijpqsu5xl3nhlkdscgqad
289    #[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}