Skip to main content

wlan_rsn/auth/
mod.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod psk;
6
7use crate::Error;
8use crate::key::Pmk;
9use crate::key::exchange::Key;
10use crate::rsna::{
11    AuthRejectedReason, AuthStatus, Dot11VerifiedKeyFrame, SecAssocUpdate, UpdateSink,
12};
13use fidl_fuchsia_wlan_mlme::SaeFrame;
14use ieee80211::{MacAddr, MacAddrBytes, Ssid};
15use log::warn;
16use wlan_common::ie::rsn::akm::{AKM_OWE, AKM_SAE};
17use wlan_fcg_crypto::{owe, sae};
18use zerocopy::SplitByteSlice;
19
20/// IEEE Std 802.11-2016, 12.4.4.1
21/// Elliptic curve group 19 is the default supported group -- all SAE peers must support it, and in
22/// practice it is generally used.
23const DEFAULT_GROUP_ID: u16 = 19;
24
25#[derive(Error, Debug)]
26pub enum AuthError {
27    #[error("Failed to construct auth method from the given configuration: {:?}", _0)]
28    FailedConstruction(anyhow::Error),
29    #[error("Non-SAE auth method received an SAE event")]
30    UnexpectedSaeEvent,
31    #[error("Non-OWE auth method received an OWE event")]
32    UnexpectedOweEvent,
33    #[error("Failed to initiate OWE: {:?}", _0)]
34    FailedInitiateOwe(anyhow::Error),
35    #[error("Failed to handle OWE public key: {:?}", _0)]
36    FailedHandleOwePublicKey(anyhow::Error),
37}
38
39pub struct SaeData {
40    peer: MacAddr,
41    pub pmk: Option<sae::Key>,
42    handshake: Box<dyn sae::SaeHandshake>,
43    // Our timer interface does not support cancellation, so we instead use a counter to skip
44    // outdated timouts.
45    retransmit_timeout_id: u64,
46}
47
48pub struct OweData {
49    pub pmk: Option<Vec<u8>>,
50    handshake: Box<dyn owe::ClientOweHandshake>,
51}
52
53#[derive(Debug, PartialEq, Clone)]
54pub enum Config {
55    ComputedPsk(psk::Psk),
56    Sae {
57        ssid: Ssid,
58        password: Vec<u8>,
59        mac: MacAddr,
60        peer_mac: MacAddr,
61        pwe_method: sae::PweMethod,
62    },
63    DriverSae {
64        password: Vec<u8>,
65    },
66    Owe,
67}
68
69impl Config {
70    pub fn method_name(&self) -> MethodName {
71        match self {
72            Config::ComputedPsk(_) => MethodName::Psk,
73            Config::Sae { .. } | Config::DriverSae { .. } => MethodName::Sae,
74            Config::Owe => MethodName::Owe,
75        }
76    }
77}
78
79pub enum Method {
80    Psk(psk::Psk),
81    Sae(SaeData),
82    /// SAE handled in the driver/firmware, so the PMK will just eventually arrive.
83    DriverSae(Option<sae::Key>),
84    Owe(OweData),
85}
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub enum MethodName {
89    Psk,
90    Sae,
91    Owe,
92}
93
94impl std::fmt::Debug for Method {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
96        match self {
97            Self::Psk(psk) => write!(f, "Method::Psk({:?})", psk),
98            Self::Sae(sae_data) => write!(
99                f,
100                "Method::Sae {{ peer: {:?}, pmk: {}, .. }}",
101                sae_data.peer,
102                match sae_data.pmk {
103                    Some(_) => "Some(_)",
104                    None => "None",
105                }
106            ),
107            Self::DriverSae(key) => write!(f, "Method::DriverSae({:?})", key),
108            Self::Owe(owe_data) => write!(
109                f,
110                "Method::Owe {{ pmk: {}, .. }}",
111                match owe_data.pmk {
112                    Some(_) => "Some(_)",
113                    None => "None",
114                }
115            ),
116        }
117    }
118}
119
120impl Method {
121    pub fn from_config(cfg: Config) -> Result<Method, AuthError> {
122        match cfg {
123            Config::ComputedPsk(psk) => Ok(Method::Psk(psk)),
124            Config::Sae { ssid, password, mac, peer_mac, pwe_method } => {
125                // TODO(https://fxbug.dev/42173568): Use PweMethod::Direct here for SAE Hash-to-Element.
126                let handshake = sae::new_sae_handshake(
127                    DEFAULT_GROUP_ID,
128                    AKM_SAE,
129                    pwe_method,
130                    ssid,
131                    password,
132                    None, // Not required for PweMethod::Loop
133                    mac,
134                    peer_mac.clone(),
135                )
136                .map_err(AuthError::FailedConstruction)?;
137                Ok(Method::Sae(SaeData {
138                    peer: peer_mac,
139                    pmk: None,
140                    handshake,
141                    retransmit_timeout_id: 0,
142                }))
143            }
144            Config::DriverSae { .. } => Ok(Method::DriverSae(None)),
145            Config::Owe => {
146                let handshake = owe::new_client_owe_handshake(DEFAULT_GROUP_ID, AKM_OWE)
147                    .map_err(AuthError::FailedConstruction)?;
148                Ok(Method::Owe(OweData { pmk: None, handshake }))
149            }
150        }
151    }
152
153    // Unused as only PSK is supported so far.
154    pub fn on_eapol_key_frame<B: SplitByteSlice>(
155        &self,
156        _update_sink: &mut UpdateSink,
157        _frame: Dot11VerifiedKeyFrame<B>,
158    ) -> Result<(), AuthError> {
159        Ok(())
160    }
161
162    /// Currently only used so that an SAE handshake managed in firmware can send
163    /// the PMK upward.
164    pub fn on_pmk_available(
165        &mut self,
166        pmk: &[u8],
167        pmkid: &[u8],
168        assoc_update_sink: &mut UpdateSink,
169    ) -> Result<(), AuthError> {
170        match self {
171            Method::DriverSae(key) => {
172                key.replace(sae::Key { pmk: pmk.to_vec(), pmkid: pmkid.to_vec() });
173                assoc_update_sink.push(SecAssocUpdate::Key(Key::Pmk(Pmk::new(
174                    pmk.to_vec(),
175                    Some(pmkid.to_vec()),
176                ))));
177                Ok(())
178            }
179            _ => Err(AuthError::UnexpectedSaeEvent),
180        }
181    }
182
183    pub fn on_sae_handshake_ind(
184        &mut self,
185        assoc_update_sink: &mut UpdateSink,
186    ) -> Result<(), AuthError> {
187        match self {
188            Method::Sae(sae_data) => {
189                let mut sae_update_sink = sae::SaeUpdateSink::default();
190                sae_data.handshake.initiate_sae(&mut sae_update_sink);
191                process_sae_updates(sae_data, assoc_update_sink, sae_update_sink);
192                Ok(())
193            }
194            _ => Err(AuthError::UnexpectedSaeEvent),
195        }
196    }
197
198    pub fn on_sae_frame_rx(
199        &mut self,
200        assoc_update_sink: &mut UpdateSink,
201        frame: SaeFrame,
202    ) -> Result<(), AuthError> {
203        match self {
204            Method::Sae(sae_data) => {
205                let mut sae_update_sink = sae::SaeUpdateSink::default();
206                let frame_rx = sae::AuthFrameRx {
207                    seq: frame.seq_num,
208                    status_code: frame.status_code,
209                    body: &frame.sae_fields[..],
210                };
211                sae_data.handshake.handle_frame(&mut sae_update_sink, &frame_rx);
212                process_sae_updates(sae_data, assoc_update_sink, sae_update_sink);
213                Ok(())
214            }
215            _ => Err(AuthError::UnexpectedSaeEvent),
216        }
217    }
218
219    pub fn on_sae_timeout(
220        &mut self,
221        assoc_update_sink: &mut UpdateSink,
222        event_id: u64,
223    ) -> Result<(), AuthError> {
224        match self {
225            Method::Sae(sae_data) => {
226                if sae_data.retransmit_timeout_id == event_id {
227                    sae_data.retransmit_timeout_id += 1;
228                    let mut sae_update_sink = sae::SaeUpdateSink::default();
229                    sae_data
230                        .handshake
231                        .handle_timeout(&mut sae_update_sink, sae::Timeout::Retransmission);
232                    process_sae_updates(sae_data, assoc_update_sink, sae_update_sink);
233                }
234                Ok(())
235            }
236            _ => Err(AuthError::UnexpectedSaeEvent),
237        }
238    }
239
240    pub fn initiate_owe(&mut self, assoc_update_sink: &mut UpdateSink) -> Result<(), AuthError> {
241        match self {
242            Method::Owe(owe_data) => {
243                let mut owe_update_sink = owe::OweUpdateSink::default();
244                owe_data
245                    .handshake
246                    .initiate_owe(&mut owe_update_sink)
247                    .map_err(AuthError::FailedInitiateOwe)?;
248                process_owe_updates(owe_data, assoc_update_sink, owe_update_sink);
249                Ok(())
250            }
251            _ => Err(AuthError::UnexpectedOweEvent),
252        }
253    }
254
255    pub fn on_owe_public_key_rx(
256        &mut self,
257        assoc_update_sink: &mut UpdateSink,
258        group: u16,
259        public_key: Vec<u8>,
260    ) -> Result<(), AuthError> {
261        match self {
262            Method::Owe(owe_data) => {
263                let mut owe_update_sink = owe::OweUpdateSink::default();
264                owe_data
265                    .handshake
266                    .handle_public_key(&mut owe_update_sink, group, public_key)
267                    .map_err(AuthError::FailedHandleOwePublicKey)?;
268                process_owe_updates(owe_data, assoc_update_sink, owe_update_sink);
269                Ok(())
270            }
271            _ => Err(AuthError::UnexpectedOweEvent),
272        }
273    }
274}
275
276fn process_sae_updates(
277    sae_data: &mut SaeData,
278    assoc_update_sink: &mut UpdateSink,
279    sae_update_sink: sae::SaeUpdateSink,
280) {
281    for sae_update in sae_update_sink {
282        match sae_update {
283            sae::SaeUpdate::SendFrame(frame) => {
284                let sae_frame = SaeFrame {
285                    peer_sta_address: sae_data.peer.clone().to_array(),
286                    status_code: frame.status_code,
287                    seq_num: frame.seq,
288                    sae_fields: frame.body,
289                };
290                assoc_update_sink.push(SecAssocUpdate::TxSaeFrame(sae_frame));
291            }
292            sae::SaeUpdate::Success(key) => {
293                sae_data.pmk.replace(key.clone());
294                assoc_update_sink
295                    .push(SecAssocUpdate::Key(Key::Pmk(Pmk::new(key.pmk, Some(key.pmkid)))));
296                assoc_update_sink.push(SecAssocUpdate::SaeAuthStatus(AuthStatus::Success));
297            }
298            sae::SaeUpdate::Reject(reason) => {
299                warn!("SAE handshake rejected: {:?}", reason);
300                let status = match reason {
301                    sae::RejectReason::AuthFailed => {
302                        AuthStatus::Rejected(AuthRejectedReason::AuthFailed)
303                    }
304                    sae::RejectReason::KeyExpiration => {
305                        AuthStatus::Rejected(AuthRejectedReason::PmksaExpired)
306                    }
307                    sae::RejectReason::TooManyRetries => {
308                        AuthStatus::Rejected(AuthRejectedReason::TooManyRetries)
309                    }
310                    sae::RejectReason::InternalError(_) => AuthStatus::InternalError,
311                };
312                assoc_update_sink.push(SecAssocUpdate::SaeAuthStatus(status));
313            }
314            sae::SaeUpdate::ResetTimeout(timer) => {
315                match timer {
316                    sae::Timeout::KeyExpiration => (), // We don't use this event.
317                    sae::Timeout::Retransmission => {
318                        sae_data.retransmit_timeout_id += 1;
319                        assoc_update_sink.push(SecAssocUpdate::ScheduleSaeTimeout(
320                            sae_data.retransmit_timeout_id,
321                        ));
322                    }
323                };
324            }
325            sae::SaeUpdate::CancelTimeout(timer) => {
326                match timer {
327                    sae::Timeout::KeyExpiration => (),
328                    sae::Timeout::Retransmission => {
329                        sae_data.retransmit_timeout_id += 1;
330                    }
331                };
332            }
333        }
334    }
335}
336
337fn process_owe_updates(
338    owe_data: &mut OweData,
339    assoc_update_sink: &mut UpdateSink,
340    owe_update_sink: owe::OweUpdateSink,
341) {
342    for owe_update in owe_update_sink {
343        match owe_update {
344            owe::OweUpdate::TxPublicKey { group_id, key } => {
345                assoc_update_sink.push(SecAssocUpdate::TxOwePublicKey { group_id, key });
346            }
347            owe::OweUpdate::Success { key } => {
348                owe_data.pmk.replace(key.clone());
349                assoc_update_sink.push(SecAssocUpdate::Key(Key::Pmk(Pmk::from_pmk(key))));
350            }
351        }
352    }
353}
354
355#[cfg(test)]
356mod test {
357    use super::*;
358    use assert_matches::assert_matches;
359    use fuchsia_sync::Mutex;
360    use std::sync::Arc;
361
362    #[test]
363    fn psk_rejects_sae() {
364        let mut auth = Method::from_config(Config::ComputedPsk(Box::new([0x8; 16])))
365            .expect("Failed to construct PSK auth method");
366        let mut sink = UpdateSink::default();
367        auth.on_sae_handshake_ind(&mut sink).expect_err("PSK auth method accepted SAE ind");
368        let frame = SaeFrame {
369            peer_sta_address: [0xaa; 6],
370            status_code: fidl_fuchsia_wlan_ieee80211::StatusCode::Success,
371            seq_num: 1,
372            sae_fields: vec![0u8; 10],
373        };
374        auth.on_sae_frame_rx(&mut sink, frame).expect_err("PSK auth method accepted SAE frame");
375        // No updates should be queued for these invalid ops.
376        assert!(sink.is_empty());
377    }
378
379    #[derive(Default)]
380    struct SaeCounter {
381        initiated: bool,
382        handled_commits: u32,
383        handled_confirms: u32,
384        handled_timeouts: u32,
385    }
386
387    struct DummySae(Arc<Mutex<SaeCounter>>);
388
389    // This sends dummy frames as though it is the SAE initiator.
390    impl sae::SaeHandshake for DummySae {
391        fn initiate_sae(&mut self, sink: &mut sae::SaeUpdateSink) {
392            self.0.lock().initiated = true;
393            sink.push(sae::SaeUpdate::SendFrame(sae::AuthFrameTx {
394                seq: 1,
395                status_code: fidl_fuchsia_wlan_ieee80211::StatusCode::Success,
396                body: vec![],
397            }));
398        }
399        fn handle_commit(
400            &mut self,
401            _sink: &mut sae::SaeUpdateSink,
402            _commit_msg: &sae::CommitMsg<'_>,
403        ) {
404            assert!(self.0.lock().initiated);
405            self.0.lock().handled_commits += 1;
406        }
407        fn handle_confirm(
408            &mut self,
409            sink: &mut sae::SaeUpdateSink,
410            _confirm_msg: &sae::ConfirmMsg<'_>,
411        ) {
412            assert!(self.0.lock().initiated);
413            self.0.lock().handled_confirms += 1;
414            sink.push(sae::SaeUpdate::SendFrame(sae::AuthFrameTx {
415                seq: 2,
416                status_code: fidl_fuchsia_wlan_ieee80211::StatusCode::Success,
417                body: vec![],
418            }));
419            sink.push(sae::SaeUpdate::Success(sae::Key { pmk: vec![0xaa], pmkid: vec![0xbb] }))
420        }
421        fn handle_anti_clogging_token(
422            &mut self,
423            _sink: &mut sae::SaeUpdateSink,
424            _msg: &sae::AntiCloggingTokenMsg<'_>,
425        ) {
426            panic!("The SAE initiator should never receive an anti-clogging token.");
427        }
428        fn handle_timeout(&mut self, _sink: &mut sae::SaeUpdateSink, _timeout: sae::Timeout) {
429            self.0.lock().handled_timeouts += 1;
430        }
431    }
432
433    // These are not valid commit and confirm bodies, but are appropriately sized so they will parse.
434
435    const COMMIT: [u8; 98] = [
436        0x13, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
437        0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
438        0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
439        0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
440        0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
441        0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
442        0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
443    ];
444    const CONFIRM: [u8; 34] = [
445        0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
446        0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
447        0xbb, 0xbb, 0xbb, 0xbb,
448    ];
449
450    #[test]
451    fn sae_executes_handshake() {
452        let sae_counter = Arc::new(Mutex::new(SaeCounter::default()));
453        let mut auth = Method::Sae(SaeData {
454            peer: MacAddr::from([0xaa; 6]),
455            pmk: None,
456            handshake: Box::new(DummySae(sae_counter.clone())),
457            retransmit_timeout_id: 0,
458        });
459        let mut sink = UpdateSink::default();
460
461        auth.on_sae_handshake_ind(&mut sink).expect("SAE handshake should accept SAE ind");
462        assert!(sae_counter.lock().initiated);
463        assert_matches!(sink.pop(), Some(SecAssocUpdate::TxSaeFrame(_)));
464
465        let commit_frame = SaeFrame {
466            peer_sta_address: [0xaa; 6],
467            status_code: fidl_fuchsia_wlan_ieee80211::StatusCode::Success,
468            seq_num: 1,
469            sae_fields: COMMIT.to_vec(),
470        };
471        auth.on_sae_frame_rx(&mut sink, commit_frame).expect("SAE handshake should accept commit");
472        assert_eq!(sae_counter.lock().handled_commits, 1);
473        assert!(sink.is_empty());
474
475        let confirm_frame = SaeFrame {
476            peer_sta_address: [0xaa; 6],
477            status_code: fidl_fuchsia_wlan_ieee80211::StatusCode::Success,
478            seq_num: 2,
479            sae_fields: CONFIRM.to_vec(),
480        };
481        auth.on_sae_frame_rx(&mut sink, confirm_frame)
482            .expect("SAE handshake should accept confirm");
483        assert_eq!(sae_counter.lock().handled_confirms, 1);
484        assert_eq!(sink.len(), 3);
485        assert_matches!(sink.remove(0), SecAssocUpdate::TxSaeFrame(_));
486        assert_matches!(sink.remove(0), SecAssocUpdate::Key(_));
487        assert_matches!(sink.remove(0), SecAssocUpdate::SaeAuthStatus(AuthStatus::Success));
488        match auth {
489            Method::Sae(sae_data) => assert!(sae_data.pmk.is_some()),
490            _ => unreachable!(),
491        };
492    }
493
494    #[test]
495    fn sae_handles_current_timeouts() {
496        let sae_counter = Arc::new(Mutex::new(SaeCounter::default()));
497        let mut sae = Method::Sae(SaeData {
498            peer: MacAddr::from([0xaa; 6]),
499            pmk: None,
500            handshake: Box::new(DummySae(sae_counter.clone())),
501            retransmit_timeout_id: 0,
502        });
503        let mut sink = UpdateSink::default();
504
505        if let Method::Sae(data) = &mut sae {
506            process_sae_updates(
507                data,
508                &mut sink,
509                vec![sae::SaeUpdate::ResetTimeout(sae::Timeout::Retransmission)],
510            );
511        };
512        let event_id = assert_matches!(sink.pop(),
513            Some(SecAssocUpdate::ScheduleSaeTimeout(id)) => id
514        );
515        sae.on_sae_timeout(&mut sink, event_id).expect("SAE handshake should accept timeout");
516        assert_eq!(sae_counter.lock().handled_timeouts, 1);
517        // Don't handle the same timeout twice.
518        sae.on_sae_timeout(&mut sink, event_id).expect("SAE handshake should accept timeout");
519        assert_eq!(sae_counter.lock().handled_timeouts, 1); // No timeout handled.
520
521        // Don't handle a cancelled timeout.
522        if let Method::Sae(data) = &mut sae {
523            process_sae_updates(
524                data,
525                &mut sink,
526                vec![
527                    sae::SaeUpdate::ResetTimeout(sae::Timeout::Retransmission),
528                    sae::SaeUpdate::CancelTimeout(sae::Timeout::Retransmission),
529                ],
530            );
531        };
532        let event_id = assert_matches!(sink.pop(),
533                Some(SecAssocUpdate::ScheduleSaeTimeout(id)) => id
534        );
535        sae.on_sae_timeout(&mut sink, event_id).expect("SAE handshake should accept timeout");
536        assert_eq!(sae_counter.lock().handled_timeouts, 1); // No timeout handled.
537    }
538
539    #[test]
540    fn sae_key_expiration_no_op() {
541        let sae_counter = Arc::new(Mutex::new(SaeCounter::default()));
542        let mut data = SaeData {
543            peer: MacAddr::from([0xaa; 6]),
544            pmk: None,
545            handshake: Box::new(DummySae(sae_counter.clone())),
546            retransmit_timeout_id: 0,
547        };
548        let mut sink = UpdateSink::new();
549        process_sae_updates(
550            &mut data,
551            &mut sink,
552            vec![
553                sae::SaeUpdate::ResetTimeout(sae::Timeout::KeyExpiration),
554                sae::SaeUpdate::CancelTimeout(sae::Timeout::KeyExpiration),
555            ],
556        );
557        assert!(sink.is_empty(), "KeyExpiration should not produce updates.");
558    }
559
560    #[test]
561    fn driver_sae_handles_pmk() {
562        let mut auth = Method::from_config(Config::DriverSae { password: vec![0xbb; 8] })
563            .expect("Failed to construct PSK auth method");
564        let mut sink = UpdateSink::default();
565        auth.on_pmk_available(&[0xcc; 8][..], &[0xdd; 8][..], &mut sink)
566            .expect("Driver SAE should handle on_pmk_available");
567        assert_eq!(sink.len(), 1);
568        let pmk = assert_matches!(sink.get(0), Some(SecAssocUpdate::Key(Key::Pmk(pmk))) => pmk);
569        assert_eq!(pmk.pmk, vec![0xcc; 8]);
570        assert_eq!(pmk.pmkid, Some(vec![0xdd; 8]));
571    }
572
573    #[test]
574    fn driver_sae_rejects_sme_sae_calls() {
575        let mut auth = Method::from_config(Config::DriverSae { password: vec![0xbb; 8] })
576            .expect("Failed to construct PSK auth method");
577        let mut sink = UpdateSink::default();
578        auth.on_sae_handshake_ind(&mut sink).expect_err("Driver SAE shouldn't handle SAE ind");
579        let frame = SaeFrame {
580            peer_sta_address: [0xaa; 6],
581            status_code: fidl_fuchsia_wlan_ieee80211::StatusCode::Success,
582            seq_num: 1,
583            sae_fields: COMMIT.to_vec(),
584        };
585        auth.on_sae_frame_rx(&mut sink, frame).expect_err("Driver SAE shouldn't handle frames");
586        auth.on_sae_timeout(&mut sink, 0).expect_err("Driver SAE shouldn't handle SAE timeouts");
587        assert!(sink.is_empty());
588    }
589
590    #[test]
591    fn owe_initiates_and_handles_public_key() {
592        let mut auth =
593            Method::from_config(Config::Owe).expect("Failed to construct OWE auth method");
594        let mut sink = UpdateSink::default();
595
596        auth.initiate_owe(&mut sink).expect("OWE handshake should initiate");
597        assert_eq!(sink.len(), 1);
598        let (group_id, key) = assert_matches!(sink.remove(0),
599            SecAssocUpdate::TxOwePublicKey { group_id, key } => (group_id, key)
600        );
601        assert_eq!(group_id, 19);
602        assert!(!key.is_empty());
603
604        const AP_PUBLIC_KEY: [u8; 32] = [
605            0xa9, 0x8c, 0x47, 0xc5, 0xbd, 0xcf, 0x1d, 0x5e, 0x2c, 0x3c, 0x95, 0x8e, 0x10, 0xf3,
606            0x71, 0x61, 0xc4, 0x61, 0x02, 0x13, 0x22, 0xb2, 0x95, 0xf6, 0xc7, 0x81, 0x1e, 0xf8,
607            0x14, 0xc6, 0x03, 0x17,
608        ];
609        auth.on_owe_public_key_rx(&mut sink, group_id, AP_PUBLIC_KEY.to_vec())
610            .expect("OWE handshake should handle public key");
611        assert_eq!(sink.len(), 1);
612        let pmk = assert_matches!(sink.remove(0), SecAssocUpdate::Key(Key::Pmk(pmk)) => pmk);
613        assert!(!pmk.pmk.is_empty());
614        assert_eq!(pmk.pmkid, None);
615    }
616}