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 safelog::util::write_end_redacted;
15
use subtle::ConstantTimeEq;
16
use thiserror::Error;
17
use tor_basic_utils::{StrExt as _, impl_debug_hex};
18
use tor_key_forge::ToEncodableKey;
19
use tor_llcrypto::d::Sha3_256;
20
use tor_llcrypto::pk::ed25519::{Ed25519PublicKey, Ed25519SigningKey};
21
use tor_llcrypto::pk::{curve25519, ed25519, keymanip};
22
use tor_llcrypto::util::ct::CtByteArray;
23
use tor_llcrypto::{
24
    derive_deftly_template_ConstantTimeEq, derive_deftly_template_PartialEqFromCtEq,
25
};
26

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

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

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

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

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

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

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

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

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

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

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

            
135
    // We here display some of the end.  We don't want to display the
136
    // *start* because vanity domains, which would perhaps suffer from
137
    // reduced deniability.
138
49142
    fn fmt_redacted(&self, f: &mut fmt::Formatter) -> fmt::Result {
139
49142
        let unredacted = self.display_unredacted().to_string();
140
49142
        assert!(unredacted.ends_with(HSID_ONION_SUFFIX));
141

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

            
150
49142
        write_end_redacted(f, &unredacted, 3 + HSID_ONION_SUFFIX.len(), "[…]")
151
49142
    }
152
}
153

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

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

            
164
safelog::derive_redacted_debug!(HsId);
165

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

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

            
175
4044
        if s.contains('.') {
176
2
            return Err(PE::HsIdContainsSubdomain);
177
4042
        }
178

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

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

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

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

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

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

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

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

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

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

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

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

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

            
274
658
        Ok((blinded_key, subcredential))
275
658
    }
276

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

            
301
5338
        subcredential_bytes.into()
302
5338
    }
303

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

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

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

            
336
1054
        h.finalize().into()
337
1054
    }
338
}
339

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

            
352
394
        let public_key = HsIdKey(*self.0.public());
353

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

            
360
394
        let h = public_key.blinding_factor(secret, cur_period);
361

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

            
364
394
        Ok((blinded_public_key, blinded_keypair.into(), subcredential))
365
394
    }
366
}
367

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
520
    use crate::macros::define_pk_keypair;
521

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

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

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

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

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

            
584
impl Eq for HsClientDescEncKey {}
585

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

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

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

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

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

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

            
625
1499
        Ok(Self(curve25519::PublicKey::from(x25519_pk)))
626
1580
    }
627
}
628

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
819
    use hex_literal::hex;
820
    use itertools::izip;
821
    use tor_basic_utils::test_rng::testing_rng;
822
    use web_time_compat::{Duration, SystemTime, SystemTimeExt};
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::get(), 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
}