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
975
    pub fn id(&self) -> HsId {
88
975
        HsId(self.0.to_bytes().into())
89
975
    }
90
}
91
impl TryFrom<HsId> for HsIdKey {
92
    type Error = signature::Error;
93

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

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

            
110
impl From<HsIdKeypair> for HsIdKey {
111
715
    fn from(value: HsIdKeypair) -> Self {
112
715
        Self(*value.0.public())
113
715
    }
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
53241
    fn fmt_unredacted(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
124
        // rend-spec-v3 s.6 [ONIONADDRESS]
125
53241
        let checksum = self.onion_checksum();
126
53241
        let binary = chain!(self.0.as_ref(), &checksum, &[HSID_ONION_VERSION],)
127
53241
            .cloned()
128
53241
            .collect_vec();
129
53241
        let mut b32 = data_encoding::BASE32_NOPAD.encode(&binary);
130
53241
        b32.make_ascii_lowercase();
131
53241
        write!(f, "{}{}", b32, HSID_ONION_SUFFIX)
132
53241
    }
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
    #[allow(clippy::string_slice)] // TODO
138
49077
    fn fmt_redacted(&self, f: &mut fmt::Formatter) -> fmt::Result {
139
49077
        let unredacted = self.display_unredacted().to_string();
140
        /// Length of the base32 data part of the address
141
        const DATA: usize = 56;
142
49077
        assert_eq!(unredacted.len(), DATA + HSID_ONION_SUFFIX.len());
143

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

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

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

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

            
166
safelog::derive_redacted_debug!(HsId);
167

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

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

            
177
3979
        if s.contains('.') {
178
2
            return Err(PE::HsIdContainsSubdomain);
179
3977
        }
180

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

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

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

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

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

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

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

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

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

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

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

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

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

            
276
658
        Ok((blinded_key, subcredential))
277
658
    }
278

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

            
303
5858
        subcredential_bytes.into()
304
5858
    }
305

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

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

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

            
338
1054
        h.finalize().into()
339
1054
    }
340
}
341

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

            
354
394
        let public_key = HsIdKey(*self.0.public());
355

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

            
362
394
        let h = public_key.blinding_factor(secret, cur_period);
363

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

            
366
394
        Ok((blinded_public_key, blinded_keypair.into(), subcredential))
367
394
    }
368
}
369

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
522
    use crate::macros::define_pk_keypair;
523

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

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

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

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

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

            
586
impl Eq for HsClientDescEncKey {}
587

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

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

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

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

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

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

            
627
1499
        Ok(Self(curve25519::PublicKey::from(x25519_pk)))
628
1580
    }
629
}
630

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
821
    use hex_literal::hex;
822
    use itertools::izip;
823
    use tor_basic_utils::test_rng::testing_rng;
824
    use web_time_compat::{Duration, SystemTime, SystemTimeExt};
825

            
826
    use super::*;
827

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
962
            assert_eq!(err, InvalidFormat);
963
        }
964

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

            
969
        assert_eq!(err, InvalidFormat);
970

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

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

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

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

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

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