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