1use crate::ap::authenticator::Authenticator;
6use crate::ap::event::*;
7use crate::ap::remote_client::RemoteClient;
8use crate::ap::{Context, RsnCfg, aid};
9use anyhow::{ensure, format_err};
10
11use fuchsia_sync::Mutex;
12use ieee80211::MacAddr;
13use log::error;
14use std::sync::Arc;
15use wlan_common::ie::SupportedRate;
16use wlan_common::ie::rsn::rsne;
17use wlan_common::mac::{Aid, CapabilityInfo};
18use wlan_common::timer::EventHandle;
19use wlan_rsn::gtk::GtkProvider;
20use wlan_rsn::nonce::NonceReader;
21use wlan_rsn::rsna::{SecAssocStatus, SecAssocUpdate, UpdateSink};
22use wlan_rsn::{NegotiatedProtection, ProtectionInfo};
23use wlan_statemachine::*;
24use {fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme};
25
26const ASSOCIATION_TIMEOUT_SECONDS: i64 = 300;
29
30pub struct Authenticating;
37
38impl Authenticating {
39 fn handle_auth_ind(
47 &self,
48 r_sta: &mut RemoteClient,
49 ctx: &mut Context,
50 auth_type: fidl_mlme::AuthenticationTypes,
51 ) -> Result<EventHandle, anyhow::Error> {
52 if auth_type != fidl_mlme::AuthenticationTypes::OpenSystem {
54 return Err(format_err!("unsupported authentication type: {:?}", auth_type));
55 }
56
57 let event = ClientEvent::AssociationTimeout;
58 let timeout_event = r_sta.schedule_at(
59 ctx,
60 zx::MonotonicInstant::after(zx::MonotonicDuration::from_seconds(
61 ASSOCIATION_TIMEOUT_SECONDS,
62 )),
63 event,
64 );
65
66 Ok(timeout_event)
67 }
68}
69
70fn new_authenticator_from_rsne(
72 device_addr: MacAddr,
73 client_addr: MacAddr,
74 s_rsne_bytes: &[u8],
75 a_rsn: &RsnCfg,
76) -> Result<Box<dyn Authenticator>, anyhow::Error> {
77 let (_, s_rsne) =
78 rsne::from_bytes(s_rsne_bytes).map_err(|e| format_err!("failed to parse RSNE: {:?}", e))?;
79 ensure!(s_rsne.is_valid_subset_of(&a_rsn.rsne)?, "incompatible client RSNE");
80
81 let nonce_reader = NonceReader::new(&device_addr)?;
82
83 let gtk_provider =
91 GtkProvider::new(NegotiatedProtection::from_rsne(&s_rsne)?.group_data, 1, 0)?;
92
93 Ok(Box::new(wlan_rsn::Authenticator::new_wpa2psk_ccmp128(
94 nonce_reader,
97 Arc::new(Mutex::new(gtk_provider)),
98 a_rsn.psk.clone(),
99 client_addr,
100 ProtectionInfo::Rsne(s_rsne),
101 device_addr,
102 ProtectionInfo::Rsne(a_rsn.rsne.clone()),
103 )?))
104}
105
106pub struct Authenticated {
112 _timeout_event: EventHandle,
113}
114
115struct AssociationError {
118 error: anyhow::Error,
119 result_code: fidl_mlme::AssociateResultCode,
120 reason_code: fidl_ieee80211::ReasonCode,
121}
122
123struct Association {
125 aid: Aid,
126 capabilities: CapabilityInfo,
127 rates: Vec<SupportedRate>,
128 rsna_link_state: Option<RsnaLinkState>,
129}
130
131impl Authenticated {
132 #[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
142 fn handle_assoc_ind(
143 &self,
144 r_sta: &mut RemoteClient,
145 ctx: &mut Context,
146 aid_map: &mut aid::Map,
147 client_capablities: u16,
148 client_rates: Vec<SupportedRate>,
149 rsn_cfg: &Option<RsnCfg>,
150 s_rsne: Option<Vec<u8>>,
151 ) -> Result<Association, AssociationError> {
152 let rsna_link_state = match (s_rsne.as_ref(), rsn_cfg) {
153 (Some(s_rsne_bytes), Some(a_rsn)) => {
154 let authenticator = new_authenticator_from_rsne(
155 ctx.device_info.sta_addr.into(),
156 r_sta.addr,
157 s_rsne_bytes,
158 a_rsn,
159 )
160 .map_err(|error| AssociationError {
161 error,
162 result_code: fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch,
163 reason_code: fidl_ieee80211::ReasonCode::Ieee8021XAuthFailed,
164 })?;
165
166 Some(RsnaLinkState::new(authenticator))
167 }
168 (None, None) => None,
169 _ => {
170 return Err(AssociationError {
171 error: format_err!("unexpected RSN element: {:?}", s_rsne),
172 result_code: fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch,
173 reason_code: fidl_ieee80211::ReasonCode::ReasonInvalidElement,
174 });
175 }
176 };
177
178 let aid = aid_map.assign_aid().map_err(|error| AssociationError {
179 error,
180 result_code: fidl_mlme::AssociateResultCode::RefusedReasonUnspecified,
181 reason_code: fidl_ieee80211::ReasonCode::UnspecifiedReason,
182 })?;
183
184 let (capabilities, rates) = (CapabilityInfo(client_capablities), client_rates);
187 Ok(Association {
188 capabilities: capabilities
189 .with_privacy(rsna_link_state.is_some()),
193 aid,
194 rates,
195 rsna_link_state,
196 })
197 }
198}
199
200#[derive(Debug)]
203struct RsnaLinkState {
204 authenticator: Box<dyn Authenticator>,
205
206 last_key_frame: Option<eapol::KeyFrameBuf>,
209
210 request_attempts: usize,
211 request_timeout: Option<EventHandle>,
212 negotiation_timeout: Option<EventHandle>,
213}
214
215pub const RSNA_NEGOTIATION_REQUEST_MAX_ATTEMPTS: usize = 4;
216pub const RSNA_NEGOTIATION_REQUEST_TIMEOUT_SECONDS: i64 = 1;
217pub const RSNA_NEGOTIATION_TIMEOUT_SECONDS: i64 = 5;
218
219impl RsnaLinkState {
220 fn new(authenticator: Box<dyn Authenticator>) -> Self {
221 Self {
222 authenticator,
223 last_key_frame: None,
224 request_attempts: 0,
225 request_timeout: None,
226 negotiation_timeout: None,
227 }
228 }
229
230 fn initiate_key_exchange(
234 &mut self,
235 r_sta: &mut RemoteClient,
236 ctx: &mut Context,
237 ) -> Result<(), anyhow::Error> {
238 let mut update_sink = vec![];
239 self.authenticator.reset();
240 self.authenticator.initiate(&mut update_sink)?;
241 self.process_authenticator_updates(r_sta, ctx, &update_sink);
242
243 if self.last_key_frame.is_none() {
244 return Err(format_err!("no key frame was produced on authenticator initiation"));
245 }
246
247 self.negotiation_timeout = Some(r_sta.schedule_at(
248 ctx,
249 zx::MonotonicInstant::after(zx::MonotonicDuration::from_seconds(
250 RSNA_NEGOTIATION_TIMEOUT_SECONDS,
251 )),
252 ClientEvent::RsnaTimeout(RsnaTimeout::Negotiation),
253 ));
254
255 self.reschedule_request_timeout(r_sta, ctx);
256 Ok(())
257 }
258
259 fn reschedule_request_timeout(&mut self, r_sta: &mut RemoteClient, ctx: &mut Context) {
260 self.request_timeout = Some(r_sta.schedule_at(
261 ctx,
262 zx::MonotonicInstant::after(zx::MonotonicDuration::from_seconds(
263 RSNA_NEGOTIATION_REQUEST_TIMEOUT_SECONDS,
264 )),
265 ClientEvent::RsnaTimeout(RsnaTimeout::Request),
266 ));
267 }
268
269 fn handle_rsna_timeout(
270 &mut self,
271 r_sta: &mut RemoteClient,
272 ctx: &mut Context,
273 timeout_type: RsnaTimeout,
274 ) -> Result<(), RsnaNegotiationError> {
275 match timeout_type {
276 RsnaTimeout::Request => self.handle_rsna_request_timeout(r_sta, ctx),
277 RsnaTimeout::Negotiation => self.handle_rsna_negotiation_timeout(),
278 }
279 }
280
281 fn handle_rsna_request_timeout(
282 &mut self,
283 r_sta: &mut RemoteClient,
284 ctx: &mut Context,
285 ) -> Result<(), RsnaNegotiationError> {
286 self.request_timeout = None;
287
288 self.request_attempts += 1;
289 if self.request_attempts >= RSNA_NEGOTIATION_REQUEST_MAX_ATTEMPTS {
290 return Err(RsnaNegotiationError::Timeout);
291 }
292
293 let frame = self
294 .last_key_frame
295 .as_ref()
296 .ok_or(RsnaNegotiationError::Error(format_err!("no key frame available to resend?")))?;
297
298 r_sta.send_eapol_req(ctx, frame.clone());
299 self.reschedule_request_timeout(r_sta, ctx);
300 Ok(())
301 }
302
303 fn handle_rsna_negotiation_timeout(&mut self) -> Result<(), RsnaNegotiationError> {
304 self.negotiation_timeout = None;
305 Err(RsnaNegotiationError::Timeout)
306 }
307
308 fn process_authenticator_updates(
310 &mut self,
311 r_sta: &mut RemoteClient,
312 ctx: &mut Context,
313 update_sink: &UpdateSink,
314 ) {
315 for update in update_sink {
316 match update {
317 SecAssocUpdate::TxEapolKeyFrame { frame, .. } => {
318 r_sta.send_eapol_req(ctx, frame.clone());
319 self.last_key_frame = Some(frame.clone());
320 }
321 SecAssocUpdate::Key(key) => r_sta.send_key(ctx, key),
322 SecAssocUpdate::Status(status) => {
323 if *status == SecAssocStatus::EssSaEstablished {
324 r_sta.send_set_controlled_port_req(
325 ctx,
326 fidl_mlme::ControlledPortState::Open,
327 );
328
329 self.last_key_frame = None;
332 self.request_timeout = None;
333 self.negotiation_timeout = None;
334 }
335 }
336 update => error!("Unhandled association update: {:?}", update),
337 }
338 }
339 }
340
341 fn handle_eapol_frame(
343 &mut self,
344 r_sta: &mut RemoteClient,
345 ctx: &mut Context,
346 data: &[u8],
347 ) -> Result<(), anyhow::Error> {
348 self.request_attempts = 0;
349
350 let authenticator = self.authenticator.as_mut();
351 let key_frame = eapol::KeyFrameRx::parse(
352 authenticator.get_negotiated_protection().mic_size as usize,
353 data,
354 )?;
355
356 let mut update_sink = vec![];
357 authenticator.on_eapol_frame(&mut update_sink, eapol::Frame::Key(key_frame))?;
359 self.process_authenticator_updates(r_sta, ctx, &update_sink);
360 Ok(())
361 }
362
363 fn handle_eapol_conf(
364 &mut self,
365 r_sta: &mut RemoteClient,
366 ctx: &mut Context,
367 result: fidl_mlme::EapolResultCode,
368 ) -> Result<(), anyhow::Error> {
369 let authenticator = self.authenticator.as_mut();
370
371 let mut update_sink = vec![];
372 authenticator.on_eapol_conf(&mut update_sink, result)?;
373 self.process_authenticator_updates(r_sta, ctx, &update_sink);
374 Ok(())
375 }
376}
377
378pub struct Associated {
381 aid: Aid,
382 rsna_link_state: Option<RsnaLinkState>,
383}
384
385enum RsnaNegotiationError {
386 Error(anyhow::Error),
387 Timeout,
388}
389
390impl Associated {
391 fn aid(&self) -> Aid {
392 self.aid
393 }
394
395 fn handle_rsna_timeout(
398 &mut self,
399 r_sta: &mut RemoteClient,
400 ctx: &mut Context,
401 timeout_type: RsnaTimeout,
402 ) -> Result<(), RsnaNegotiationError> {
403 match self.rsna_link_state.as_mut() {
404 Some(rsna_link_state) => rsna_link_state.handle_rsna_timeout(r_sta, ctx, timeout_type),
405 None => Ok(()),
406 }
407 }
408
409 fn handle_eapol_ind(
412 &mut self,
413 r_sta: &mut RemoteClient,
414 ctx: &mut Context,
415 data: &[u8],
416 ) -> Result<(), anyhow::Error> {
417 match self.rsna_link_state.as_mut() {
418 Some(rsna_link_state) => rsna_link_state.handle_eapol_frame(r_sta, ctx, data),
419 None => Err(format_err!("received EAPoL indication without RSNA link state")),
420 }
421 }
422
423 fn handle_eapol_conf(
426 &mut self,
427 r_sta: &mut RemoteClient,
428 ctx: &mut Context,
429 result: fidl_mlme::EapolResultCode,
430 ) -> Result<(), anyhow::Error> {
431 match self.rsna_link_state.as_mut() {
432 Some(rsna_link_state) => rsna_link_state.handle_eapol_conf(r_sta, ctx, result),
433 None => Err(format_err!("received EAPoL confirm without RSNA link state")),
434 }
435 }
436
437 fn handle_disassoc_ind(
440 &self,
441 r_sta: &mut RemoteClient,
442 ctx: &mut Context,
443 aid_map: &mut aid::Map,
444 ) -> EventHandle {
445 aid_map.release_aid(self.aid);
446 let event = ClientEvent::AssociationTimeout;
447 r_sta.schedule_at(
448 ctx,
449 zx::MonotonicInstant::after(zx::MonotonicDuration::from_seconds(
450 ASSOCIATION_TIMEOUT_SECONDS,
451 )),
452 event,
453 )
454 }
455}
456
457statemachine!(
458 pub enum States,
459
460 () => Authenticating,
461 Authenticating => Authenticated,
462 Authenticated => Associated,
463
464 Associated => Authenticated,
465 Authenticated => Authenticating,
466
467 Associated => Authenticating,
469);
470
471impl States {
473 pub fn new_initial() -> States {
474 States::from(State::new(Authenticating))
475 }
476
477 pub fn aid(&self) -> Option<Aid> {
481 match self {
482 States::Associated(state) => Some(state.aid()),
483 _ => None,
484 }
485 }
486
487 pub fn authenticated(&self) -> bool {
489 !matches!(self, States::Authenticating(..))
490 }
491
492 pub fn handle_auth_ind(
500 self,
501 r_sta: &mut RemoteClient,
502 ctx: &mut Context,
503 auth_type: fidl_mlme::AuthenticationTypes,
504 ) -> States {
505 match self {
506 States::Authenticating(state) => match state.handle_auth_ind(r_sta, ctx, auth_type) {
507 Ok(timeout_event) => {
508 r_sta.send_authenticate_resp(ctx, fidl_mlme::AuthenticateResultCode::Success);
509 state.transition_to(Authenticated { _timeout_event: timeout_event }).into()
510 }
511 Err(e) => {
512 error!("client {:02X?} MLME-AUTHENTICATE.indication: {}", r_sta.addr, e);
513 r_sta.send_authenticate_resp(ctx, fidl_mlme::AuthenticateResultCode::Refused);
514 state.into()
515 }
516 },
517 _ => {
518 r_sta.send_authenticate_resp(ctx, fidl_mlme::AuthenticateResultCode::Refused);
519 self
520 }
521 }
522 }
523
524 #[allow(clippy::too_many_arguments, reason = "mass allow for https://fxbug.dev/381896734")]
533 pub fn handle_assoc_ind(
534 self,
535 r_sta: &mut RemoteClient,
536 ctx: &mut Context,
537 aid_map: &mut aid::Map,
538 client_capablities: u16,
539 client_rates: Vec<SupportedRate>,
540 rsn_cfg: &Option<RsnCfg>,
541 s_rsne: Option<Vec<u8>>,
542 ) -> States {
543 match self {
544 States::Authenticated(state) => {
545 match state.handle_assoc_ind(
546 r_sta,
547 ctx,
548 aid_map,
549 client_capablities,
550 client_rates,
551 rsn_cfg,
552 s_rsne,
553 ) {
554 Ok(Association { aid, capabilities, rates, mut rsna_link_state }) => {
555 r_sta.send_associate_resp(
556 ctx,
557 fidl_mlme::AssociateResultCode::Success,
558 aid,
559 capabilities,
560 rates,
561 );
562
563 if let Some(rsna_link_state) = rsna_link_state.as_mut()
565 && let Err(error) = rsna_link_state.initiate_key_exchange(r_sta, ctx)
566 {
567 error!(
568 "client {:02X?} MLME-ASSOCIATE.indication (key exchange): {}",
569 r_sta.addr, error
570 );
571 r_sta.send_deauthenticate_req(
572 ctx,
573 fidl_ieee80211::ReasonCode::Ieee8021XAuthFailed,
574 );
575 return state.transition_to(Authenticating).into();
576 }
577
578 state.transition_to(Associated { aid, rsna_link_state }).into()
579 }
580 Err(AssociationError { error, result_code, reason_code }) => {
581 error!("client {:02X?} MLME-ASSOCIATE.indication: {}", r_sta.addr, error);
582 r_sta.send_associate_resp(ctx, result_code, 0, CapabilityInfo(0), vec![]);
583 r_sta.send_deauthenticate_req(ctx, reason_code);
584 state.transition_to(Authenticating).into()
585 }
586 }
587 }
588 _ => {
589 r_sta.send_associate_resp(
590 ctx,
591 fidl_mlme::AssociateResultCode::RefusedReasonUnspecified,
592 0,
593 CapabilityInfo(0),
594 vec![],
595 );
596 self
597 }
598 }
599 }
600
601 pub fn handle_disassoc_ind(
605 self,
606 r_sta: &mut RemoteClient,
607 ctx: &mut Context,
608 aid_map: &mut aid::Map,
609 ) -> States {
610 match self {
611 States::Associated(state) => {
612 let timeout_event = state.handle_disassoc_ind(r_sta, ctx, aid_map);
613 state.transition_to(Authenticated { _timeout_event: timeout_event }).into()
614 }
615 _ => self,
616 }
617 }
618
619 pub fn handle_eapol_ind(
623 self,
624 r_sta: &mut RemoteClient,
625 ctx: &mut Context,
626 data: &[u8],
627 ) -> States {
628 match self {
629 States::Associated(mut state) => {
630 if let Err(e) = state.handle_eapol_ind(r_sta, ctx, data) {
631 error!("client {:02X?} EAPOL.indication: {}", r_sta.addr, e);
632 }
633 state.into()
634 }
635 _ => self,
636 }
637 }
638
639 pub fn handle_eapol_conf(
643 self,
644 r_sta: &mut RemoteClient,
645 ctx: &mut Context,
646 result: fidl_mlme::EapolResultCode,
647 ) -> States {
648 match self {
649 States::Associated(mut state) => {
650 if let Err(e) = state.handle_eapol_conf(r_sta, ctx, result) {
651 error!("client {:02X?} EAPOL.confirm: {}", r_sta.addr, e);
652 }
653 state.into()
654 }
655 _ => self,
656 }
657 }
658
659 pub fn handle_timeout(
681 self,
682 r_sta: &mut RemoteClient,
683 ctx: &mut Context,
684 event: ClientEvent,
685 ) -> States {
686 match event {
687 ClientEvent::AssociationTimeout => match self {
688 States::Authenticated(state) => {
689 r_sta.send_deauthenticate_req(
690 ctx,
691 fidl_ieee80211::ReasonCode::InvalidAuthentication,
693 );
694 state.transition_to(Authenticating).into()
695 }
696 States::Associated(state) => {
697 state.into()
699 }
700 _ => {
701 error!(
702 "client {:02X?} received AssociationTimeout in unexpected state; \
703 ignoring timeout",
704 r_sta.addr,
705 );
706 self
707 }
708 },
709 ClientEvent::RsnaTimeout(timeout_type) => match self {
710 States::Associated(state) => {
711 let (transition, mut state) = state.release_data();
712 match state.handle_rsna_timeout(r_sta, ctx, timeout_type) {
713 Ok(()) => transition.to(state).into(),
714 Err(e) => {
715 let reason_code = match e {
716 RsnaNegotiationError::Error(e) => {
717 error!(
718 "client {:02X?} RSNA negotiation error: {}",
719 r_sta.addr, e
720 );
721 fidl_ieee80211::ReasonCode::UnspecifiedReason
722 }
723 RsnaNegotiationError::Timeout => {
724 fidl_ieee80211::ReasonCode::FourwayHandshakeTimeout
725 }
726 };
727 r_sta.send_deauthenticate_req(ctx, reason_code);
728 transition.to(Authenticating).into()
729 }
730 }
731 }
732 _ => {
733 error!(
734 "client {:02X?} received RsnaTimeout in unexpected state; \
735 ignoring timeout",
736 r_sta.addr,
737 );
738 self
739 }
740 },
741 }
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748 use crate::ap::create_rsn_cfg;
749 use crate::ap::test_utils::MockAuthenticator;
750 use crate::{MlmeRequest, MlmeSink, MlmeStream, test_utils};
751 use assert_matches::assert_matches;
752 use futures::channel::mpsc;
753 use ieee80211::{MacAddr, MacAddrBytes, Ssid};
754 use std::sync::LazyLock;
755 use wlan_common::ie::rsn::akm::AKM_PSK;
756 use wlan_common::ie::rsn::cipher::{CIPHER_CCMP_128, CIPHER_GCMP_256};
757 use wlan_common::ie::rsn::rsne::Rsne;
758 use wlan_common::test_utils::fake_features::fake_spectrum_management_support_empty;
759 use wlan_common::timer;
760 use wlan_rsn::key::exchange::Key;
761
762 static AP_ADDR: LazyLock<MacAddr> = LazyLock::new(|| [6u8; 6].into());
763 static CLIENT_ADDR: LazyLock<MacAddr> = LazyLock::new(|| [7u8; 6].into());
764
765 fn make_remote_client() -> RemoteClient {
766 RemoteClient::new(*CLIENT_ADDR)
767 }
768
769 fn make_env() -> (Context, MlmeStream, timer::EventStream<Event>) {
770 let device_info = test_utils::fake_device_info(*AP_ADDR);
771 let (mlme_sink, mlme_stream) = mpsc::unbounded();
772 let (timer, time_stream) = timer::create_timer();
773 let ctx = Context {
774 device_info,
775 spectrum_management_support: fake_spectrum_management_support_empty(),
776 mlme_sink: MlmeSink::new(mlme_sink),
777 timer,
778 };
779 (ctx, mlme_stream, time_stream)
780 }
781
782 #[test]
783 fn authenticating_goes_to_authenticated() {
784 let mut r_sta = make_remote_client();
785 let (mut ctx, mut mlme_stream, mut time_stream) = make_env();
786
787 let state = States::from(State::new(Authenticating));
788 let state =
789 state.handle_auth_ind(&mut r_sta, &mut ctx, fidl_mlme::AuthenticationTypes::OpenSystem);
790
791 let (_, Authenticated { _timeout_event }) = match state {
792 States::Authenticated(state) => state.release_data(),
793 _ => panic!("unexpected state"),
794 };
795
796 let (_, timed_event, _) = time_stream.try_next().unwrap().expect("expected timed event");
797 assert_eq!(timed_event.id, _timeout_event.id());
798 assert_matches!(timed_event.event, Event::Client { addr, event } => {
799 assert_eq!(addr, *CLIENT_ADDR);
800 assert_matches!(event, ClientEvent::AssociationTimeout);
801 });
802
803 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
804 assert_matches!(mlme_event, MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
805 peer_sta_address,
806 result_code,
807 }) => {
808 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
809 assert_eq!(result_code, fidl_mlme::AuthenticateResultCode::Success);
810 });
811 }
812
813 #[test]
814 fn authenticating_stays_authenticating_with_unsupported_authentication_type() {
815 let mut r_sta = make_remote_client();
816 let (mut ctx, mut mlme_stream, _) = make_env();
817
818 let state = States::from(State::new(Authenticating));
819 let state =
820 state.handle_auth_ind(&mut r_sta, &mut ctx, fidl_mlme::AuthenticationTypes::SharedKey);
821
822 let (_, Authenticating) = match state {
823 States::Authenticating(state) => state.release_data(),
824 _ => panic!("unexpected state"),
825 };
826
827 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
828 assert_matches!(mlme_event, MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
829 peer_sta_address,
830 result_code,
831 }) => {
832 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
833 assert_eq!(result_code, fidl_mlme::AuthenticateResultCode::Refused);
834 });
835 }
836
837 #[test]
838 fn authenticating_refuses_association() {
839 let mut r_sta = make_remote_client();
840 let (mut ctx, mut mlme_stream, _) = make_env();
841
842 let state = States::from(State::new(Authenticating));
843
844 let mut aid_map = aid::Map::default();
845 let state = state.handle_assoc_ind(
846 &mut r_sta,
847 &mut ctx,
848 &mut aid_map,
849 CapabilityInfo(0).with_short_preamble(true).raw(),
850 vec![SupportedRate(0b11111000)],
851 &None,
852 None,
853 );
854
855 let (_, Authenticating) = match state {
856 States::Authenticating(state) => state.release_data(),
857 _ => panic!("unexpected state"),
858 };
859
860 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
861 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
862 peer_sta_address,
863 association_id,
864 result_code,
865 capability_info,
866 rates,
867 }) => {
868 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
869 assert_eq!(association_id, 0);
870 assert_eq!(result_code, fidl_mlme::AssociateResultCode::RefusedReasonUnspecified);
871 assert_eq!(capability_info, 0);
872 assert_eq!(rates, Vec::<u8>::new());
873 });
874 }
875
876 #[test]
877 fn authenticated_refuses_authentication() {
878 let mut r_sta = make_remote_client();
879 let (mut ctx, mut mlme_stream, _) = make_env();
880
881 let state: States = State::new(Authenticating)
882 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
883 .into();
884 let state =
885 state.handle_auth_ind(&mut r_sta, &mut ctx, fidl_mlme::AuthenticationTypes::SharedKey);
886
887 let (_, Authenticated { .. }) = match state {
888 States::Authenticated(state) => state.release_data(),
889 _ => panic!("unexpected state"),
890 };
891
892 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
893 assert_matches!(mlme_event, MlmeRequest::AuthResponse(fidl_mlme::AuthenticateResponse {
894 peer_sta_address,
895 result_code,
896 }) => {
897 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
898 assert_eq!(result_code, fidl_mlme::AuthenticateResultCode::Refused);
899 });
900 }
901
902 #[test]
903 fn authenticated_deauthenticates_on_timeout() {
904 let mut r_sta = make_remote_client();
905 let (mut ctx, mut mlme_stream, _) = make_env();
906
907 let state: States = State::new(Authenticating)
908 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
909 .into();
910 let state = state.handle_timeout(&mut r_sta, &mut ctx, ClientEvent::AssociationTimeout);
911
912 let (_, Authenticating) = match state {
913 States::Authenticating(state) => state.release_data(),
914 _ => panic!("unexpected state"),
915 };
916
917 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
918 assert_matches!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
919 peer_sta_address,
920 reason_code,
921 }) => {
922 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
923 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::InvalidAuthentication);
924 });
925 }
926
927 #[test]
928 fn authenticated_goes_to_associated_no_rsn() {
929 let mut r_sta = make_remote_client();
930 let (mut ctx, mut mlme_stream, _) = make_env();
931
932 let state: States = State::new(Authenticating)
933 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
934 .into();
935
936 let mut aid_map = aid::Map::default();
937 let state = state.handle_assoc_ind(
938 &mut r_sta,
939 &mut ctx,
940 &mut aid_map,
941 CapabilityInfo(0).with_short_preamble(true).raw(),
942 vec![SupportedRate(0b11111000)],
943 &None,
944 None,
945 );
946
947 let (_, Associated { rsna_link_state, aid }) = match state {
948 States::Associated(state) => state.release_data(),
949 _ => panic!("unexpected_state"),
950 };
951
952 assert_matches!(rsna_link_state, None);
953 assert_eq!(aid, 1);
954
955 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
956 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
957 peer_sta_address,
958 result_code,
959 capability_info,
960 rates,
961 ..
962 }) => {
963 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
964 assert_eq!(result_code, fidl_mlme::AssociateResultCode::Success);
965 assert_eq!(capability_info, CapabilityInfo(0).with_short_preamble(true).raw());
966 assert_eq!(rates, vec![0b11111000]);
967 });
968 }
969
970 #[test]
971 fn authenticated_goes_to_associated() {
972 let mut r_sta = make_remote_client();
973 let (mut ctx, mut mlme_stream, _) = make_env();
974
975 let state: States = State::new(Authenticating)
976 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
977 .into();
978
979 let mut aid_map = aid::Map::default();
980 let _next_state = state.handle_assoc_ind(
981 &mut r_sta,
982 &mut ctx,
983 &mut aid_map,
984 CapabilityInfo(0).with_short_preamble(true).raw(),
985 vec![SupportedRate(0b11111000), SupportedRate(0b01111010)],
986 &None,
987 None,
988 );
989
990 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
991 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
992 capability_info,
993 rates,
994 ..
995 }) => {
996 assert_eq!(capability_info, CapabilityInfo(0).with_short_preamble(true).raw());
997 assert_eq!(rates, vec![0b11111000, 0b01111010]);
998 });
999 }
1000
1001 #[test]
1002 fn authenticated_goes_to_authenticating_out_of_aids() {
1003 let mut r_sta = make_remote_client();
1004 let (mut ctx, mut mlme_stream, _) = make_env();
1005
1006 let state: States = State::new(Authenticating)
1007 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1008 .into();
1009
1010 let mut aid_map = aid::Map::default();
1011 while aid_map.assign_aid().is_ok() {
1012 }
1014
1015 let state = state.handle_assoc_ind(
1016 &mut r_sta,
1017 &mut ctx,
1018 &mut aid_map,
1019 CapabilityInfo(0).with_short_preamble(true).raw(),
1020 vec![SupportedRate(0b11111000)],
1021 &None,
1022 None,
1023 );
1024
1025 let (_, Authenticating) = match state {
1026 States::Authenticating(state) => state.release_data(),
1027 _ => panic!("unexpected state"),
1028 };
1029
1030 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1031 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1032 peer_sta_address,
1033 result_code,
1034 ..
1035 }) => {
1036 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1037 assert_eq!(result_code, fidl_mlme::AssociateResultCode::RefusedReasonUnspecified);
1038 });
1039
1040 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1041 assert_matches!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
1042 peer_sta_address,
1043 reason_code,
1044 ..
1045 }) => {
1046 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1047 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::UnspecifiedReason);
1048 });
1049 }
1050
1051 #[test]
1052 fn authenticated_goes_to_authenticating_with_bogus_rsn_ind() {
1053 let mut r_sta = make_remote_client();
1054 let (mut ctx, mut mlme_stream, _) = make_env();
1055
1056 let state: States = State::new(Authenticating)
1057 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1058 .into();
1059
1060 let s_rsne = Rsne::wpa2_rsne();
1061 let mut s_rsne_vec = Vec::with_capacity(s_rsne.len());
1062 s_rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1063
1064 let mut aid_map = aid::Map::default();
1065 let state = state.handle_assoc_ind(
1066 &mut r_sta,
1067 &mut ctx,
1068 &mut aid_map,
1069 CapabilityInfo(0).with_short_preamble(true).raw(),
1070 vec![SupportedRate(0b11111000)],
1071 &None,
1072 Some(s_rsne_vec),
1073 );
1074
1075 let (_, Authenticating) = match state {
1076 States::Authenticating(state) => state.release_data(),
1077 _ => panic!("unexpected state"),
1078 };
1079
1080 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1081 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1082 peer_sta_address,
1083 result_code,
1084 ..
1085 }) => {
1086 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1087 assert_eq!(result_code, fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch);
1088 });
1089
1090 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1091 assert_matches!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
1092 peer_sta_address,
1093 reason_code,
1094 ..
1095 }) => {
1096 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1097 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::ReasonInvalidElement);
1098 });
1099 }
1100
1101 #[test]
1102 fn authenticated_goes_to_authenticating_with_incompatible_rsn() {
1103 let mut r_sta = make_remote_client();
1104 let (mut ctx, mut mlme_stream, _) = make_env();
1105
1106 let state: States = State::new(Authenticating)
1107 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1108 .into();
1109
1110 let mut rsn_cfg =
1111 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1112 rsn_cfg.rsne = Rsne {
1113 group_data_cipher_suite: Some(CIPHER_GCMP_256),
1114 pairwise_cipher_suites: vec![CIPHER_CCMP_128],
1115 akm_suites: vec![AKM_PSK],
1116 ..Default::default()
1117 };
1118
1119 let s_rsne = Rsne {
1120 group_data_cipher_suite: Some(CIPHER_CCMP_128),
1121 pairwise_cipher_suites: vec![CIPHER_CCMP_128],
1122 akm_suites: vec![AKM_PSK],
1123 ..Default::default()
1124 };
1125 let mut s_rsne_vec = Vec::with_capacity(s_rsne.len());
1126 s_rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1127
1128 let mut aid_map = aid::Map::default();
1129 let state = state.handle_assoc_ind(
1130 &mut r_sta,
1131 &mut ctx,
1132 &mut aid_map,
1133 CapabilityInfo(0).with_short_preamble(true).raw(),
1134 vec![SupportedRate(0b11111000)],
1135 &Some(rsn_cfg),
1136 Some(s_rsne_vec),
1137 );
1138
1139 let (_, Authenticating) = match state {
1140 States::Authenticating(state) => state.release_data(),
1141 _ => panic!("unexpected state"),
1142 };
1143
1144 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1145 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1146 peer_sta_address,
1147 result_code,
1148 ..
1149 }) => {
1150 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1151 assert_eq!(result_code, fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch);
1152 });
1153
1154 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1155 assert_matches!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
1156 peer_sta_address,
1157 reason_code,
1158 ..
1159 }) => {
1160 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1161 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::Ieee8021XAuthFailed);
1162 });
1163 }
1164
1165 #[test]
1166 fn authenticated_goes_to_associated_rsn() {
1167 let mut r_sta = make_remote_client();
1168 let (mut ctx, mut mlme_stream, _) = make_env();
1169
1170 let state: States = State::new(Authenticating)
1171 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1172 .into();
1173
1174 let rsn_cfg =
1175 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1176
1177 let mut s_rsne_vec = Vec::with_capacity(rsn_cfg.rsne.len());
1178 rsn_cfg.rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1179
1180 let mut aid_map = aid::Map::default();
1181 let state = state.handle_assoc_ind(
1182 &mut r_sta,
1183 &mut ctx,
1184 &mut aid_map,
1185 CapabilityInfo(0).with_short_preamble(true).raw(),
1186 vec![SupportedRate(0b11111000)],
1187 &Some(rsn_cfg),
1188 Some(s_rsne_vec),
1189 );
1190
1191 let (_, Associated { rsna_link_state, aid }) = match state {
1192 States::Associated(state) => state.release_data(),
1193 _ => panic!("unexpected_state"),
1194 };
1195
1196 assert_eq!(aid, 1);
1197 assert_matches!(rsna_link_state, Some(_));
1198
1199 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1200 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1201 peer_sta_address,
1202 result_code,
1203 capability_info,
1204 rates,
1205 ..
1206 }) => {
1207 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1208 assert_eq!(result_code, fidl_mlme::AssociateResultCode::Success);
1209 assert_eq!(capability_info, CapabilityInfo(0).with_short_preamble(true).with_privacy(true).raw());
1210 assert_eq!(rates, vec![0b11111000]);
1211 });
1212
1213 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1214 assert_matches!(mlme_event, MlmeRequest::Eapol(fidl_mlme::EapolRequest { .. }));
1215 }
1216
1217 #[test]
1218 fn authenticated_goes_to_associated_rsn_different_cap() {
1219 let mut r_sta = make_remote_client();
1220 let (mut ctx, mut mlme_stream, _) = make_env();
1221
1222 let state: States = State::new(Authenticating)
1223 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1224 .into();
1225
1226 let rsn_cfg =
1227 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1228
1229 let mut s_rsne_vec = Vec::with_capacity(rsn_cfg.rsne.len());
1230 rsn_cfg.rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1231
1232 let mut aid_map = aid::Map::default();
1233 let state = state.handle_assoc_ind(
1234 &mut r_sta,
1235 &mut ctx,
1236 &mut aid_map,
1237 CapabilityInfo(0).with_short_preamble(true).with_spectrum_mgmt(true).raw(),
1238 vec![SupportedRate(0b11111000)],
1239 &Some(rsn_cfg),
1240 Some(s_rsne_vec),
1241 );
1242
1243 let (_, Associated { rsna_link_state, aid }) = match state {
1244 States::Associated(state) => state.release_data(),
1245 _ => panic!("unexpected_state"),
1246 };
1247
1248 assert_eq!(aid, 1);
1249 assert_matches!(rsna_link_state, Some(_));
1250
1251 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1252 assert_matches!(mlme_event, MlmeRequest::AssocResponse(fidl_mlme::AssociateResponse {
1253 peer_sta_address,
1254 result_code,
1255 capability_info,
1256 rates,
1257 ..
1258 }) => {
1259 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1260 assert_eq!(result_code, fidl_mlme::AssociateResultCode::Success);
1261 assert_eq!(
1262 capability_info,
1263 CapabilityInfo(0)
1264 .with_short_preamble(true)
1265 .with_spectrum_mgmt(true)
1266 .with_privacy(true)
1267 .raw());
1268 assert_eq!(rates, vec![0b11111000]);
1269 });
1270
1271 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1272 assert_matches!(mlme_event, MlmeRequest::Eapol(fidl_mlme::EapolRequest { .. }));
1273 }
1274
1275 #[test]
1276 fn associated_goes_to_authenticated() {
1277 let mut r_sta = make_remote_client();
1278 let (mut ctx, _, mut time_stream) = make_env();
1279 let mut aid_map = aid::Map::default();
1280
1281 let aid = aid_map.assign_aid().unwrap();
1282
1283 let state: States = State::new(Authenticating)
1284 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1285 .transition_to(Associated { aid, rsna_link_state: None })
1286 .into();
1287
1288 let state = state.handle_disassoc_ind(&mut r_sta, &mut ctx, &mut aid_map);
1289
1290 let (_, Authenticated { _timeout_event }) = match state {
1291 States::Authenticated(state) => state.release_data(),
1292 _ => panic!("unexpected state"),
1293 };
1294
1295 assert_eq!(aid, aid_map.assign_aid().unwrap());
1296
1297 let (_, timed_event, _) = time_stream.try_next().unwrap().expect("expected timed event");
1298 assert_eq!(timed_event.id, _timeout_event.id());
1299 assert_matches!(timed_event.event, Event::Client { addr, event } => {
1300 assert_eq!(addr, *CLIENT_ADDR);
1301 assert_matches!(event, ClientEvent::AssociationTimeout);
1302 });
1303 }
1304
1305 #[test]
1306 fn associated_ignores_rsna_negotiation_timeout_without_rsna_link_state() {
1307 let mut r_sta = make_remote_client();
1308 let (mut ctx, _, mut time_stream) = make_env();
1309
1310 let state: States = State::new(Authenticating)
1311 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1312 .transition_to(Associated { aid: 1, rsna_link_state: None })
1313 .into();
1314
1315 let state = state.handle_timeout(
1316 &mut r_sta,
1317 &mut ctx,
1318 ClientEvent::RsnaTimeout(RsnaTimeout::Negotiation),
1319 );
1320
1321 let (_, Associated { .. }) = match state {
1322 States::Associated(state) => state.release_data(),
1323 _ => panic!("unexpected_state"),
1324 };
1325
1326 assert_matches!(time_stream.try_next(), Err(_));
1327 }
1328
1329 #[test]
1330 fn associated_ignores_rsna_request_timeout_without_rsna_link_state() {
1331 let mut r_sta = make_remote_client();
1332 let (mut ctx, _, mut time_stream) = make_env();
1333
1334 let state: States = State::new(Authenticating)
1335 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1336 .transition_to(Associated { aid: 1, rsna_link_state: None })
1337 .into();
1338
1339 let state = state.handle_timeout(
1340 &mut r_sta,
1341 &mut ctx,
1342 ClientEvent::RsnaTimeout(RsnaTimeout::Request),
1343 );
1344
1345 let (_, Associated { .. }) = match state {
1346 States::Associated(state) => state.release_data(),
1347 _ => panic!("unexpected_state"),
1348 };
1349
1350 assert_matches!(time_stream.try_next(), Err(_));
1351 }
1352
1353 #[test]
1354 fn associated_handles_rsna_request_timeout() {
1355 let mut r_sta = make_remote_client();
1356 let (mut ctx, mut mlme_stream, mut time_stream) = make_env();
1357
1358 let rsn_cfg =
1359 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1360
1361 let s_rsne = Rsne::wpa2_rsne();
1362 let mut s_rsne_vec = Vec::with_capacity(s_rsne.len());
1363 s_rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1364
1365 let state: States = State::new(Authenticating)
1366 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1367 .transition_to(Associated {
1368 aid: 1,
1369 rsna_link_state: Some(RsnaLinkState {
1370 request_attempts: 0,
1371 last_key_frame: Some(test_utils::eapol_key_frame()),
1372 request_timeout: Some(EventHandle::new_test(2)),
1373 negotiation_timeout: Some(EventHandle::new_test(3)),
1374 authenticator: new_authenticator_from_rsne(
1375 *AP_ADDR,
1376 *CLIENT_ADDR,
1377 &s_rsne_vec[..],
1378 &rsn_cfg,
1379 )
1380 .unwrap(),
1381 }),
1382 })
1383 .into();
1384
1385 let state = state.handle_timeout(
1386 &mut r_sta,
1387 &mut ctx,
1388 ClientEvent::RsnaTimeout(RsnaTimeout::Request),
1389 );
1390
1391 let (_, Associated { rsna_link_state, .. }) = match state {
1392 States::Associated(state) => state.release_data(),
1393 _ => panic!("unexpected_state"),
1394 };
1395
1396 assert_eq!(rsna_link_state.as_ref().unwrap().request_attempts, 1);
1397
1398 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1399 assert_matches!(mlme_event, MlmeRequest::Eapol(fidl_mlme::EapolRequest { .. }));
1400
1401 let (_, timed_event, _) = time_stream.try_next().unwrap().expect("expected timed event");
1402 assert_eq!(
1403 timed_event.id,
1404 rsna_link_state.as_ref().unwrap().request_timeout.as_ref().unwrap().id()
1405 );
1406 assert_matches!(timed_event.event, Event::Client { addr, event } => {
1407 assert_eq!(addr, *CLIENT_ADDR);
1408 assert_matches!(event, ClientEvent::RsnaTimeout(RsnaTimeout::Request));
1409 });
1410 }
1411
1412 #[test]
1413 fn associated_handles_rsna_negotiation_timeout() {
1414 let mut r_sta = make_remote_client();
1415 let (mut ctx, mut mlme_stream, _) = make_env();
1416
1417 let rsn_cfg =
1418 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1419
1420 let s_rsne = Rsne::wpa2_rsne();
1421 let mut s_rsne_vec = Vec::with_capacity(s_rsne.len());
1422 s_rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1423
1424 let state: States = State::new(Authenticating)
1425 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1426 .transition_to(Associated {
1427 aid: 1,
1428 rsna_link_state: Some(RsnaLinkState {
1429 request_attempts: 3,
1430 last_key_frame: Some(test_utils::eapol_key_frame()),
1431 request_timeout: Some(EventHandle::new_test(2)),
1432 negotiation_timeout: Some(EventHandle::new_test(3)),
1433 authenticator: new_authenticator_from_rsne(
1434 *AP_ADDR,
1435 *CLIENT_ADDR,
1436 &s_rsne_vec[..],
1437 &rsn_cfg,
1438 )
1439 .unwrap(),
1440 }),
1441 })
1442 .into();
1443
1444 let state = state.handle_timeout(
1445 &mut r_sta,
1446 &mut ctx,
1447 ClientEvent::RsnaTimeout(RsnaTimeout::Negotiation),
1448 );
1449
1450 let (_, Authenticating) = match state {
1451 States::Authenticating(state) => state.release_data(),
1452 _ => panic!("unexpected_state"),
1453 };
1454
1455 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1456 assert_matches!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
1457 peer_sta_address,
1458 reason_code,
1459 }) => {
1460 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1461 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::FourwayHandshakeTimeout);
1462 });
1463 }
1464
1465 #[test]
1466 fn associated_handles_rsna_key_frame_resets_request_attempts() {
1467 let mut r_sta = make_remote_client();
1468 let (mut ctx, _, _) = make_env();
1469
1470 let rsn_cfg =
1471 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1472
1473 let s_rsne = Rsne::wpa2_rsne();
1474 let mut s_rsne_vec = Vec::with_capacity(s_rsne.len());
1475 s_rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1476
1477 let state: States = State::new(Authenticating)
1478 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1479 .transition_to(Associated {
1480 aid: 1,
1481 rsna_link_state: Some(RsnaLinkState {
1482 request_attempts: 3,
1483 last_key_frame: Some(test_utils::eapol_key_frame()),
1484 request_timeout: Some(EventHandle::new_test(1)),
1485 negotiation_timeout: Some(EventHandle::new_test(2)),
1486 authenticator: new_authenticator_from_rsne(
1487 *AP_ADDR,
1488 *CLIENT_ADDR,
1489 &s_rsne_vec[..],
1490 &rsn_cfg,
1491 )
1492 .unwrap(),
1493 }),
1494 })
1495 .into();
1496
1497 let state = state.handle_eapol_ind(
1498 &mut r_sta,
1499 &mut ctx,
1500 &Vec::<u8>::from(test_utils::eapol_key_frame())[..],
1501 );
1502
1503 let (_, Associated { rsna_link_state, .. }) = match state {
1504 States::Associated(state) => state.release_data(),
1505 _ => panic!("unexpected_state"),
1506 };
1507
1508 assert_eq!(rsna_link_state.as_ref().unwrap().request_attempts, 0);
1509 }
1510
1511 #[test]
1512 fn associated_handles_rsna_request_timeout_last_attempt() {
1513 let mut r_sta = make_remote_client();
1514 let (mut ctx, mut mlme_stream, _) = make_env();
1515
1516 let rsn_cfg =
1517 create_rsn_cfg(&Ssid::try_from("coolnet").unwrap(), b"password").unwrap().unwrap();
1518
1519 let s_rsne = Rsne::wpa2_rsne();
1520 let mut s_rsne_vec = Vec::with_capacity(s_rsne.len());
1521 s_rsne.write_into(&mut s_rsne_vec).expect("error writing RSNE");
1522
1523 let state: States = State::new(Authenticating)
1524 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1525 .transition_to(Associated {
1526 aid: 1,
1527 rsna_link_state: Some(RsnaLinkState {
1528 request_attempts: 3,
1529 last_key_frame: Some(test_utils::eapol_key_frame()),
1530 request_timeout: Some(EventHandle::new_test(2)),
1531 negotiation_timeout: Some(EventHandle::new_test(3)),
1532 authenticator: new_authenticator_from_rsne(
1533 *AP_ADDR,
1534 *CLIENT_ADDR,
1535 &s_rsne_vec[..],
1536 &rsn_cfg,
1537 )
1538 .unwrap(),
1539 }),
1540 })
1541 .into();
1542
1543 let state = state.handle_timeout(
1544 &mut r_sta,
1545 &mut ctx,
1546 ClientEvent::RsnaTimeout(RsnaTimeout::Request),
1547 );
1548
1549 let (_, Authenticating) = match state {
1550 States::Authenticating(state) => state.release_data(),
1551 _ => panic!("unexpected state"),
1552 };
1553
1554 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1555 assert_matches!(mlme_event, MlmeRequest::Deauthenticate(fidl_mlme::DeauthenticateRequest {
1556 peer_sta_address,
1557 reason_code,
1558 }) => {
1559 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1560 assert_eq!(reason_code, fidl_ieee80211::ReasonCode::FourwayHandshakeTimeout);
1561 });
1562 }
1563
1564 #[test]
1565 fn associated_handles_eapol_key_frame() {
1566 let mut r_sta = make_remote_client();
1567 let (mut ctx, mut mlme_stream, _) = make_env();
1568
1569 let state: States = State::new(Authenticating)
1570 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1571 .transition_to(Associated {
1572 aid: 1,
1573 rsna_link_state: Some(RsnaLinkState {
1574 request_attempts: 0,
1575 last_key_frame: Some(test_utils::eapol_key_frame()),
1576 request_timeout: Some(EventHandle::new_test(2)),
1577 negotiation_timeout: Some(EventHandle::new_test(3)),
1578 authenticator: Box::new(MockAuthenticator::new(
1579 Arc::new(Mutex::new(vec![])),
1580 Arc::new(Mutex::new(vec![SecAssocUpdate::TxEapolKeyFrame {
1581 frame: test_utils::eapol_key_frame(),
1582 expect_response: false,
1583 }])),
1584 )),
1585 }),
1586 })
1587 .into();
1588
1589 let _next_state = state.handle_eapol_ind(
1590 &mut r_sta,
1591 &mut ctx,
1592 &Vec::<u8>::from(test_utils::eapol_key_frame())[..],
1593 );
1594
1595 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1596 assert_matches!(mlme_event, MlmeRequest::Eapol(fidl_mlme::EapolRequest {
1597 src_addr,
1598 dst_addr,
1599 data,
1600 }) => {
1601 assert_eq!(&src_addr, AP_ADDR.as_array());
1602 assert_eq!(&dst_addr, CLIENT_ADDR.as_array());
1603 assert_eq!(data, Vec::<u8>::from(test_utils::eapol_key_frame()));
1604 });
1605 }
1606
1607 #[test]
1608 fn associated_handles_eapol_conf() {
1609 let mut r_sta = make_remote_client();
1610 let (mut ctx, _mlme_stream, _) = make_env();
1611
1612 let state: States = State::new(Authenticating)
1613 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1614 .transition_to(Associated {
1615 aid: 1,
1616 rsna_link_state: Some(RsnaLinkState {
1617 request_attempts: 0,
1618 last_key_frame: Some(test_utils::eapol_key_frame()),
1619 request_timeout: Some(EventHandle::new_test(2)),
1620 negotiation_timeout: Some(EventHandle::new_test(3)),
1621 authenticator: Box::new(MockAuthenticator::new(
1622 Arc::new(Mutex::new(vec![])),
1623 Arc::new(Mutex::new(vec![SecAssocUpdate::TxEapolKeyFrame {
1624 frame: test_utils::eapol_key_frame(),
1625 expect_response: false,
1626 }])),
1627 )),
1628 }),
1629 })
1630 .into();
1631
1632 let state =
1633 state.handle_eapol_conf(&mut r_sta, &mut ctx, fidl_mlme::EapolResultCode::Success);
1634 match state {
1635 States::Associated(_) => (),
1636 _ => panic!("Eapol conf should leave us in Associated"),
1637 }
1638
1639 }
1641
1642 #[test]
1643 fn associated_handles_eapol_key() {
1644 let mut r_sta = make_remote_client();
1645 let (mut ctx, mut mlme_stream, _) = make_env();
1646
1647 let state: States = State::new(Authenticating)
1648 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1649 .transition_to(Associated {
1650 aid: 1,
1651 rsna_link_state: Some(RsnaLinkState {
1652 request_attempts: 0,
1653 last_key_frame: Some(test_utils::eapol_key_frame()),
1654 request_timeout: Some(EventHandle::new_test(2)),
1655 negotiation_timeout: Some(EventHandle::new_test(3)),
1656 authenticator: Box::new(MockAuthenticator::new(
1657 Arc::new(Mutex::new(vec![])),
1658 Arc::new(Mutex::new(vec![SecAssocUpdate::Key(
1659 Key::Ptk(test_utils::ptk()),
1660 )])),
1661 )),
1662 }),
1663 })
1664 .into();
1665
1666 let _next_state = state.handle_eapol_ind(
1667 &mut r_sta,
1668 &mut ctx,
1669 &Vec::<u8>::from(test_utils::eapol_key_frame())[..],
1670 );
1671
1672 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1673 assert_matches!(mlme_event, MlmeRequest::SetKeys(fidl_mlme::SetKeysRequest { keylist }) => {
1674 assert_eq!(keylist.len(), 1);
1675 let k = keylist.first().expect("expect key descriptor");
1676 assert_eq!(k.key, vec![0xCCu8; test_utils::cipher().tk_bytes().unwrap() as usize]);
1677 assert_eq!(k.key_id, 0);
1678 assert_eq!(k.key_type, fidl_mlme::KeyType::Pairwise);
1679 assert_eq!(&k.address, CLIENT_ADDR.as_array());
1680 assert_eq!(k.rsc, 0);
1681 assert_eq!(k.cipher_suite_oui, [0x00, 0x0F, 0xAC]);
1682 assert_eq!(k.cipher_suite_type, fidl_ieee80211::CipherSuiteType::from_primitive_allow_unknown(4));
1683 });
1684 }
1685
1686 #[test]
1687 fn associated_handles_esssa_established() {
1688 let mut r_sta = make_remote_client();
1689 let (mut ctx, mut mlme_stream, _) = make_env();
1690
1691 let state: States = State::new(Authenticating)
1692 .transition_to(Authenticated { _timeout_event: EventHandle::new_test(1) })
1693 .transition_to(Associated {
1694 aid: 1,
1695 rsna_link_state: Some(RsnaLinkState {
1696 request_attempts: 0,
1697 last_key_frame: Some(test_utils::eapol_key_frame()),
1698 request_timeout: Some(EventHandle::new_test(2)),
1699 negotiation_timeout: Some(EventHandle::new_test(3)),
1700 authenticator: Box::new(MockAuthenticator::new(
1701 Arc::new(Mutex::new(vec![])),
1702 Arc::new(Mutex::new(vec![SecAssocUpdate::Status(
1703 SecAssocStatus::EssSaEstablished,
1704 )])),
1705 )),
1706 }),
1707 })
1708 .into();
1709
1710 let state = state.handle_eapol_ind(
1711 &mut r_sta,
1712 &mut ctx,
1713 &Vec::<u8>::from(test_utils::eapol_key_frame())[..],
1714 );
1715
1716 let (_, Associated { rsna_link_state, .. }) = match state {
1717 States::Associated(state) => state.release_data(),
1718 _ => panic!("unexpected_state"),
1719 };
1720
1721 assert_matches!(&rsna_link_state.as_ref().unwrap().last_key_frame, None);
1722 assert_matches!(&rsna_link_state.as_ref().unwrap().request_timeout, None);
1723 assert_matches!(&rsna_link_state.as_ref().unwrap().negotiation_timeout, None);
1724
1725 let mlme_event = mlme_stream.try_next().unwrap().expect("expected mlme event");
1726 assert_matches!(mlme_event, MlmeRequest::SetCtrlPort(fidl_mlme::SetControlledPortRequest {
1727 peer_sta_address,
1728 state,
1729 }) => {
1730 assert_eq!(&peer_sta_address, CLIENT_ADDR.as_array());
1731 assert_eq!(state, fidl_mlme::ControlledPortState::Open);
1732 });
1733 }
1734}