diff --git a/crates/tor-cell/src/relaycell/msg.rs b/crates/tor-cell/src/relaycell/msg.rs index 1dd2b3f1c..3897eab9e 100644 --- a/crates/tor-cell/src/relaycell/msg.rs +++ b/crates/tor-cell/src/relaycell/msg.rs @@ -72,6 +72,9 @@ pub enum RelayMsg { /// Establish Rendezvous #[cfg(feature = "onion-service")] EstablishRendezvous(onion_service::EstablishRendezvous), + /// Introduce1 + #[cfg(feature = "onion-service")] + Introduce1(onion_service::Introduce1), /// An unrecognized command. Unrecognized(Unrecognized), @@ -123,6 +126,8 @@ impl RelayMsg { EstablishIntro(_) => RelayCmd::ESTABLISH_INTRO, #[cfg(feature = "onion-service")] EstablishRendezvous(_) => RelayCmd::ESTABLISH_RENDEZVOUS, + #[cfg(feature = "onion-service")] + Introduce1(_) => RelayCmd::INTRODUCE1, Unrecognized(u) => u.cmd(), } } @@ -160,6 +165,10 @@ impl RelayMsg { RelayCmd::ESTABLISH_RENDEZVOUS => RelayMsg::EstablishRendezvous( onion_service::EstablishRendezvous::decode_from_reader(r)?, ), + #[cfg(feature = "onion-service")] + RelayCmd::INTRODUCE1 => { + RelayMsg::Introduce1(onion_service::Introduce1::decode_from_reader(r)?) + } _ => RelayMsg::Unrecognized(Unrecognized::decode_with_cmd(c, r)?), }) } @@ -192,6 +201,8 @@ impl RelayMsg { EstablishIntro(b) => b.encode_onto(w), #[cfg(feature = "onion-service")] EstablishRendezvous(b) => b.encode_onto(w), + #[cfg(feature = "onion-service")] + Introduce1(b) => b.encode_onto(w), Unrecognized(b) => b.encode_onto(w), } } diff --git a/crates/tor-cell/src/relaycell/onion_service.rs b/crates/tor-cell/src/relaycell/onion_service.rs index 4b7da0a17..c39396d0c 100644 --- a/crates/tor-cell/src/relaycell/onion_service.rs +++ b/crates/tor-cell/src/relaycell/onion_service.rs @@ -5,7 +5,7 @@ use super::msg; use caret::caret_int; -use tor_bytes::{EncodeError, EncodeResult, Readable, Result, Writeable}; +use tor_bytes::{EncodeError, EncodeResult, Error as BytesError, Readable, Result, Writeable}; use tor_bytes::{Reader, Writer}; use tor_units::BoundedInt32; @@ -251,3 +251,65 @@ impl msg::Body for EstablishRendezvous { w.write(&self.cookie) } } + +/// A message sent from client to introduction point. +#[derive(Debug, Clone)] +pub struct Introduce1 { + /// Introduction point auth key type and the type of + /// the MAC used in `handshake_auth`. + auth_key_type: AuthKeyType, + /// The public introduction point auth key. + auth_key: Vec, + /// Up to end of relay payload. + encrypted: Vec, +} + +impl msg::Body for Introduce1 { + fn into_message(self) -> msg::RelayMsg { + msg::RelayMsg::Introduce1(self) + } + fn decode_from_reader(r: &mut Reader<'_>) -> Result { + let legacy_key_id: [u8; 20] = r.extract()?; + if legacy_key_id.iter().any(|b| *b != 0_u8) { + return Err(BytesError::BadMessage("legacy key id in Introduce1.")); + } + let auth_key_type = r.take_u8()?.into(); + let auth_key_len = r.take_u16()?; + let auth_key = r.take(auth_key_len as usize)?.into(); + let n_ext = r.take_u8()?; + for _ in 0..n_ext { + let _ext_type = r.take_u8()?; + r.read_nested_u8len(|r| { + r.take_rest(); + Ok(()) + })?; + } + let encrypted = r.take_rest().into(); + Ok(Self { + auth_key_type, + auth_key, + encrypted, + }) + } + fn encode_onto(self, w: &mut Vec) -> EncodeResult<()> { + w.write_all(&[0_u8; 20]); + w.write_u8(self.auth_key_type.get()); + w.write_u16(u16::try_from(self.auth_key.len()).map_err(|_| EncodeError::BadLengthValue)?); + w.write_all(&self.auth_key[..]); + // No Introduce1 extension for now. + w.write_u8(0_u8); + w.write_all(&self.encrypted[..]); + Ok(()) + } +} + +impl Introduce1 { + /// All arguments constructor + pub fn new(auth_key_type: AuthKeyType, auth_key: Vec, encrypted: Vec) -> Self { + Self { + auth_key_type, + auth_key, + encrypted, + } + } +} diff --git a/crates/tor-cell/tests/testvec_relaymsg.rs b/crates/tor-cell/tests/testvec_relaymsg.rs index e3d910534..0dbdf796c 100644 --- a/crates/tor-cell/tests/testvec_relaymsg.rs +++ b/crates/tor-cell/tests/testvec_relaymsg.rs @@ -670,7 +670,7 @@ fn test_establish_intro() { let sig = vec![0, 1, 2, 3]; assert_eq!(Into::::into(cmd), 32); - // Establish intro with one recognzied extention + // Establish intro with one recognzied extension let mut es_intro = EstablishIntro::new(auth_key_type, auth_key, handshake_auth, sig); es_intro.set_extension_dos(extension_dos); msg( @@ -682,7 +682,7 @@ fn test_establish_intro() { &es_intro.into(), ); - // Establish intro with no extention + // Establish intro with no extension let auth_key = vec![0, 1, 2, 3]; let sig = vec![0, 1, 2, 3]; msg( @@ -694,11 +694,10 @@ fn test_establish_intro() { &EstablishIntro::new(auth_key_type, auth_key, handshake_auth, sig).into(), ); - // Establish intro with one recognzied extention - // and one unknown extention + // Establish intro with one recognzied extension + // and one unknown extension let auth_key = vec![0, 1, 2, 3]; let sig = vec![0, 1, 2, 3]; - let extension_dos = EstIntroExtDoS::new(Some(1_i32), Some(2_i32)) .expect("invalid EST_INTRO_DOS_EXT parameter(s)"); @@ -721,6 +720,58 @@ fn test_establish_intro() { assert_eq!(actual_bytes, expect_bytes); } +#[cfg(feature = "onion-service")] +#[test] +fn test_introduce1() { + use tor_cell::relaycell::{ + msg::RelayMsg, + onion_service::{AuthKeyType, Introduce1}, + }; + + let cmd = RelayCmd::INTRODUCE1; + let auth_key_type = AuthKeyType::ED25519_SHA3_256; + let auth_key = vec![0, 1, 2, 3]; + let encrypted = vec![1, 9, 8, 4]; + assert_eq!(Into::::into(cmd), 34); + + // Introduce1 with no extension + let intro1 = Introduce1::new(auth_key_type, auth_key, encrypted); + msg( + cmd, + "0000000000000000000000000000000000000000 + 02 0004 00010203 + 00 + 01090804", + &intro1.clone().into(), + ); + + // Introduce1 with unknown extensions + let body = "0000000000000000000000000000000000000000 + 02 0004 00010203 + 02 01 01 00 02 01 00 + 01090804"; + let actual_msg = decode(cmd, &unhex(body)[..]).unwrap(); + let mut actual_bytes = vec![]; + let mut expect_bytes = vec![]; + actual_msg + .encode_onto(&mut actual_bytes) + .expect("Encode msg onto byte vector"); + let expected_msg: RelayMsg = intro1.into(); + expected_msg + .encode_onto(&mut expect_bytes) + .expect("Encode msg onto byte vector"); + assert_eq!(actual_bytes, expect_bytes); + + // Introduce1 with legacy key id + msg_error( + cmd, + "1000000000000000000000000000000000000000 + 02 0004 00010203 + 00 + 01090804", + BytesError::BadMessage("legacy key id in Introduce1."), + ); +} // TODO: need to add tests for: // - unrecognized // - data