1
//! Implements the HS ntor key exchange, as used in v3 onion services.
2
//!
3
//! The Ntor protocol of this section is specified in section
4
//! [NTOR-WITH-EXTRA-DATA] of rend-spec-v3.txt.
5
//!
6
//! The main difference between this HS Ntor handshake and the standard Ntor
7
//! handshake in ./ntor.rs is that this one allows each party to encrypt data
8
//! (without forward secrecy) after it sends the first message. This
9
//! opportunistic encryption property is used by clients in the onion service
10
//! protocol to encrypt introduction data in the INTRODUCE1 cell, and by
11
//! services to encrypt data in the RENDEZVOUS1 cell.
12
//!
13
//! # Status
14
//!
15
//! This module is a work in progress, and is not actually used anywhere yet
16
//! or tested: please expect the API to change.
17
//!
18
//! This module is available only when the `hs` feature is enabled.
19

            
20
// We want to use the exact variable names from the rend-spec-v3.txt proposal.
21
// This means that we allow variables to be named x (privkey) and X (pubkey).
22
#![allow(non_snake_case)]
23
// This module is still unused: so allow some dead code for now.
24
#![allow(dead_code)]
25
#![allow(unreachable_pub)]
26

            
27
use crate::crypto::handshake::KeyGenerator;
28
use crate::crypto::ll::kdf::{Kdf, ShakeKdf};
29
use crate::{Error, Result, SecretBytes};
30
use tor_bytes::{Reader, Writer};
31
use tor_llcrypto::d::Sha3_256;
32
use tor_llcrypto::pk::{curve25519, ed25519};
33
use tor_llcrypto::util::rand_compat::RngCompatExt;
34

            
35
use cipher::{NewCipher, StreamCipher};
36

            
37
use digest::Digest;
38
use generic_array::GenericArray;
39
use rand_core::{CryptoRng, RngCore};
40
use std::convert::TryInto;
41
use tor_error::into_internal;
42
use tor_llcrypto::cipher::aes::Aes256Ctr;
43
use zeroize::Zeroizing;
44

            
45
/// The ENC_KEY from the HS Ntor protocol
46
type EncKey = [u8; 32];
47
/// The MAC_KEY from the HS Ntor protocol
48
type MacKey = [u8; 32];
49
/// A generic 256-bit MAC tag
50
type MacTag = [u8; 32];
51
/// The AUTH_INPUT_MAC from the HS Ntor protocol
52
type AuthInputMac = MacTag;
53
/// The Service's subcredential
54
pub type Subcredential = [u8; 32];
55

            
56
/// The key generator used by the HS ntor handshake.  Implements the simple key
57
/// expansion protocol specified in section "Key expansion" of rend-spec-v3.txt .
58
pub struct HsNtorHkdfKeyGenerator {
59
    /// Secret data derived from the handshake, used as input to HKDF
60
    seed: SecretBytes,
61
}
62

            
63
impl HsNtorHkdfKeyGenerator {
64
    /// Create a new key generator to expand a given seed
65
2
    pub fn new(seed: SecretBytes) -> Self {
66
2
        HsNtorHkdfKeyGenerator { seed }
67
2
    }
68
}
69

            
70
impl KeyGenerator for HsNtorHkdfKeyGenerator {
71
    /// Expand the seed into a keystream of 'keylen' size
72
2
    fn expand(self, keylen: usize) -> Result<SecretBytes> {
73
2
        ShakeKdf::new().derive(&self.seed[..], keylen)
74
2
    }
75
}
76

            
77
/*********************** Client Side Code ************************************/
78

            
79
/// The input to enter the HS Ntor protocol as a client
80
1
#[derive(Clone)]
81
pub struct HsNtorClientInput {
82
    /// Introduction point encryption key (aka B)
83
    /// (found in the HS descriptor)
84
    pub B: curve25519::PublicKey,
85

            
86
    /// Introduction point authentication key (aka AUTH_KEY)
87
    /// (found in the HS descriptor)
88
    pub auth_key: ed25519::PublicKey,
89

            
90
    /// Service subcredential
91
    pub subcredential: Subcredential,
92

            
93
    /// The plaintext that should be encrypted into ENCRYPTED_DATA It's
94
    /// structure is irrelevant for this crate, but can be found in section
95
    /// \[PROCESS_INTRO2\] of the spec
96
    pub plaintext: Vec<u8>,
97

            
98
    /// The data of the INTRODUCE1 cell from the beginning and up to the start
99
    /// of the ENCRYPTED_DATA. It's used to compute the MAC at the end of the
100
    /// INTRODUCE1 cell.
101
    pub intro_cell_data: Vec<u8>,
102
}
103

            
104
impl HsNtorClientInput {
105
    /// Create a new `HsNtorClientInput`
106
1
    pub fn new(
107
1
        B: curve25519::PublicKey,
108
1
        auth_key: ed25519::PublicKey,
109
1
        subcredential: Subcredential,
110
1
        plaintext: Vec<u8>,
111
1
        intro_cell_data: Vec<u8>,
112
1
    ) -> Self {
113
1
        HsNtorClientInput {
114
1
            B,
115
1
            auth_key,
116
1
            subcredential,
117
1
            plaintext,
118
1
            intro_cell_data,
119
1
        }
120
1
    }
121
}
122

            
123
/// Client state for an ntor handshake.
124
pub struct HsNtorClientState {
125
    /// Keys received from our caller when we started the protocol. The rest of
126
    /// the keys in this state structure have been created during the protocol.
127
    proto_input: HsNtorClientInput,
128

            
129
    /// The temporary curve25519 secret that we generated for this handshake.
130
    x: curve25519::StaticSecret,
131
    /// The corresponding private key
132
    X: curve25519::PublicKey,
133
}
134

            
135
/// Encrypt the 'plaintext' using 'enc_key'. Then compute the intro cell MAC
136
/// using 'mac_key' and return (ciphertext, mac_tag).
137
1
fn encrypt_and_mac(
138
1
    mut plaintext: Vec<u8>,
139
1
    other_data: &[u8],
140
1
    enc_key: EncKey,
141
1
    mac_key: MacKey,
142
1
) -> Result<(Vec<u8>, MacTag)> {
143
1
    // Encrypt the introduction data using 'enc_key'
144
1
    let zero_iv = GenericArray::default();
145
1
    let mut cipher = Aes256Ctr::new(&enc_key.into(), &zero_iv);
146
1
    cipher.apply_keystream(&mut plaintext);
147
1
    let ciphertext = plaintext; // it's now encrypted
148
1

            
149
1
    // Now staple the other INTRODUCE1 data right before the ciphertext to
150
1
    // create the body of the MAC tag
151
1
    let mut mac_body: Vec<u8> = Vec::new();
152
1
    mac_body.extend(other_data);
153
1
    mac_body.extend(&ciphertext);
154
1
    let mac_tag = hs_ntor_mac(&mac_body, &mac_key)?;
155

            
156
1
    Ok((ciphertext, mac_tag))
157
1
}
158

            
159
/// The client is about to make an INTRODUCE1 cell. Perform the first part of
160
/// the client handshake.
161
///
162
/// Return a state object containing the current progress of the handshake, and
163
/// the data that should be written in the INTRODUCE1 cell. The data that is
164
/// written is:
165
///
166
///  CLIENT_PK                [PK_PUBKEY_LEN bytes]
167
///  ENCRYPTED_DATA           [Padded to length of plaintext]
168
///  MAC                      [MAC_LEN bytes]
169
1
pub fn client_send_intro<R>(
170
1
    rng: &mut R,
171
1
    proto_input: &HsNtorClientInput,
172
1
) -> Result<(HsNtorClientState, Vec<u8>)>
173
1
where
174
1
    R: RngCore + CryptoRng,
175
1
{
176
1
    // Create client's ephemeral keys to be used for this handshake
177
1
    let x = curve25519::StaticSecret::new(rng.rng_compat());
178
1
    let X = curve25519::PublicKey::from(&x);
179
1

            
180
1
    // Get EXP(B,x)
181
1
    let bx = x.diffie_hellman(&proto_input.B);
182
1

            
183
1
    // Compile our state structure
184
1
    let state = HsNtorClientState {
185
1
        proto_input: proto_input.clone(),
186
1
        x,
187
1
        X,
188
1
    };
189

            
190
    // Compute keys required to finish this part of the handshake
191
1
    let (enc_key, mac_key) = get_introduce1_key_material(
192
1
        &bx,
193
1
        &proto_input.auth_key,
194
1
        &X,
195
1
        &proto_input.B,
196
1
        &proto_input.subcredential,
197
1
    )?;
198

            
199
1
    let (ciphertext, mac_tag) = encrypt_and_mac(
200
1
        proto_input.plaintext.clone(),
201
1
        &proto_input.intro_cell_data,
202
1
        enc_key,
203
1
        mac_key,
204
1
    )?;
205

            
206
    // Create the relevant parts of INTRO1
207
1
    let mut response: Vec<u8> = Vec::new();
208
1
    response.write(&X);
209
1
    response.write(&ciphertext);
210
1
    response.write(&mac_tag);
211
1

            
212
1
    Ok((state, response))
213
1
}
214

            
215
/// The introduction has been completed and the service has replied with a
216
/// RENDEZVOUS1.
217
///
218
/// Handle it by computing and verifying the MAC, and if it's legit return a
219
/// key generator based on the result of the key exchange.
220
1
pub fn client_receive_rend<T>(state: &HsNtorClientState, msg: T) -> Result<HsNtorHkdfKeyGenerator>
221
1
where
222
1
    T: AsRef<[u8]>,
223
1
{
224
1
    // Extract the public key of the service from the message
225
1
    let mut cur = Reader::from_slice(msg.as_ref());
226
1
    let Y: curve25519::PublicKey = cur.extract()?;
227
1
    let mac_tag: MacTag = cur.extract()?;
228

            
229
    // Get EXP(Y,x) and EXP(B,x)
230
1
    let xy = state.x.diffie_hellman(&Y);
231
1
    let xb = state.x.diffie_hellman(&state.proto_input.B);
232

            
233
1
    let (keygen, my_mac_tag) = get_rendezvous1_key_material(
234
1
        &xy,
235
1
        &xb,
236
1
        &state.proto_input.auth_key,
237
1
        &state.proto_input.B,
238
1
        &state.X,
239
1
        &Y,
240
1
    )?;
241

            
242
    // Validate the MAC!
243
1
    if my_mac_tag != mac_tag {
244
        return Err(Error::BadCircHandshake);
245
1
    }
246
1

            
247
1
    Ok(keygen)
248
1
}
249

            
250
/*********************** Server Side Code ************************************/
251

            
252
/// The input required to enter the HS Ntor protocol as a service
253
pub struct HsNtorServiceInput {
254
    /// Introduction point encryption privkey
255
    pub b: curve25519::StaticSecret,
256
    /// Introduction point encryption pubkey
257
    pub B: curve25519::PublicKey,
258

            
259
    /// Introduction point authentication key (aka AUTH_KEY)
260
    pub auth_key: ed25519::PublicKey,
261

            
262
    /// Our subcredential
263
    pub subcredential: Subcredential,
264

            
265
    /// The data of the INTRODUCE1 cell from the beginning and up to the start
266
    /// of the ENCRYPTED_DATA. Will be used to verify the MAC at the end of the
267
    /// INTRODUCE1 cell.
268
    pub intro_cell_data: Vec<u8>,
269
}
270

            
271
impl HsNtorServiceInput {
272
    /// Create a new `HsNtorServiceInput`
273
1
    pub fn new(
274
1
        b: curve25519::StaticSecret,
275
1
        B: curve25519::PublicKey,
276
1
        auth_key: ed25519::PublicKey,
277
1
        subcredential: Subcredential,
278
1
        intro_cell_data: Vec<u8>,
279
1
    ) -> Self {
280
1
        HsNtorServiceInput {
281
1
            b,
282
1
            B,
283
1
            auth_key,
284
1
            subcredential,
285
1
            intro_cell_data,
286
1
        }
287
1
    }
288
}
289

            
290
/// Conduct the HS Ntor handshake as the service.
291
///
292
/// Return a key generator which is the result of the key exchange, the
293
/// RENDEZVOUS1 response to the client, and the introduction plaintext that we decrypted.
294
///
295
/// The response to the client is:
296
///    SERVER_PK   Y                         [PK_PUBKEY_LEN bytes]
297
///    AUTH        AUTH_INPUT_MAC            [MAC_LEN bytes]
298
1
pub fn server_receive_intro<R, T>(
299
1
    rng: &mut R,
300
1
    proto_input: &HsNtorServiceInput,
301
1
    msg: T,
302
1
) -> Result<(HsNtorHkdfKeyGenerator, Vec<u8>, Vec<u8>)>
303
1
where
304
1
    R: RngCore + CryptoRng,
305
1
    T: AsRef<[u8]>,
306
1
{
307
1
    // Extract all the useful pieces from the message
308
1
    let mut cur = Reader::from_slice(msg.as_ref());
309
1
    let X: curve25519::PublicKey = cur.extract()?;
310
1
    let remaining_bytes = cur.remaining();
311
1
    let ciphertext = &mut cur.take(remaining_bytes - 32)?.to_vec();
312
1
    let mac_tag: MacTag = cur.extract()?;
313

            
314
    // Now derive keys needed for handling the INTRO1 cell
315
1
    let bx = proto_input.b.diffie_hellman(&X);
316
1
    let (enc_key, mac_key) = get_introduce1_key_material(
317
1
        &bx,
318
1
        &proto_input.auth_key,
319
1
        &X,
320
1
        &proto_input.B,
321
1
        &proto_input.subcredential,
322
1
    )?;
323

            
324
    // Now validate the MAC: Staple the previous INTRODUCE1 data along with the
325
    // ciphertext to create the body of the MAC tag
326
1
    let mut mac_body: Vec<u8> = Vec::new();
327
1
    mac_body.extend(proto_input.intro_cell_data.clone());
328
1
    mac_body.extend(ciphertext.clone());
329
1
    let my_mac_tag = hs_ntor_mac(&mac_body, &mac_key)?;
330

            
331
1
    if my_mac_tag != mac_tag {
332
        return Err(Error::BadCircHandshake);
333
1
    }
334
1

            
335
1
    // Decrypt the ENCRYPTED_DATA from the intro cell
336
1
    let zero_iv = GenericArray::default();
337
1
    let mut cipher = Aes256Ctr::new(&enc_key.into(), &zero_iv);
338
1
    cipher.apply_keystream(ciphertext);
339
1
    let plaintext = ciphertext; // it's now decrypted
340
1

            
341
1
    // Generate ephemeral keys for this handshake
342
1
    let y = curve25519::EphemeralSecret::new(rng.rng_compat());
343
1
    let Y = curve25519::PublicKey::from(&y);
344
1

            
345
1
    // Compute EXP(X,y) and EXP(X,b)
346
1
    let xy = y.diffie_hellman(&X);
347
1
    let xb = proto_input.b.diffie_hellman(&X);
348

            
349
1
    let (keygen, auth_input_mac) =
350
1
        get_rendezvous1_key_material(&xy, &xb, &proto_input.auth_key, &proto_input.B, &X, &Y)?;
351

            
352
    // Set up RENDEZVOUS1 reply to the client
353
1
    let mut reply: Vec<u8> = Vec::new();
354
1
    reply.write(&Y);
355
1
    reply.write(&auth_input_mac);
356
1

            
357
1
    Ok((keygen, reply, plaintext.clone()))
358
1
}
359

            
360
/*********************** Helper functions ************************************/
361

            
362
/// Implement the MAC function used as part of the HS ntor handshake:
363
/// MAC(k, m) is H(k_len | k | m) where k_len is htonll(len(k)).
364
10
fn hs_ntor_mac(key: &[u8], message: &[u8]) -> Result<MacTag> {
365
10
    let k_len = key.len();
366
10

            
367
10
    let mut d = Sha3_256::new();
368
10
    d.update((k_len as u64).to_be_bytes());
369
10
    d.update(key);
370
10
    d.update(message);
371
10

            
372
10
    let result = d.finalize();
373
10
    result
374
10
        .try_into()
375
10
        .map_err(into_internal!("failed MAC computation"))
376
10
        .map_err(Error::from)
377
10
}
378

            
379
/// Helper function: Compute the part of the HS ntor handshake that generates
380
/// key material for creating and handling INTRODUCE1 cells. Function used
381
/// by both client and service. Specifically, calculate the following:
382
///
383
/// ```pseudocode
384
///  intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
385
///  info = m_hsexpand | subcredential
386
///  hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
387
///  ENC_KEY = hs_keys[0:S_KEY_LEN]
388
///  MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
389
/// ```
390
///
391
/// Return (ENC_KEY, MAC_KEY).
392
2
fn get_introduce1_key_material(
393
2
    bx: &curve25519::SharedSecret,
394
2
    auth_key: &ed25519::PublicKey,
395
2
    X: &curve25519::PublicKey,
396
2
    B: &curve25519::PublicKey,
397
2
    subcredential: &Subcredential,
398
2
) -> Result<(EncKey, MacKey)> {
399
2
    let hs_ntor_protoid_constant = &b"tor-hs-ntor-curve25519-sha3-256-1"[..];
400
2
    let hs_ntor_key_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_extract"[..];
401
2
    let hs_ntor_expand_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_expand"[..];
402
2

            
403
2
    // Construct hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
404
2
    // Start by getting 'intro_secret_hs_input'
405
2
    let mut secret_input = Zeroizing::new(Vec::new());
406
2
    secret_input.write(bx); // EXP(B,x)
407
2
    secret_input.write(auth_key); // AUTH_KEY
408
2
    secret_input.write(X); // X
409
2
    secret_input.write(B); // B
410
2
    secret_input.write(hs_ntor_protoid_constant); // PROTOID
411
2

            
412
2
    // Now fold in the t_hsenc
413
2
    secret_input.write(hs_ntor_key_constant);
414
2

            
415
2
    // and fold in the 'info'
416
2
    secret_input.write(hs_ntor_expand_constant);
417
2
    secret_input.write(subcredential);
418

            
419
2
    let hs_keys = ShakeKdf::new().derive(&secret_input[..], 32 + 32)?;
420
    // Extract the keys into arrays
421
2
    let enc_key = hs_keys[0..32]
422
2
        .try_into()
423
2
        .map_err(into_internal!("converting enc_key"))
424
2
        .map_err(Error::from)?;
425
2
    let mac_key = hs_keys[32..64]
426
2
        .try_into()
427
2
        .map_err(into_internal!("converting mac_key"))
428
2
        .map_err(Error::from)?;
429

            
430
2
    Ok((enc_key, mac_key))
431
2
}
432

            
433
/// Helper function: Compute the last part of the HS ntor handshake which
434
/// derives key material necessary to create and handle RENDEZVOUS1
435
/// cells. Function used by both client and service. The actual calculations is
436
/// as follows:
437
///
438
///  rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
439
///  NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
440
///  verify = MAC(rend_secret_hs_input, t_hsverify)
441
///  auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
442
///  AUTH_INPUT_MAC = MAC(auth_input, t_hsmac)
443
///
444
/// Return (keygen, AUTH_INPUT_MAC), where keygen is a key generator based on
445
/// NTOR_KEY_SEED.
446
2
fn get_rendezvous1_key_material(
447
2
    xy: &curve25519::SharedSecret,
448
2
    xb: &curve25519::SharedSecret,
449
2
    auth_key: &ed25519::PublicKey,
450
2
    B: &curve25519::PublicKey,
451
2
    X: &curve25519::PublicKey,
452
2
    Y: &curve25519::PublicKey,
453
2
) -> Result<(HsNtorHkdfKeyGenerator, AuthInputMac)> {
454
2
    let hs_ntor_protoid_constant = &b"tor-hs-ntor-curve25519-sha3-256-1"[..];
455
2
    let hs_ntor_mac_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_mac"[..];
456
2
    let hs_ntor_verify_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_verify"[..];
457
2
    let server_string_constant = &b"Server"[..];
458
2
    let hs_ntor_expand_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_expand"[..];
459
2
    let hs_ntor_key_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_extract"[..];
460
2

            
461
2
    // Start with rend_secret_hs_input
462
2
    let mut secret_input = Zeroizing::new(Vec::new());
463
2
    secret_input.write(xy); // EXP(X,y)
464
2
    secret_input.write(xb); // EXP(X,b)
465
2
    secret_input.write(auth_key); // AUTH_KEY
466
2
    secret_input.write(B); // B
467
2
    secret_input.write(X); // X
468
2
    secret_input.write(Y); // Y
469
2
    secret_input.write(hs_ntor_protoid_constant); // PROTOID
470

            
471
    // Build NTOR_KEY_SEED and verify
472
2
    let ntor_key_seed = hs_ntor_mac(&secret_input, hs_ntor_key_constant)?;
473
2
    let verify = hs_ntor_mac(&secret_input, hs_ntor_verify_constant)?;
474

            
475
    // Start building 'auth_input'
476
2
    let mut auth_input = Zeroizing::new(Vec::new());
477
2
    auth_input.write(&verify);
478
2
    auth_input.write(auth_key); // AUTH_KEY
479
2
    auth_input.write(B); // B
480
2
    auth_input.write(Y); // Y
481
2
    auth_input.write(X); // X
482
2
    auth_input.write(hs_ntor_protoid_constant); // PROTOID
483
2
    auth_input.write(server_string_constant); // "Server"
484

            
485
    // Get AUTH_INPUT_MAC
486
2
    let auth_input_mac = hs_ntor_mac(&auth_input, hs_ntor_mac_constant)?;
487

            
488
    // Now finish up with the KDF construction
489
2
    let mut kdf_seed = Zeroizing::new(Vec::new());
490
2
    kdf_seed.write(&ntor_key_seed);
491
2
    kdf_seed.write(hs_ntor_expand_constant);
492
2
    let keygen = HsNtorHkdfKeyGenerator::new(Zeroizing::new(kdf_seed.to_vec()));
493
2

            
494
2
    Ok((keygen, auth_input_mac))
495
2
}
496

            
497
/*********************** Unit Tests ******************************************/
498

            
499
#[cfg(test)]
500
mod test {
501
    use super::*;
502
    use hex_literal::hex;
503

            
504
    #[test]
505
    /// Basic HS Ntor test that does the handshake between client and service
506
    /// and makes sure that the resulting keys and KDF is legit.
507
    fn hs_ntor() -> Result<()> {
508
        let mut rng = rand::thread_rng().rng_compat();
509

            
510
        // Let's initialize keys for the client (and the intro point)
511
        let intro_b_privkey = curve25519::StaticSecret::new(&mut rng);
512
        let intro_b_pubkey = curve25519::PublicKey::from(&intro_b_privkey);
513
        let intro_auth_key_privkey = ed25519::SecretKey::generate(&mut rng);
514
        let intro_auth_key_pubkey = ed25519::PublicKey::from(&intro_auth_key_privkey);
515

            
516
        // Create keys for client and service
517
        let client_keys = HsNtorClientInput::new(
518
            intro_b_pubkey,
519
            intro_auth_key_pubkey,
520
            [5; 32],
521
            vec![66; 10],
522
            vec![42; 60],
523
        );
524

            
525
        let service_keys = HsNtorServiceInput::new(
526
            intro_b_privkey,
527
            intro_b_pubkey,
528
            intro_auth_key_pubkey,
529
            [5; 32],
530
            vec![42; 60],
531
        );
532

            
533
        // Client: Sends an encrypted INTRODUCE1 cell
534
        let (state, cmsg) = client_send_intro(&mut rng, &client_keys)?;
535

            
536
        // Service: Decrypt INTRODUCE1 cell, and reply with RENDEZVOUS1 cell
537
        let (skeygen, smsg, s_plaintext) = server_receive_intro(&mut rng, &service_keys, cmsg)?;
538

            
539
        // Check that the plaintext received by the service is the one that the
540
        // client sent
541
        assert_eq!(s_plaintext, vec![66; 10]);
542

            
543
        // Client: Receive RENDEZVOUS1 and create key material
544
        let ckeygen = client_receive_rend(&state, smsg)?;
545

            
546
        // Test that RENDEZVOUS1 key material match
547
        let skeys = skeygen.expand(128)?;
548
        let ckeys = ckeygen.expand(128)?;
549
        assert_eq!(skeys, ckeys);
550

            
551
        Ok(())
552
    }
553

            
554
    #[test]
555
    /// Test vectors generated with hs_ntor_ref.py from little-t-tor.
556
    fn ntor_mac() -> Result<()> {
557
        let result = hs_ntor_mac("who".as_bytes(), b"knows?")?;
558
        assert_eq!(
559
            &result,
560
            &hex!("5e7da329630fdaa3eab7498bb1dc625bbb9ca968f10392b6af92d51d5db17473")
561
        );
562

            
563
        let result = hs_ntor_mac("gone".as_bytes(), b"by")?;
564
        assert_eq!(
565
            &result,
566
            &hex!("90071aabb06d3f7c777db41542f4790c7dd9e2e7b2b842f54c9c42bbdb37e9a0")
567
        );
568

            
569
        Ok(())
570
    }
571
}