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 derive_deftly::Deftly;
11
use digest::Digest;
12
use itertools::{Itertools, chain};
13
use safelog::DisplayRedacted;
14
use subtle::ConstantTimeEq;
15
use thiserror::Error;
16
use tor_basic_utils::{StrExt as _, impl_debug_hex};
17
use tor_key_forge::ToEncodableKey;
18
use tor_llcrypto::d::Sha3_256;
19
use tor_llcrypto::pk::ed25519::{Ed25519PublicKey, Ed25519SigningKey};
20
use tor_llcrypto::pk::{curve25519, ed25519, keymanip};
21
use tor_llcrypto::util::ct::CtByteArray;
22
use tor_llcrypto::{
23
    derive_deftly_template_ConstantTimeEq, derive_deftly_template_PartialEqFromCtEq,
24
};
25

            
26
use crate::macros::{define_bytes, define_pk_keypair};
27
use crate::time::TimePeriod;
28

            
29
#[allow(deprecated)]
30
pub use hs_client_intro_auth::{HsClientIntroAuthKey, HsClientIntroAuthKeypair};
31

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

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

            
83
impl HsIdKey {
84
    /// Return a representation of this key as an [`HsId`].
85
    ///
86
    /// ([`HsId`] is much smaller, and easier to store.)
87
945
    pub fn id(&self) -> HsId {
88
945
        HsId(self.0.to_bytes().into())
89
945
    }
90
}
91
impl TryFrom<HsId> for HsIdKey {
92
    type Error = signature::Error;
93

            
94
128
    fn try_from(value: HsId) -> Result<Self, Self::Error> {
95
128
        ed25519::PublicKey::from_bytes(value.0.as_ref()).map(HsIdKey)
96
128
    }
97
}
98
impl From<HsIdKey> for HsId {
99
315
    fn from(value: HsIdKey) -> Self {
100
315
        value.id()
101
315
    }
102
}
103

            
104
impl From<&HsIdKeypair> for HsIdKey {
105
126
    fn from(value: &HsIdKeypair) -> Self {
106
126
        Self(*value.0.public())
107
126
    }
108
}
109

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

            
116
/// VERSION from rend-spec-v3 s.6 \[ONIONADDRESS]
117
const HSID_ONION_VERSION: u8 = 0x03;
118

            
119
/// The fixed string `.onion`
120
pub const HSID_ONION_SUFFIX: &str = ".onion";
121

            
122
impl safelog::DisplayRedacted for HsId {
123
6180
    fn fmt_unredacted(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
124
        // rend-spec-v3 s.6 [ONIONADDRESS]
125
6180
        let checksum = self.onion_checksum();
126
6180
        let binary = chain!(self.0.as_ref(), &checksum, &[HSID_ONION_VERSION],)
127
6180
            .cloned()
128
6180
            .collect_vec();
129
6180
        let mut b32 = data_encoding::BASE32_NOPAD.encode(&binary);
130
6180
        b32.make_ascii_lowercase();
131
6180
        write!(f, "{}{}", b32, HSID_ONION_SUFFIX)
132
6180
    }
133

            
134
    // We here display some of the end.  We don't want to display the
135
    // *start* because vanity domains, which would perhaps suffer from
136
    // reduced deniability.
137
2144
    fn fmt_redacted(&self, f: &mut fmt::Formatter) -> fmt::Result {
138
2144
        let unredacted = self.display_unredacted().to_string();
139
        /// Length of the base32 data part of the address
140
        const DATA: usize = 56;
141
2144
        assert_eq!(unredacted.len(), DATA + HSID_ONION_SUFFIX.len());
142

            
143
        // We show this part of the domain:
144
        //     e     n     l     5     s     i     d     .onion
145
        //   KKKKK KKKKK KCCCC CCCCC CCCCC CCVVV VVVVV
146
        //                           ^^^^^^^^^^^^^^^^^ ^^^^^^^^^
147
        // This contains 3 characters of base32, which is 15 bits.
148
        // 8 of those bits are the version, which is currently always 0x03.
149
        // So we are showing 7 bits derived from the site key.
150

            
151
2144
        write!(f, "[…]{}", &unredacted[DATA - 3..])
152
2144
    }
153
}
154

            
155
impl safelog::DebugRedacted for HsId {
156
1638
    fn fmt_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157
1638
        write!(f, "HsId({})", self.display_redacted())
158
1638
    }
159

            
160
2
    fn fmt_unredacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161
2
        write!(f, "HsId({})", self.display_unredacted())
162
2
    }
163
}
164

            
165
safelog::derive_redacted_debug!(HsId);
166

            
167
impl FromStr for HsId {
168
    type Err = HsIdParseError;
169
3859
    fn from_str(s: &str) -> Result<Self, HsIdParseError> {
170
        use HsIdParseError as PE;
171

            
172
3859
        let s = s
173
3859
            .strip_suffix_ignore_ascii_case(HSID_ONION_SUFFIX)
174
3859
            .ok_or(PE::NotOnionDomain)?;
175

            
176
3857
        if s.contains('.') {
177
2
            return Err(PE::HsIdContainsSubdomain);
178
3855
        }
179

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

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

            
195
3853
        let pubkey: [u8; 32] = binary.extract()?;
196
3851
        let checksum: [u8; 2] = binary.extract()?;
197
3851
        let version: u8 = binary.extract()?;
198
3851
        let tentative = HsId(pubkey.into());
199

            
200
        // Check version before checksum; maybe a future version does checksum differently
201
3851
        if version != HSID_ONION_VERSION {
202
2
            return Err(PE::UnsupportedVersion(version));
203
3849
        }
204
3849
        if checksum != tentative.onion_checksum() {
205
2
            return Err(PE::WrongChecksum);
206
3847
        }
207
3847
        Ok(tentative)
208
3859
    }
209
}
210

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

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

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

            
229
    /// Unsupported `.onion` address version
230
    #[error("Unsupported .onion address version, v{0}")]
231
    UnsupportedVersion(u8),
232

            
233
    /// Checksum failed
234
    #[error("Checksum failed, .onion address corrupted")]
235
    WrongChecksum,
236

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

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

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

            
271
638
        let blinded_key = keymanip::blind_pubkey(&self.0, h)?.into();
272
        // rend-spec-v3 section 2.1
273
638
        let subcredential = self.compute_subcredential(&blinded_key, cur_period);
274

            
275
638
        Ok((blinded_key, subcredential))
276
638
    }
277

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

            
302
5174
        subcredential_bytes.into()
303
5174
    }
304

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

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

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

            
337
1022
        h.finalize().into()
338
1022
    }
339
}
340

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

            
353
382
        let public_key = HsIdKey(*self.0.public());
354

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

            
361
382
        let h = public_key.blinding_factor(secret, cur_period);
362

            
363
382
        let blinded_keypair = keymanip::blind_keypair(&self.0, h)?;
364

            
365
382
        Ok((blinded_public_key, blinded_keypair.into(), subcredential))
366
382
    }
367
}
368

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

            
384
#[derive(Deftly)]
385
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
386
HsBlindIdKeypair(ed25519::ExpandedKeypair);
387
}
388

            
389
impl From<HsBlindIdKeypair> for HsBlindIdKey {
390
    fn from(kp: HsBlindIdKeypair) -> HsBlindIdKey {
391
        HsBlindIdKey(kp.0.into())
392
    }
393
}
394

            
395
define_bytes! {
396
/// A blinded onion service identity, represented in a compact format. (`KP_hs_blind_id`)
397
///
398
/// Note: This is a separate type from [`HsBlindIdKey`] because it is about
399
/// 6x smaller.
400
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
401
pub struct HsBlindId([u8; 32]);
402
}
403
impl_debug_hex! { HsBlindId .0 }
404

            
405
impl HsBlindIdKey {
406
    /// Return a representation of this key as a [`HsBlindId`].
407
    ///
408
    /// ([`HsBlindId`] is much smaller, and easier to store.)
409
1012
    pub fn id(&self) -> HsBlindId {
410
1012
        HsBlindId(self.0.to_bytes().into())
411
1012
    }
412
}
413
impl TryFrom<HsBlindId> for HsBlindIdKey {
414
    type Error = signature::Error;
415

            
416
    fn try_from(value: HsBlindId) -> Result<Self, Self::Error> {
417
        ed25519::PublicKey::from_bytes(value.0.as_ref()).map(HsBlindIdKey)
418
    }
419
}
420

            
421
impl From<&HsBlindIdKeypair> for HsBlindIdKey {
422
9513
    fn from(value: &HsBlindIdKeypair) -> Self {
423
9513
        HsBlindIdKey(*value.0.public())
424
9513
    }
425
}
426

            
427
impl From<HsBlindIdKey> for HsBlindId {
428
630
    fn from(value: HsBlindIdKey) -> Self {
429
630
        value.id()
430
630
    }
431
}
432
impl From<ed25519::Ed25519Identity> for HsBlindId {
433
12537
    fn from(value: ed25519::Ed25519Identity) -> Self {
434
12537
        Self(CtByteArray::from(<[u8; 32]>::from(value)))
435
12537
    }
436
}
437

            
438
impl Ed25519SigningKey for HsBlindIdKeypair {
439
4790
    fn sign(&self, message: &[u8]) -> ed25519::Signature {
440
4790
        self.0.sign(message)
441
4790
    }
442
}
443

            
444
impl Ed25519PublicKey for HsBlindIdKeypair {
445
4788
    fn public_key(&self) -> ed25519::PublicKey {
446
4788
        *self.0.public()
447
4788
    }
448
}
449

            
450
define_pk_keypair! {
451
/// A key used to sign onion service descriptors. (`KP_desc_sign`)
452
///
453
/// It is authenticated with a [`HsBlindIdKey`] to prove that it belongs to
454
/// the right onion service, and is used in turn to sign the descriptor that
455
/// tells clients what they need to know about contacting an onion service.
456
///
457
/// Onion services create a new `DescSigningKey` every time the
458
/// `HsBlindIdKey` rotates, to prevent descriptors made in one time period
459
/// from being linkable to those made in another.
460
///
461
/// Note: we use a separate signing key here, rather than using the
462
/// `HsBlindIdKey` directly, so that the [`HsBlindIdKeypair`]
463
/// can be kept offline.
464
#[derive(Deftly)]
465
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
466
pub struct HsDescSigningKey(ed25519::PublicKey) /
467

            
468
#[derive(Deftly)]
469
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
470
HsDescSigningKeypair(ed25519::Keypair);
471
}
472

            
473
define_pk_keypair! {
474
/// A key used to identify and authenticate an onion service at a single
475
/// introduction point. (`KP_hs_ipt_sid`)
476
///
477
/// This key is included in the onion service's descriptor; a different one is
478
/// used at each introduction point.  Introduction points don't know the
479
/// relation of this key to the onion service: they only recognize the same key
480
/// when they see it again.
481
#[derive(Deftly)]
482
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
483
pub struct HsIntroPtSessionIdKey(ed25519::PublicKey) /
484

            
485
#[derive(Deftly)]
486
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
487
HsIntroPtSessionIdKeypair(ed25519::Keypair);
488
}
489

            
490
define_pk_keypair! {
491
/// A key used in the HsNtor handshake between the client and the onion service.
492
/// (`KP_hss_ntor`)
493
///
494
/// The onion service chooses a different one of these to use with each
495
/// introduction point, though it does not need to tell the introduction points
496
/// about these keys.
497
#[derive(Deftly)]
498
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
499
pub struct HsSvcNtorKey(curve25519::PublicKey) /
500

            
501
#[derive(Deftly)]
502
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
503
HsSvcNtorSecretKey(curve25519::StaticSecret);
504

            
505
#[derive(Deftly)]
506
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
507
curve25519_pair as HsSvcNtorKeypair;
508
}
509

            
510
mod hs_client_intro_auth {
511
    #![allow(deprecated)]
512
    //! Key type wrappers for the deprecated `HsClientIntroKey`/`HsClientIntroKeypair` types.
513

            
514
    use subtle::ConstantTimeEq;
515
    use tor_llcrypto::pk::ed25519;
516
    use tor_llcrypto::{
517
        derive_deftly::Deftly, derive_deftly_template_ConstantTimeEq,
518
        derive_deftly_template_PartialEqFromCtEq,
519
    };
520

            
521
    use crate::macros::define_pk_keypair;
522

            
523
    define_pk_keypair! {
524
    /// First type of client authorization key, used for the introduction protocol.
525
    /// (`KP_hsc_intro_auth`)
526
    ///
527
    /// This is used to sign a nonce included in an extension in the encrypted
528
    /// portion of an introduce cell.
529
    #[deprecated(note = "This key type is not used in the protocol implemented today.")]
530
    #[derive(Deftly)]
531
    #[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
532
    pub struct HsClientIntroAuthKey(ed25519::PublicKey) /
533

            
534
    #[deprecated(note = "This key type is not used in the protocol implemented today.")]
535
    #[derive(Deftly)]
536
    #[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
537
    HsClientIntroAuthKeypair(ed25519::Keypair);
538
    }
539
}
540

            
541
define_pk_keypair! {
542
/// Client service discovery key, used for onion descriptor
543
/// decryption. (`KP_hsc_desc_enc`)
544
///
545
/// Any client who knows the secret key corresponding to this key can decrypt
546
/// the inner layer of the onion service descriptor.
547
///
548
/// The [`Display`] and [`FromStr`] representation of keys of this type is
549
/// `descriptor:x25519:<base32-encoded-x25519-public-key>`.
550
/// Note: the base32 encoding of the key is unpadded and case-insensitive,
551
/// for compatibility with the format accepted by C Tor.
552
/// See also `CLIENT AUTHORIZATION` in `tor(1)`.
553
///
554
/// # Example
555
///
556
/// ```rust
557
/// # use tor_hscrypto::pk::HsClientDescEncKey;
558
/// # use std::str::FromStr;
559
/// // A client service discovery key for connecting
560
/// // to a service running in restricted discovery mode,
561
/// // with an uppercase base32 encoding for the key material.
562
/// const CLIENT_KEY1: &str = "descriptor:x25519:ZPRRMIV6DV6SJFL7SFBSVLJ5VUNPGCDFEVZ7M23LTLVTCCXJQBKA";
563
/// // An identical key using lowercase base32 encoding for the key material.
564
/// const CLIENT_KEY2: &str = "descriptor:x25519:zprrmiv6dv6sjfl7sfbsvlj5vunpgcdfevz7m23ltlvtccxjqbka";
565
///
566
/// // Both key encodings parse successfully
567
/// let key1 = HsClientDescEncKey::from_str(CLIENT_KEY1).unwrap();
568
/// let key2 = HsClientDescEncKey::from_str(CLIENT_KEY2).unwrap();
569
/// // The keys are identical
570
/// assert_eq!(key1, key2);
571
/// ```
572
#[derive(Deftly)]
573
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
574
pub struct HsClientDescEncKey(curve25519::PublicKey) /
575

            
576
#[derive(Deftly)]
577
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
578
HsClientDescEncSecretKey(curve25519::StaticSecret);
579

            
580
#[derive(Deftly)]
581
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
582
curve25519_pair as HsClientDescEncKeypair;
583
}
584

            
585
impl Eq for HsClientDescEncKey {}
586

            
587
impl Display for HsClientDescEncKey {
588
1577
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
589
1577
        let x25519_pk = data_encoding::BASE32_NOPAD.encode(&self.0.to_bytes());
590
1577
        write!(f, "descriptor:x25519:{}", x25519_pk)
591
1577
    }
592
}
593

            
594
impl FromStr for HsClientDescEncKey {
595
    type Err = HsClientDescEncKeyParseError;
596

            
597
1532
    fn from_str(key: &str) -> Result<Self, HsClientDescEncKeyParseError> {
598
1532
        let (auth_type, key_type, encoded_key) = key
599
1532
            .split(':')
600
1532
            .collect_tuple()
601
1532
            .ok_or(HsClientDescEncKeyParseError::InvalidFormat)?;
602

            
603
1522
        if auth_type != "descriptor" {
604
2
            return Err(HsClientDescEncKeyParseError::InvalidAuthType(
605
2
                auth_type.into(),
606
2
            ));
607
1520
        }
608

            
609
1520
        if key_type != "x25519" {
610
2
            return Err(HsClientDescEncKeyParseError::InvalidKeyType(
611
2
                key_type.into(),
612
2
            ));
613
1518
        }
614

            
615
        // Note: Tor's base32 decoder is case-insensitive, so we can't assume the input
616
        // is all uppercase.
617
        //
618
        // TODO: consider using `data_encoding_macro::new_encoding` to create a new Encoding
619
        // with an alphabet that includes lowercase letters instead of to_uppercase()ing the string.
620
1518
        let encoded_key = encoded_key.to_uppercase();
621
1518
        let x25519_pk = data_encoding::BASE32_NOPAD.decode(encoded_key.as_bytes())?;
622
1453
        let x25519_pk: [u8; 32] = x25519_pk
623
1453
            .try_into()
624
1453
            .map_err(|_| HsClientDescEncKeyParseError::InvalidKeyMaterial)?;
625

            
626
1453
        Ok(Self(curve25519::PublicKey::from(x25519_pk)))
627
1532
    }
628
}
629

            
630
/// Error that can occur parsing an `HsClientDescEncKey` from C Tor format.
631
#[derive(Error, Clone, Debug, PartialEq)]
632
#[non_exhaustive]
633
pub enum HsClientDescEncKeyParseError {
634
    /// The auth type is not "descriptor".
635
    #[error("Invalid auth type {0}")]
636
    InvalidAuthType(String),
637

            
638
    /// The key type is not "x25519".
639
    #[error("Invalid key type {0}")]
640
    InvalidKeyType(String),
641

            
642
    /// The key is not in the `<auth-type>:x25519:<base32-encoded-public-key>` format.
643
    #[error("Invalid key format")]
644
    InvalidFormat,
645

            
646
    /// The encoded key material is invalid.
647
    #[error("Invalid key material")]
648
    InvalidKeyMaterial,
649

            
650
    /// Base32 decoding failed.
651
    #[error("Invalid base32 in client key")]
652
    InvalidBase32(#[from] data_encoding::DecodeError),
653
}
654

            
655
define_pk_keypair! {
656
/// Server key, used for diffie hellman during onion descriptor decryption.
657
/// (`KP_hss_desc_enc`)
658
///
659
/// This key is created for a single descriptor, and then thrown away.
660
#[derive(Deftly)]
661
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
662
pub struct HsSvcDescEncKey(curve25519::PublicKey) /
663

            
664
#[derive(Deftly)]
665
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
666
HsSvcDescEncSecretKey(curve25519::StaticSecret);
667
}
668

            
669
impl From<&HsClientDescEncSecretKey> for HsClientDescEncKey {
670
    fn from(ks: &HsClientDescEncSecretKey) -> Self {
671
        Self(curve25519::PublicKey::from(&ks.0))
672
    }
673
}
674

            
675
impl From<&HsClientDescEncKeypair> for HsClientDescEncKey {
676
    fn from(ks: &HsClientDescEncKeypair) -> Self {
677
        Self(**ks.public())
678
    }
679
}
680

            
681
/// An ephemeral x25519 keypair, generated by an onion service
682
/// and used to for onion service encryption.
683
#[allow(clippy::exhaustive_structs)]
684
#[derive(Debug, Deftly)]
685
#[derive_deftly(ConstantTimeEq, PartialEqFromCtEq)]
686
pub struct HsSvcDescEncKeypair {
687
    /// The public part of the key.
688
    pub public: HsSvcDescEncKey,
689
    /// The secret part of the key.
690
    pub secret: HsSvcDescEncSecretKey,
691
}
692

            
693
// TODO: let the define_ed25519_keypair/define_curve25519_keypair macros
694
// auto-generate these impls.
695
//
696
// For some of the keys here, this currently cannot be done
697
// because the macro doesn't support generating expanded ed25519 keys.
698

            
699
impl ToEncodableKey for HsClientDescEncKeypair {
700
    type Key = curve25519::StaticKeypair;
701
    type KeyPair = HsClientDescEncKeypair;
702

            
703
378
    fn to_encodable_key(self) -> Self::Key {
704
378
        self.into()
705
378
    }
706

            
707
1386
    fn from_encodable_key(key: Self::Key) -> Self {
708
1386
        HsClientDescEncKeypair::new(key.public.into(), key.secret.into())
709
1386
    }
710
}
711

            
712
impl ToEncodableKey for HsBlindIdKeypair {
713
    type Key = ed25519::ExpandedKeypair;
714
    type KeyPair = HsBlindIdKeypair;
715

            
716
252
    fn to_encodable_key(self) -> Self::Key {
717
252
        self.into()
718
252
    }
719

            
720
9324
    fn from_encodable_key(key: Self::Key) -> Self {
721
9324
        HsBlindIdKeypair::from(key)
722
9324
    }
723
}
724

            
725
impl ToEncodableKey for HsBlindIdKey {
726
    type Key = ed25519::PublicKey;
727
    type KeyPair = HsBlindIdKeypair;
728

            
729
252
    fn to_encodable_key(self) -> Self::Key {
730
252
        self.into()
731
252
    }
732

            
733
252
    fn from_encodable_key(key: Self::Key) -> Self {
734
252
        HsBlindIdKey::from(key)
735
252
    }
736
}
737

            
738
impl ToEncodableKey for HsIdKeypair {
739
    type Key = ed25519::ExpandedKeypair;
740
    type KeyPair = HsIdKeypair;
741

            
742
567
    fn to_encodable_key(self) -> Self::Key {
743
567
        self.into()
744
567
    }
745

            
746
10584
    fn from_encodable_key(key: Self::Key) -> Self {
747
10584
        HsIdKeypair::from(key)
748
10584
    }
749
}
750

            
751
impl ToEncodableKey for HsIdKey {
752
    type Key = ed25519::PublicKey;
753
    type KeyPair = HsIdKeypair;
754

            
755
252
    fn to_encodable_key(self) -> Self::Key {
756
252
        self.into()
757
252
    }
758

            
759
4662
    fn from_encodable_key(key: Self::Key) -> Self {
760
4662
        HsIdKey::from(key)
761
4662
    }
762
}
763

            
764
impl ToEncodableKey for HsDescSigningKeypair {
765
    type Key = ed25519::Keypair;
766
    type KeyPair = HsDescSigningKeypair;
767

            
768
252
    fn to_encodable_key(self) -> Self::Key {
769
252
        self.into()
770
252
    }
771

            
772
4536
    fn from_encodable_key(key: Self::Key) -> Self {
773
4536
        HsDescSigningKeypair::from(key)
774
4536
    }
775
}
776

            
777
impl ToEncodableKey for HsIntroPtSessionIdKeypair {
778
    type Key = ed25519::Keypair;
779
    type KeyPair = HsIntroPtSessionIdKeypair;
780

            
781
    fn to_encodable_key(self) -> Self::Key {
782
        self.into()
783
    }
784

            
785
1260
    fn from_encodable_key(key: Self::Key) -> Self {
786
1260
        key.into()
787
1260
    }
788
}
789

            
790
impl ToEncodableKey for HsSvcNtorKeypair {
791
    type Key = curve25519::StaticKeypair;
792
    type KeyPair = HsSvcNtorKeypair;
793

            
794
    fn to_encodable_key(self) -> Self::Key {
795
        self.into()
796
    }
797

            
798
1260
    fn from_encodable_key(key: Self::Key) -> Self {
799
1260
        key.into()
800
1260
    }
801
}
802

            
803
#[cfg(test)]
804
mod test {
805
    // @@ begin test lint list maintained by maint/add_warning @@
806
    #![allow(clippy::bool_assert_comparison)]
807
    #![allow(clippy::clone_on_copy)]
808
    #![allow(clippy::dbg_macro)]
809
    #![allow(clippy::mixed_attributes_style)]
810
    #![allow(clippy::print_stderr)]
811
    #![allow(clippy::print_stdout)]
812
    #![allow(clippy::single_char_pattern)]
813
    #![allow(clippy::unwrap_used)]
814
    #![allow(clippy::unchecked_time_subtraction)]
815
    #![allow(clippy::useless_vec)]
816
    #![allow(clippy::needless_pass_by_value)]
817
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
818

            
819
    use hex_literal::hex;
820
    use itertools::izip;
821
    use std::time::{Duration, SystemTime};
822
    use tor_basic_utils::test_rng::testing_rng;
823

            
824
    use super::*;
825

            
826
    #[test]
827
    fn hsid_strings() {
828
        use HsIdParseError as PE;
829

            
830
        // From C Tor src/test/test_hs_common.c test_build_address
831
        let hex = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
832
        let b32 = "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid";
833

            
834
        let hsid: [u8; 32] = hex::decode(hex).unwrap().try_into().unwrap();
835
        let hsid = HsId::from(hsid);
836
        let onion = format!("{}.onion", b32);
837

            
838
        assert_eq!(onion.parse::<HsId>().unwrap(), hsid);
839
        assert_eq!(hsid.display_unredacted().to_string(), onion);
840

            
841
        let weird_case: String = izip!(onion.chars(), [false, true].iter().cloned().cycle(),)
842
            .map(|(c, swap)| if swap { c.to_ascii_uppercase() } else { c })
843
            .collect();
844
        dbg!(&weird_case);
845
        assert_eq!(weird_case.parse::<HsId>().unwrap(), hsid);
846

            
847
        macro_rules! chk_err { { $s:expr, $($pat:tt)* } => {
848
            let e = $s.parse::<HsId>();
849
            assert!(matches!(e, Err($($pat)*)), "{:?}", &e);
850
        } }
851
        let edited = |i, c| {
852
            let mut s = b32.to_owned().into_bytes();
853
            s[i] = c;
854
            format!("{}.onion", String::from_utf8(s).unwrap())
855
        };
856

            
857
        chk_err!("wrong", PE::NotOnionDomain);
858
        chk_err!("@.onion", PE::InvalidBase32(..));
859
        chk_err!("aaaaaaaa.onion", PE::InvalidData(..));
860
        chk_err!(edited(55, b'E'), PE::UnsupportedVersion(4));
861
        chk_err!(edited(53, b'X'), PE::WrongChecksum);
862
        chk_err!(&format!("www.{}", &onion), PE::HsIdContainsSubdomain);
863

            
864
        safelog::with_safe_logging_suppressed(|| {
865
            assert_eq!(format!("{:?}", &hsid), format!("HsId({})", onion));
866
        });
867

            
868
        assert_eq!(format!("{}", hsid.display_redacted()), "[…]sid.onion");
869
    }
870

            
871
    #[test]
872
    fn key_blinding_blackbox() {
873
        let mut rng = testing_rng();
874
        let offset = Duration::new(12 * 60 * 60, 0);
875
        let when = TimePeriod::new(Duration::from_secs(3600), SystemTime::now(), offset).unwrap();
876
        let keypair = ed25519::Keypair::generate(&mut rng);
877
        let id_pub = HsIdKey::from(keypair.verifying_key());
878
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
879

            
880
        let (blinded_pub, subcred1) = id_pub.compute_blinded_key(when).unwrap();
881
        let (blinded_pub2, blinded_keypair, subcred2) =
882
            id_keypair.compute_blinded_key(when).unwrap();
883

            
884
        assert_eq!(subcred1.as_ref(), subcred2.as_ref());
885
        assert_eq!(blinded_pub.0.to_bytes(), blinded_pub2.0.to_bytes());
886
        assert_eq!(blinded_pub.id(), blinded_pub2.id());
887

            
888
        let message = b"Here is a terribly important string to authenticate.";
889
        let other_message = b"Hey, that is not what I signed!";
890
        let sign = blinded_keypair.sign(message);
891

            
892
        assert!(blinded_pub.as_ref().verify(message, &sign).is_ok());
893
        assert!(blinded_pub.as_ref().verify(other_message, &sign).is_err());
894
    }
895

            
896
    #[test]
897
    fn key_blinding_testvec() {
898
        // Test vectors generated with C tor.
899
        let id = HsId::from(hex!(
900
            "833990B085C1A688C1D4C8B1F6B56AFAF5A2ECA674449E1D704F83765CCB7BC6"
901
        ));
902
        let id_pubkey = HsIdKey::try_from(id).unwrap();
903
        let id_seckey = HsIdKeypair::from(
904
            ed25519::ExpandedKeypair::from_secret_key_bytes(hex!(
905
                "D8C7FF0E31295B66540D789AF3E3DF992038A9592EEA01D8B7CBA06D6E66D159
906
                 4D6167696320576F7264733A20737065697373636F62616C742062697669756D"
907
            ))
908
            .unwrap(),
909
        );
910
        let time_period = TimePeriod::new(
911
            humantime::parse_duration("1 day").unwrap(),
912
            humantime::parse_rfc3339("1973-05-20T01:50:33Z").unwrap(),
913
            humantime::parse_duration("12 hours").unwrap(),
914
        )
915
        .unwrap();
916
        assert_eq!(time_period.interval_num, 1234);
917

            
918
        let h = id_pubkey.blinding_factor(b"", time_period);
919
        assert_eq!(
920
            h,
921
            hex!("379E50DB31FEE6775ABD0AF6FB7C371E060308F4F847DB09FE4CFE13AF602287")
922
        );
923

            
924
        let (blinded_pub1, subcred1) = id_pubkey.compute_blinded_key(time_period).unwrap();
925
        assert_eq!(
926
            blinded_pub1.0.to_bytes(),
927
            hex!("3A50BF210E8F9EE955AE0014F7A6917FB65EBF098A86305ABB508D1A7291B6D5")
928
        );
929
        assert_eq!(
930
            subcred1.as_ref(),
931
            &hex!("635D55907816E8D76398A675A50B1C2F3E36B42A5CA77BA3A0441285161AE07D")
932
        );
933

            
934
        let (blinded_pub2, blinded_sec, subcred2) =
935
            id_seckey.compute_blinded_key(time_period).unwrap();
936
        assert_eq!(blinded_pub1.0.to_bytes(), blinded_pub2.0.to_bytes());
937
        assert_eq!(subcred1.as_ref(), subcred2.as_ref());
938
        assert_eq!(
939
            blinded_sec.0.to_secret_key_bytes(),
940
            hex!(
941
                "A958DC83AC885F6814C67035DE817A2C604D5D2F715282079448F789B656350B
942
                 4540FE1F80AA3F7E91306B7BF7A8E367293352B14A29FDCC8C19F3558075524B"
943
            )
944
        );
945
    }
946

            
947
    #[test]
948
    fn parse_client_desc_enc_key() {
949
        use HsClientDescEncKeyParseError::*;
950

            
951
        /// Valid base32-encoded x25519 public key.
952
        const VALID_KEY_BASE32: &str = "dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja";
953

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

            
957
        for key in WRONG_FORMAT {
958
            let err = HsClientDescEncKey::from_str(key).unwrap_err();
959

            
960
            assert_eq!(err, InvalidFormat);
961
        }
962

            
963
        let err =
964
            HsClientDescEncKey::from_str(&format!("foo:descriptor:x25519:{VALID_KEY_BASE32}"))
965
                .unwrap_err();
966

            
967
        assert_eq!(err, InvalidFormat);
968

            
969
        // A key with an invalid auth type
970
        let err = HsClientDescEncKey::from_str("bar:x25519:aa==").unwrap_err();
971
        assert_eq!(err, InvalidAuthType("bar".into()));
972

            
973
        // A key with an invalid key type
974
        let err = HsClientDescEncKey::from_str("descriptor:not-x25519:aa==").unwrap_err();
975
        assert_eq!(err, InvalidKeyType("not-x25519".into()));
976

            
977
        // A key with an invalid base32 part
978
        let err = HsClientDescEncKey::from_str("descriptor:x25519:aa==").unwrap_err();
979
        assert!(matches!(err, InvalidBase32(_)));
980

            
981
        // A valid client desc enc key
982
        let _key =
983
            HsClientDescEncKey::from_str(&format!("descriptor:x25519:{VALID_KEY_BASE32}")).unwrap();
984

            
985
        // Roundtrip
986
        let desc_enc_key = HsClientDescEncKey::from(curve25519::PublicKey::from(
987
            &curve25519::StaticSecret::random_from_rng(testing_rng()),
988
        ));
989

            
990
        assert_eq!(
991
            desc_enc_key,
992
            HsClientDescEncKey::from_str(&desc_enc_key.to_string()).unwrap()
993
        );
994
    }
995
}