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