1use crate::auth;
6use anyhow::{bail, Error};
7use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
8use log::error;
9use wlan_common::mac;
10use zerocopy::SplitByteSlice;
11
12#[derive(Debug)]
15pub enum AkmState {
16 InProgress,
18 Failed,
20 AuthComplete,
22}
23
24pub trait AkmAction {
27 fn send_auth_frame(
29 &mut self,
30 auth_type: mac::AuthAlgorithmNumber,
31 seq_num: u16,
32 status_code: mac::StatusCode,
33 auth_content: &[u8],
34 ) -> Result<(), Error>;
35 fn forward_sme_sae_rx(
37 &mut self,
38 seq_num: u16,
39 status_code: fidl_ieee80211::StatusCode,
40 sae_fields: Vec<u8>,
41 );
42 fn forward_sae_handshake_ind(&mut self);
43}
44
45pub enum AkmAlgorithm {
47 _OpenAp,
48 OpenSupplicant,
49 Sae,
50}
51
52impl std::fmt::Debug for AkmAlgorithm {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
54 f.write_str(match self {
55 AkmAlgorithm::_OpenAp { .. } => "Open authentication AP",
56 AkmAlgorithm::OpenSupplicant { .. } => "Open authentication SAE",
57 AkmAlgorithm::Sae { .. } => "SAE authentication",
58 })
59 }
60}
61
62impl AkmAlgorithm {
63 pub fn initiate<A: AkmAction>(&mut self, actions: &mut A) -> Result<AkmState, Error> {
64 match self {
65 AkmAlgorithm::_OpenAp => {
66 error!("OpenAp AKM does not support initiating an auth exchange.");
67 Ok(AkmState::Failed)
68 }
69 AkmAlgorithm::OpenSupplicant => {
70 actions.send_auth_frame(
71 mac::AuthAlgorithmNumber::OPEN,
72 1,
73 fidl_ieee80211::StatusCode::Success.into(),
74 &[],
75 )?;
76 Ok(AkmState::InProgress)
77 }
78 AkmAlgorithm::Sae => {
79 actions.forward_sae_handshake_ind();
80 Ok(AkmState::InProgress)
81 }
82 }
83 }
84
85 pub fn handle_auth_frame<A: AkmAction, B: SplitByteSlice>(
86 &mut self,
87 actions: &mut A,
88 auth_frame: mac::AuthFrame<B>,
89 ) -> Result<AkmState, Error> {
90 let (auth_hdr, auth_body) = auth_frame.into_auth_body();
91 match self {
92 AkmAlgorithm::_OpenAp => bail!("OpenAp akm not yet implemented"),
93 AkmAlgorithm::OpenSupplicant { .. } => match auth::validate_ap_resp(&auth_hdr) {
94 Ok(auth::ValidFrame::Open) => Ok(AkmState::AuthComplete),
95 Ok(frame_type) => {
96 error!("Received unhandled auth frame type {:?}", frame_type);
97 Ok(AkmState::Failed)
98 }
99 Err(e) => {
100 error!("Received invalid auth frame: {}", e);
101 Ok(AkmState::Failed)
102 }
103 },
104 AkmAlgorithm::Sae { .. } => {
105 let sae_fields = auth_body.to_vec();
106 actions.forward_sme_sae_rx(
107 auth_hdr.auth_txn_seq_num,
108 Option::<fidl_ieee80211::StatusCode>::from(auth_hdr.status_code)
110 .unwrap_or(fidl_ieee80211::StatusCode::RefusedReasonUnspecified),
111 sae_fields,
112 );
113 Ok(AkmState::InProgress)
114 }
115 }
116 }
117
118 pub fn handle_sae_resp<A: AkmAction>(
119 &mut self,
120 _actions: &mut A,
121 status_code: fidl_ieee80211::StatusCode,
122 ) -> Result<AkmState, Error> {
123 match self {
124 AkmAlgorithm::_OpenAp => bail!("OpenAp akm not yet implemented"),
125 AkmAlgorithm::OpenSupplicant { .. } => {
126 bail!("Open supplicant doesn't expect an SaeResp")
127 }
128 AkmAlgorithm::Sae { .. } => match status_code {
129 fidl_ieee80211::StatusCode::Success => Ok(AkmState::AuthComplete),
130 _ => Ok(AkmState::Failed),
131 },
132 }
133 }
134
135 pub fn handle_sme_sae_tx<A: AkmAction>(
136 &mut self,
137 actions: &mut A,
138 seq_num: u16,
139 status_code: fidl_ieee80211::StatusCode,
140 sae_fields: &[u8],
141 ) -> Result<AkmState, Error> {
142 match self {
143 AkmAlgorithm::_OpenAp => bail!("OpenAp akm not yet implemented"),
144 AkmAlgorithm::OpenSupplicant { .. } => {
145 bail!("Open supplicant cannot transmit SAE frames")
146 }
147 AkmAlgorithm::Sae { .. } => {
148 actions.send_auth_frame(
149 mac::AuthAlgorithmNumber::SAE,
150 seq_num,
151 status_code.into(),
152 sae_fields,
153 )?;
154 Ok(AkmState::InProgress)
156 }
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use fidl_fuchsia_wlan_mlme as fidl_mlme;
165 use wlan_common::assert_variant;
166 use wlan_common::mac::IntoBytesExt;
167
168 struct MockAkmAction {
169 sent_frames: Vec<(mac::AuthAlgorithmNumber, u16, mac::StatusCode, Vec<u8>)>,
170 sent_sae_rx: Vec<(u16, fidl_ieee80211::StatusCode, Vec<u8>)>,
171 accept_frames: bool,
172 published_pmks: Vec<fidl_mlme::PmkInfo>,
173 sae_ind_sent: u16,
174 }
175
176 impl MockAkmAction {
177 fn new() -> Self {
178 MockAkmAction {
179 sent_frames: vec![],
180 sent_sae_rx: vec![],
181 accept_frames: true,
182 published_pmks: vec![],
183 sae_ind_sent: 0,
184 }
185 }
186 }
187
188 impl AkmAction for MockAkmAction {
189 fn send_auth_frame(
190 &mut self,
191 auth_type: mac::AuthAlgorithmNumber,
192 seq_num: u16,
193 status_code: mac::StatusCode,
194 auth_content: &[u8],
195 ) -> Result<(), Error> {
196 if self.accept_frames {
197 self.sent_frames.push((auth_type, seq_num, status_code, auth_content.to_vec()));
198 Ok(())
199 } else {
200 bail!("send_auth_frames disabled by test");
201 }
202 }
203
204 fn forward_sme_sae_rx(
205 &mut self,
206 seq_num: u16,
207 status_code: fidl_ieee80211::StatusCode,
208 sae_fields: Vec<u8>,
209 ) {
210 self.sent_sae_rx.push((seq_num, status_code, sae_fields))
211 }
212
213 fn forward_sae_handshake_ind(&mut self) {
214 self.sae_ind_sent += 1
215 }
216 }
217
218 #[test]
219 fn open_supplicant_success() {
220 let mut actions = MockAkmAction::new();
221 let mut supplicant = AkmAlgorithm::OpenSupplicant;
222
223 assert_variant!(supplicant.initiate(&mut actions), Ok(AkmState::InProgress));
225 assert_eq!(actions.sent_frames.len(), 1);
226 assert_eq!(
227 actions.sent_frames.remove(0),
228 (mac::AuthAlgorithmNumber::OPEN, 1, fidl_ieee80211::StatusCode::Success.into(), vec![])
229 );
230
231 assert_variant!(
232 supplicant.handle_auth_frame(
233 &mut actions,
234 mac::AuthFrame {
236 auth_hdr: mac::AuthHdr {
237 auth_alg_num: mac::AuthAlgorithmNumber::OPEN,
238 auth_txn_seq_num: 2,
239 status_code: fidl_ieee80211::StatusCode::Success.into(),
240 }
241 .as_bytes_ref(),
242 elements: &[][..],
243 },
244 ),
245 Ok(AkmState::AuthComplete)
246 );
247
248 assert_eq!(actions.sent_frames.len(), 0);
250 assert_eq!(actions.published_pmks.len(), 0);
251 }
252
253 #[test]
254 fn open_supplicant_reject() {
255 let mut actions = MockAkmAction::new();
256 let mut supplicant = AkmAlgorithm::OpenSupplicant;
257
258 assert_variant!(supplicant.initiate(&mut actions), Ok(AkmState::InProgress));
260 assert_eq!(actions.sent_frames.len(), 1);
261 actions.sent_frames.clear();
262
263 assert_variant!(
264 supplicant.handle_auth_frame(
265 &mut actions,
266 mac::AuthFrame {
268 auth_hdr: mac::AuthHdr {
269 auth_alg_num: mac::AuthAlgorithmNumber::OPEN,
270 auth_txn_seq_num: 2,
271 status_code: fidl_ieee80211::StatusCode::RefusedReasonUnspecified.into(),
272 }
273 .as_bytes_ref(),
274 elements: &[][..],
275 },
276 ),
277 Ok(AkmState::Failed)
278 );
279
280 assert_eq!(actions.sent_frames.len(), 0);
282 assert_eq!(actions.published_pmks.len(), 0);
283 }
284
285 #[test]
286 fn sae_supplicant_success() {
287 let mut actions = MockAkmAction::new();
288 let mut supplicant = AkmAlgorithm::Sae;
289
290 assert_variant!(supplicant.initiate(&mut actions), Ok(AkmState::InProgress));
291 assert_eq!(actions.sae_ind_sent, 1);
292 assert_eq!(actions.sent_frames.len(), 0);
293
294 assert_variant!(
298 supplicant.handle_sme_sae_tx(
299 &mut actions,
300 1,
301 fidl_ieee80211::StatusCode::Success,
302 &[0x12, 0x34][..],
303 ),
304 Ok(AkmState::InProgress)
305 );
306 assert_eq!(actions.sent_frames.len(), 1);
307 assert_eq!(
308 actions.sent_frames[0],
309 (
310 mac::AuthAlgorithmNumber::SAE,
311 1,
312 fidl_ieee80211::StatusCode::Success.into(),
313 vec![0x12, 0x34]
314 )
315 );
316 actions.sent_frames.clear();
317
318 assert_variant!(
319 supplicant.handle_auth_frame(
320 &mut actions,
321 mac::AuthFrame {
322 auth_hdr: mac::AuthHdr {
323 auth_alg_num: mac::AuthAlgorithmNumber::SAE,
324 auth_txn_seq_num: 1,
325 status_code: fidl_ieee80211::StatusCode::Success.into(),
326 }
327 .as_bytes_ref(),
328 elements: &[0x56, 0x78][..],
329 },
330 ),
331 Ok(AkmState::InProgress)
332 );
333 assert_eq!(actions.sent_sae_rx.len(), 1);
334 assert_eq!(
335 actions.sent_sae_rx[0],
336 (1, fidl_ieee80211::StatusCode::Success, vec![0x56, 0x78])
337 );
338 actions.sent_sae_rx.clear();
339
340 assert_variant!(
341 supplicant.handle_sae_resp(&mut actions, fidl_ieee80211::StatusCode::Success),
342 Ok(AkmState::AuthComplete)
343 );
344 }
345
346 #[test]
347 fn sae_supplicant_rejected() {
348 let mut actions = MockAkmAction::new();
349 let mut supplicant = AkmAlgorithm::Sae;
350
351 assert_variant!(supplicant.initiate(&mut actions), Ok(AkmState::InProgress));
352 assert_variant!(
353 supplicant.handle_sae_resp(
354 &mut actions,
355 fidl_ieee80211::StatusCode::RefusedReasonUnspecified
356 ),
357 Ok(AkmState::Failed)
358 );
359 }
360}