wlan_sae/
lib.rs

1// Copyright 2019 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.
4mod boringssl;
5mod ecc;
6mod frame;
7pub mod hmac_utils;
8mod state;
9
10use anyhow::{bail, Error};
11use boringssl::{Bignum, EcGroupId};
12use fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211;
13pub use frame::{AntiCloggingTokenMsg, CommitMsg, ConfirmMsg};
14use hmac_utils::{HmacUtils, HmacUtilsImpl};
15use ieee80211::{MacAddr, Ssid};
16use log::warn;
17use mundane::hash::Sha256;
18use num::FromPrimitive;
19use wlan_common::ie::rsn::akm::{self, Akm};
20
21/// Maximum number of incorrect frames sent before SAE fails.
22const MAX_RETRIES_PER_EXCHANGE: u16 = 3;
23
24/// A shared key computed by an SAE handshake.
25#[derive(Clone, PartialEq, Debug)]
26pub struct Key {
27    pub pmk: Vec<u8>,
28    pub pmkid: Vec<u8>,
29}
30
31/// IEEE Std 802.11-2020 9.4.2.241
32/// Method used to generate the PWE from a password.
33#[derive(Debug, Clone, PartialEq)]
34pub enum PweMethod {
35    /// IEEE Std 802.11-2020, 12.4.4.2.2/12.4.4.3.2
36    /// Generate the PWE using the looping hunt-and-peck method.
37    Loop = 0,
38
39    /// IEEE Std 802.11-2020, 12.4.4.2.3/12.4.4.3.3
40    /// Generate the PWE using the direct hashing method, hash-to-curve or hash-to-element.
41    Direct = 1,
42}
43
44/// Types of timeout that are used by SAE handshakes. Duration and scheduling of these timeouts
45/// is left to the user of this library.
46#[derive(Debug, Clone, PartialEq)]
47pub enum Timeout {
48    /// Timeout before the most recent message(s) should be resent.
49    Retransmission,
50    /// Timeout before the PMK produced by a successful handshake is considered invalid.
51    KeyExpiration,
52}
53
54#[derive(Debug)]
55pub enum RejectReason {
56    /// We experienced a failure that was unrelated to data received from the peer. This likely
57    /// means we are not in a good state.
58    InternalError(Error),
59    /// Data received from the peer failed validation, and we cannot generate a PMK.
60    AuthFailed,
61    /// The peer has failed to respond or sent incorrect responses too many times.
62    TooManyRetries,
63    /// The SAE PMKSA has expired, reauthenticate.
64    KeyExpiration,
65}
66
67impl From<Error> for RejectReason {
68    fn from(e: Error) -> Self {
69        Self::InternalError(e)
70    }
71}
72
73#[derive(Debug)]
74pub struct AuthFrameRx<'a> {
75    pub seq: u16,
76    pub status_code: fidl_ieee80211::StatusCode,
77    pub body: &'a [u8],
78}
79
80#[derive(Debug, Clone, Eq, PartialEq)]
81pub struct AuthFrameTx {
82    pub seq: u16,
83    pub status_code: fidl_ieee80211::StatusCode,
84    pub body: Vec<u8>,
85}
86
87/// An update generated to progress an SAE handshake. These updates should generally be converted
88/// into a frame and sent to the SAE peer.
89#[derive(Debug)]
90pub enum SaeUpdate {
91    /// Send an auth frame to the peer.
92    SendFrame(AuthFrameTx),
93    /// Indicates the handshake is complete. The handshake should *not* be deleted at this point.
94    Success(Key),
95    /// Indicates that the handshake has failed and must be aborted or restarted.
96    Reject(RejectReason),
97    /// Request the user of the library to set or reset a timeout. If this timeout expires, it
98    /// should be passed to SaeHandshake::handle_timeout.
99    ResetTimeout(Timeout),
100    /// Request the user of the library to cancel a timeout that was previously set.
101    CancelTimeout(Timeout),
102}
103
104pub type SaeUpdateSink = Vec<SaeUpdate>;
105
106/// IEEE 802.11-2016 12.4: Simultaneous Authentication of Equals (SAE)
107///
108/// An SAE handshake with a peer is a symmetric handshake that may be used in place of open
109/// authentication as the AKM. A full handshake consists of both peers sending a Commit and Confirm
110/// frame, at which point they have both derived a shared key that is unique to those peers and that
111/// session.
112///
113/// Structs implementing this trait are responsible for handling both a successful SAE handshake,
114/// various failure modes, and edge cases such as retries and timeouts.
115///
116/// None of the functions in this trait return errors. Instead, non-fatal errors are logged, and
117/// fatal errors push an SaeUpdate::Reject to the update sink. Once an SaeUpdate::Reject is pushed,
118/// all further operations are no-ops.
119pub trait SaeHandshake: Send {
120    /// Initiate SAE by sending the first commit message. If the peer STA sends the first commit
121    /// message, handle_commit should be called first and initiate_sae should never be called.
122    fn initiate_sae(&mut self, sink: &mut SaeUpdateSink);
123
124    fn handle_commit(&mut self, sink: &mut SaeUpdateSink, commit_msg: &CommitMsg<'_>);
125    fn handle_confirm(&mut self, sink: &mut SaeUpdateSink, confirm_msg: &ConfirmMsg<'_>);
126    fn handle_anti_clogging_token(
127        &mut self,
128        sink: &mut SaeUpdateSink,
129        act_msg: &AntiCloggingTokenMsg<'_>,
130    );
131    fn handle_timeout(&mut self, sink: &mut SaeUpdateSink, timeout: Timeout);
132
133    fn handle_frame(&mut self, sink: &mut SaeUpdateSink, frame: &AuthFrameRx<'_>) {
134        match frame::parse(frame) {
135            Ok(parse) => match parse {
136                frame::ParseSuccess::Commit(commit) => self.handle_commit(sink, &commit),
137                frame::ParseSuccess::Confirm(confirm) => self.handle_confirm(sink, &confirm),
138                frame::ParseSuccess::AntiCloggingToken(act_msg) => {
139                    self.handle_anti_clogging_token(sink, &act_msg)
140                }
141            },
142            Err(e) => warn!("Failed to parse SAE auth frame: {}", e),
143        }
144    }
145}
146
147/// Creates a new SAE handshake for the given group ID and authentication parameters.
148pub fn new_sae_handshake(
149    group_id: u16,
150    akm: Akm,
151    pwe_method: PweMethod,
152    ssid: Ssid,
153    password: Vec<u8>,
154    password_id: Option<Vec<u8>>,
155    mac: MacAddr,
156    peer_mac: MacAddr,
157) -> Result<Box<dyn SaeHandshake>, Error> {
158    match akm.suite_type {
159        akm::SAE | akm::FT_SAE => (),
160        _ => bail!("Cannot construct SAE handshake with AKM {:?}", akm),
161    };
162    let (hmac, group_constructor) = match EcGroupId::from_u16(group_id) {
163        Some(EcGroupId::P256) => {
164            // IEEE 802.11-2020 12.4.2
165            // Group 19 has a 256-bit prime length, thus we use SHA256.
166            let hmac = Box::new(HmacUtilsImpl::<Sha256>::new());
167            let group_constructor = Box::new(|| {
168                ecc::Group::new(EcGroupId::P256).map(|group| {
169                    Box::new(group)
170                        as Box<
171                            dyn internal::FiniteCyclicGroup<
172                                Element = <ecc::Group as internal::FiniteCyclicGroup>::Element,
173                            >,
174                        >
175                })
176            });
177            (hmac, group_constructor)
178        }
179        _ => bail!("Unsupported SAE group id: {}", group_id),
180    };
181    Ok(Box::new(state::SaeHandshakeImpl::new(
182        group_constructor,
183        internal::SaeParameters {
184            hmac,
185            pwe_method,
186            ssid,
187            password,
188            password_id,
189            sta_a_mac: mac,
190            sta_b_mac: peer_mac,
191        },
192    )?))
193}
194
195/// Creates a new SAE handshake in response to a first message from a peer, using the FCG indiated
196/// by the peer if possible. In a successful handshake, this will immediately push a Commit and
197/// Confirm to the given update sink.
198pub fn join_sae_handshake(
199    sink: &mut SaeUpdateSink,
200    first_frame: &AuthFrameRx<'_>,
201    akm: Akm,
202    ssid: Ssid,
203    password: Vec<u8>,
204    mac: MacAddr,
205    peer_mac: MacAddr,
206) -> Result<Box<dyn SaeHandshake>, Error> {
207    let parsed_frame = frame::parse(first_frame)?;
208    match parsed_frame {
209        frame::ParseSuccess::Commit(commit) => {
210            let mut handshake = new_sae_handshake(
211                commit.group_id,
212                akm,
213                PweMethod::Loop,
214                ssid,
215                password,
216                None,
217                mac,
218                peer_mac,
219            )?;
220            handshake.handle_commit(sink, &commit);
221            Ok(handshake)
222        }
223        _ => bail!("Recieved incorrect first frame of SAE handshake"),
224    }
225}
226
227// Internal mod for structs with mod-public visibility.
228mod internal {
229    use super::*;
230
231    /// IEEE 802.11-2016 12.4.4
232    /// SAE may use many different finite cyclic groups (FCGs) to compute the various values used
233    /// during the handshake. This trait allows our SAE implementation to seamlessly handle
234    /// different classes of FCG. IEEE 802.11-2016 defines support for both elliptic curve groups
235    /// and finite field cryptography groups.
236    ///
237    /// All functions provided by this trait will only return an Error when something internal has
238    /// gone wrong.
239    pub trait FiniteCyclicGroup {
240        /// Different classes of FCG have different Element types, but scalars can always be
241        /// represented by a Bignum.
242        type Element;
243
244        fn group_id(&self) -> u16;
245
246        /// IEEE 802.11-2016 12.4.3
247        /// Generates a new password element, a secret value shared by the two peers in SAE.
248        fn generate_pwe(&self, params: &SaeParameters) -> Result<Self::Element, Error>;
249
250        /// IEEE 12.4.4.1
251        /// These three operators are used to manipulate FCG elements for the purposes of the
252        /// Diffie-Hellman key exchange used by SAE.
253        fn scalar_op(
254            &self,
255            scalar: &Bignum,
256            element: &Self::Element,
257        ) -> Result<Self::Element, Error>;
258        fn elem_op(
259            &self,
260            element1: &Self::Element,
261            element2: &Self::Element,
262        ) -> Result<Self::Element, Error>;
263        fn inverse_op(&self, element: Self::Element) -> Result<Self::Element, Error>;
264
265        /// Returns the prime order of the FCG.
266        fn order(&self) -> Result<Bignum, Error>;
267        /// IEEE 802.11-2016 12.4.5.4
268        /// Maps the given secret element to the shared secret value. Returns None if this is the
269        /// identity element for this FCG, indicating that we have in invalid secret element.
270        fn map_to_secret_value(&self, element: &Self::Element) -> Result<Option<Vec<u8>>, Error>;
271        /// IEEE 802.11-2016 12.4.2: The FCG Element must convert into an octet string such
272        /// that it may be included in the confirmation hash when completing SAE.
273        fn element_to_octets(&self, element: &Self::Element) -> Result<Vec<u8>, Error>;
274        /// Convert octets into an element. Returns None if the given octet string does not
275        /// contain a valid element for this group.
276        fn element_from_octets(&self, octets: &[u8]) -> Result<Option<Self::Element>, Error>;
277
278        /// Return the expected size of scalar and element values when serialized into a frame.
279        fn scalar_size(&self) -> Result<usize, Error> {
280            self.order().map(|order| order.len())
281        }
282    }
283
284    pub struct SaeParameters {
285        pub hmac: Box<dyn HmacUtils + Send>,
286        pub pwe_method: PweMethod,
287        // IEEE Std 802.11-2020 12.4.4.2.3/12.4.4.3.3: The SSID is needed to generate a password
288        // seed.
289        pub ssid: Ssid,
290        // IEEE Std 802.11-2020 12.4.3
291        pub password: Vec<u8>,
292        pub password_id: Option<Vec<u8>>,
293        // IEEE Std 802.11-2016 12.4.4.2.2: The MacAddrs are needed to generate a password seed.
294        pub sta_a_mac: MacAddr,
295        pub sta_b_mac: MacAddr,
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    #![allow(unused_variables)] // Allow unused variables in tests.
302    use super::*;
303    use lazy_static::lazy_static;
304    use std::convert::TryFrom;
305    use wlan_common::assert_variant;
306    use wlan_common::ie::rsn::akm::{AKM_PSK, AKM_SAE};
307
308    // IEEE 802.11-2016 Annex J.10 SAE test vector
309    const TEST_SSID: &'static str = "SSID not in 802.11-2016";
310    const TEST_PWD: &'static str = "thisisreallysecret";
311
312    lazy_static! {
313        static ref TEST_STA_A: MacAddr = MacAddr::from([0x7b, 0x88, 0x56, 0x20, 0x2d, 0x8d]);
314        static ref TEST_STA_B: MacAddr = MacAddr::from([0xe2, 0x47, 0x1c, 0x0a, 0x5a, 0xcb]);
315    }
316
317    #[test]
318    fn bad_akm() {
319        let akm = AKM_PSK;
320        let res = new_sae_handshake(
321            19,
322            akm,
323            PweMethod::Loop,
324            Ssid::try_from(TEST_SSID).unwrap(),
325            Vec::from(TEST_PWD),
326            None, // Not required for PweMethod::Loop
327            *TEST_STA_A,
328            *TEST_STA_B,
329        );
330        assert!(res.is_err());
331        assert!(format!("{}", res.err().unwrap())
332            .contains("Cannot construct SAE handshake with AKM 00-0F-AC:2"));
333    }
334
335    #[test]
336    fn bad_fcg() {
337        let akm = AKM_SAE;
338        let res = new_sae_handshake(
339            200,
340            akm,
341            PweMethod::Loop,
342            Ssid::try_from(TEST_SSID).unwrap(),
343            Vec::from(TEST_PWD),
344            None, // Not required for PweMethod::Loop
345            *TEST_STA_A,
346            *TEST_STA_B,
347        );
348        assert!(res.is_err());
349        assert!(format!("{}", res.err().unwrap()).contains("Unsupported SAE group id: 200"));
350    }
351
352    struct TestHandshake {
353        sta1: Box<dyn SaeHandshake>,
354        sta2: Box<dyn SaeHandshake>,
355    }
356
357    // Helper structs for differentiating Commit/Confirm messages once they've been converted into
358    // generic auth frames.
359    #[derive(Clone, Eq, PartialEq, Debug)]
360    struct CommitTx(AuthFrameTx);
361    #[derive(Clone, Eq, PartialEq, Debug)]
362    struct ConfirmTx(AuthFrameTx);
363    struct CommitRx<'a>(AuthFrameRx<'a>);
364    struct ConfirmRx<'a>(AuthFrameRx<'a>);
365
366    fn to_rx(frame: &AuthFrameTx) -> AuthFrameRx<'_> {
367        AuthFrameRx { seq: frame.seq, status_code: frame.status_code, body: &frame.body[..] }
368    }
369
370    impl CommitTx {
371        fn to_rx(&self) -> CommitRx<'_> {
372            CommitRx(to_rx(&self.0))
373        }
374    }
375
376    impl ConfirmTx {
377        fn to_rx(&self) -> ConfirmRx<'_> {
378            ConfirmRx(to_rx(&self.0))
379        }
380    }
381
382    impl<'a> CommitRx<'a> {
383        fn msg(&'a self) -> CommitMsg<'a> {
384            assert_variant!(frame::parse(&self.0),
385                Ok(frame::ParseSuccess::Commit(commit)) => commit)
386        }
387    }
388
389    impl<'a> ConfirmRx<'a> {
390        fn msg(&'a self) -> ConfirmMsg<'a> {
391            assert_variant!(frame::parse(&self.0),
392                Ok(frame::ParseSuccess::Confirm(confirm)) => confirm)
393        }
394    }
395
396    fn expect_commit(sink: &mut Vec<SaeUpdate>) -> CommitTx {
397        let commit = assert_variant!(sink.remove(0), SaeUpdate::SendFrame(frame) => frame);
398        assert_variant!(frame::parse(&to_rx(&commit)), Ok(frame::ParseSuccess::Commit(msg)));
399        CommitTx(commit)
400    }
401
402    fn expect_confirm(sink: &mut Vec<SaeUpdate>) -> ConfirmTx {
403        let confirm = assert_variant!(sink.remove(0), SaeUpdate::SendFrame(frame) => frame);
404        assert_variant!(frame::parse(&to_rx(&confirm)), Ok(frame::ParseSuccess::Confirm(msg)));
405        ConfirmTx(confirm)
406    }
407
408    fn expect_reset_timeout(sink: &mut Vec<SaeUpdate>, timeout: Timeout) {
409        assert_variant!(sink.remove(0), SaeUpdate::ResetTimeout(timeout));
410    }
411
412    fn expect_cancel_timeout(sink: &mut Vec<SaeUpdate>, timeout: Timeout) {
413        assert_variant!(sink.remove(0), SaeUpdate::CancelTimeout(timeout));
414    }
415
416    // Test helper to advance through successful steps of an SAE handshake.
417    impl TestHandshake {
418        fn new() -> Self {
419            let akm = AKM_SAE;
420            let sta1 = new_sae_handshake(
421                19,
422                akm.clone(),
423                PweMethod::Loop,
424                Ssid::try_from(TEST_SSID).unwrap(),
425                Vec::from(TEST_PWD),
426                None, // Not required for PweMethod::Loop
427                *TEST_STA_A,
428                *TEST_STA_B,
429            )
430            .unwrap();
431            let sta2 = new_sae_handshake(
432                19,
433                akm,
434                PweMethod::Loop,
435                Ssid::try_from(TEST_SSID).unwrap(),
436                Vec::from(TEST_PWD),
437                None, // Not required for PweMethod::Loop
438                *TEST_STA_B,
439                *TEST_STA_A,
440            )
441            .unwrap();
442            Self { sta1, sta2 }
443        }
444
445        fn sta1_init(&mut self) -> CommitTx {
446            let mut sink = vec![];
447            self.sta1.initiate_sae(&mut sink);
448            assert_eq!(sink.len(), 2);
449            let commit = expect_commit(&mut sink);
450            expect_reset_timeout(&mut sink, Timeout::Retransmission);
451            commit
452        }
453
454        fn sta2_handle_commit(&mut self, commit1: CommitRx<'_>) -> (CommitTx, ConfirmTx) {
455            let mut sink = vec![];
456            self.sta2.handle_commit(&mut sink, &commit1.msg());
457            assert_eq!(sink.len(), 3);
458            let commit2 = expect_commit(&mut sink);
459            let confirm2 = expect_confirm(&mut sink);
460            expect_reset_timeout(&mut sink, Timeout::Retransmission);
461            (commit2, confirm2)
462        }
463
464        fn sta1_handle_commit(&mut self, commit2: CommitRx<'_>) -> ConfirmTx {
465            let mut sink = vec![];
466            self.sta1.handle_commit(&mut sink, &commit2.msg());
467            assert_eq!(sink.len(), 2);
468            let confirm1 = expect_confirm(&mut sink);
469            expect_reset_timeout(&mut sink, Timeout::Retransmission);
470            confirm1
471        }
472
473        fn sta1_handle_confirm(&mut self, confirm2: ConfirmRx<'_>) -> Key {
474            Self::__internal_handle_confirm(&mut self.sta1, confirm2.msg())
475        }
476
477        fn sta2_handle_confirm(&mut self, confirm1: ConfirmRx<'_>) -> Key {
478            Self::__internal_handle_confirm(&mut self.sta2, confirm1.msg())
479        }
480
481        fn __internal_handle_confirm(
482            sta: &mut Box<dyn SaeHandshake>,
483            confirm: ConfirmMsg<'_>,
484        ) -> Key {
485            let mut sink = vec![];
486            sta.handle_confirm(&mut sink, &confirm);
487            assert_eq!(sink.len(), 3);
488            expect_cancel_timeout(&mut sink, Timeout::Retransmission);
489            expect_reset_timeout(&mut sink, Timeout::KeyExpiration);
490            assert_variant!(sink.remove(0), SaeUpdate::Success(key) => key)
491        }
492    }
493
494    #[test]
495    fn sae_handshake_success() {
496        let mut handshake = TestHandshake::new();
497        let commit1 = handshake.sta1_init();
498        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
499        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
500        let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
501        let key2 = handshake.sta2_handle_confirm(confirm1.to_rx());
502        assert_eq!(key1, key2);
503    }
504
505    #[test]
506    fn password_mismatch() {
507        let akm = AKM_SAE;
508        let sta1 = new_sae_handshake(
509            19,
510            akm.clone(),
511            PweMethod::Loop,
512            Ssid::try_from(TEST_SSID).unwrap(),
513            Vec::from(TEST_PWD),
514            None, // Not required for PweMethod::Loop
515            *TEST_STA_A,
516            *TEST_STA_B,
517        )
518        .unwrap();
519        let sta2 = new_sae_handshake(
520            19,
521            akm,
522            PweMethod::Loop,
523            Ssid::try_from(TEST_SSID).unwrap(),
524            Vec::from("other_pwd"),
525            None, // Not required for PweMethod::Loop
526            *TEST_STA_B,
527            *TEST_STA_A,
528        )
529        .unwrap();
530        let mut handshake = TestHandshake { sta1, sta2 };
531
532        let commit1 = handshake.sta1_init();
533        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
534        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
535
536        let mut sink1 = vec![];
537        handshake.sta1.handle_confirm(&mut sink1, &confirm2.to_rx().msg());
538        let mut sink2 = vec![];
539        handshake.sta2.handle_confirm(&mut sink2, &confirm1.to_rx().msg());
540        // The confirm is dropped both ways.
541        assert_eq!(sink1.len(), 0);
542        assert_eq!(sink2.len(), 0);
543    }
544
545    #[test]
546    fn retry_commit_on_unexpected_confirm() {
547        let mut handshake = TestHandshake::new();
548
549        let commit1 = handshake.sta1_init();
550        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
551        let mut sink = vec![];
552        handshake.sta1.handle_confirm(&mut sink, &confirm2.to_rx().msg());
553        assert_eq!(sink.len(), 2);
554        let commit1_retry = expect_commit(&mut sink);
555        assert_variant!(sink.remove(0), SaeUpdate::ResetTimeout(Timeout::Retransmission));
556
557        // We retransmit the same commit in response to a faulty confirm.
558        assert_eq!(commit1, commit1_retry);
559    }
560
561    #[test]
562    fn retry_commit_on_anti_clogging_token() {
563        let mut handshake = TestHandshake::new();
564
565        let commit1 = handshake.sta1_init();
566
567        // Simulate an anti-clogging token sent to sta1.
568        let mut sink = vec![];
569        let anti_clogging_token = "anticloggingtokentext";
570        let act_msg = AntiCloggingTokenMsg {
571            group_id: 19,
572            anti_clogging_token: anti_clogging_token.as_bytes(),
573        };
574        handshake.sta1.handle_anti_clogging_token(&mut sink, &act_msg);
575        let commit1_retry = expect_commit(&mut sink);
576        assert_eq!(
577            commit1_retry.clone().to_rx().msg().anti_clogging_token,
578            Some(anti_clogging_token.as_bytes())
579        );
580
581        // Finish the handshake.
582        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1_retry.to_rx());
583        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
584        let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
585        let key2 = handshake.sta2_handle_confirm(confirm1.to_rx());
586        assert_eq!(key1, key2);
587    }
588
589    #[test]
590    fn ignore_wrong_confirm() {
591        let mut handshake = TestHandshake::new();
592
593        let commit1 = handshake.sta1_init();
594        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
595        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
596
597        let mut sink = vec![];
598        let confirm2_wrong = ConfirmTx(frame::write_confirm(1, &[1; 32][..]));
599        handshake.sta1.handle_confirm(&mut sink, &confirm2_wrong.to_rx().msg());
600        assert_eq!(sink.len(), 0); // Ignored.
601
602        // STA1 should still be able to handle a subsequent correct confirm.
603        handshake.sta1_handle_confirm(confirm2.to_rx());
604    }
605
606    #[test]
607    fn handle_resent_commit() {
608        let mut handshake = TestHandshake::new();
609        let commit1 = handshake.sta1_init();
610        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
611        let (commit2_retry, confirm2_retry) = handshake.sta2_handle_commit(commit1.to_rx());
612
613        // The resent commit message should be unchanged, but the resent confirm should increment
614        // sc and produce a different value.
615        assert_eq!(commit2, commit2_retry);
616        assert_eq!(confirm2.to_rx().msg().send_confirm, 1);
617        assert_eq!(confirm2_retry.to_rx().msg().send_confirm, 2);
618        assert!(confirm2.to_rx().msg().confirm != confirm2_retry.to_rx().msg().confirm);
619
620        // Now complete the handshake.
621        let confirm1 = handshake.sta1_handle_commit(commit2_retry.to_rx());
622        let key1 = handshake.sta1_handle_confirm(confirm2_retry.to_rx());
623        let key2 = handshake.sta2_handle_confirm(confirm1.to_rx());
624        assert_eq!(key1, key2);
625    }
626
627    #[test]
628    fn completed_handshake_handles_resent_confirm() {
629        let mut handshake = TestHandshake::new();
630        let commit1 = handshake.sta1_init();
631        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
632        let (commit2_retry, confirm2_retry) = handshake.sta2_handle_commit(commit1.to_rx());
633        // Send STA1 the second confirm message first.
634        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
635        let key1 = handshake.sta1_handle_confirm(confirm2.clone().to_rx());
636
637        // STA1 should respond to the second confirm with its own confirm.
638        let mut sink = vec![];
639        handshake.sta1.handle_confirm(&mut sink, &confirm2_retry.to_rx().msg());
640        assert_eq!(sink.len(), 1);
641        let confirm1_retry = expect_confirm(&mut sink);
642        assert!(confirm1.to_rx().msg().confirm != confirm1_retry.to_rx().msg().confirm);
643        assert_eq!(confirm1_retry.to_rx().msg().send_confirm, u16::max_value());
644
645        // STA2 should complete the handshake with the resent confirm.
646        let key2 = handshake.sta2_handle_confirm(confirm1_retry.to_rx());
647        assert_eq!(key1, key2);
648
649        // STA1 should silently drop either of our confirm frames now.
650        handshake.sta1.handle_confirm(&mut sink, &confirm2_retry.to_rx().msg());
651        assert!(sink.is_empty());
652        handshake.sta1.handle_confirm(&mut sink, &confirm2.to_rx().msg());
653        assert!(sink.is_empty());
654
655        // STA1 should also silently drop an incorrect confirm, even if send_confirm is incremented.
656        let confirm2_wrong = ConfirmMsg { send_confirm: 10, confirm: &[0xab; 32][..] };
657        handshake.sta1.handle_confirm(&mut sink, &confirm2_wrong);
658        assert!(sink.is_empty());
659    }
660
661    #[test]
662    fn completed_handshake_ignores_commit() {
663        let mut handshake = TestHandshake::new();
664        let commit1 = handshake.sta1_init();
665        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
666        handshake.sta1_handle_commit(commit2.to_rx());
667        handshake.sta1_handle_confirm(confirm2.clone().to_rx());
668
669        // STA1 has completed it's side of the handshake.
670        let mut sink = vec![];
671        handshake.sta1.handle_confirm(&mut sink, &confirm2.to_rx().msg());
672        assert!(sink.is_empty());
673    }
674
675    #[test]
676    fn bad_first_commit_rejects_auth() {
677        let mut handshake = TestHandshake::new();
678        let commit1_wrong = CommitMsg {
679            group_id: 19,
680            scalar: &[0xab; 32][..],
681            element: &[0xcd; 64][..],
682            anti_clogging_token: None,
683        };
684
685        let mut sink = vec![];
686        handshake.sta1.handle_commit(&mut sink, &commit1_wrong);
687        assert_eq!(sink.len(), 1);
688        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::AuthFailed));
689    }
690
691    #[test]
692    fn bad_second_commit_ignored() {
693        let mut handshake = TestHandshake::new();
694        let commit1 = handshake.sta1_init();
695        let (_commit1, _confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
696        let commit2_wrong = CommitMsg {
697            group_id: 19,
698            scalar: &[0xab; 32][..],
699            element: &[0xcd; 64][..],
700            anti_clogging_token: None,
701        };
702        let mut sink = vec![];
703        handshake.sta1.handle_commit(&mut sink, &commit2_wrong);
704        assert_eq!(sink.len(), 0);
705    }
706
707    #[test]
708    fn reflected_commit_discarded() {
709        let mut handshake = TestHandshake::new();
710        let commit1 = handshake.sta1_init();
711
712        let mut sink = vec![];
713        handshake.sta1.handle_commit(&mut sink, &commit1.to_rx().msg());
714        assert_eq!(sink.len(), 1);
715        assert_variant!(sink.remove(0), SaeUpdate::ResetTimeout(Timeout::Retransmission));
716    }
717
718    #[test]
719    fn maximum_commit_retries() {
720        let mut handshake = TestHandshake::new();
721        let commit1 = handshake.sta1_init();
722        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
723
724        // STA2 should allow MAX_RETRIES_PER_EXCHANGE retry operations before giving up.
725        for i in 0..MAX_RETRIES_PER_EXCHANGE {
726            let (commit2_retry, confirm2_retry) =
727                handshake.sta2_handle_commit(commit1.clone().to_rx());
728            assert_eq!(commit2, commit2_retry);
729            assert_eq!(confirm2_retry.to_rx().msg().send_confirm, i + 2);
730        }
731
732        // The last straw!
733        let mut sink = vec![];
734        handshake.sta2.handle_commit(&mut sink, &commit1.to_rx().msg());
735        assert_eq!(sink.len(), 1);
736        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
737    }
738
739    #[test]
740    fn completed_exchange_fails_after_retries() {
741        let mut handshake = TestHandshake::new();
742        let commit1 = handshake.sta1_init();
743        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
744
745        // STA2 should allow MAX_RETRIES_PER_EXCHANGE retry operations before giving up. We subtract 1
746        // here for the reason explained in the note below.
747        for i in 0..(MAX_RETRIES_PER_EXCHANGE - 1) {
748            let (commit2_retry, confirm2_retry) =
749                handshake.sta2_handle_commit(commit1.clone().to_rx());
750            assert_eq!(commit2, commit2_retry);
751            assert_eq!(confirm2_retry.to_rx().msg().send_confirm, i + 2);
752        }
753
754        let mut sink = vec![];
755
756        // Generate 3 different confirm messages for our testing...
757        let confirm1_sc1 = handshake.sta1_handle_commit(commit2.clone().to_rx());
758        handshake.sta1.handle_commit(&mut sink, &commit2.to_rx().msg());
759        assert_eq!(sink.len(), 3);
760        sink.remove(0);
761        let confirm1_sc2 = expect_confirm(&mut sink);
762        sink.clear();
763        let confirm1_invalid = ConfirmMsg { send_confirm: 3, confirm: &[0xab; 32][..] };
764
765        // STA2 completes the handshake. However, one more indication that STA1 is misbehaving will
766        // immediately kill the authentication.
767        handshake.sta2_handle_confirm(confirm1_sc1.clone().to_rx());
768
769        // NOTE: We run all of the operations here two times. This is because of a quirk in the SAE
770        // state machine: while only certain operations *increment* sync, all invalid operations
771        // will *check* sync. We can test whether sync is being incremented by running twice to see
772        // if this pushes us over the MAX_RETRIES_PER_EXCHANGE threshold.
773
774        // STA2 ignores commits.
775        handshake.sta2.handle_commit(&mut sink, &commit1.to_rx().msg());
776        handshake.sta2.handle_commit(&mut sink, &commit1.to_rx().msg());
777        assert_eq!(sink.len(), 0);
778
779        // STA2 ignores invalid confirm.
780        handshake.sta2.handle_confirm(&mut sink, &confirm1_invalid);
781        handshake.sta2.handle_confirm(&mut sink, &confirm1_invalid);
782        assert_eq!(sink.len(), 0);
783
784        // STA2 ignores old confirm.
785        handshake.sta2.handle_confirm(&mut sink, &confirm1_sc1.to_rx().msg());
786        handshake.sta2.handle_confirm(&mut sink, &confirm1_sc1.to_rx().msg());
787        assert_eq!(sink.len(), 0);
788
789        // But another valid confirm increments sync!
790        handshake.sta2.handle_confirm(&mut sink, &confirm1_sc2.to_rx().msg());
791        assert_eq!(sink.len(), 1);
792        expect_confirm(&mut sink);
793        handshake.sta2.handle_confirm(&mut sink, &confirm1_sc2.to_rx().msg());
794        assert_eq!(sink.len(), 1);
795        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
796    }
797
798    #[test]
799    fn resend_commit_after_retransmission_timeout() {
800        let mut handshake = TestHandshake::new();
801        let commit1 = handshake.sta1_init();
802
803        let mut sink = vec![];
804        handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
805        let commit1_retry = expect_commit(&mut sink);
806        expect_reset_timeout(&mut sink, Timeout::Retransmission);
807        assert_eq!(commit1, commit1_retry);
808    }
809
810    #[test]
811    fn resend_confirm_after_retransmission_timeout() {
812        let mut handshake = TestHandshake::new();
813        let commit1 = handshake.sta1_init();
814        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
815
816        let mut sink = vec![];
817        handshake.sta2.handle_timeout(&mut sink, Timeout::Retransmission);
818        // On timeout we should only send commit and confirm.
819        let confirm2_retry = expect_confirm(&mut sink);
820        expect_reset_timeout(&mut sink, Timeout::Retransmission);
821        assert_eq!(
822            confirm2.to_rx().msg().send_confirm + 1,
823            confirm2_retry.to_rx().msg().send_confirm
824        );
825    }
826
827    #[test]
828    fn abort_commit_after_too_many_timeouts() {
829        let mut handshake = TestHandshake::new();
830        let commit1 = handshake.sta1_init();
831
832        let mut sink = vec![];
833        for i in 0..MAX_RETRIES_PER_EXCHANGE {
834            handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
835            let commit1_retry = expect_commit(&mut sink);
836            expect_reset_timeout(&mut sink, Timeout::Retransmission);
837            assert_eq!(commit1, commit1_retry);
838        }
839
840        // This camel can't hold another straw!
841        handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
842        assert_eq!(sink.len(), 1);
843        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
844    }
845
846    #[test]
847    fn abort_confirm_after_too_many_timeouts() {
848        let mut handshake = TestHandshake::new();
849        let commit1 = handshake.sta1_init();
850        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.clone().to_rx());
851
852        let mut sink = vec![];
853        for i in 0..MAX_RETRIES_PER_EXCHANGE {
854            handshake.sta2.handle_timeout(&mut sink, Timeout::Retransmission);
855            // On timeout we should only send commit and confirm.
856            let confirm2_retry = expect_confirm(&mut sink);
857            expect_reset_timeout(&mut sink, Timeout::Retransmission);
858            assert_eq!(
859                confirm2.to_rx().msg().send_confirm + i + 1,
860                confirm2_retry.to_rx().msg().send_confirm
861            );
862        }
863
864        handshake.sta2.handle_timeout(&mut sink, Timeout::Retransmission);
865        assert_eq!(sink.len(), 1);
866        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::TooManyRetries));
867    }
868
869    #[test]
870    fn ignore_unexpected_retransmit_timeout() {
871        let mut handshake = TestHandshake::new();
872        let mut sink = vec![];
873        // Timeout::Retransmission is ignored while in New state.
874        handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
875        assert!(sink.is_empty());
876
877        let commit1 = handshake.sta1_init();
878        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
879        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
880        let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
881
882        // Timeout::Retransmission is ignored while in Accepted state.
883        handshake.sta1.handle_timeout(&mut sink, Timeout::Retransmission);
884        assert!(sink.is_empty());
885    }
886
887    #[test]
888    fn fail_on_early_key_expiration() {
889        let mut handshake = TestHandshake::new();
890        handshake.sta1_init();
891
892        // Early key expiration indicates that something has gone very wrong, so we abort.
893        let mut sink = vec![];
894        handshake.sta1.handle_timeout(&mut sink, Timeout::KeyExpiration);
895        assert_eq!(sink.len(), 1);
896        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::InternalError(_)));
897    }
898
899    #[test]
900    fn key_expiration_timeout() {
901        let mut handshake = TestHandshake::new();
902        // Timeout::KeyExpiration is only expected once our handshake has completed.
903        let commit1 = handshake.sta1_init();
904        let (commit2, confirm2) = handshake.sta2_handle_commit(commit1.to_rx());
905        let confirm1 = handshake.sta1_handle_commit(commit2.to_rx());
906        let key1 = handshake.sta1_handle_confirm(confirm2.to_rx());
907
908        let mut sink = vec![];
909        handshake.sta1.handle_timeout(&mut sink, Timeout::KeyExpiration);
910        assert_eq!(sink.len(), 1);
911        assert_variant!(sink.remove(0), SaeUpdate::Reject(RejectReason::KeyExpiration));
912    }
913}