mod.rs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215
  1. //! Implementation of the _Connection Rendezvous Protocol_.
  2. use std::collections::HashMap;
  3. use duplicate::duplicate_item;
  4. use educe::Educe;
  5. use libthreema_macros::{DebugVariantNames, VariantNames};
  6. use prost::Message as _;
  7. use rand::{self, Rng as _};
  8. use tracing::{debug, trace, warn};
  9. use zeroize::ZeroizeOnDrop;
  10. pub use crate::d2d_rendezvous::frame::{RendezvousIncomingFrame, RendezvousOutgoingFrame};
  11. use crate::{
  12. crypto::x25519,
  13. d2d_rendezvous::frame::FrameDecoder,
  14. protobuf::d2d_rendezvous as protobuf,
  15. utils::time::{Duration, Instant},
  16. };
  17. mod frame;
  18. mod rxdak;
  19. mod rxdtk;
  20. mod rxdxk;
  21. /// An error occurred while running the Connection Rendezvous Protocol.
  22. ///
  23. /// Note: Errors can occur when using the API incorrectly or when the remote party behaves
  24. /// incorrectly. Since the Connection Rendezvous Protocol is short-lived and all involved parties
  25. /// are required to behave on all paths, none of these errors are considered recoverable.
  26. ///
  27. /// When encountering an error:
  28. ///
  29. /// 1. Let `error` be the provided [`RendezvousProtocolError`].
  30. /// 2. Abort the protocol due to `error`.
  31. ///
  32. /// TODO(LIB-30): Align errors with other protocols.
  33. #[derive(Debug, thiserror::Error)]
  34. #[cfg_attr(feature = "uniffi", derive(uniffi::Error), uniffi(flat_error))]
  35. pub enum RendezvousProtocolError {
  36. /// Invalid parameter provided by foreign code.
  37. #[cfg(feature = "uniffi")]
  38. #[error("Invalid parameter: {0}")]
  39. InvalidParameter(&'static str),
  40. /// Exhausted the available sequence numbers to use for sending/receiving frames.
  41. #[error("Sequence number would overflow")]
  42. SequenceNumberOverflow,
  43. /// Oversized frame received.
  44. #[error("Oversized frame of {0} bytes")]
  45. OversizedFrame(usize),
  46. /// Unable to decrypt a frame's payload.
  47. #[error("Decryption failed")]
  48. DecryptionFailed,
  49. /// Unable to encrypt a frame's payload.
  50. #[error("Encryption failed")]
  51. EncryptionFailed,
  52. /// Unable to decode a protobuf message.
  53. #[error("Decoding failed: {0}")]
  54. ProtobufDecodeFailed(#[from] prost::DecodeError),
  55. /// Incoming RRD's `Hello` message is invalid.
  56. #[error("Invalid RRD Hello message: {0}")]
  57. InvalidRrdHelloMessage(String),
  58. /// Incoming RID's `AuthHello` message is invalid.
  59. #[error("Invalid RID AuthHello message: {0}")]
  60. InvalidRidAuthHelloMessage(String),
  61. /// Incoming RRD's `Auth` message is invalid.
  62. #[error("Invalid RRD Auth message: {0}")]
  63. InvalidRrdAuthMessage(String),
  64. /// Unexpected frame received (e.g. during the nomination phase where only one role is allowed
  65. /// to send frames).
  66. #[error("Frame received unexpectedly")]
  67. UnexpectedFrame,
  68. /// Unable to find the referenced path. It was either never created or already dropped due to
  69. /// nomination of another path.
  70. #[error("Unknown or dropped path with PID {0}")]
  71. UnknownOrDroppedPath(u32),
  72. /// The referenced path has been closed (most likely due to a previous error encountered on the
  73. /// path).
  74. #[error("Path with PID {0} is closed")]
  75. PathClosed(u32),
  76. /// The local role does not allow for nomination.
  77. #[error("Nomination is not allowed for the local role")]
  78. NominateNotAllowed,
  79. /// Nomination already occurred for a path.
  80. #[error("Nomination already occurred for PID {0}")]
  81. NominationAlreadyDone(u32),
  82. /// Nomination is not allowed in the current state.
  83. #[error("Nomination is not allowed in state '{0}'")]
  84. InvalidStateForNomination(&'static str),
  85. /// Nomination is required before sending ULP data.
  86. #[error("Nomination is required before sending ULP data")]
  87. NominationRequired,
  88. /// Public key was non-contributory.
  89. #[error("Remote public key was non-contributory")]
  90. NonContributoryPublicKey,
  91. }
  92. /// Authentication Key (AK).
  93. #[derive(ZeroizeOnDrop)]
  94. pub struct RendezvousAuthenticationKey(pub [u8; 32]);
  95. /// Rendezvous Path Hash (RPH), derived from the Shared Transport Key (STK).
  96. pub struct RendezvousPathHash(pub [u8; 32]);
  97. /// A path state update.
  98. #[derive(DebugVariantNames, VariantNames)]
  99. pub enum RendezvousPathStateUpdate {
  100. /// The handshake on this path was successful and is await nomination (or being dropped).
  101. AwaitingNominate {
  102. /// RTT measured during the handshake, to be used by the nominator to select a path.
  103. measured_rtt: Duration,
  104. },
  105. /// The path has been nominated, allowing for ULP frames to be exchanged.
  106. Nominated {
  107. /// The Rendezvous Path Hash (RPH) of the nominated path.
  108. rph: RendezvousPathHash,
  109. },
  110. }
  111. /// Result returned when interacting with the protocol state machine. The result is associated to
  112. /// the path whose PID was used when calling a function that yielded this result.
  113. ///
  114. /// When handling this result, run the following steps:
  115. ///
  116. /// 1. Let `path` be the context of the path whose PID was used in the function call that yielded this result.
  117. /// 2. If the current phase is the _handshake and nomination phase_:
  118. /// 1. If `incoming_ulp_data` is present, abort the protocol due to an error and abort these steps.
  119. /// 2. If `outgoing_frame` is present, enqueue it to be sent on `path`.
  120. /// 3. If `state_update` is [`RendezvousPathStateUpdate::AwaitingNominate`] and the protocol took the role
  121. /// of the nominator, run the _Path Awaiting Nomination Steps_ with `path` and abort these steps.
  122. /// 4. If `state_update` is [`RendezvousPathStateUpdate::Nominated`]:
  123. /// 1. Mark `path` as _nominated_.
  124. /// 2. If the protocol took the role of the nominee, mark all other paths except `path` as _disregarded_
  125. /// and close them (for WebSocket, use close code `1000`).
  126. /// 3. If the protocol took the rule of the nominator, mark all other paths except `path` as
  127. /// _disregarded_ but keep them open with a timeout of 15s after which they should be closed (for
  128. /// WebSocket, use close code `1000`).[^close-race]
  129. /// 4. Enter the _ULP phase_. At this point, the ULP may start creating frames through
  130. /// [`RendezvousProtocol::create_ulp_frame`].
  131. /// 5. (Unreachable)
  132. /// 3. If the current phase is the _ULP phase_:
  133. /// 1. If `path` is not marked as _nominated_, abort the protocol due to an error and abort these steps.
  134. /// 2. If `state_update` is present, abort the protocol due to an error and abort these steps.
  135. /// 3. If `outgoing_frame` is present, enqueue it to be sent on `path`.
  136. /// 4. If `incoming_ulp_data` is present, hand it off to the ULP.
  137. /// 4. (Unreachable)
  138. ///
  139. /// [^close-race]: This prevents a race condition between RID nominating a path and path close
  140. /// detection on RRD's side.
  141. #[derive(Debug)]
  142. pub struct RendezvousPathProcessResult {
  143. /// The path's state updated.
  144. pub state_update: Option<RendezvousPathStateUpdate>,
  145. /// An outgoing frame is ready to be sent on the path.
  146. pub outgoing_frame: Option<RendezvousOutgoingFrame>,
  147. /// An incoming frame has been reassembled and is ready to be handed off to the ULP.
  148. pub incoming_ulp_data: Option<Vec<u8>>,
  149. }
  150. /// 16 byte random authentication challenge.
  151. struct Challenge([u8; 16]);
  152. impl Challenge {
  153. fn random() -> Self {
  154. let mut challenge = Self([0_u8; 16]);
  155. rand::thread_rng().fill(&mut challenge.0);
  156. challenge
  157. }
  158. }
  159. /// Ephemeral transport key (ETK).
  160. struct EphemeralTransportKey(x25519::SharedSecretHSalsa20);
  161. /// Protocol context passed around to the various roles and states.
  162. struct Context {
  163. is_nominator: bool,
  164. ak: RendezvousAuthenticationKey,
  165. }
  166. impl Context {
  167. fn new(is_nominator: bool, ak: RendezvousAuthenticationKey) -> Self {
  168. Self { is_nominator, ak }
  169. }
  170. }
  171. /// Path states of RID.
  172. #[derive(VariantNames, DebugVariantNames)]
  173. enum RidPathState {
  174. /// Briefly used internally when moving from one state to another.
  175. Invalid,
  176. /// Awaiting an `RrdToRid.Hello` to start the handshake.
  177. AwaitingHello { authentication_keys: rxdak::ForRid },
  178. /// Sent an `RidToRrd.AuthHello`, awaiting an `RrdToRid.Auth`.
  179. AwaitingAuth {
  180. authentication_keys: rxdak::ForRid,
  181. sent_at: Instant,
  182. local_challenge: Challenge,
  183. shared_etk: EphemeralTransportKey,
  184. },
  185. /// Expecting a `Nominate` to be sent or received (depending on the configuration).
  186. AwaitingNominate {
  187. transport_keys: rxdtk::ForRid,
  188. rph: RendezvousPathHash,
  189. },
  190. /// The connection path was `Nominate`d and can now be used by the ULP.
  191. Nominated { transport_keys: rxdtk::ForRid },
  192. /// The connection path closed.
  193. Closed,
  194. }
  195. /// RID path protocol.
  196. struct RidPath {
  197. pid: u32,
  198. decoder: FrameDecoder,
  199. state: RidPathState,
  200. }
  201. impl RidPath {
  202. fn new(ak: &RendezvousAuthenticationKey, pid: u32) -> Self {
  203. Self {
  204. pid,
  205. decoder: FrameDecoder::new(vec![]),
  206. state: RidPathState::AwaitingHello {
  207. authentication_keys: rxdak::ForRid::new(ak, pid),
  208. },
  209. }
  210. }
  211. fn process_frame(
  212. &mut self,
  213. ctx: &Context,
  214. mut incoming_frame: RendezvousIncomingFrame,
  215. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  216. trace!(state = ?self.state, ?incoming_frame, "Processing frame");
  217. if let RidPathState::Nominated { transport_keys } = &mut self.state {
  218. // Handle `Nominated` state where the transport can be used by the ULP.
  219. Self::handle_ulp_data(transport_keys, incoming_frame).map(|incoming_ulp_data| {
  220. RendezvousPathProcessResult {
  221. state_update: None,
  222. outgoing_frame: None,
  223. incoming_ulp_data: Some(incoming_ulp_data),
  224. }
  225. })
  226. } else {
  227. // Handle `Closed` state
  228. if let RidPathState::Closed = &self.state {
  229. return Err(RendezvousProtocolError::PathClosed(self.pid));
  230. }
  231. // Handle states that should immediately transition into another state
  232. //
  233. // IMPORTANT: All match arms must be infallible!
  234. match core::mem::replace(&mut self.state, RidPathState::Invalid) {
  235. RidPathState::AwaitingHello {
  236. mut authentication_keys,
  237. } => {
  238. // Handle `RrdToRid.Hello`, create `RidToRrd.AuthHello` and update state.
  239. Self::handle_hello(&mut authentication_keys, &mut incoming_frame).map(
  240. |(local_challenge, shared_etk, outgoing_frame)| {
  241. (
  242. RidPathState::AwaitingAuth {
  243. authentication_keys,
  244. sent_at: Instant::now(),
  245. local_challenge,
  246. shared_etk,
  247. },
  248. RendezvousPathProcessResult {
  249. state_update: None,
  250. outgoing_frame: Some(outgoing_frame),
  251. incoming_ulp_data: None,
  252. },
  253. )
  254. },
  255. )
  256. },
  257. RidPathState::AwaitingAuth {
  258. mut authentication_keys,
  259. sent_at,
  260. local_challenge,
  261. shared_etk,
  262. } => {
  263. // Calculate RTT
  264. let measured_rtt = Instant::now().duration_since(sent_at);
  265. // Handle `RrdToRid.Auth` and update state.
  266. Self::handle_auth(&mut authentication_keys, &local_challenge, &mut incoming_frame).map(
  267. |()| {
  268. let (transport_keys, rph) =
  269. rxdtk::ForRid::new(&ctx.ak, authentication_keys, shared_etk);
  270. (
  271. RidPathState::AwaitingNominate { transport_keys, rph },
  272. RendezvousPathProcessResult {
  273. state_update: Some(RendezvousPathStateUpdate::AwaitingNominate {
  274. measured_rtt,
  275. }),
  276. outgoing_frame: None,
  277. incoming_ulp_data: None,
  278. },
  279. )
  280. },
  281. )
  282. },
  283. RidPathState::AwaitingNominate {
  284. mut transport_keys,
  285. rph,
  286. } => {
  287. // Check if the remote side is allowed to `Nominate`.
  288. if ctx.is_nominator {
  289. return Err(RendezvousProtocolError::UnexpectedFrame);
  290. }
  291. // Handle `Nominate` and update state.
  292. Self::handle_nominate(&mut transport_keys, &mut incoming_frame).map(|()| {
  293. (
  294. RidPathState::Nominated { transport_keys },
  295. RendezvousPathProcessResult {
  296. state_update: Some(RendezvousPathStateUpdate::Nominated { rph }),
  297. outgoing_frame: None,
  298. incoming_ulp_data: None,
  299. },
  300. )
  301. })
  302. },
  303. // States that must have been covered by code above
  304. RidPathState::Invalid | RidPathState::Nominated { .. } | RidPathState::Closed => {
  305. unreachable!("State should have been handled")
  306. },
  307. }
  308. .map(|(state, result)| {
  309. self.state = state;
  310. debug!(state = ?self.state, "Changed state");
  311. result
  312. })
  313. }
  314. .map_err(|error| {
  315. self.state = RidPathState::Closed;
  316. warn!(?error, state = ?self.state, "Closed due to error");
  317. error
  318. })
  319. }
  320. fn nominate(&mut self) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  321. // Ensure we are in the correct state to nominate
  322. if !matches!(&self.state, RidPathState::AwaitingNominate { .. }) {
  323. return Err(RendezvousProtocolError::InvalidStateForNomination(
  324. self.state.variant_name(),
  325. ));
  326. }
  327. // Nominate
  328. if let RidPathState::AwaitingNominate {
  329. mut transport_keys,
  330. rph,
  331. } = core::mem::replace(&mut self.state, RidPathState::Invalid)
  332. {
  333. Self::create_nominate(&mut transport_keys).map(|outgoing_frame| {
  334. (
  335. RidPathState::Nominated { transport_keys },
  336. RendezvousPathProcessResult {
  337. state_update: Some(RendezvousPathStateUpdate::Nominated { rph }),
  338. outgoing_frame: Some(outgoing_frame),
  339. incoming_ulp_data: None,
  340. },
  341. )
  342. })
  343. } else {
  344. unreachable!("Expected AwaitingNominate state")
  345. }
  346. .map(|(state, result)| {
  347. self.state = state;
  348. debug!(state = ?self.state, "Changed state");
  349. result
  350. })
  351. .map_err(|error| {
  352. self.state = RidPathState::Closed;
  353. warn!(?error, state = ?self.state, "Closed due to error");
  354. error
  355. })
  356. }
  357. fn create_ulp_frame(
  358. &mut self,
  359. outgoing_data: Vec<u8>,
  360. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  361. match &mut self.state {
  362. RidPathState::Nominated { transport_keys } => {
  363. Self::create_ulp_data(transport_keys, outgoing_data).map(|outgoing_frame| {
  364. RendezvousPathProcessResult {
  365. state_update: None,
  366. outgoing_frame: Some(outgoing_frame),
  367. incoming_ulp_data: None,
  368. }
  369. })
  370. },
  371. _ => Err(RendezvousProtocolError::NominationRequired),
  372. }
  373. }
  374. fn handle_hello(
  375. keys: &mut rxdak::ForRid,
  376. incoming_frame: &mut RendezvousIncomingFrame,
  377. ) -> Result<(Challenge, EphemeralTransportKey, RendezvousOutgoingFrame), RendezvousProtocolError> {
  378. // Decrypt and decode into a `RrdToRid.Hello`
  379. let (remote_challenge, remote_etk) = {
  380. keys.rrdak.decrypt(&mut incoming_frame.0)?;
  381. let hello = protobuf::handshake::rrd_to_rid::Hello::decode(incoming_frame.0.as_ref())?;
  382. // Validate `RrdToRid.Hello`
  383. let remote_challenge = Challenge(hello.challenge.as_slice().try_into().map_err(|_| {
  384. RendezvousProtocolError::InvalidRrdHelloMessage(format!(
  385. "Expected 16 challenge bytes, got {}",
  386. hello.challenge.len()
  387. ))
  388. })?);
  389. let remote_etk =
  390. x25519::PublicKey::from(<[u8; 32]>::try_from(hello.etk.as_ref()).map_err(|_| {
  391. RendezvousProtocolError::InvalidRrdHelloMessage(format!(
  392. "Invalid remote ETK, got {} bytes",
  393. hello.etk.len()
  394. ))
  395. })?);
  396. (remote_challenge, remote_etk)
  397. };
  398. // Encode and encrypt `RidToRrd.AuthHello`
  399. let (local_challenge, shared_etk, outgoing_frame) = {
  400. // Generate a challenge
  401. let local_challenge = Challenge::random();
  402. // Generate local part of ETK
  403. let local_etk = x25519::EphemeralSecret::random();
  404. // Encode and encrypt `RidToRrd.AuthHello`
  405. let local_auth_hello = protobuf::handshake::rid_to_rrd::AuthHello {
  406. response: remote_challenge.0.to_vec(),
  407. challenge: local_challenge.0.to_vec(),
  408. etk: x25519::PublicKey::from(&local_etk).as_bytes().to_vec(),
  409. };
  410. let mut outgoing_data = local_auth_hello.encode_to_vec();
  411. keys.ridak.encrypt(&mut outgoing_data)?;
  412. // Derive ETK
  413. let shared_etk = EphemeralTransportKey(
  414. local_etk
  415. .diffie_hellman(&remote_etk)
  416. .ok_or(RendezvousProtocolError::NonContributoryPublicKey)?
  417. .into(),
  418. );
  419. (
  420. local_challenge,
  421. shared_etk,
  422. RendezvousOutgoingFrame(outgoing_data),
  423. )
  424. };
  425. // Done
  426. Ok((local_challenge, shared_etk, outgoing_frame))
  427. }
  428. fn handle_auth(
  429. keys: &mut rxdak::ForRid,
  430. local_challenge: &Challenge,
  431. incoming_frame: &mut RendezvousIncomingFrame,
  432. ) -> Result<(), RendezvousProtocolError> {
  433. // Decrypt and decode into a `RrdToRid.Auth`
  434. keys.rrdak.decrypt(&mut incoming_frame.0)?;
  435. let remote_auth = protobuf::handshake::rrd_to_rid::Auth::decode(incoming_frame.0.as_ref())?;
  436. // Validate `RrdToRid.Auth`
  437. if remote_auth.response.as_ref() != local_challenge.0 {
  438. return Err(RendezvousProtocolError::InvalidRrdAuthMessage(format!(
  439. "Challenge response of {} bytes does not match",
  440. remote_auth.response.len()
  441. )));
  442. }
  443. // Done
  444. Ok(())
  445. }
  446. fn create_nominate(keys: &mut rxdtk::ForRid) -> Result<RendezvousOutgoingFrame, RendezvousProtocolError> {
  447. // Encode and encrypt a `Nominate`
  448. let local_nominate = protobuf::Nominate {};
  449. let mut outgoing_data = local_nominate.encode_to_vec();
  450. keys.ridtk.encrypt(&mut outgoing_data)?;
  451. Ok(RendezvousOutgoingFrame(outgoing_data))
  452. }
  453. fn handle_nominate(
  454. keys: &mut rxdtk::ForRid,
  455. incoming_frame: &mut RendezvousIncomingFrame,
  456. ) -> Result<(), RendezvousProtocolError> {
  457. // Decrypt and decode into a `Nominate`
  458. keys.rrdtk.decrypt(&mut incoming_frame.0)?;
  459. let _ = protobuf::Nominate::decode(incoming_frame.0.as_ref())?;
  460. Ok(())
  461. }
  462. fn create_ulp_data(
  463. keys: &mut rxdtk::ForRid,
  464. mut outgoing_data: Vec<u8>,
  465. ) -> Result<RendezvousOutgoingFrame, RendezvousProtocolError> {
  466. // Encode and encrypt ULP data
  467. keys.ridtk.encrypt(&mut outgoing_data)?;
  468. Ok(RendezvousOutgoingFrame(outgoing_data))
  469. }
  470. fn handle_ulp_data(
  471. keys: &mut rxdtk::ForRid,
  472. mut incoming_frame: RendezvousIncomingFrame,
  473. ) -> Result<Vec<u8>, RendezvousProtocolError> {
  474. // Decrypt and decode ULP data
  475. keys.rrdtk.decrypt(&mut incoming_frame.0)?;
  476. Ok(incoming_frame.0)
  477. }
  478. }
  479. /// Path states of RID.
  480. #[derive(VariantNames, DebugVariantNames)]
  481. enum RrdPathState {
  482. /// Briefly used internally when moving from one state to another.
  483. Invalid,
  484. /// Sent an `RrdtoRid.Hello`, awaiting an `RidToRrd.AuthHello`.
  485. AwaitingAuthHello {
  486. authentication_keys: rxdak::ForRrd,
  487. sent_at: Instant,
  488. local_challenge: Challenge,
  489. local_etk: x25519::EphemeralSecret,
  490. },
  491. /// Expecting a `Nominate` to be sent or received (depending on the configuration).
  492. AwaitingNominate {
  493. transport_keys: rxdtk::ForRrd,
  494. rph: RendezvousPathHash,
  495. },
  496. /// The connection path was `Nominate`d and can now be used by the ULP.
  497. Nominated { transport_keys: rxdtk::ForRrd },
  498. /// The connection path closed.
  499. Closed,
  500. }
  501. /// RRD path protocol.
  502. struct RrdPath {
  503. pid: u32,
  504. decoder: FrameDecoder,
  505. state: RrdPathState,
  506. }
  507. impl RrdPath {
  508. fn new(ak: &RendezvousAuthenticationKey, pid: u32) -> (Self, RendezvousOutgoingFrame) {
  509. // Create initial state
  510. let mut authentication_keys = rxdak::ForRrd::new(ak, pid);
  511. let (local_challenge, local_etk, outgoing_frame) = Self::create_hello(&mut authentication_keys);
  512. // Create path
  513. let path = Self {
  514. pid,
  515. decoder: FrameDecoder::new(vec![]),
  516. state: RrdPathState::AwaitingAuthHello {
  517. authentication_keys,
  518. sent_at: Instant::now(),
  519. local_challenge,
  520. local_etk,
  521. },
  522. };
  523. (path, outgoing_frame)
  524. }
  525. fn process_frame(
  526. &mut self,
  527. ctx: &Context,
  528. mut incoming_frame: RendezvousIncomingFrame,
  529. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  530. trace!(state = ?self.state, ?incoming_frame, "Processing frame");
  531. if let RrdPathState::Nominated { transport_keys } = &mut self.state {
  532. // Handle `Nominated` state where the transport can be used by the ULP.
  533. Self::handle_ulp_data(transport_keys, incoming_frame).map(|incoming_ulp_data| {
  534. RendezvousPathProcessResult {
  535. state_update: None,
  536. outgoing_frame: None,
  537. incoming_ulp_data: Some(incoming_ulp_data),
  538. }
  539. })
  540. } else {
  541. // Handle `Closed` state
  542. if let RrdPathState::Closed = &self.state {
  543. return Err(RendezvousProtocolError::PathClosed(self.pid));
  544. }
  545. // Handle states that should immediately transition into another state
  546. //
  547. // IMPORTANT: All match arms must be infallible!
  548. match core::mem::replace(&mut self.state, RrdPathState::Invalid) {
  549. RrdPathState::AwaitingAuthHello {
  550. mut authentication_keys,
  551. sent_at,
  552. local_challenge,
  553. local_etk,
  554. } => {
  555. // Calculate RTT
  556. let measured_rtt = Instant::now().duration_since(sent_at);
  557. // Handle `RidToRrd.AuthHello` and update state.
  558. Self::handle_auth_hello(
  559. &mut authentication_keys,
  560. &local_challenge,
  561. local_etk,
  562. &mut incoming_frame,
  563. )
  564. .map(|(shared_etk, outgoing_frame)| {
  565. let (transport_keys, rph) =
  566. rxdtk::ForRrd::new(&ctx.ak, authentication_keys, shared_etk);
  567. (
  568. RrdPathState::AwaitingNominate { transport_keys, rph },
  569. RendezvousPathProcessResult {
  570. state_update: Some(RendezvousPathStateUpdate::AwaitingNominate {
  571. measured_rtt,
  572. }),
  573. outgoing_frame: Some(outgoing_frame),
  574. incoming_ulp_data: None,
  575. },
  576. )
  577. })
  578. },
  579. RrdPathState::AwaitingNominate {
  580. mut transport_keys,
  581. rph,
  582. } => {
  583. // Check if the remote side is allowed to `Nominate`.
  584. if ctx.is_nominator {
  585. return Err(RendezvousProtocolError::UnexpectedFrame);
  586. }
  587. // Handle `Nominate` and update state.
  588. Self::handle_nominate(&mut transport_keys, &mut incoming_frame).map(|()| {
  589. (
  590. RrdPathState::Nominated { transport_keys },
  591. RendezvousPathProcessResult {
  592. state_update: Some(RendezvousPathStateUpdate::Nominated { rph }),
  593. outgoing_frame: None,
  594. incoming_ulp_data: None,
  595. },
  596. )
  597. })
  598. },
  599. // States that must have been covered by code above
  600. RrdPathState::Invalid | RrdPathState::Nominated { .. } | RrdPathState::Closed => {
  601. unreachable!("State should have been handled")
  602. },
  603. }
  604. .map(|(state, result)| {
  605. self.state = state;
  606. debug!(state = ?self.state, "Changed state");
  607. result
  608. })
  609. }
  610. .map_err(|error| {
  611. self.state = RrdPathState::Closed;
  612. warn!(?error, state = ?self.state, "Closed due to error");
  613. error
  614. })
  615. }
  616. fn nominate(&mut self) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  617. // Ensure we are in the correct state to nominate
  618. if !matches!(&self.state, RrdPathState::AwaitingNominate { .. }) {
  619. return Err(RendezvousProtocolError::InvalidStateForNomination(
  620. self.state.variant_name(),
  621. ));
  622. }
  623. // Nominate
  624. if let RrdPathState::AwaitingNominate {
  625. mut transport_keys,
  626. rph,
  627. } = core::mem::replace(&mut self.state, RrdPathState::Invalid)
  628. {
  629. Self::create_nominate(&mut transport_keys).map(|outgoing_frame| {
  630. (
  631. RrdPathState::Nominated { transport_keys },
  632. RendezvousPathProcessResult {
  633. state_update: Some(RendezvousPathStateUpdate::Nominated { rph }),
  634. outgoing_frame: Some(outgoing_frame),
  635. incoming_ulp_data: None,
  636. },
  637. )
  638. })
  639. } else {
  640. unreachable!("Expected AwaitingNominate state")
  641. }
  642. .map(|(state, result)| {
  643. self.state = state;
  644. debug!(state = ?self.state, "Changed state");
  645. result
  646. })
  647. .map_err(|error| {
  648. self.state = RrdPathState::Closed;
  649. warn!(?error, state = ?self.state, "Closed due to error");
  650. error
  651. })
  652. }
  653. fn create_ulp_frame(
  654. &mut self,
  655. outgoing_data: Vec<u8>,
  656. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  657. match &mut self.state {
  658. RrdPathState::Nominated { transport_keys } => {
  659. Self::create_ulp_data(transport_keys, outgoing_data).map(|outgoing_frame| {
  660. RendezvousPathProcessResult {
  661. state_update: None,
  662. outgoing_frame: Some(outgoing_frame),
  663. incoming_ulp_data: None,
  664. }
  665. })
  666. },
  667. _ => Err(RendezvousProtocolError::NominationRequired),
  668. }
  669. }
  670. fn create_hello(
  671. keys: &mut rxdak::ForRrd,
  672. ) -> (Challenge, x25519::EphemeralSecret, RendezvousOutgoingFrame) {
  673. // Generate a challenge
  674. let local_challenge = Challenge::random();
  675. // Generate local part of ETK
  676. let local_etk = x25519::EphemeralSecret::random();
  677. // Encode and encrypt `RrdToRid.Hello`
  678. let local_hello = protobuf::handshake::rrd_to_rid::Hello {
  679. challenge: local_challenge.0.to_vec(),
  680. etk: x25519::PublicKey::from(&local_etk).as_bytes().to_vec(),
  681. };
  682. let mut outgoing_data = local_hello.encode_to_vec();
  683. keys.rrdak
  684. .encrypt(&mut outgoing_data)
  685. .expect("Encrypting initial RrdToRid.Hello should work");
  686. (local_challenge, local_etk, RendezvousOutgoingFrame(outgoing_data))
  687. }
  688. fn handle_auth_hello(
  689. keys: &mut rxdak::ForRrd,
  690. local_challenge: &Challenge,
  691. local_etk: x25519::EphemeralSecret,
  692. incoming_frame: &mut RendezvousIncomingFrame,
  693. ) -> Result<(EphemeralTransportKey, RendezvousOutgoingFrame), RendezvousProtocolError> {
  694. // Decrypt and decode into a `RidToRrd.AuthHello`
  695. let (remote_challenge, remote_etk) = {
  696. keys.ridak.decrypt(&mut incoming_frame.0)?;
  697. let remote_auth_hello =
  698. protobuf::handshake::rid_to_rrd::AuthHello::decode(incoming_frame.0.as_ref())?;
  699. // Validate `RidToRrd.AuthHello`
  700. if remote_auth_hello.response != local_challenge.0 {
  701. return Err(RendezvousProtocolError::InvalidRidAuthHelloMessage(format!(
  702. "Challenge response of {} bytes does not match",
  703. remote_auth_hello.response.len()
  704. )));
  705. }
  706. let remote_challenge =
  707. Challenge(remote_auth_hello.challenge.as_slice().try_into().map_err(|_| {
  708. RendezvousProtocolError::InvalidRrdHelloMessage(format!(
  709. "Expected 16 challenge bytes, got {}",
  710. remote_auth_hello.challenge.len()
  711. ))
  712. })?);
  713. let remote_etk = x25519::PublicKey::from(
  714. <[u8; 32]>::try_from(remote_auth_hello.etk.as_ref()).map_err(|_| {
  715. RendezvousProtocolError::InvalidRidAuthHelloMessage(format!(
  716. "Invalid remote ETK, got {} bytes",
  717. remote_auth_hello.etk.len()
  718. ))
  719. })?,
  720. );
  721. (remote_challenge, remote_etk)
  722. };
  723. // Encode and encrypt `RrdToRid.Auth`
  724. let (shared_etk, outgoing_frame) = {
  725. // Encode and encrypt `RrdToRid.Auth`
  726. let local_auth = protobuf::handshake::rrd_to_rid::Auth {
  727. response: remote_challenge.0.to_vec(),
  728. };
  729. let mut outgoing_data = local_auth.encode_to_vec();
  730. keys.rrdak.encrypt(&mut outgoing_data)?;
  731. // Derive ETK
  732. let shared_etk = EphemeralTransportKey(
  733. local_etk
  734. .diffie_hellman(&remote_etk)
  735. .ok_or(RendezvousProtocolError::NonContributoryPublicKey)?
  736. .into(),
  737. );
  738. (shared_etk, RendezvousOutgoingFrame(outgoing_data))
  739. };
  740. // Done
  741. Ok((shared_etk, outgoing_frame))
  742. }
  743. fn create_nominate(keys: &mut rxdtk::ForRrd) -> Result<RendezvousOutgoingFrame, RendezvousProtocolError> {
  744. // Encode and encrypt a `Nominate`
  745. let local_nominate = protobuf::Nominate {};
  746. let mut outgoing_data = local_nominate.encode_to_vec();
  747. keys.rrdtk.encrypt(&mut outgoing_data)?;
  748. Ok(RendezvousOutgoingFrame(outgoing_data))
  749. }
  750. fn handle_nominate(
  751. keys: &mut rxdtk::ForRrd,
  752. incoming_frame: &mut RendezvousIncomingFrame,
  753. ) -> Result<(), RendezvousProtocolError> {
  754. // Decrypt and decode into a `Nominate`
  755. keys.ridtk.decrypt(&mut incoming_frame.0)?;
  756. let _ = protobuf::Nominate::decode(incoming_frame.0.as_ref())?;
  757. Ok(())
  758. }
  759. fn create_ulp_data(
  760. keys: &mut rxdtk::ForRrd,
  761. mut outgoing_data: Vec<u8>,
  762. ) -> Result<RendezvousOutgoingFrame, RendezvousProtocolError> {
  763. // Encode and encrypt ULP data
  764. keys.rrdtk.encrypt(&mut outgoing_data)?;
  765. Ok(RendezvousOutgoingFrame(outgoing_data))
  766. }
  767. fn handle_ulp_data(
  768. keys: &mut rxdtk::ForRrd,
  769. mut incoming_frame: RendezvousIncomingFrame,
  770. ) -> Result<Vec<u8>, RendezvousProtocolError> {
  771. // Decrypt and decode ULP data
  772. keys.ridtk.decrypt(&mut incoming_frame.0)?;
  773. Ok(incoming_frame.0)
  774. }
  775. }
  776. trait Path: Send {
  777. fn add_chunks(&mut self, chunks: &[&[u8]]) -> Result<(), RendezvousProtocolError>;
  778. fn process_frame(
  779. &mut self,
  780. ctx: &Context,
  781. ) -> Result<Option<RendezvousPathProcessResult>, RendezvousProtocolError>;
  782. fn nominate(&mut self) -> Result<RendezvousPathProcessResult, RendezvousProtocolError>;
  783. fn create_ulp_frame(
  784. &mut self,
  785. outgoing_data: Vec<u8>,
  786. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError>;
  787. }
  788. #[duplicate_item(
  789. path_type path_state_type;
  790. [ RidPath ] [ RidPathState ];
  791. [ RrdPath ] [ RrdPathState ];
  792. )]
  793. impl Path for path_type {
  794. #[inline]
  795. fn add_chunks(&mut self, chunks: &[&[u8]]) -> Result<(), RendezvousProtocolError> {
  796. let length = self.decoder.add_chunks(chunks);
  797. // Check if the frame exceeds the maximum supported length of this state
  798. let max_length = if matches!(&self.state, path_state_type::Nominated { .. }) {
  799. FrameDecoder::MAX_LENGTH_AFTER_NOMINATION
  800. } else {
  801. FrameDecoder::MAX_LENGTH_BEFORE_NOMINATION
  802. };
  803. if length > max_length {
  804. return Err(RendezvousProtocolError::OversizedFrame(length));
  805. }
  806. Ok(())
  807. }
  808. fn process_frame(
  809. &mut self,
  810. ctx: &Context,
  811. ) -> Result<Option<RendezvousPathProcessResult>, RendezvousProtocolError> {
  812. self.decoder
  813. .next_frame_and_then(|incoming_frame| RendezvousIncomingFrame(incoming_frame.to_vec()))
  814. .map(|incoming_frame| self.process_frame(ctx, incoming_frame))
  815. .transpose()
  816. }
  817. #[inline]
  818. fn nominate(&mut self) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  819. self.nominate()
  820. }
  821. #[inline]
  822. fn create_ulp_frame(
  823. &mut self,
  824. outgoing_data: Vec<u8>,
  825. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  826. self.create_ulp_frame(outgoing_data)
  827. }
  828. }
  829. /// Internal protocol state.
  830. #[derive(DebugVariantNames, VariantNames)]
  831. enum ProtocolState {
  832. /// The paths are currently racing, meaning we are in the _handshake and nomination phase_.
  833. RacingPaths(HashMap<u32, Box<dyn Path>>),
  834. /// One path has been nominated, all other paths have been discarded, meaning we are in the
  835. /// _ULP phase_.
  836. Nominated { pid: u32, path: Box<dyn Path> },
  837. }
  838. /// Connection Rendezvous Protocol state machine.
  839. ///
  840. /// The protocol state machine can be constructed from a formerly exchanged a `RendezvousInit` and
  841. /// the associated roles by using [`RendezvousProtocol::new_as_rid`] and
  842. /// [`RendezvousProtocol::new_as_rrd`].
  843. ///
  844. /// Any interaction with the protocol state machine that changes the internal state will yield a
  845. /// [`RendezvousPathProcessResult`] that must be handled according to its documentation.
  846. ///
  847. /// The protocol goes through exactly two phases:
  848. ///
  849. /// - The _handshake and nomination phase_ where all paths are racing the handshake simultaneously until one
  850. /// has been nominated by the nominator.
  851. /// - The _ULP phase_ where ULP frames can be exchanged on the nominated path.
  852. ///
  853. /// The following steps are defined as the _Path Awaiting Nomination Steps_:
  854. ///
  855. /// 1. Let `path` be the associated path.
  856. /// 2. If the protocol did not take the role of the nominator, abort the protocol due to an error and abort
  857. /// these steps.
  858. /// 3. If `path` is the only path, run [`RendezvousProtocol::nominate_path`] for `path` and abort these steps.
  859. /// 4. (Unreachable / TODO(LIB-10): As of today, only one path is expected to be used.)
  860. ///
  861. /// When a path closed, run the following steps:
  862. ///
  863. /// 1. Let `path` be the path that closed (initiated locally or remotely).
  864. /// 2. If `path` is marked as _nominated_, abort the protocol normally with any available close information
  865. /// and abort these steps.
  866. /// 3. If `path` is marked as _disregarded_, log a notice and abort these steps.
  867. /// 4. If `path` is the last path that closed (i.e. all other paths already closed or there are no other
  868. /// paths), log a warning that all paths closed before nomination, abort the protocol normally with any
  869. /// available close information and abort these steps.
  870. /// 5. Log a warning that `path` closed before nomination.
  871. ///
  872. /// When receiving data on a path:
  873. ///
  874. /// 1. Run [`RendezvousProtocol::add_chunks`] with the respective path's PID.
  875. /// 2. In a loop, run [`RendezvousProtocol::process_frame`] with the respective path's PID and handle the
  876. /// result until it no longer produces a [`RendezvousPathProcessResult`].
  877. ///
  878. /// When the protocol is being aborted:
  879. ///
  880. /// 1. Let `cause` be an error or any information associated to normal closure.
  881. /// 2. Log the protocol abort due to `cause` as a notice or an error respectively.
  882. /// 3. Tear down the protocol state machine.
  883. /// 4. Close all remaining paths exceptionally.
  884. /// 5. Hand off `cause` to the ULP.
  885. #[derive(Educe)]
  886. #[educe(Debug)]
  887. pub struct RendezvousProtocol {
  888. #[educe(Debug(ignore))]
  889. ctx: Context,
  890. state: ProtocolState,
  891. }
  892. // TODO(LIB-11): Add construction of the `RendezvousInit` from the paths here.
  893. impl RendezvousProtocol {
  894. /// Create a new Connection Rendezvous Protocol as the Rendezvous Initiator Device (RID) from a
  895. /// formerly exchanged `RendezvousInit`.
  896. ///
  897. /// `pids` must contain the set of available pre-initiated paths with their associated Path IDs
  898. /// (PID).
  899. ///
  900. /// Returns the protocol state machine instance.
  901. #[tracing::instrument(skip_all, fields(?is_nominator, ?pids))]
  902. pub fn new_as_rid(is_nominator: bool, ak: RendezvousAuthenticationKey, pids: &[u32]) -> Self {
  903. debug!("Creating D2D rendezvous protocol");
  904. let ctx = Context::new(is_nominator, ak);
  905. // Create paths
  906. let racing_paths = pids
  907. .iter()
  908. .map(|pid| {
  909. let path = RidPath::new(&ctx.ak, *pid);
  910. (*pid, Box::new(path) as Box<dyn Path>)
  911. })
  912. .collect();
  913. // Create protocol
  914. Self {
  915. ctx,
  916. state: ProtocolState::RacingPaths(racing_paths),
  917. }
  918. }
  919. /// Create a new Connection Rendezvous Protocol as the Rendezvous Responder Device (RRD) from a
  920. /// formerly exchanged `RendezvousInit`.
  921. ///
  922. /// `pids` must contain the set of available pre-initiated paths with their associated Path IDs
  923. /// (PID).
  924. ///
  925. /// Returns a tuple of the protocol state machine instance and a list of PIDs and outgoing
  926. /// frames to be enqueued on the respective paths immediately.
  927. #[tracing::instrument(skip_all, fields(?is_nominator, ?pids))]
  928. pub fn new_as_rrd(
  929. is_nominator: bool,
  930. ak: RendezvousAuthenticationKey,
  931. pids: &[u32],
  932. ) -> (Self, Vec<(u32, RendezvousOutgoingFrame)>) {
  933. debug!("Creating protocol");
  934. let ctx = Context::new(is_nominator, ak);
  935. let mut outgoing_frames = vec![];
  936. // Create paths
  937. let racing_paths = pids
  938. .iter()
  939. .map(|pid| {
  940. let (path, outgoing_frame) = RrdPath::new(&ctx.ak, *pid);
  941. outgoing_frames.push((*pid, outgoing_frame));
  942. (*pid, Box::new(path) as Box<dyn Path>)
  943. })
  944. .collect();
  945. // Create protocol
  946. let protocol = Self {
  947. ctx,
  948. state: ProtocolState::RacingPaths(racing_paths),
  949. };
  950. (protocol, outgoing_frames)
  951. }
  952. /// Return whether the protocol took the role of the nominator.
  953. #[must_use]
  954. pub const fn is_nominator(&self) -> bool {
  955. self.ctx.is_nominator
  956. }
  957. /// Return the nominated path's PID, if available.
  958. #[must_use]
  959. pub const fn nominated_path(&self) -> Option<u32> {
  960. if let ProtocolState::Nominated { pid, .. } = &self.state {
  961. Some(*pid)
  962. } else {
  963. None
  964. }
  965. }
  966. /// Add chunks received on the specified path. The chunks may or may not contain complete frames
  967. /// or even contain multiple complete frames.
  968. ///
  969. /// # Errors
  970. ///
  971. /// Returns [`RendezvousProtocolError::UnknownOrDroppedPath`] if the path associated to `pid`
  972. /// could not be found.
  973. #[tracing::instrument(skip_all, fields(
  974. ?self, ?pid,
  975. chunks_byte_length = chunks.iter().map(|chunk| chunk.len()).sum::<usize>(),
  976. ))]
  977. pub fn add_chunks(&mut self, pid: u32, chunks: &[&[u8]]) -> Result<(), RendezvousProtocolError> {
  978. let path = Self::lookup_path(&mut self.state, pid)?;
  979. path.add_chunks(chunks)
  980. }
  981. /// Process any available buffered complete frame for the specified path.
  982. ///
  983. /// # Errors
  984. ///
  985. /// Returns [`RendezvousProtocolError`] for a plethora of reasons, e.g. if the path associated
  986. /// to `pid` could not be found, an incoming frame could not be decoded or decrypted, or an
  987. /// unexpected message was received, or, as a response to it, another outgoing frame could not
  988. /// be encrypted.
  989. #[tracing::instrument(skip_all, fields(?self, ?pid))]
  990. pub fn process_frame(
  991. &mut self,
  992. pid: u32,
  993. ) -> Result<Option<RendezvousPathProcessResult>, RendezvousProtocolError> {
  994. let path = Self::lookup_path(&mut self.state, pid)?;
  995. // Decode and process the next frame, if any can be decoded
  996. let result = path.process_frame(&self.ctx)?;
  997. trace!(?result, "Processed frame");
  998. let Some(result) = result else {
  999. return Ok(result);
  1000. };
  1001. // Update state if the path was nominated and we are still racing paths
  1002. if let (ProtocolState::RacingPaths(racing_paths), Some(RendezvousPathStateUpdate::Nominated { .. })) =
  1003. (&mut self.state, &result.state_update)
  1004. {
  1005. // Nominate the path
  1006. let path = racing_paths
  1007. .remove(&pid)
  1008. .ok_or(RendezvousProtocolError::UnknownOrDroppedPath(pid))?;
  1009. debug!(
  1010. dropped_pids = ?racing_paths.keys(),
  1011. "Remote nominated, dropping all other paths"
  1012. );
  1013. self.state = ProtocolState::Nominated { pid, path };
  1014. }
  1015. // Return the result
  1016. Ok(Some(result))
  1017. }
  1018. /// Nominate a path.
  1019. ///
  1020. /// # Errors
  1021. ///
  1022. /// Returns [`RendezvousProtocolError`] if the protocol did not take the role of the nominator,
  1023. /// the path associated to `pid` could not be found, the path is not ready to be nominated or
  1024. /// nomination already happened.
  1025. #[tracing::instrument(skip_all, fields(?self, ?pid))]
  1026. pub fn nominate_path(
  1027. &mut self,
  1028. pid: u32,
  1029. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  1030. // Ensure we're allowed to nominate
  1031. if !self.ctx.is_nominator {
  1032. return Err(RendezvousProtocolError::NominateNotAllowed);
  1033. }
  1034. // Nominate if the paths are still racing
  1035. match &mut self.state {
  1036. ProtocolState::RacingPaths(racing_paths) => {
  1037. // Attempt to nominate the path
  1038. let mut path = racing_paths
  1039. .remove(&pid)
  1040. .ok_or(RendezvousProtocolError::UnknownOrDroppedPath(pid))?;
  1041. let result = path.nominate()?;
  1042. debug!(
  1043. dropped_pids = ?racing_paths.keys(),
  1044. "Local nominated, dropping all other paths"
  1045. );
  1046. self.state = ProtocolState::Nominated { pid, path };
  1047. Ok(result)
  1048. },
  1049. ProtocolState::Nominated { pid, .. } => {
  1050. // Nomination already happened
  1051. Err(RendezvousProtocolError::NominationAlreadyDone(*pid))
  1052. },
  1053. }
  1054. }
  1055. /// Create a ULP frame to be encrypted and sent as an outgoing frame on the nominated path.
  1056. ///
  1057. /// # Errors
  1058. ///
  1059. /// Returns [`RendezvousProtocolError`] if nomination of a path is still pending or the ULP
  1060. /// frame could not be encrypted or encoded.
  1061. #[tracing::instrument(skip_all, fields(?self, outgoing_data_length = outgoing_data.len()))]
  1062. pub fn create_ulp_frame(
  1063. &mut self,
  1064. outgoing_data: Vec<u8>,
  1065. ) -> Result<RendezvousPathProcessResult, RendezvousProtocolError> {
  1066. match &mut self.state {
  1067. ProtocolState::RacingPaths(..) => Err(RendezvousProtocolError::NominationRequired),
  1068. ProtocolState::Nominated { path, .. } => path.create_ulp_frame(outgoing_data),
  1069. }
  1070. }
  1071. fn lookup_path(
  1072. state: &mut ProtocolState,
  1073. pid: u32,
  1074. ) -> Result<&mut Box<dyn Path>, RendezvousProtocolError> {
  1075. // Lookup path based on the current state.
  1076. let path = match state {
  1077. // The nomination race is still ongoing. Lookup the path by its PID.
  1078. ProtocolState::RacingPaths(racing_paths) => racing_paths
  1079. .get_mut(&pid)
  1080. .ok_or(RendezvousProtocolError::UnknownOrDroppedPath(pid))?,
  1081. // There's only one nominated path. Ensure it's the correct one.
  1082. ProtocolState::Nominated {
  1083. pid: nominated_pid,
  1084. path,
  1085. } => {
  1086. if pid != *nominated_pid {
  1087. return Err(RendezvousProtocolError::UnknownOrDroppedPath(pid));
  1088. }
  1089. path
  1090. },
  1091. };
  1092. Ok(path)
  1093. }
  1094. }