1use crate::recorded_request_stream::RecordedRequestStream;
6use assert_matches::assert_matches;
7use fidl_fuchsia_wlan_fullmac as fidl_fullmac;
8use fidl_fuchsia_wlan_internal as fidl_wlan_security;
9use fidl_fuchsia_wlan_mlme as fidl_mlme;
10use fidl_fuchsia_wlan_mlme::EapolResultCode;
11use fuchsia_sync::Mutex;
12use ieee80211::MacAddr;
13use std::sync::Arc;
14use wlan_common::bss;
15use wlan_common::ie::rsn::cipher::{CIPHER_BIP_CMAC_128, CIPHER_CCMP_128};
16use wlan_common::ie::rsn::rsne;
17use wlan_rsn::Authenticator;
18use wlan_rsn::rsna::{SecAssocUpdate, UpdateSink};
19use zerocopy::IntoBytes;
20
21pub fn create_wpa2_authenticator(
23 client_mac_addr: MacAddr,
24 bss_description: &bss::BssDescription,
25 credentials: fidl_wlan_security::WpaCredentials,
26) -> Authenticator {
27 assert_eq!(bss_description.protection(), bss::Protection::Wpa2Personal);
28
29 let advertised_protection_info = get_protection_info(bss_description);
31 let supplicant_protection_info = get_protection_info(bss_description);
32
33 let nonce_rdr = wlan_rsn::nonce::NonceReader::new(&bss_description.bssid.clone().into())
34 .expect("creating nonce reader");
35 let gtk_provider =
36 wlan_rsn::GtkProvider::new(CIPHER_CCMP_128, 1, 0).expect("creating gtk provider");
37
38 let psk = match credentials {
39 fidl_wlan_security::WpaCredentials::Passphrase(passphrase) => {
40 wlan_rsn::psk::compute(passphrase.as_bytes(), &bss_description.ssid)
41 .expect("Could not compute psk")
42 }
43 fidl_wlan_security::WpaCredentials::Psk(psk) => Box::new(psk),
44 _ => panic!("Unsupported credential type"),
45 };
46
47 Authenticator::new_wpa2psk_ccmp128(
48 nonce_rdr,
49 Arc::new(Mutex::new(gtk_provider)),
50 psk,
51 client_mac_addr,
52 supplicant_protection_info,
53 bss_description.bssid.clone().into(),
54 advertised_protection_info,
55 )
56 .expect("Failed to create authenticator")
57}
58
59pub fn create_wpa3_authenticator(
61 client_mac_addr: MacAddr,
62 bss_description: &bss::BssDescription,
63 credentials: fidl_wlan_security::WpaCredentials,
64) -> Authenticator {
65 assert_eq!(bss_description.protection(), bss::Protection::Wpa3Personal);
66
67 let advertised_protection_info = get_protection_info(bss_description);
69 let supplicant_protection_info = get_protection_info(bss_description);
70
71 let password =
72 assert_matches!(credentials, fidl_wlan_security::WpaCredentials::Passphrase(p) => p);
73
74 let nonce_rdr = wlan_rsn::nonce::NonceReader::new(&bss_description.bssid.clone().into())
75 .expect("creating nonce reader");
76 let gtk_provider =
77 wlan_rsn::GtkProvider::new(CIPHER_CCMP_128, 1, 0).expect("creating gtk provider");
78 let igtk_provider =
79 wlan_rsn::IgtkProvider::new(CIPHER_BIP_CMAC_128).expect("error creating IgtkProvider");
80
81 Authenticator::new_wpa3(
82 nonce_rdr,
83 Arc::new(Mutex::new(gtk_provider)),
84 Arc::new(Mutex::new(igtk_provider)),
85 bss_description.ssid.clone(),
86 password,
87 client_mac_addr,
88 supplicant_protection_info.clone(),
89 bss_description.bssid.into(),
90 advertised_protection_info,
91 )
92 .expect("Failed to create authenticator")
93}
94
95pub async fn handle_sae_exchange(
108 authenticator: &mut Authenticator,
109 fullmac_req_stream: &mut RecordedRequestStream,
110 fullmac_ifc_proxy: &fidl_fullmac::WlanFullmacImplIfcProxy,
111) -> UpdateSink {
112 let mut update_sink = UpdateSink::new();
113
114 let supplicant_commit_frame = get_sae_frame_from_test_realm(fullmac_req_stream).await;
116 authenticator
117 .on_sae_frame_rx(&mut update_sink, supplicant_commit_frame)
118 .expect("Failed to send SAE commit frame to authenticator");
119
120 let authenticator_commit_frame = assert_matches!(
123 &update_sink[0], SecAssocUpdate::TxSaeFrame(frame) => frame.clone());
124 let authenticator_confirm_frame = assert_matches!(
125 &update_sink[1], SecAssocUpdate::TxSaeFrame(frame) => frame.clone());
126 update_sink.clear();
127
128 send_sae_frame_to_test_realm(authenticator_commit_frame, fullmac_ifc_proxy).await;
130
131 let supplicant_confirm_frame = get_sae_frame_from_test_realm(fullmac_req_stream).await;
133 authenticator
134 .on_sae_frame_rx(&mut update_sink, supplicant_confirm_frame)
135 .expect("Failed to send SAE confirm frame to authenticator");
136
137 send_sae_frame_to_test_realm(authenticator_confirm_frame, fullmac_ifc_proxy).await;
139
140 update_sink
141}
142
143pub async fn handle_fourway_eapol_handshake(
152 authenticator: &mut Authenticator,
153 frame_to_client: eapol::KeyFrameBuf,
154 bssid: [u8; 6],
155 client_sta_addr: [u8; 6],
156 fullmac_req_stream: &mut RecordedRequestStream,
157 fullmac_ifc_proxy: &fidl_fullmac::WlanFullmacImplIfcProxy,
158) -> UpdateSink {
159 let mut update_sink = UpdateSink::new();
160 let mic_size = authenticator.get_negotiated_protection().mic_size;
161
162 send_eapol_frame_to_test_realm(
163 authenticator,
164 frame_to_client,
165 bssid.clone(),
166 client_sta_addr.clone(),
167 fullmac_ifc_proxy,
168 )
169 .await;
170 let frame_to_auth_data =
171 get_eapol_frame_from_test_realm(bssid.clone(), fullmac_req_stream, fullmac_ifc_proxy).await;
172 let frame_to_auth = eapol::KeyFrameRx::parse(mic_size as usize, &frame_to_auth_data[..])
173 .expect("Could not parse EAPOL key frame");
174 authenticator
175 .on_eapol_frame(&mut update_sink, eapol::Frame::Key(frame_to_auth))
176 .expect("Could not send EAPOL frame to authenticator");
177
178 let frame_to_client = assert_matches!(update_sink.remove(0), SecAssocUpdate::TxEapolKeyFrame { frame, .. } => frame);
179 send_eapol_frame_to_test_realm(
180 authenticator,
181 frame_to_client,
182 bssid.clone(),
183 client_sta_addr.clone(),
184 fullmac_ifc_proxy,
185 )
186 .await;
187 let frame_to_auth_data =
188 get_eapol_frame_from_test_realm(bssid.clone(), fullmac_req_stream, fullmac_ifc_proxy).await;
189 let frame_to_auth = eapol::KeyFrameRx::parse(mic_size as usize, &frame_to_auth_data[..])
190 .expect("Could not parse EAPOL key frame");
191 authenticator
192 .on_eapol_frame(&mut update_sink, eapol::Frame::Key(frame_to_auth))
193 .expect("Could not send EAPOL frame to authenticator");
194
195 update_sink
196}
197
198async fn send_eapol_frame_to_test_realm(
200 authenticator: &mut Authenticator,
201 frame: eapol::KeyFrameBuf,
202 authenticator_addr: [u8; 6],
203 client_addr: [u8; 6],
204 fullmac_ifc_proxy: &fidl_fullmac::WlanFullmacImplIfcProxy,
205) {
206 fullmac_ifc_proxy
207 .eapol_ind(&fidl_fullmac::WlanFullmacImplIfcEapolIndRequest {
208 src_addr: Some(authenticator_addr),
209 dst_addr: Some(client_addr),
210 data: Some(frame.into()),
211 ..Default::default()
212 })
213 .await
214 .expect("Could not send EAPOL ind");
215 let mut update_sink = UpdateSink::new();
216 authenticator
217 .on_eapol_conf(&mut update_sink, EapolResultCode::Success)
218 .expect("Could not send EAPOL conf to authenticator");
219 assert_eq!(update_sink.len(), 0);
220}
221
222async fn get_eapol_frame_from_test_realm(
224 authenticator_addr: [u8; 6],
225 fullmac_req_stream: &mut RecordedRequestStream,
226 fullmac_ifc_proxy: &fidl_fullmac::WlanFullmacImplIfcProxy,
227) -> Vec<u8> {
228 let frame_data = assert_matches!(fullmac_req_stream.next().await,
229 fidl_fullmac::WlanFullmacImpl_Request::EapolTx { payload, responder } => {
230 responder
231 .send()
232 .expect("Failed to respond to EapolTx");
233 payload.data.unwrap()
234 });
235
236 fullmac_ifc_proxy
237 .eapol_conf(&fidl_fullmac::WlanFullmacImplIfcEapolConfRequest {
238 result_code: Some(fidl_fullmac::EapolTxResult::Success),
239 dst_addr: Some(authenticator_addr),
240 ..Default::default()
241 })
242 .await
243 .expect("Could not send EAPOL conf");
244
245 frame_data
246}
247
248fn get_protection_info(bss_description: &bss::BssDescription) -> wlan_rsn::ProtectionInfo {
249 let (_, rsne) = rsne::from_bytes(bss_description.rsne().unwrap()).expect("Could not get RSNE");
250 wlan_rsn::ProtectionInfo::Rsne(rsne)
251}
252
253async fn get_sae_frame_from_test_realm(
254 fullmac_req_stream: &mut RecordedRequestStream,
255) -> fidl_mlme::SaeFrame {
256 let fullmac_sae_frame = assert_matches!(fullmac_req_stream.next().await,
257 fidl_fullmac::WlanFullmacImpl_Request::SaeFrameTx { frame, responder } => {
258 responder
259 .send()
260 .expect("Failed to respond to SaeFrameTx");
261 frame
262 });
263
264 fidl_mlme::SaeFrame {
265 peer_sta_address: fullmac_sae_frame.peer_sta_address.unwrap(),
266 status_code: fullmac_sae_frame.status_code.unwrap(),
267 seq_num: fullmac_sae_frame.seq_num.unwrap(),
268 sae_fields: fullmac_sae_frame.sae_fields.unwrap(),
269 }
270}
271
272async fn send_sae_frame_to_test_realm(
273 frame: fidl_mlme::SaeFrame,
274 fullmac_ifc_proxy: &fidl_fullmac::WlanFullmacImplIfcProxy,
275) {
276 fullmac_ifc_proxy
277 .sae_frame_rx(&fidl_fullmac::SaeFrame {
278 peer_sta_address: Some(frame.peer_sta_address),
279 status_code: Some(frame.status_code),
280 seq_num: Some(frame.seq_num),
281 sae_fields: Some(frame.sae_fields.clone()),
282 ..Default::default()
283 })
284 .await
285 .expect("Could not send authenticator SAE commit frame");
286}