1
//! Key type wrappers of various kinds used in onion services.
2
//!
3
//! (We define wrappers here as a safety net against confusing one kind of
4
//! key for another: without a system like this, it can get pretty hard making
5
//! sure that each key is used only in the right way.)
6

            
7
use std::fmt::{self, Debug, Display};
8
use std::str::FromStr;
9

            
10
use digest::Digest;
11
use itertools::{chain, Itertools};
12
use thiserror::Error;
13
use tor_basic_utils::{impl_debug_hex, StrExt as _};
14
use tor_key_forge::ToEncodableKey;
15
use tor_llcrypto::d::Sha3_256;
16
use tor_llcrypto::pk::ed25519::{Ed25519PublicKey, Ed25519SigningKey};
17
use tor_llcrypto::pk::{curve25519, ed25519, keymanip};
18
use tor_llcrypto::util::ct::CtByteArray;
19

            
20
use crate::macros::{define_bytes, define_pk_keypair};
21
use crate::time::TimePeriod;
22

            
23
#[allow(deprecated)]
24
pub use hs_client_intro_auth::{HsClientIntroAuthKey, HsClientIntroAuthKeypair};
25

            
26
define_bytes! {
27
/// The identity of a v3 onion service. (KP_hs_id)
28
///
29
/// This is the decoded and validated ed25519 public key that is encoded as a
30
/// `${base32}.onion` address.  When expanded, it is a public key whose
31
/// corresponding secret key is controlled by the onion service.
32
///
33
/// `HsId`'s `Display` and `FromStr` representation is the domain name
34
/// `"${base32}.onion"`.  (Without any subdomains.)
35
///
36
/// Note: This is a separate type from [`HsIdKey`] because it is about 6x
37
/// smaller.
38
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
39
pub struct HsId([u8; 32]);
40
}
41

            
42
impl fmt::LowerHex for HsId {
43
2
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44
2
        write!(f, "HsId(0x")?;
45
64
        for v in self.0.as_ref() {
46
64
            write!(f, "{:02x}", v)?;
47
        }
48
2
        write!(f, ")")?;
49
2
        Ok(())
50
2
    }
51
}
52

            
53
impl Debug for HsId {
54
1182
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55
1182
        write!(f, "HsId({})", self)
56
1182
    }
57
}
58

            
59
define_pk_keypair! {
60
/// The identity of a v3 onion service, expanded into a public key. (KP_hs_id)
61
///
62
/// This is the decoded and validated ed25519 public key that is encoded as
63
/// a `${base32}.onion` address.
64
///
65
/// This key is not used to sign or validate anything on its own; instead, it is
66
/// used to derive a [`HsBlindIdKey`].
67
///
68
/// Note: This is a separate type from [`HsId`] because it is about 6x
69
/// larger.  It is an expanded form, used for doing actual cryptography.
70
//
71
// NOTE: This is called the "master" key in rend-spec-v3, but we're deprecating
72
// that vocabulary generally.
73
pub struct HsIdKey(ed25519::PublicKey) /
74
    ///
75
    /// This is stored as an expanded secret key, for compatibility with the C
76
    /// tor implementation, and in order to support custom-generated addresses.
77
    ///
78
    /// (About custom generated addresses: When making a vanity onion address,
79
    /// it is inefficient to search for a compact secret key `s` and compute
80
    /// `SHA512(s)=(a,r)` and `A=aB` until you find an `s` that produces an `A`
81
    /// that you like.  Instead, most folks use the algorithm of
82
    /// rend-spec-v3.txt appendix C, wherein you search for a good `a` directly
83
    /// by repeatedly adding `8B` to A until you find an `A` you like.  The only
84
    /// major drawback is that once you have found a good `a`, you can't get an
85
    /// `s` for it, since you presumably can't find SHA512 preimages.  And that
86
    /// is why we store the private key in (a,r) form.)
87
    HsIdKeypair(ed25519::ExpandedKeypair);
88
}
89

            
90
impl HsIdKey {
91
    /// Return a representation of this key as an [`HsId`].
92
    ///
93
    /// ([`HsId`] is much smaller, and easier to store.)
94
531
    pub fn id(&self) -> HsId {
95
531
        HsId(self.0.to_bytes().into())
96
531
    }
97
}
98
impl TryFrom<HsId> for HsIdKey {
99
    type Error = signature::Error;
100

            
101
120
    fn try_from(value: HsId) -> Result<Self, Self::Error> {
102
120
        ed25519::PublicKey::from_bytes(value.0.as_ref()).map(HsIdKey)
103
120
    }
104
}
105
impl From<HsIdKey> for HsId {
106
295
    fn from(value: HsIdKey) -> Self {
107
295
        value.id()
108
295
    }
109
}
110

            
111
impl From<&HsIdKeypair> for HsIdKey {
112
118
    fn from(value: &HsIdKeypair) -> Self {
113
118
        Self(*value.0.public())
114
118
    }
115
}
116

            
117
impl From<HsIdKeypair> for HsIdKey {
118
177
    fn from(value: HsIdKeypair) -> Self {
119
177
        Self(*value.0.public())
120
177
    }
121
}
122

            
123
/// VERSION from rend-spec-v3 s.6 \[ONIONADDRESS]
124
const HSID_ONION_VERSION: u8 = 0x03;
125

            
126
/// The fixed string `.onion`
127
pub const HSID_ONION_SUFFIX: &str = ".onion";
128

            
129
impl Display for HsId {
130
2720
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131
2720
        // rend-spec-v3 s.6 [ONIONADDRESS]
132
2720
        let checksum = self.onion_checksum();
133
2720
        let binary = chain!(self.0.as_ref(), &checksum, &[HSID_ONION_VERSION],)
134
2720
            .cloned()
135
2720
            .collect_vec();
136
2720
        let mut b32 = data_encoding::BASE32_NOPAD.encode(&binary);
137
2720
        b32.make_ascii_lowercase();
138
2720
        write!(f, "{}{}", b32, HSID_ONION_SUFFIX)
139
2720
    }
140
}
141

            
142
impl safelog::Redactable for HsId {
143
    // We here display some of the end.  We don't want to display the
144
    // *start* because vanity domains, which would perhaps suffer from
145
    // reduced deniability.
146
2
    fn display_redacted(&self, f: &mut fmt::Formatter) -> fmt::Result {
147
2
        let unredacted = self.to_string();
148
        /// Length of the base32 data part of the address
149
        const DATA: usize = 56;
150
2
        assert_eq!(unredacted.len(), DATA + HSID_ONION_SUFFIX.len());
151

            
152
        // We show this part of the domain:
153
        //     e     n     l     5     s     i     d     .onion
154
        //   KKKKK KKKKK KCCCC CCCCC CCCCC CCVVV VVVVV
155
        //                           ^^^^^^^^^^^^^^^^^ ^^^^^^^^^
156
        // This contains 3 characters of base32, which is 15 bits.
157
        // 8 of those bits are the version, which is currently always 0x03.
158
        // So we are showing 7 bits derived from the site key.
159

            
160
2
        write!(f, "???{}", &unredacted[DATA - 3..])
161
2
    }
162
}
163

            
164
impl FromStr for HsId {
165
    type Err = HsIdParseError;
166
1668
    fn from_str(s: &str) -> Result<Self, HsIdParseError> {
167
        use HsIdParseError as PE;
168

            
169
1668
        let s = s
170
1668
            .strip_suffix_ignore_ascii_case(HSID_ONION_SUFFIX)
171
1668
            .ok_or(PE::NotOnionDomain)?;
172

            
173
1666
        if s.contains('.') {
174
2
            return Err(PE::HsIdContainsSubdomain);
175
1664
        }
176
1664

            
177
1664
        // We must convert to uppercase because RFC4648 says so and that's what Rust
178
1664
        // ecosystem libraries for base32 expect.  All this allocation and copying is
179
1664
        // still probably less work than the SHA3 for the checksum.
180
1664
        // However, we are going to use this function to *detect* and filter .onion
181
1664
        // addresses, so it should have a fast path to reject thm.
182
1664
        let mut s = s.to_owned();
183
1664
        s.make_ascii_uppercase();
184

            
185
        // Ideally we'd have code here that would provide a clear error message if
186
        // we encounter an address with the wrong version.  But that is very complicated
187
        // because the encoding format does not make that at all convenient.
188
        // So instead our errors tell you what aspect of the parsing went wrong.
189
1664
        let binary = data_encoding::BASE32_NOPAD.decode(s.as_bytes())?;
190
1662
        let mut binary = tor_bytes::Reader::from_slice(&binary);
191

            
192
1662
        let pubkey: [u8; 32] = binary.extract()?;
193
1660
        let checksum: [u8; 2] = binary.extract()?;
194
1660
        let version: u8 = binary.extract()?;
195
1660
        let tentative = HsId(pubkey.into());
196
1660

            
197
1660
        // Check version before checksum; maybe a future version does checksum differently
198
1660
        if version != HSID_ONION_VERSION {
199
2
            return Err(PE::UnsupportedVersion(version));
200
1658
        }
201
1658
        if checksum != tentative.onion_checksum() {
202
2
            return Err(PE::WrongChecksum);
203
1656
        }
204
1656
        Ok(tentative)
205
1668
    }
206
}
207

            
208
/// Error that can occur parsing an `HsId` from a v3 `.onion` domain name
209
#[derive(Error, Clone, Debug)]
210
#[non_exhaustive]
211
pub enum HsIdParseError {
212
    /// Supplied domain name string does not end in `.onion`
213
    #[error("Domain name does not end in .onion")]
214
    NotOnionDomain,
215

            
216
    /// Base32 decoding failed
217
    ///
218
    /// `position` is indeed the (byte) position in the input string
219
    #[error("Invalid base32 in .onion address")]
220
    InvalidBase32(#[from] data_encoding::DecodeError),
221

            
222
    /// Encoded binary data is invalid
223
    #[error("Invalid encoded binary data in .onion address")]
224
    InvalidData(#[from] tor_bytes::Error),
225

            
226
    /// Unsupported `.onion` address version
227
    #[error("Unsupported .onion address version, v{0}")]
228
    UnsupportedVersion(u8),
229

            
230
    /// Checksum failed
231
    #[error("Checksum failed, .onion address corrupted")]
232
    WrongChecksum,
233

            
234
    /// If you try to parse a domain with subdomains as an `HsId`
235
    #[error("`.onion` address with subdomain passed where not expected")]
236
    HsIdContainsSubdomain,
237
}
238

            
239
impl HsId {
240
    /// Calculates CHECKSUM rend-spec-v3 s.6 \[ONIONADDRESS]
241
4378
    fn onion_checksum(&self) -> [u8; 2] {
242
4378
        let mut h = Sha3_256::new();
243
4378
        h.update(b".onion checksum");
244
4378
        h.update(self.0.as_ref());
245
4378
        h.update([HSID_ONION_VERSION]);
246
4378
        h.finalize()[..2]
247
4378
            .try_into()
248
4378
            .expect("slice of fixed size wasn't that size")
249
4378
    }
250
}
251

            
252
impl HsIdKey {
253
    /// Derive the blinded key and subcredential for this identity during `cur_period`.
254
598
    pub fn compute_blinded_key(
255
598
        &self,
256
598
        cur_period: TimePeriod,
257
598
    ) -> Result<(HsBlindIdKey, crate::Subcredential), keymanip::BlindingError> {
258
598
        // TODO: someday we might want to support this kinds of a shared secret
259
598
        // in our protocol. (C tor does not.)  If we did, it would be an
260
598
        // additional piece of information about an onion service that you would
261
598
        // need to know in order to connect to it.
262
598
        //
263
598
        // This is the "optional secret s" mentioned in the key-blinding
264
598
        // appendix to rend-spec.txt.
265
598
        let secret = b"";
266
598
        let h = self.blinding_factor(secret, cur_period);
267

            
268
598
        let blinded_key = keymanip::blind_pubkey(&self.0, h)?.into();
269
598
        // rend-spec-v3 section 2.1
270
598
        let subcredential = self.compute_subcredential(&blinded_key, cur_period);
271
598

            
272
598
        Ok((blinded_key, subcredential))
273
598
    }
274

            
275
    /// Given a time period and a blinded public key, compute the subcredential.
276
4374
    pub fn compute_subcredential(
277
4374
        &self,
278
4374
        blinded_key: &HsBlindIdKey,
279
4374
        cur_period: TimePeriod,
280
4374
    ) -> crate::Subcredential {
281
4374
        // rend-spec-v3 section 2.1
282
4374
        let subcredential_bytes: [u8; 32] = {
283
4374
            // N_hs_subcred = H("subcredential" | N_hs_cred | blinded-public-key).
284
4374
            // where
285
4374
            //    N_hs_cred = H("credential" | public-identity-key)
286
4374
            let n_hs_cred: [u8; 32] = {
287
4374
                let mut h = Sha3_256::new();
288
4374
                h.update(b"credential");
289
4374
                h.update(self.0.as_bytes());
290
4374
                h.finalize().into()
291
4374
            };
292
4374
            let mut h = Sha3_256::new();
293
4374
            h.update(b"subcredential");
294
4374
            h.update(n_hs_cred);
295
4374
            h.update(blinded_key.as_bytes());
296
4374
            h.finalize().into()
297
4374
        };
298
4374

            
299
4374
        subcredential_bytes.into()
300
4374
    }
301

            
302
    /// Compute the 32-byte "blinding factor" used to compute blinded public
303
    /// (and secret) keys.
304
    ///
305
    /// Returns the value `h = H(...)`, from rend-spec-v3 A.2., before clamping.
306
958
    fn blinding_factor(&self, secret: &[u8], cur_period: TimePeriod) -> [u8; 32] {
307
        // rend-spec-v3 appendix A.2
308
        // We generate our key blinding factor as
309
        //    h = H(BLIND_STRING | A | s | B | N)
310
        // Where:
311
        //    H is SHA3-256.
312
        //    A is this public key.
313
        //    BLIND_STRING = "Derive temporary signing key" | INT_1(0)
314
        //    s is an optional secret (not implemented here.)
315
        //    B is the ed25519 basepoint.
316
        //    N = "key-blind" || INT_8(period_num) || INT_8(period_length).
317

            
318
        /// String used as part of input to blinding hash.
319
        const BLIND_STRING: &[u8] = b"Derive temporary signing key\0";
320
        /// String representation of our Ed25519 basepoint.
321
        const ED25519_BASEPOINT: &[u8] =
322
            b"(15112221349535400772501151409588531511454012693041857206046113283949847762202, \
323
               46316835694926478169428394003475163141307993866256225615783033603165251855960)";
324

            
325
958
        let mut h = Sha3_256::new();
326
958
        h.update(BLIND_STRING);
327
958
        h.update(self.0.as_bytes());
328
958
        h.update(secret);
329
958
        h.update(ED25519_BASEPOINT);
330
958
        h.update(b"key-blind");
331
958
        h.update(cur_period.interval_num.to_be_bytes());
332
958
        h.update((u64::from(cur_period.length.as_minutes())).to_be_bytes());
333
958

            
334
958
        h.finalize().into()
335
958
    }
336
}
337

            
338
impl HsIdKeypair {
339
    /// Derive the blinded key and subcredential for this identity during `cur_period`.
340
358
    pub fn compute_blinded_key(
341
358
        &self,
342
358
        cur_period: TimePeriod,
343
358
    ) -> Result<(HsBlindIdKey, HsBlindIdKeypair, crate::Subcredential), keymanip::BlindingError>
344
358
    {
345
358
        // TODO: as discussed above in `HsId::compute_blinded_key`, we might
346
358
        // someday want to implement nonempty values for this secret, if we
347
358
        // decide it would be good for something.
348
358
        let secret = b"";
349
358

            
350
358
        let public_key = HsIdKey(*self.0.public());
351

            
352
        // Note: This implementation is somewhat inefficient, as it recomputes
353
        // the PublicKey, and computes our blinding factor twice.  But we
354
        // only do this on an onion service once per time period: the
355
        // performance does not matter.
356
358
        let (blinded_public_key, subcredential) = public_key.compute_blinded_key(cur_period)?;
357

            
358
358
        let h = public_key.blinding_factor(secret, cur_period);
359

            
360
358
        let blinded_keypair = keymanip::blind_keypair(&self.0, h)?;
361

            
362
358
        Ok((blinded_public_key, blinded_keypair.into(), subcredential))
363
358
    }
364
}
365

            
366
define_pk_keypair! {
367
/// The "blinded" identity of a v3 onion service. (`KP_hs_blind_id`)
368
///
369
/// This key is derived via a one-way transformation from an
370
/// `HsIdKey` and the current time period.
371
///
372
/// It is used for two purposes: first, to compute an index into the HSDir
373
/// ring, and second, to sign a `DescSigningKey`.
374
///
375
/// Note: This is a separate type from [`HsBlindId`] because it is about 6x
376
/// larger.  It is an expanded form, used for doing actual cryptography.
377
pub struct HsBlindIdKey(ed25519::PublicKey) / HsBlindIdKeypair(ed25519::ExpandedKeypair);
378
}
379

            
380
impl From<HsBlindIdKeypair> for HsBlindIdKey {
381
    fn from(kp: HsBlindIdKeypair) -> HsBlindIdKey {
382
        HsBlindIdKey(kp.0.into())
383
    }
384
}
385

            
386
define_bytes! {
387
/// A blinded onion service identity, represented in a compact format. (`KP_hs_blind_id`)
388
///
389
/// Note: This is a separate type from [`HsBlindIdKey`] because it is about
390
/// 6x smaller.
391
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
392
pub struct HsBlindId([u8; 32]);
393
}
394
impl_debug_hex! { HsBlindId .0 }
395

            
396
impl HsBlindIdKey {
397
    /// Return a representation of this key as a [`HsBlindId`].
398
    ///
399
    /// ([`HsBlindId`] is much smaller, and easier to store.)
400
712
    pub fn id(&self) -> HsBlindId {
401
712
        HsBlindId(self.0.to_bytes().into())
402
712
    }
403
}
404
impl TryFrom<HsBlindId> for HsBlindIdKey {
405
    type Error = signature::Error;
406

            
407
    fn try_from(value: HsBlindId) -> Result<Self, Self::Error> {
408
        ed25519::PublicKey::from_bytes(value.0.as_ref()).map(HsBlindIdKey)
409
    }
410
}
411

            
412
impl From<&HsBlindIdKeypair> for HsBlindIdKey {
413
7965
    fn from(value: &HsBlindIdKeypair) -> Self {
414
7965
        HsBlindIdKey(*value.0.public())
415
7965
    }
416
}
417

            
418
impl From<HsBlindIdKey> for HsBlindId {
419
590
    fn from(value: HsBlindIdKey) -> Self {
420
590
        value.id()
421
590
    }
422
}
423
impl From<ed25519::Ed25519Identity> for HsBlindId {
424
10797
    fn from(value: ed25519::Ed25519Identity) -> Self {
425
10797
        Self(CtByteArray::from(<[u8; 32]>::from(value)))
426
10797
    }
427
}
428

            
429
impl Ed25519SigningKey for HsBlindIdKeypair {
430
4014
    fn sign(&self, message: &[u8]) -> ed25519::Signature {
431
4014
        self.0.sign(message)
432
4014
    }
433
}
434

            
435
impl Ed25519PublicKey for HsBlindIdKeypair {
436
4012
    fn public_key(&self) -> ed25519::PublicKey {
437
4012
        *self.0.public()
438
4012
    }
439
}
440

            
441
define_pk_keypair! {
442
/// A key used to sign onion service descriptors. (`KP_desc_sign`)
443
///
444
/// It is authenticated with a [`HsBlindIdKey`] to prove that it belongs to
445
/// the right onion service, and is used in turn to sign the descriptor that
446
/// tells clients what they need to know about contacting an onion service.
447
///
448
/// Onion services create a new `DescSigningKey` every time the
449
/// `HsBlindIdKey` rotates, to prevent descriptors made in one time period
450
/// from being linkable to those made in another.
451
///
452
/// Note: we use a separate signing key here, rather than using the
453
/// `HsBlindIdKey` directly, so that the [`HsBlindIdKeypair`]
454
/// can be kept offline.
455
pub struct HsDescSigningKey(ed25519::PublicKey) / HsDescSigningKeypair(ed25519::Keypair);
456
}
457

            
458
define_pk_keypair! {
459
/// A key used to identify and authenticate an onion service at a single
460
/// introduction point. (`KP_hs_ipt_sid`)
461
///
462
/// This key is included in the onion service's descriptor; a different one is
463
/// used at each introduction point.  Introduction points don't know the
464
/// relation of this key to the onion service: they only recognize the same key
465
/// when they see it again.
466
pub struct HsIntroPtSessionIdKey(ed25519::PublicKey) / HsIntroPtSessionIdKeypair(ed25519::Keypair);
467
}
468

            
469
define_pk_keypair! {
470
/// A key used in the HsNtor handshake between the client and the onion service.
471
/// (`KP_hss_ntor`)
472
///
473
/// The onion service chooses a different one of these to use with each
474
/// introduction point, though it does not need to tell the introduction points
475
/// about these keys.
476
pub struct HsSvcNtorKey(curve25519::PublicKey) / HsSvcNtorSecretKey(curve25519::StaticSecret);
477
curve25519_pair as HsSvcNtorKeypair;
478
}
479

            
480
mod hs_client_intro_auth {
481
    #![allow(deprecated)]
482
    //! Key type wrappers for the deprecated `HsClientIntroKey`/`HsClientIntroKeypair` types.
483

            
484
    use tor_llcrypto::pk::ed25519;
485

            
486
    use crate::macros::define_pk_keypair;
487

            
488
    define_pk_keypair! {
489
    /// First type of client authorization key, used for the introduction protocol.
490
    /// (`KP_hsc_intro_auth`)
491
    ///
492
    /// This is used to sign a nonce included in an extension in the encrypted
493
    /// portion of an introduce cell.
494
    #[deprecated(note = "This key type is not used in the protocol implemented today.")]
495
    pub struct HsClientIntroAuthKey(ed25519::PublicKey) /
496
    #[deprecated(note = "This key type is not used in the protocol implemented today.")]
497
    HsClientIntroAuthKeypair(ed25519::Keypair);
498
    }
499
}
500

            
501
define_pk_keypair! {
502
/// Client service discovery key, used for onion descriptor
503
/// decryption. (`KP_hsc_desc_enc`)
504
///
505
/// Any client who knows the secret key corresponding to this key can decrypt
506
/// the inner layer of the onion service descriptor.
507
///
508
/// The [`Display`] and [`FromStr`] representation of keys of this type is
509
/// `descriptor:x25519:<base32-encoded-x25519-public-key>`.
510
/// Note: the base32 encoding of the key is unpadded and case-insensitive,
511
/// for compatibility with the format accepted by C Tor.
512
/// See also `CLIENT AUTHORIZATION` in `tor(1)`.
513
///
514
/// # Example
515
///
516
/// ```rust
517
/// # use tor_hscrypto::pk::HsClientDescEncKey;
518
/// # use std::str::FromStr;
519
/// // A client service discovery key for connecting
520
/// // to a service running in restricted discovery mode,
521
/// // with an uppercase base32 encoding for the key material.
522
/// const CLIENT_KEY1: &str = "descriptor:x25519:ZPRRMIV6DV6SJFL7SFBSVLJ5VUNPGCDFEVZ7M23LTLVTCCXJQBKA";
523
/// // An identical key using lowercase base32 encoding for the key material.
524
/// const CLIENT_KEY2: &str = "descriptor:x25519:zprrmiv6dv6sjfl7sfbsvlj5vunpgcdfevz7m23ltlvtccxjqbka";
525
///
526
/// // Both key encodings parse successfully
527
/// let key1 = HsClientDescEncKey::from_str(CLIENT_KEY1).unwrap();
528
/// let key2 = HsClientDescEncKey::from_str(CLIENT_KEY2).unwrap();
529
/// // The keys are identical
530
/// assert_eq!(key1, key2);
531
/// ```
532
pub struct HsClientDescEncKey(curve25519::PublicKey) / HsClientDescEncSecretKey(curve25519::StaticSecret);
533
curve25519_pair as HsClientDescEncKeypair;
534
}
535

            
536
impl PartialEq for HsClientDescEncKey {
537
1418
    fn eq(&self, other: &Self) -> bool {
538
1418
        self.0 == other.0
539
1418
    }
540
}
541

            
542
impl Eq for HsClientDescEncKey {}
543

            
544
impl Display for HsClientDescEncKey {
545
1064
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546
1064
        let x25519_pk = data_encoding::BASE32_NOPAD.encode(&self.0.to_bytes());
547
1064
        write!(f, "descriptor:x25519:{}", x25519_pk)
548
1064
    }
549
}
550

            
551
impl FromStr for HsClientDescEncKey {
552
    type Err = HsClientDescEncKeyParseError;
553

            
554
1436
    fn from_str(key: &str) -> Result<Self, HsClientDescEncKeyParseError> {
555
1436
        let (auth_type, key_type, encoded_key) = key
556
1436
            .split(':')
557
1436
            .collect_tuple()
558
1436
            .ok_or(HsClientDescEncKeyParseError::InvalidFormat)?;
559

            
560
1426
        if auth_type != "descriptor" {
561
2
            return Err(HsClientDescEncKeyParseError::InvalidAuthType(
562
2
                auth_type.into(),
563
2
            ));
564
1424
        }
565
1424

            
566
1424
        if key_type != "x25519" {
567
2
            return Err(HsClientDescEncKeyParseError::InvalidKeyType(
568
2
                key_type.into(),
569
2
            ));
570
1422
        }
571
1422

            
572
1422
        // Note: Tor's base32 decoder is case-insensitive, so we can't assume the input
573
1422
        // is all uppercase.
574
1422
        //
575
1422
        // TODO: consider using `data_encoding_macro::new_encoding` to create a new Encoding
576
1422
        // with an alphabet that includes lowercase letters instead of to_uppercase()ing the string.
577
1422
        let encoded_key = encoded_key.to_uppercase();
578
1422
        let x25519_pk = data_encoding::BASE32_NOPAD.decode(encoded_key.as_bytes())?;
579
1361
        let x25519_pk: [u8; 32] = x25519_pk
580
1361
            .try_into()
581
1361
            .map_err(|_| HsClientDescEncKeyParseError::InvalidKeyMaterial)?;
582

            
583
1361
        Ok(Self(curve25519::PublicKey::from(x25519_pk)))
584
1436
    }
585
}
586

            
587
/// Error that can occur parsing an `HsClientDescEncKey` from C Tor format.
588
#[derive(Error, Clone, Debug, PartialEq)]
589
#[non_exhaustive]
590
pub enum HsClientDescEncKeyParseError {
591
    /// The auth type is not "descriptor".
592
    #[error("Invalid auth type {0}")]
593
    InvalidAuthType(String),
594

            
595
    /// The key type is not "x25519".
596
    #[error("Invalid key type {0}")]
597
    InvalidKeyType(String),
598

            
599
    /// The key is not in the `<auth-type>:x25519:<base32-encoded-public-key>` format.
600
    #[error("Invalid key format")]
601
    InvalidFormat,
602

            
603
    /// The encoded key material is invalid.
604
    #[error("Invalid key material")]
605
    InvalidKeyMaterial,
606

            
607
    /// Base32 decoding failed.
608
    #[error("Invalid base32 in client key")]
609
    InvalidBase32(#[from] data_encoding::DecodeError),
610
}
611

            
612
define_pk_keypair! {
613
/// Server key, used for diffie hellman during onion descriptor decryption.
614
/// (`KP_hss_desc_enc`)
615
///
616
/// This key is created for a single descriptor, and then thrown away.
617
pub struct HsSvcDescEncKey(curve25519::PublicKey) / HsSvcDescEncSecretKey(curve25519::StaticSecret);
618
}
619

            
620
impl From<&HsClientDescEncSecretKey> for HsClientDescEncKey {
621
    fn from(ks: &HsClientDescEncSecretKey) -> Self {
622
        Self(curve25519::PublicKey::from(&ks.0))
623
    }
624
}
625

            
626
impl From<&HsClientDescEncKeypair> for HsClientDescEncKey {
627
    fn from(ks: &HsClientDescEncKeypair) -> Self {
628
        Self(**ks.public())
629
    }
630
}
631

            
632
/// An ephemeral x25519 keypair, generated by an onion service
633
/// and used to for onion service encryption.
634
#[allow(clippy::exhaustive_structs)]
635
#[derive(Debug)]
636
pub struct HsSvcDescEncKeypair {
637
    /// The public part of the key.
638
    pub public: HsSvcDescEncKey,
639
    /// The secret part of the key.
640
    pub secret: HsSvcDescEncSecretKey,
641
}
642

            
643
// TODO: let the define_ed25519_keypair/define_curve25519_keypair macros
644
// auto-generate these impls.
645
//
646
// For some of the keys here, this currently cannot be done
647
// because the macro doesn't support generating expanded ed25519 keys.
648

            
649
impl ToEncodableKey for HsClientDescEncKeypair {
650
    type Key = curve25519::StaticKeypair;
651
    type KeyPair = HsClientDescEncKeypair;
652

            
653
    fn to_encodable_key(self) -> Self::Key {
654
        self.into()
655
    }
656

            
657
295
    fn from_encodable_key(key: Self::Key) -> Self {
658
295
        HsClientDescEncKeypair::new(key.public.into(), key.secret.into())
659
295
    }
660
}
661

            
662
impl ToEncodableKey for HsBlindIdKeypair {
663
    type Key = ed25519::ExpandedKeypair;
664
    type KeyPair = HsBlindIdKeypair;
665

            
666
236
    fn to_encodable_key(self) -> Self::Key {
667
236
        self.into()
668
236
    }
669

            
670
7788
    fn from_encodable_key(key: Self::Key) -> Self {
671
7788
        HsBlindIdKeypair::from(key)
672
7788
    }
673
}
674

            
675
impl ToEncodableKey for HsBlindIdKey {
676
    type Key = ed25519::PublicKey;
677
    type KeyPair = HsBlindIdKeypair;
678

            
679
236
    fn to_encodable_key(self) -> Self::Key {
680
236
        self.into()
681
236
    }
682

            
683
    fn from_encodable_key(key: Self::Key) -> Self {
684
        HsBlindIdKey::from(key)
685
    }
686
}
687

            
688
impl ToEncodableKey for HsIdKeypair {
689
    type Key = ed25519::ExpandedKeypair;
690
    type KeyPair = HsIdKeypair;
691

            
692
354
    fn to_encodable_key(self) -> Self::Key {
693
354
        self.into()
694
354
    }
695

            
696
8142
    fn from_encodable_key(key: Self::Key) -> Self {
697
8142
        HsIdKeypair::from(key)
698
8142
    }
699
}
700

            
701
impl ToEncodableKey for HsIdKey {
702
    type Key = ed25519::PublicKey;
703
    type KeyPair = HsIdKeypair;
704

            
705
236
    fn to_encodable_key(self) -> Self::Key {
706
236
        self.into()
707
236
    }
708

            
709
3776
    fn from_encodable_key(key: Self::Key) -> Self {
710
3776
        HsIdKey::from(key)
711
3776
    }
712
}
713

            
714
impl ToEncodableKey for HsDescSigningKeypair {
715
    type Key = ed25519::Keypair;
716
    type KeyPair = HsDescSigningKeypair;
717

            
718
236
    fn to_encodable_key(self) -> Self::Key {
719
236
        self.into()
720
236
    }
721

            
722
3776
    fn from_encodable_key(key: Self::Key) -> Self {
723
3776
        HsDescSigningKeypair::from(key)
724
3776
    }
725
}
726

            
727
impl ToEncodableKey for HsIntroPtSessionIdKeypair {
728
    type Key = ed25519::Keypair;
729
    type KeyPair = HsIntroPtSessionIdKeypair;
730

            
731
    fn to_encodable_key(self) -> Self::Key {
732
        self.into()
733
    }
734

            
735
1180
    fn from_encodable_key(key: Self::Key) -> Self {
736
1180
        key.into()
737
1180
    }
738
}
739

            
740
impl ToEncodableKey for HsSvcNtorKeypair {
741
    type Key = curve25519::StaticKeypair;
742
    type KeyPair = HsSvcNtorKeypair;
743

            
744
    fn to_encodable_key(self) -> Self::Key {
745
        self.into()
746
    }
747

            
748
1180
    fn from_encodable_key(key: Self::Key) -> Self {
749
1180
        key.into()
750
1180
    }
751
}
752

            
753
#[cfg(test)]
754
mod test {
755
    // @@ begin test lint list maintained by maint/add_warning @@
756
    #![allow(clippy::bool_assert_comparison)]
757
    #![allow(clippy::clone_on_copy)]
758
    #![allow(clippy::dbg_macro)]
759
    #![allow(clippy::mixed_attributes_style)]
760
    #![allow(clippy::print_stderr)]
761
    #![allow(clippy::print_stdout)]
762
    #![allow(clippy::single_char_pattern)]
763
    #![allow(clippy::unwrap_used)]
764
    #![allow(clippy::unchecked_duration_subtraction)]
765
    #![allow(clippy::useless_vec)]
766
    #![allow(clippy::needless_pass_by_value)]
767
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
768

            
769
    use hex_literal::hex;
770
    use itertools::izip;
771
    use safelog::Redactable;
772
    use std::time::{Duration, SystemTime};
773
    use tor_basic_utils::test_rng::testing_rng;
774

            
775
    use super::*;
776

            
777
    #[test]
778
    fn hsid_strings() {
779
        use HsIdParseError as PE;
780

            
781
        // From C Tor src/test/test_hs_common.c test_build_address
782
        let hex = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
783
        let b32 = "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid";
784

            
785
        let hsid: [u8; 32] = hex::decode(hex).unwrap().try_into().unwrap();
786
        let hsid = HsId::from(hsid);
787
        let onion = format!("{}.onion", b32);
788

            
789
        assert_eq!(onion.parse::<HsId>().unwrap(), hsid);
790
        assert_eq!(hsid.to_string(), onion);
791

            
792
        let weird_case: String = izip!(onion.chars(), [false, true].iter().cloned().cycle(),)
793
            .map(|(c, swap)| if swap { c.to_ascii_uppercase() } else { c })
794
            .collect();
795
        dbg!(&weird_case);
796
        assert_eq!(weird_case.parse::<HsId>().unwrap(), hsid);
797

            
798
        macro_rules! chk_err { { $s:expr, $($pat:tt)* } => {
799
            let e = $s.parse::<HsId>();
800
            assert!(matches!(e, Err($($pat)*)), "{:?}", &e);
801
        } }
802
        let edited = |i, c| {
803
            let mut s = b32.to_owned().into_bytes();
804
            s[i] = c;
805
            format!("{}.onion", String::from_utf8(s).unwrap())
806
        };
807

            
808
        chk_err!("wrong", PE::NotOnionDomain);
809
        chk_err!("@.onion", PE::InvalidBase32(..));
810
        chk_err!("aaaaaaaa.onion", PE::InvalidData(..));
811
        chk_err!(edited(55, b'E'), PE::UnsupportedVersion(4));
812
        chk_err!(edited(53, b'X'), PE::WrongChecksum);
813
        chk_err!(&format!("www.{}", &onion), PE::HsIdContainsSubdomain);
814

            
815
        assert_eq!(format!("{:x}", &hsid), format!("HsId(0x{})", hex));
816
        assert_eq!(format!("{:?}", &hsid), format!("HsId({})", onion));
817

            
818
        assert_eq!(format!("{}", hsid.redacted()), "???sid.onion");
819
    }
820

            
821
    #[test]
822
    fn key_blinding_blackbox() {
823
        let mut rng = testing_rng();
824
        let offset = Duration::new(12 * 60 * 60, 0);
825
        let when = TimePeriod::new(Duration::from_secs(3600), SystemTime::now(), offset).unwrap();
826
        let keypair = ed25519::Keypair::generate(&mut rng);
827
        let id_pub = HsIdKey::from(keypair.verifying_key());
828
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
829

            
830
        let (blinded_pub, subcred1) = id_pub.compute_blinded_key(when).unwrap();
831
        let (blinded_pub2, blinded_keypair, subcred2) =
832
            id_keypair.compute_blinded_key(when).unwrap();
833

            
834
        assert_eq!(subcred1.as_ref(), subcred2.as_ref());
835
        assert_eq!(blinded_pub.0.to_bytes(), blinded_pub2.0.to_bytes());
836
        assert_eq!(blinded_pub.id(), blinded_pub2.id());
837

            
838
        let message = b"Here is a terribly important string to authenticate.";
839
        let other_message = b"Hey, that is not what I signed!";
840
        let sign = blinded_keypair.sign(message);
841

            
842
        assert!(blinded_pub.as_ref().verify(message, &sign).is_ok());
843
        assert!(blinded_pub.as_ref().verify(other_message, &sign).is_err());
844
    }
845

            
846
    #[test]
847
    fn key_blinding_testvec() {
848
        // Test vectors generated with C tor.
849
        let id = HsId::from(hex!(
850
            "833990B085C1A688C1D4C8B1F6B56AFAF5A2ECA674449E1D704F83765CCB7BC6"
851
        ));
852
        let id_pubkey = HsIdKey::try_from(id).unwrap();
853
        let id_seckey = HsIdKeypair::from(
854
            ed25519::ExpandedKeypair::from_secret_key_bytes(hex!(
855
                "D8C7FF0E31295B66540D789AF3E3DF992038A9592EEA01D8B7CBA06D6E66D159
856
                 4D6167696320576F7264733A20737065697373636F62616C742062697669756D"
857
            ))
858
            .unwrap(),
859
        );
860
        let time_period = TimePeriod::new(
861
            humantime::parse_duration("1 day").unwrap(),
862
            humantime::parse_rfc3339("1973-05-20T01:50:33Z").unwrap(),
863
            humantime::parse_duration("12 hours").unwrap(),
864
        )
865
        .unwrap();
866
        assert_eq!(time_period.interval_num, 1234);
867

            
868
        let h = id_pubkey.blinding_factor(b"", time_period);
869
        assert_eq!(
870
            h,
871
            hex!("379E50DB31FEE6775ABD0AF6FB7C371E060308F4F847DB09FE4CFE13AF602287")
872
        );
873

            
874
        let (blinded_pub1, subcred1) = id_pubkey.compute_blinded_key(time_period).unwrap();
875
        assert_eq!(
876
            blinded_pub1.0.to_bytes(),
877
            hex!("3A50BF210E8F9EE955AE0014F7A6917FB65EBF098A86305ABB508D1A7291B6D5")
878
        );
879
        assert_eq!(
880
            subcred1.as_ref(),
881
            &hex!("635D55907816E8D76398A675A50B1C2F3E36B42A5CA77BA3A0441285161AE07D")
882
        );
883

            
884
        let (blinded_pub2, blinded_sec, subcred2) =
885
            id_seckey.compute_blinded_key(time_period).unwrap();
886
        assert_eq!(blinded_pub1.0.to_bytes(), blinded_pub2.0.to_bytes());
887
        assert_eq!(subcred1.as_ref(), subcred2.as_ref());
888
        assert_eq!(
889
            blinded_sec.0.to_secret_key_bytes(),
890
            hex!(
891
                "A958DC83AC885F6814C67035DE817A2C604D5D2F715282079448F789B656350B
892
                 4540FE1F80AA3F7E91306B7BF7A8E367293352B14A29FDCC8C19F3558075524B"
893
            )
894
        );
895
    }
896

            
897
    #[test]
898
    fn parse_client_desc_enc_key() {
899
        use HsClientDescEncKeyParseError::*;
900

            
901
        /// Valid base32-encoded x25519 public key.
902
        const VALID_KEY_BASE32: &str = "dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja";
903

            
904
        // Some keys that are in the wrong format
905
        const WRONG_FORMAT: &[&str] = &["a:b:c:d:e", "descriptor:", "descriptor:x25519", ""];
906

            
907
        for key in WRONG_FORMAT {
908
            let err = HsClientDescEncKey::from_str(key).unwrap_err();
909

            
910
            assert_eq!(err, InvalidFormat);
911
        }
912

            
913
        let err =
914
            HsClientDescEncKey::from_str(&format!("foo:descriptor:x25519:{VALID_KEY_BASE32}"))
915
                .unwrap_err();
916

            
917
        assert_eq!(err, InvalidFormat);
918

            
919
        // A key with an invalid auth type
920
        let err = HsClientDescEncKey::from_str("bar:x25519:aa==").unwrap_err();
921
        assert_eq!(err, InvalidAuthType("bar".into()));
922

            
923
        // A key with an invalid key type
924
        let err = HsClientDescEncKey::from_str("descriptor:not-x25519:aa==").unwrap_err();
925
        assert_eq!(err, InvalidKeyType("not-x25519".into()));
926

            
927
        // A key with an invalid base32 part
928
        let err = HsClientDescEncKey::from_str("descriptor:x25519:aa==").unwrap_err();
929
        assert!(matches!(err, InvalidBase32(_)));
930

            
931
        // A valid client desc enc key
932
        let _key =
933
            HsClientDescEncKey::from_str(&format!("descriptor:x25519:{VALID_KEY_BASE32}")).unwrap();
934

            
935
        // Roundtrip
936
        let desc_enc_key = HsClientDescEncKey::from(curve25519::PublicKey::from(
937
            &curve25519::StaticSecret::random_from_rng(testing_rng()),
938
        ));
939

            
940
        assert_eq!(
941
            desc_enc_key,
942
            HsClientDescEncKey::from_str(&desc_enc_key.to_string()).unwrap()
943
        );
944
    }
945
}