1#![allow(non_snake_case)]
23
24use crate::crypto::handshake::KeyGenerator;
25use crate::crypto::ll::kdf::{Kdf, ShakeKdf};
26use crate::{Error, Result};
27use tor_bytes::{Reader, SecretBuf, Writer};
28use tor_hscrypto::{
29 ops::{hs_mac, HS_MAC_LEN},
30 pk::{HsIntroPtSessionIdKey, HsSvcNtorKey},
31 Subcredential,
32};
33use tor_llcrypto::pk::{curve25519, ed25519};
34use tor_llcrypto::util::ct::CtByteArray;
35
36use cipher::{KeyIvInit, StreamCipher};
37
38use tor_error::into_internal;
39use tor_llcrypto::cipher::aes::Aes256Ctr;
40use zeroize::{Zeroize as _, Zeroizing};
41
42#[cfg(any(test, feature = "hs-service"))]
43use tor_hscrypto::pk::HsSvcNtorKeypair;
44
45type EncKey = Zeroizing<[u8; 32]>;
49type MacKey = [u8; 32];
51type MacTag = CtByteArray<HS_MAC_LEN>;
53type AuthInputMac = MacTag;
55
56pub struct HsNtorHkdfKeyGenerator {
59 seed: SecretBuf,
61}
62
63impl HsNtorHkdfKeyGenerator {
64 pub fn new(seed: SecretBuf) -> Self {
66 HsNtorHkdfKeyGenerator { seed }
67 }
68}
69
70impl KeyGenerator for HsNtorHkdfKeyGenerator {
71 fn expand(self, keylen: usize) -> Result<SecretBuf> {
73 ShakeKdf::new().derive(&self.seed[..], keylen)
74 }
75}
76
77#[derive(Clone)]
82#[cfg(any(test, feature = "hs-client"))]
83pub struct HsNtorServiceInfo {
84 B: HsSvcNtorKey,
87
88 auth_key: HsIntroPtSessionIdKey,
98
99 subcredential: Subcredential,
101}
102
103#[cfg(any(test, feature = "hs-client"))]
104impl HsNtorServiceInfo {
105 pub fn new(
107 B: HsSvcNtorKey,
108 auth_key: HsIntroPtSessionIdKey,
109 subcredential: Subcredential,
110 ) -> Self {
111 HsNtorServiceInfo {
112 B,
113 auth_key,
114 subcredential,
115 }
116 }
117}
118
119#[cfg(any(test, feature = "hs-client"))]
121pub struct HsNtorClientState {
122 service_info: HsNtorServiceInfo,
124
125 x: curve25519::StaticSecret,
127 X: curve25519::PublicKey,
129
130 Bx: curve25519::SharedSecret,
134
135 intro1_target_len: usize,
137}
138
139#[cfg(any(test, feature = "hs-client"))]
146const INTRO1_TARGET_LEN: usize = 490;
147
148#[cfg(any(test, feature = "hs-client"))]
149impl HsNtorClientState {
150 pub fn new<R>(rng: &mut R, service_info: HsNtorServiceInfo) -> Self
156 where
157 R: rand::RngCore + rand::CryptoRng,
158 {
159 let x = curve25519::StaticSecret::random_from_rng(rng);
160 Self::new_no_keygen(service_info, x)
161 }
162
163 #[cfg(test)]
165 fn set_intro1_target_len(&mut self, len: usize) {
166 self.intro1_target_len = len;
167 }
168
169 fn new_no_keygen(service_info: HsNtorServiceInfo, x: curve25519::StaticSecret) -> Self {
171 let X = curve25519::PublicKey::from(&x);
172 let Bx = x.diffie_hellman(&service_info.B);
173 Self {
174 service_info,
175 x,
176 X,
177 Bx,
178 intro1_target_len: INTRO1_TARGET_LEN,
179 }
180 }
181
182 pub fn client_send_intro(&self, intro_header: &[u8], plaintext_body: &[u8]) -> Result<Vec<u8>> {
193 const ENC_OVERHEAD: usize = 32 + 32;
195
196 let state = self;
197 let service = &state.service_info;
198
199 let (enc_key, mac_key) = get_introduce_key_material(
201 &state.Bx,
202 &service.auth_key,
203 &state.X,
204 &service.B,
205 &service.subcredential,
206 )?;
207
208 let padded_body_target_len = self
209 .intro1_target_len
210 .saturating_sub(intro_header.len() + ENC_OVERHEAD);
211 let mut padded_body = plaintext_body.to_vec();
212 if padded_body.len() < padded_body_target_len {
213 padded_body.resize(padded_body_target_len, 0);
214 }
215 debug_assert!(padded_body.len() >= padded_body_target_len);
216
217 let (ciphertext, mac_tag) =
218 encrypt_and_mac(&padded_body, intro_header, &state.X, &enc_key, mac_key);
219 padded_body.zeroize();
220
221 let mut response: Vec<u8> = Vec::new();
223 response
224 .write(&state.X)
225 .and_then(|_| response.write(&ciphertext))
226 .and_then(|_| response.write(&mac_tag))
227 .map_err(into_internal!("Can't encode hs-ntor client handshake."))?;
228
229 Ok(response)
230 }
231
232 pub fn client_receive_rend(&self, msg: &[u8]) -> Result<HsNtorHkdfKeyGenerator> {
238 let state = self;
239
240 let mut cur = Reader::from_slice(msg);
242 let Y: curve25519::PublicKey = cur
243 .extract()
244 .map_err(|e| Error::from_bytes_err(e, "hs_ntor handshake"))?;
245 let mac_tag: MacTag = cur
246 .extract()
247 .map_err(|e| Error::from_bytes_err(e, "hs_ntor handshake"))?;
248
249 let xy = state.x.diffie_hellman(&Y);
251 let xb = state.x.diffie_hellman(&state.service_info.B);
252
253 let (keygen, my_mac_tag) = get_rendezvous_key_material(
254 &xy,
255 &xb,
256 &state.service_info.auth_key,
257 &state.service_info.B,
258 &state.X,
259 &Y,
260 )?;
261
262 if my_mac_tag != mac_tag {
264 return Err(Error::BadCircHandshakeAuth);
265 }
266
267 Ok(keygen)
268 }
269}
270
271#[cfg(any(test, feature = "hs-client"))]
275fn encrypt_and_mac(
276 plaintext: &[u8],
277 other_data: &[u8],
278 public_key: &curve25519::PublicKey,
279 enc_key: &EncKey,
280 mac_key: MacKey,
281) -> (Vec<u8>, MacTag) {
282 let mut ciphertext = plaintext.to_vec();
283 let zero_iv = Default::default();
285 let mut cipher = Aes256Ctr::new(enc_key.as_ref().into(), &zero_iv);
286 cipher.apply_keystream(&mut ciphertext);
287
288 let mut mac_body: Vec<u8> = Vec::new();
291 mac_body.extend(other_data);
292 mac_body.extend(public_key.as_bytes());
293 mac_body.extend(&ciphertext);
294 let mac_tag = hs_mac(&mac_key, &mac_body);
295
296 (ciphertext, mac_tag)
297}
298
299#[cfg(any(test, feature = "hs-service"))]
312pub fn server_receive_intro<R>(
313 rng: &mut R,
314 k_hss_ntor: &HsSvcNtorKeypair,
315 auth_key: &HsIntroPtSessionIdKey,
316 subcredential: &[Subcredential],
317 intro_header: &[u8],
318 msg: &[u8],
319) -> Result<(HsNtorHkdfKeyGenerator, Vec<u8>, Vec<u8>)>
320where
321 R: rand::RngCore + rand::CryptoRng,
322{
323 let y = curve25519::StaticSecret::random_from_rng(rng);
324 server_receive_intro_no_keygen(&y, k_hss_ntor, auth_key, subcredential, intro_header, msg)
325}
326
327#[cfg(any(test, feature = "hs-service"))]
329fn server_receive_intro_no_keygen(
330 y: &curve25519::StaticSecret,
333 k_hss_ntor: &HsSvcNtorKeypair,
334 auth_key: &HsIntroPtSessionIdKey,
335 subcredential: &[Subcredential],
336 intro_header: &[u8],
337 msg: &[u8],
338) -> Result<(HsNtorHkdfKeyGenerator, Vec<u8>, Vec<u8>)> {
339 let mut cur = Reader::from_slice(msg);
341 let X: curve25519::PublicKey = cur
342 .extract()
343 .map_err(|e| Error::from_bytes_err(e, "hs ntor handshake"))?;
344 let remaining_bytes = cur.remaining();
345 let ciphertext = &mut cur
346 .take(remaining_bytes - HS_MAC_LEN)
347 .map_err(|e| Error::from_bytes_err(e, "hs ntor handshake"))?
348 .to_vec();
349 let mac_tag: MacTag = cur
350 .extract()
351 .map_err(|e| Error::from_bytes_err(e, "hs ntor handshake"))?;
352
353 let bx = k_hss_ntor.secret().as_ref().diffie_hellman(&X);
355
356 let mut found_dec_key = None;
362
363 for subcredential in subcredential {
364 let (dec_key, mac_key) =
365 get_introduce_key_material(&bx, auth_key, &X, k_hss_ntor.public(), subcredential)?; let mut mac_body: Vec<u8> = Vec::new();
370 mac_body.extend(intro_header);
371 mac_body.extend(X.as_bytes());
372 mac_body.extend(&ciphertext[..]);
373 let my_mac_tag = hs_mac(&mac_key, &mac_body);
374
375 if my_mac_tag == mac_tag {
376 found_dec_key = Some(dec_key);
378 }
379 }
380
381 let Some(dec_key) = found_dec_key else {
382 return Err(Error::BadCircHandshakeAuth);
383 };
384
385 let zero_iv = Default::default();
387 let mut cipher = Aes256Ctr::new(dec_key.as_ref().into(), &zero_iv);
388 cipher.apply_keystream(ciphertext);
389 let plaintext = ciphertext; let Y = curve25519::PublicKey::from(y);
393
394 let xy = y.diffie_hellman(&X);
396 let xb = k_hss_ntor.secret().as_ref().diffie_hellman(&X);
397
398 let (keygen, auth_input_mac) =
399 get_rendezvous_key_material(&xy, &xb, auth_key, k_hss_ntor.public(), &X, &Y)?;
400
401 let mut reply: Vec<u8> = Vec::new();
403 reply
404 .write(&Y)
405 .and_then(|_| reply.write(&auth_input_mac))
406 .map_err(into_internal!("Can't encode hs-ntor server handshake."))?;
407
408 Ok((keygen, reply, plaintext.clone()))
409}
410
411fn get_introduce_key_material(
427 bx: &curve25519::SharedSecret,
428 auth_key: &ed25519::PublicKey,
429 X: &curve25519::PublicKey,
430 B: &curve25519::PublicKey,
431 subcredential: &Subcredential,
432) -> std::result::Result<(EncKey, MacKey), tor_error::Bug> {
433 let hs_ntor_protoid_constant = &b"tor-hs-ntor-curve25519-sha3-256-1"[..];
434 let hs_ntor_key_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_extract"[..];
435 let hs_ntor_expand_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_expand"[..];
436
437 let mut secret_input = SecretBuf::new();
440 secret_input
441 .write(bx) .and_then(|_| secret_input.write(auth_key)) .and_then(|_| secret_input.write(X)) .and_then(|_| secret_input.write(B)) .and_then(|_| secret_input.write(hs_ntor_protoid_constant)) .and_then(|_| secret_input.write(hs_ntor_key_constant))
448 .and_then(|_| secret_input.write(hs_ntor_expand_constant))
450 .and_then(|_| secret_input.write(subcredential))
451 .map_err(into_internal!("Can't generate hs-ntor kdf input."))?;
452
453 let hs_keys = ShakeKdf::new()
454 .derive(&secret_input[..], 32 + 32)
455 .map_err(into_internal!("Can't compute SHAKE"))?;
456 let enc_key = Zeroizing::new(
458 hs_keys[0..32]
459 .try_into()
460 .map_err(into_internal!("converting enc_key"))?,
461 );
462 let mac_key = hs_keys[32..64]
463 .try_into()
464 .map_err(into_internal!("converting mac_key"))?;
465
466 Ok((enc_key, mac_key))
467}
468
469fn get_rendezvous_key_material(
483 xy: &curve25519::SharedSecret,
484 xb: &curve25519::SharedSecret,
485 auth_key: &ed25519::PublicKey,
486 B: &curve25519::PublicKey,
487 X: &curve25519::PublicKey,
488 Y: &curve25519::PublicKey,
489) -> Result<(HsNtorHkdfKeyGenerator, AuthInputMac)> {
490 let hs_ntor_protoid_constant = &b"tor-hs-ntor-curve25519-sha3-256-1"[..];
491 let hs_ntor_mac_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_mac"[..];
492 let hs_ntor_verify_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_verify"[..];
493 let server_string_constant = &b"Server"[..];
494 let hs_ntor_expand_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_expand"[..];
495 let hs_ntor_key_constant = &b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_extract"[..];
496
497 let mut secret_input = SecretBuf::new();
499 secret_input
500 .write(xy) .and_then(|_| secret_input.write(xb)) .and_then(|_| secret_input.write(auth_key)) .and_then(|_| secret_input.write(B)) .and_then(|_| secret_input.write(X)) .and_then(|_| secret_input.write(Y)) .and_then(|_| secret_input.write(hs_ntor_protoid_constant)) .map_err(into_internal!(
508 "Can't encode input to hs-ntor key derivation."
509 ))?;
510
511 let ntor_key_seed = hs_mac(&secret_input, hs_ntor_key_constant);
513 let verify = hs_mac(&secret_input, hs_ntor_verify_constant);
514
515 let mut auth_input = Vec::new();
517 auth_input
518 .write(&verify)
519 .and_then(|_| auth_input.write(auth_key)) .and_then(|_| auth_input.write(B)) .and_then(|_| auth_input.write(Y)) .and_then(|_| auth_input.write(X)) .and_then(|_| auth_input.write(hs_ntor_protoid_constant)) .and_then(|_| auth_input.write(server_string_constant)) .map_err(into_internal!("Can't encode auth-input for hs-ntor."))?;
526
527 let auth_input_mac = hs_mac(&auth_input, hs_ntor_mac_constant);
529
530 let mut kdf_seed = SecretBuf::new();
532 kdf_seed
533 .write(&ntor_key_seed)
534 .and_then(|_| kdf_seed.write(hs_ntor_expand_constant))
535 .map_err(into_internal!("Can't encode kdf-input for hs-ntor."))?;
536 let keygen = HsNtorHkdfKeyGenerator::new(kdf_seed);
537
538 Ok((keygen, auth_input_mac))
539}
540
541#[cfg(test)]
544mod test {
545 #![allow(clippy::bool_assert_comparison)]
547 #![allow(clippy::clone_on_copy)]
548 #![allow(clippy::dbg_macro)]
549 #![allow(clippy::mixed_attributes_style)]
550 #![allow(clippy::print_stderr)]
551 #![allow(clippy::print_stdout)]
552 #![allow(clippy::single_char_pattern)]
553 #![allow(clippy::unwrap_used)]
554 #![allow(clippy::unchecked_duration_subtraction)]
555 #![allow(clippy::useless_vec)]
556 #![allow(clippy::needless_pass_by_value)]
557 use super::*;
559 use hex_literal::hex;
560 use tor_basic_utils::test_rng::testing_rng;
561
562 #[test]
563 fn hs_ntor() -> Result<()> {
566 let mut rng = testing_rng();
567
568 let intro_b_privkey = curve25519::StaticSecret::random_from_rng(&mut rng);
570 let intro_b_pubkey = curve25519::PublicKey::from(&intro_b_privkey);
571 let intro_auth_key_privkey = ed25519::Keypair::generate(&mut rng);
572 let intro_auth_key_pubkey = ed25519::PublicKey::from(&intro_auth_key_privkey);
573 drop(intro_auth_key_privkey); let client_keys = HsNtorServiceInfo::new(
577 intro_b_pubkey.into(),
578 intro_auth_key_pubkey.into(),
579 [5; 32].into(),
580 );
581
582 let k_hss_ntor = HsSvcNtorKeypair::from_secret_key(intro_b_privkey.into());
583 let auth_key = intro_auth_key_pubkey.into();
584 let subcredentials = vec![[5; 32].into()];
585
586 let state = HsNtorClientState::new(&mut rng, client_keys);
588 let cmsg = state.client_send_intro(&[66; 10], &[42; 60])?;
589 assert_eq!(cmsg.len() + 10, INTRO1_TARGET_LEN);
590
591 let (skeygen, smsg, s_plaintext) = server_receive_intro(
593 &mut rng,
594 &k_hss_ntor,
595 &auth_key,
596 &subcredentials[..],
597 &[66; 10],
598 &cmsg,
599 )?;
600
601 assert_eq!(s_plaintext[0..60], vec![42; 60]);
604
605 let ckeygen = state.client_receive_rend(&smsg)?;
607
608 let skeys = skeygen.expand(128)?;
610 let ckeys = ckeygen.expand(128)?;
611 assert_eq!(skeys, ckeys);
612
613 Ok(())
614 }
615
616 #[test]
617 fn ntor_mac() {
619 let result = hs_mac("who".as_bytes(), b"knows?");
620 assert_eq!(
621 &result,
622 &hex!("5e7da329630fdaa3eab7498bb1dc625bbb9ca968f10392b6af92d51d5db17473").into()
623 );
624
625 let result = hs_mac("gone".as_bytes(), b"by");
626 assert_eq!(
627 &result,
628 &hex!("90071aabb06d3f7c777db41542f4790c7dd9e2e7b2b842f54c9c42bbdb37e9a0").into()
629 );
630 }
631
632 #[test]
634 fn testvec() {
635 let kp_hs_ipt_sid =
636 hex!("34E171E4358E501BFF21ED907E96AC6BFEF697C779D040BBAF49ACC30FC5D21F");
637 let subcredential =
638 hex!("0085D26A9DEBA252263BF0231AEAC59B17CA11BAD8A218238AD6487CBAD68B57");
639 let kp_hss_ntor = hex!("8E5127A40E83AABF6493E41F142B6EE3604B85A3961CD7E38D247239AFF71979");
640 let ks_hss_ntor = hex!("A0ED5DBF94EEB2EDB3B514E4CF6ABFF6022051CC5F103391F1970A3FCD15296A");
641 let key_x = hex!("60B4D6BF5234DCF87A4E9D7487BDF3F4A69B6729835E825CA29089CFDDA1E341");
642 let key_y = hex!("68CB5188CA0CD7924250404FAB54EE1392D3D2B9C049A2E446513875952F8F55");
643
644 let kp_hs_ipt_sid: HsIntroPtSessionIdKey = ed25519::PublicKey::from_bytes(&kp_hs_ipt_sid)
646 .unwrap()
647 .into();
648 let subcredential: Subcredential = subcredential.into();
649 let kp_hss_ntor: HsSvcNtorKey = curve25519::PublicKey::from(kp_hss_ntor).into();
650
651 let service_info = HsNtorServiceInfo {
652 B: kp_hss_ntor,
653 auth_key: kp_hs_ipt_sid.clone(),
654 subcredential: subcredential.clone(),
655 };
656
657 let key_x: curve25519::StaticSecret = curve25519::StaticSecret::from(key_x);
659
660 let intro_header = hex!(
663 "000000000000000000000000000000000000000002002034E171E4358E501BFF
664 21ED907E96AC6BFEF697C779D040BBAF49ACC30FC5D21F00"
665 );
666 let intro_body = hex!(
667 "6BD364C12638DD5C3BE23D76ACA05B04E6CE932C0101000100200DE6130E4FCA
668 C4EDDA24E21220CC3EADAE403EF6B7D11C8273AC71908DE565450300067F0000
669 0113890214F823C4F8CC085C792E0AEE0283FE00AD7520B37D0320728D5DF39B
670 7B7077A0118A900FF4456C382F0041300ACF9C58E51C392795EF870000000000
671 0000000000000000000000000000000000000000000000000000000000000000
672 000000000000000000000000000000000000000000000000000000000000"
673 );
674 let mut client_state = HsNtorClientState::new_no_keygen(service_info, key_x);
676 client_state.set_intro1_target_len(0);
679 let encrypted_body = client_state
680 .client_send_intro(&intro_header, &intro_body)
681 .unwrap();
682
683 let mut cell_out = intro_header.to_vec();
684 cell_out.extend(&encrypted_body);
685 let expected = &hex!(
686 "000000000000000000000000000000000000000002002034E171E4358E501BFF
687 21ED907E96AC6BFEF697C779D040BBAF49ACC30FC5D21F00BF04348B46D09AED
688 726F1D66C618FDEA1DE58E8CB8B89738D7356A0C59111D5DADBECCCB38E37830
689 4DCC179D3D9E437B452AF5702CED2CCFEC085BC02C4C175FA446525C1B9D5530
690 563C362FDFFB802DAB8CD9EBC7A5EE17DA62E37DEEB0EB187FBB48C63298B0E8
691 3F391B7566F42ADC97C46BA7588278273A44CE96BC68FFDAE31EF5F0913B9A9C
692 7E0F173DBC0BDDCD4ACB4C4600980A7DDD9EAEC6E7F3FA3FC37CD95E5B8BFB3E
693 35717012B78B4930569F895CB349A07538E42309C993223AEA77EF8AEA64F25D
694 DEE97DA623F1AEC0A47F150002150455845C385E5606E41A9A199E7111D54EF2
695 D1A51B7554D8B3692D85AC587FB9E69DF990EFB776D8"
696 );
697 assert_eq!(&cell_out, &expected);
698
699 let ks_hss_ntor = curve25519::StaticSecret::from(ks_hss_ntor).into();
705 let k_hss_ntor = HsSvcNtorKeypair::from_secret_key(ks_hss_ntor);
706 let key_y = curve25519::StaticSecret::from(key_y);
707 let subcredentials = vec![subcredential];
708
709 let (service_keygen, service_reply, service_plaintext) = server_receive_intro_no_keygen(
710 &key_y,
711 &k_hss_ntor,
712 &kp_hs_ipt_sid,
713 &subcredentials[..],
714 &intro_header,
715 &encrypted_body,
716 )
717 .unwrap();
718
719 assert_eq!(&service_plaintext, &intro_body);
721
722 let expected_reply = hex!(
723 "8fbe0db4d4a9c7ff46701e3e0ee7fd05cd28be4f302460addeec9e93354ee700
724 4A92E8437B8424D5E5EC279245D5C72B25A0327ACF6DAF902079FCB643D8B208"
725 );
726 assert_eq!(&service_reply, &expected_reply);
727
728 let client_keygen = client_state.client_receive_rend(&service_reply).unwrap();
730 let bytes_client = client_keygen.expand(128).unwrap();
731 let bytes_service = service_keygen.expand(128).unwrap();
732 let mut key_seed =
733 hex!("4D0C72FE8AFF35559D95ECC18EB5A36883402B28CDFD48C8A530A5A3D7D578DB").to_vec();
734 key_seed.extend(b"tor-hs-ntor-curve25519-sha3-256-1:hs_key_expand");
735 let bytes_expected = HsNtorHkdfKeyGenerator::new(key_seed.into())
736 .expand(128)
737 .unwrap();
738 assert_eq!(&bytes_client, &bytes_service);
739 assert_eq!(&bytes_client, &bytes_expected);
740 }
741}