Skip to main content

wlan_rsn/
lib.rs

1// Copyright 2021 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
5#![cfg_attr(feature = "benchmarks", feature(test))]
6
7use thiserror::Error;
8
9// TODO(hahnr): Limit exports and rearrange modules.
10
11mod aes;
12pub mod auth;
13mod integrity;
14pub mod key;
15mod key_data;
16mod keywrap;
17pub mod nonce;
18mod prf;
19pub mod rsna;
20
21use crate::aes::AesError;
22use crate::key::exchange::handshake::{HandshakeMessageNumber, fourway, group_key};
23use crate::key::exchange::{self};
24use crate::rsna::esssa::EssSa;
25use crate::rsna::{Role, UpdateSink};
26use fidl_fuchsia_wlan_mlme::{EapolResultCode, SaeFrame};
27use fuchsia_sync::Mutex;
28use ieee80211::{MacAddr, Ssid};
29use log::warn;
30use std::sync::Arc;
31use wlan_common::ie::rsn::cipher::Cipher;
32use wlan_common::ie::rsn::rsne::{self, Rsne};
33use wlan_common::ie::wpa::WpaIe;
34use zerocopy::SplitByteSlice;
35
36pub use crate::auth::psk;
37pub use crate::key::Pmk;
38pub use crate::key::gtk::{self, GtkProvider};
39pub use crate::key::igtk::{self, IgtkProvider};
40pub use crate::rsna::NegotiatedProtection;
41pub use wlan_fcg_crypto::sae::PweMethod;
42
43#[derive(Debug)]
44pub struct Supplicant {
45    auth_method: auth::Method,
46    esssa: EssSa,
47    pub auth_cfg: auth::Config,
48}
49
50/// Any information (i.e. info elements) used to negotiate protection on an RSN.
51#[derive(Debug, Clone, PartialEq)]
52pub enum ProtectionInfo {
53    Rsne(Rsne),
54    LegacyWpa(WpaIe),
55}
56
57fn extract_pmk_helper(update_sink: &UpdateSink) -> Option<Pmk> {
58    for update in &update_sink[..] {
59        if let rsna::SecAssocUpdate::Key(key::exchange::Key::Pmk(pmk)) = update {
60            return Some(pmk.clone());
61        }
62    }
63    None
64}
65
66impl Supplicant {
67    /// WPA personal supplicant which supports 4-Way- and Group-Key Handshakes.
68    pub fn new_wpa_personal(
69        nonce_rdr: Arc<nonce::NonceReader>,
70        auth_cfg: auth::Config,
71        s_addr: MacAddr,
72        s_protection: ProtectionInfo,
73        a_addr: MacAddr,
74        a_protection: ProtectionInfo,
75        pmksa_caching_supported: bool,
76    ) -> Result<Supplicant, anyhow::Error> {
77        let negotiated_protection = NegotiatedProtection::from_protection(&s_protection)?;
78        let gtk_exch_cfg = Some(exchange::Config::GroupKeyHandshake(group_key::Config {
79            role: Role::Supplicant,
80            protection: negotiated_protection.clone(),
81        }));
82
83        let auth_method = auth::Method::from_config(auth_cfg.clone())?;
84        let pmk = match auth_cfg.clone() {
85            auth::Config::ComputedPsk(psk) => Some(Pmk::from_pmk(psk.to_vec())),
86            _ => None,
87        };
88        let esssa = EssSa::new(
89            Role::Supplicant,
90            pmk,
91            negotiated_protection,
92            exchange::Config::FourWayHandshake(fourway::Config::new(
93                Role::Supplicant,
94                s_addr,
95                s_protection,
96                a_addr,
97                a_protection,
98                nonce_rdr,
99                None,
100                None,
101                pmksa_caching_supported,
102            )?),
103            gtk_exch_cfg,
104        )?;
105
106        Ok(Supplicant { auth_method, esssa, auth_cfg })
107    }
108
109    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
110    pub fn start(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
111        self.esssa.initiate(update_sink)
112    }
113
114    pub fn reset(&mut self) {
115        // The replay counter must be reset so subsequent associations are not ignored.
116        self.esssa.reset_replay_counter();
117        self.esssa.reset_security_associations();
118    }
119
120    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
121    pub fn on_eapol_frame<B: SplitByteSlice>(
122        &mut self,
123        update_sink: &mut UpdateSink,
124        frame: eapol::Frame<B>,
125    ) -> Result<(), Error> {
126        self.esssa.on_eapol_frame(update_sink, frame)
127    }
128
129    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
130    pub fn on_eapol_conf(
131        &mut self,
132        update_sink: &mut UpdateSink,
133        result: EapolResultCode,
134    ) -> Result<(), Error> {
135        self.esssa.on_eapol_conf(update_sink, result)
136    }
137
138    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
139    pub fn on_rsna_retransmission_timeout(
140        &mut self,
141        update_sink: &mut UpdateSink,
142    ) -> Result<(), Error> {
143        self.esssa.on_rsna_retransmission_timeout(update_sink)
144    }
145
146    /// Can be called at anytime to determine the reason why the RSNA
147    /// is not complete. This is normally called when
148    /// the higher layer, usually SME, determines establishing the
149    /// RSNA failed, likely because of an expired timeout.
150    pub fn incomplete_reason(&self) -> Error {
151        self.esssa.incomplete_reason()
152    }
153
154    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
155    fn extract_sae_key(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
156        if let Some(pmk) = extract_pmk_helper(&update_sink) {
157            self.esssa.on_pmk_available(update_sink, pmk)?;
158        }
159        Ok(())
160    }
161
162    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
163    pub fn on_pmk_available(
164        &mut self,
165        update_sink: &mut UpdateSink,
166        pmk: &[u8],
167        pmkid: &[u8],
168    ) -> Result<(), Error> {
169        self.auth_method.on_pmk_available(pmk, pmkid, update_sink).map_err(Error::AuthError)?;
170        self.extract_sae_key(update_sink)
171    }
172
173    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
174    pub fn on_sae_handshake_ind(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
175        self.auth_method.on_sae_handshake_ind(update_sink).map_err(Error::AuthError)
176    }
177
178    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
179    pub fn on_sae_frame_rx(
180        &mut self,
181        update_sink: &mut UpdateSink,
182        frame: SaeFrame,
183    ) -> Result<(), Error> {
184        self.auth_method.on_sae_frame_rx(update_sink, frame).map_err(Error::AuthError)?;
185        self.extract_sae_key(update_sink)
186    }
187
188    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
189    pub fn on_sae_timeout(
190        &mut self,
191        update_sink: &mut UpdateSink,
192        event_id: u64,
193    ) -> Result<(), Error> {
194        self.auth_method.on_sae_timeout(update_sink, event_id).map_err(Error::AuthError)
195    }
196
197    #[allow(clippy::result_large_err, reason = "using existing Error type")]
198    pub fn initiate_owe(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
199        self.auth_method.initiate_owe(update_sink).map_err(Error::AuthError)
200    }
201
202    #[allow(clippy::result_large_err, reason = "using existing Error type")]
203    pub fn on_owe_public_key_rx(
204        &mut self,
205        update_sink: &mut UpdateSink,
206        group: u16,
207        public_key: Vec<u8>,
208    ) -> Result<(), Error> {
209        self.auth_method
210            .on_owe_public_key_rx(update_sink, group, public_key)
211            .map_err(Error::AuthError)?;
212        if let Some(pmk) = extract_pmk_helper(&update_sink) {
213            self.esssa.on_pmk_available(update_sink, pmk)?;
214        }
215        Ok(())
216    }
217}
218
219#[derive(Debug)]
220pub struct Authenticator {
221    auth_method: auth::Method,
222    esssa: EssSa,
223    pub auth_cfg: auth::Config,
224}
225
226impl Authenticator {
227    /// WPA2-PSK CCMP-128 Authenticator which supports 4-Way Handshake.
228    /// The Authenticator does not support GTK rotations.
229    pub fn new_wpa2psk_ccmp128(
230        nonce_rdr: Arc<nonce::NonceReader>,
231        gtk_provider: Arc<Mutex<gtk::GtkProvider>>,
232        psk: psk::Psk,
233        s_addr: MacAddr,
234        s_protection: ProtectionInfo,
235        a_addr: MacAddr,
236        a_protection: ProtectionInfo,
237    ) -> Result<Authenticator, anyhow::Error> {
238        let negotiated_protection = NegotiatedProtection::from_protection(&s_protection)?;
239        let auth_cfg = auth::Config::ComputedPsk(psk.clone());
240        let auth_method = auth::Method::from_config(auth_cfg.clone())?;
241        let esssa = EssSa::new(
242            Role::Authenticator,
243            Some(Pmk::from_pmk(psk.to_vec())),
244            negotiated_protection,
245            exchange::Config::FourWayHandshake(fourway::Config::new(
246                Role::Authenticator,
247                s_addr,
248                s_protection,
249                a_addr,
250                a_protection,
251                nonce_rdr,
252                Some(gtk_provider),
253                None,
254                false,
255            )?),
256            // Group-Key Handshake does not support Authenticator role yet.
257            None,
258        )?;
259
260        Ok(Authenticator { auth_method, esssa, auth_cfg })
261    }
262
263    /// WPA3 Authenticator which supports 4-Way Handshake.
264    /// The Authenticator does not support GTK rotations.
265    pub fn new_wpa3(
266        nonce_rdr: Arc<nonce::NonceReader>,
267        gtk_provider: Arc<Mutex<gtk::GtkProvider>>,
268        igtk_provider: Arc<Mutex<igtk::IgtkProvider>>,
269        ssid: Ssid,
270        password: Vec<u8>,
271        s_addr: MacAddr,
272        s_protection: ProtectionInfo,
273        a_addr: MacAddr,
274        a_protection: ProtectionInfo,
275    ) -> Result<Authenticator, anyhow::Error> {
276        let negotiated_protection = NegotiatedProtection::from_protection(&s_protection)?;
277        let auth_cfg = auth::Config::Sae {
278            ssid,
279            password,
280            mac: a_addr.clone(),
281            peer_mac: s_addr.clone(),
282            pwe_method: PweMethod::Loop,
283        };
284        let auth_method = auth::Method::from_config(auth_cfg.clone())?;
285
286        let esssa = EssSa::new(
287            Role::Authenticator,
288            None,
289            negotiated_protection,
290            exchange::Config::FourWayHandshake(fourway::Config::new(
291                Role::Authenticator,
292                s_addr,
293                s_protection,
294                a_addr,
295                a_protection,
296                nonce_rdr,
297                Some(gtk_provider),
298                Some(igtk_provider),
299                false,
300            )?),
301            // Group-Key Handshake does not support Authenticator role yet.
302            None,
303        )?;
304
305        Ok(Authenticator { auth_cfg, esssa, auth_method })
306    }
307
308    pub fn get_negotiated_protection(&self) -> &NegotiatedProtection {
309        &self.esssa.negotiated_protection
310    }
311
312    /// Resets all established Security Associations and invalidates all derived keys in this ESSSA.
313    /// The Authenticator must be reset or destroyed when the underlying 802.11 association
314    /// terminates. The replay counter is also reset.
315    pub fn reset(&mut self) {
316        self.esssa.reset_replay_counter();
317        self.esssa.reset_security_associations();
318
319        // Recreate auth_method to reset its state
320        match auth::Method::from_config(self.auth_cfg.clone()) {
321            Ok(auth_method) => self.auth_method = auth_method,
322            Err(e) => warn!("Unable to recreate auth::Method: {}", e),
323        }
324    }
325
326    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
327    /// `initiate(...)` must be called when the Authenticator should start establishing a
328    /// security association with a client.
329    /// The Authenticator must always initiate the security association in the current system as
330    /// EAPOL request frames from clients are not yet supported.
331    /// This method can be called multiple times to re-initiate the security association, however,
332    /// calling this method will invalidate all established security associations and their derived
333    /// keys.
334    pub fn initiate(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
335        self.esssa.initiate(update_sink)
336    }
337
338    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
339    /// Entry point for all incoming EAPOL frames. Incoming frames can be corrupted, invalid or of
340    /// unsupported types; the Authenticator will filter and drop all unexpected frames.
341    /// Outbound EAPOL frames, status and key updates will be pushed into the `update_sink`.
342    /// The method will return an `Error` if the frame was invalid.
343    pub fn on_eapol_frame<B: SplitByteSlice>(
344        &mut self,
345        update_sink: &mut UpdateSink,
346        frame: eapol::Frame<B>,
347    ) -> Result<(), Error> {
348        self.esssa.on_eapol_frame(update_sink, frame)
349    }
350
351    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
352    pub fn on_eapol_conf(
353        &mut self,
354        update_sink: &mut UpdateSink,
355        result: EapolResultCode,
356    ) -> Result<(), Error> {
357        self.esssa.on_eapol_conf(update_sink, result)
358    }
359
360    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
361    fn extract_sae_key(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
362        if let Some(pmk) = extract_pmk_helper(&update_sink) {
363            self.esssa.on_pmk_available(update_sink, pmk)?;
364        }
365        Ok(())
366    }
367
368    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
369    pub fn on_sae_handshake_ind(&mut self, update_sink: &mut UpdateSink) -> Result<(), Error> {
370        self.auth_method.on_sae_handshake_ind(update_sink).map_err(Error::AuthError)
371    }
372
373    #[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
374    pub fn on_sae_frame_rx(
375        &mut self,
376        update_sink: &mut UpdateSink,
377        frame: SaeFrame,
378    ) -> Result<(), Error> {
379        self.auth_method.on_sae_frame_rx(update_sink, frame).map_err(Error::AuthError)?;
380        self.extract_sae_key(update_sink)
381    }
382}
383
384#[derive(Debug, Error)]
385pub enum Error {
386    #[error("invalid OUI length; expected 3 bytes but received {}", _0)]
387    InvalidOuiLength(usize),
388    #[error("invalid PMKID length; expected 16 bytes but received {}", _0)]
389    InvalidPmkidLength(usize),
390    #[error("invalid passphrase length: {}", _0)]
391    InvalidPassphraseLen(usize),
392    #[error("passphrase is not valid UTF-8; failed to parse after byte at index: {:x}", _0)]
393    InvalidPassphraseEncoding(usize),
394    #[error("the config `{:?}` is incompatible with the auth method `{:?}`", _0, _1)]
395    IncompatibleConfig(auth::Config, String),
396    #[error("invalid bit size; must be a multiple of 8 but was {}", _0)]
397    InvalidBitSize(usize),
398    #[error("nonce could not be generated")]
399    NonceError,
400    #[error("error deriving PTK; invalid PMK")]
401    PtkHierarchyInvalidPmkError,
402    #[error("error deriving PTK; unsupported AKM suite")]
403    PtkHierarchyUnsupportedAkmError,
404    #[error("error deriving PTK; unsupported cipher suite")]
405    PtkHierarchyUnsupportedCipherError,
406    #[error("error deriving GTK; unsupported cipher suite")]
407    GtkHierarchyUnsupportedCipherError,
408    #[error("error deriving IGTK; unsupported cipher suite")]
409    IgtkHierarchyUnsupportedCipherError,
410    #[error("no GtkProvider for Authenticator")]
411    MissingGtkProvider,
412    #[error("no IgtkProvider for Authenticator with Management Frame Protection support")]
413    MissingIgtkProvider,
414    #[error("invalid supplicant protection: {}", _0)]
415    InvalidSupplicantProtection(String),
416    #[error("required group mgmt cipher does not match IgtkProvider cipher: {:?} != {:?}", _0, _1)]
417    WrongIgtkProviderCipher(Cipher, Cipher),
418    #[error("error determining group mgmt cipher: {:?} != {:?}", _0, _1)]
419    GroupMgmtCipherMismatch(Cipher, Cipher),
420    #[error("client requires management frame protection and ap is not capable")]
421    MgmtFrameProtectionRequiredByClient,
422    #[error("ap requires management frame protection and client is not capable")]
423    MgmtFrameProtectionRequiredByAp,
424    #[error("client set MFP required bit without setting MFP capability bit")]
425    InvalidClientMgmtFrameProtectionCapabilityBit,
426    #[error("ap set MFP required bit without setting MFP capability bit")]
427    InvalidApMgmtFrameProtectionCapabilityBit,
428    #[error("AES operation failed: {}", _0)]
429    Aes(AesError),
430    #[error("invalid key data length; must be at least 16 bytes and a multiple of 8: {}", _0)]
431    InvaidKeyDataLength(usize),
432    #[error("invalid key data; error code: {:?}", _0)]
433    InvalidKeyData(nom::error::ErrorKind),
434    #[error("unknown authentication method")]
435    UnknownAuthenticationMethod,
436    #[error("no AKM negotiated")]
437    InvalidNegotiatedAkm,
438    #[error("unknown key exchange method")]
439    UnknownKeyExchange,
440    #[error("cannot initiate Fourway Handshake as Supplicant")]
441    UnexpectedInitiationRequest,
442    #[error("cannot initiate Supplicant in current EssSa state")]
443    UnexpectedEsssaInitiation,
444    #[error("key frame transmission failed")]
445    KeyFrameTransmissionFailed,
446    #[error("no key frame transmission confirm received; dropped {} pending updates", _0)]
447    NoKeyFrameTransmissionConfirm(usize),
448    #[error("eapol handshake not started")]
449    EapolHandshakeNotStarted,
450    #[error("likely wrong credential")]
451    LikelyWrongCredential,
452    #[error("eapol handshake incomplete: {}", _0)]
453    EapolHandshakeIncomplete(String),
454    #[error("unsupported Key Descriptor Type: {:?}", _0)]
455    UnsupportedKeyDescriptor(eapol::KeyDescriptor),
456    #[error("unexpected Key Descriptor Type {:?}; expected {:?}", _0, _1)]
457    InvalidKeyDescriptor(eapol::KeyDescriptor, eapol::KeyDescriptor),
458    #[error("unsupported Key Descriptor Version: {:?}", _0)]
459    UnsupportedKeyDescriptorVersion(u16),
460    #[error("only PTK and GTK derivation is supported")]
461    UnsupportedKeyDerivation,
462    #[error("unexpected message: {:?}", _0)]
463    UnexpectedHandshakeMessage(HandshakeMessageNumber),
464    #[error("invalid install bit value; message: {:?}", _0)]
465    InvalidInstallBitValue(HandshakeMessageNumber),
466    #[error("error, install bit set for Group-/SMK-Handshake")]
467    InvalidInstallBitGroupSmkHandshake,
468    #[error("invalid key_ack bit value; message: {:?}", _0)]
469    InvalidKeyAckBitValue(HandshakeMessageNumber),
470    #[error("invalid key_mic bit value; message: {:?}", _0)]
471    InvalidKeyMicBitValue(HandshakeMessageNumber),
472    #[error("invalid secure bit value; message: {:?}", _0)]
473    InvalidSecureBitValue(HandshakeMessageNumber),
474    #[error("error, secure bit set by Authenticator before PTK is known")]
475    SecureBitWithUnknownPtk,
476    #[error("error, secure bit set must be set by Supplicant once PTK and GTK are known")]
477    SecureBitNotSetWithKnownPtkGtk,
478    #[error("invalid error bit value; message: {:?}", _0)]
479    InvalidErrorBitValue(HandshakeMessageNumber),
480    #[error("invalid request bit value; message: {:?}", _0)]
481    InvalidRequestBitValue(HandshakeMessageNumber),
482    #[error("error, Authenticator set request bit")]
483    InvalidRequestBitAuthenticator,
484    #[error("error, Authenticator set error bit")]
485    InvalidErrorBitAuthenticator,
486    #[error("error, Supplicant set key_ack bit")]
487    InvalidKeyAckBitSupplicant,
488    #[error("invalid encrypted_key_data bit value")]
489    InvalidEncryptedKeyDataBitValue(HandshakeMessageNumber),
490    #[error("encrypted_key_data bit requires MIC bit to be set")]
491    InvalidMicBitForEncryptedKeyData,
492    #[error("invalid key length {:?}; expected {:?}", _0, _1)]
493    InvalidKeyLength(usize, usize),
494    #[error("unsupported cipher suite")]
495    UnsupportedCipherSuite,
496    #[error("unsupported AKM suite")]
497    UnsupportedAkmSuite,
498    #[error("cannot compute MIC for key frames which haven't set their MIC bit")]
499    ComputingMicForUnprotectedFrame,
500    #[error("cannot compute MIC; error while encrypting")]
501    ComputingMicEncryptionError,
502    #[error("the key frame's MIC size ({}) differes from the expected size: {}", _0, _1)]
503    MicSizesDiffer(usize, usize),
504    #[error("invalid MIC size")]
505    InvalidMicSize,
506    #[error("invalid Nonce; expected to be non-zero")]
507    InvalidNonce(HandshakeMessageNumber),
508    #[error("invalid RSC; expected to be zero")]
509    InvalidRsc(HandshakeMessageNumber),
510    #[error("invalid key data; must not be zero")]
511    EmptyKeyData(HandshakeMessageNumber),
512    #[error("invalid key data")]
513    InvalidKeyDataContent,
514    #[error("invalid key data length; doesn't match with key data")]
515    InvalidKeyDataLength,
516    #[error("cannot validate MIC; PTK not yet derived")]
517    UnexpectedMic,
518    #[error("invalid MIC")]
519    InvalidMic,
520    #[error("cannot decrypt key data; PTK not yet derived")]
521    UnexpectedEncryptedKeyData,
522    #[error("invalid key replay counter {:?}; expected counter to be > {:?}", _0, _1)]
523    InvalidKeyReplayCounter(u64, u64),
524    #[error("invalid nonce; nonce must match nonce from 1st message")]
525    ErrorNonceDoesntMatch,
526    #[error("invalid IV; EAPOL protocol version: {:?}; message: {:?}", _0, _1)]
527    InvalidIv(eapol::ProtocolVersion, HandshakeMessageNumber),
528    #[error("PMKSA was not yet established")]
529    PmksaNotEstablished,
530    #[error("invalid nonce size; expected 32 bytes, found: {:?}", _0)]
531    InvalidNonceSize(usize),
532    #[error("invalid key data; expected negotiated protection")]
533    InvalidKeyDataProtection,
534    #[error("buffer too small; required: {}, available: {}", _0, _1)]
535    BufferTooSmall(usize, usize),
536    #[error("error, SMK-Handshake is not supported")]
537    SmkHandshakeNotSupported,
538    #[error("error, negotiated protection is invalid")]
539    InvalidNegotiatedProtection,
540    #[error("unknown integrity algorithm for negotiated protection")]
541    UnknownIntegrityAlgorithm,
542    #[error("unknown keywrap algorithm for negotiated protection")]
543    UnknownKeywrapAlgorithm,
544    #[error("eapol error, {}", _0)]
545    EapolError(eapol::Error),
546    #[error("auth error, {}", _0)]
547    AuthError(auth::AuthError),
548    #[error("rsne error, {}", _0)]
549    RsneError(rsne::Error),
550    #[error("rsne invalid subset, supplicant: {:?}, authenticator: {:?}", _0, _1)]
551    RsneInvalidSubset(rsne::Rsne, rsne::Rsne),
552    #[error("error, {}", _0)]
553    GenericError(String),
554}
555
556impl PartialEq for Error {
557    fn eq(&self, other: &Self) -> bool {
558        format!("{:?}", self) == format!("{:?}", other)
559    }
560}
561impl Eq for Error {}
562
563impl From<AesError> for Error {
564    fn from(error: AesError) -> Self {
565        Error::Aes(error)
566    }
567}
568
569#[macro_export]
570macro_rules! rsn_ensure {
571    ($cond:expr, $err:literal) => {
572        if !$cond {
573            return std::result::Result::Err(Error::GenericError($err.to_string()));
574        }
575    };
576    ($cond:expr, $err:expr $(,)?) => {
577        if !$cond {
578            return std::result::Result::Err($err);
579        }
580    };
581}
582
583#[macro_export]
584macro_rules! format_rsn_err {
585    ($msg:literal $(,)?) => {
586        // Handle $:literal as a special case to make cargo-expanded code more
587        // concise in the common case.
588        Error::GenericError($msg.to_string())
589    };
590    ($err:expr $(,)?) => ({
591        Error::GenericError($err)
592    });
593    ($fmt:expr, $($arg:tt)*) => {
594        Error::GenericError(format!($fmt, $($arg)*))
595    };
596}
597
598impl From<eapol::Error> for Error {
599    fn from(e: eapol::Error) -> Self {
600        Error::EapolError(e)
601    }
602}
603
604impl From<auth::AuthError> for Error {
605    fn from(e: auth::AuthError) -> Self {
606        Error::AuthError(e)
607    }
608}
609
610impl From<rsne::Error> for Error {
611    fn from(e: rsne::Error) -> Self {
612        Error::RsneError(e)
613    }
614}
615
616#[cfg(test)]
617mod tests {
618    use crate::key::exchange::Key;
619    use crate::rsna::{SecAssocStatus, SecAssocUpdate, test_util};
620    use crate::{Pmk, key_data};
621    use assert_matches::assert_matches;
622    use test_case::test_case;
623
624    #[test]
625    fn supplicant_extract_sae_key() {
626        let mut supplicant = test_util::get_wpa3_supplicant();
627        let mut dummy_update_sink = vec![
628            SecAssocUpdate::ScheduleSaeTimeout(123),
629            SecAssocUpdate::Key(Key::Pmk(vec![1, 2, 3, 4, 5, 6, 7, 8].into())),
630        ];
631        supplicant.extract_sae_key(&mut dummy_update_sink).expect("Failed to extract key");
632        // ESSSA should register the new PMK and report this.
633        assert_eq!(
634            dummy_update_sink,
635            vec![
636                SecAssocUpdate::ScheduleSaeTimeout(123),
637                SecAssocUpdate::Key(Key::Pmk(vec![1, 2, 3, 4, 5, 6, 7, 8].into())),
638                SecAssocUpdate::Status(SecAssocStatus::PmkSaEstablished),
639            ]
640        );
641    }
642
643    #[test]
644    fn supplicant_extract_sae_key_no_key() {
645        let mut supplicant = test_util::get_wpa3_supplicant();
646        let mut dummy_update_sink = vec![SecAssocUpdate::ScheduleSaeTimeout(123)];
647        supplicant.extract_sae_key(&mut dummy_update_sink).expect("Failed to extract key");
648        // No PMK means no new update.
649        assert_eq!(dummy_update_sink, vec![SecAssocUpdate::ScheduleSaeTimeout(123)]);
650    }
651
652    #[test]
653    fn authenticator_extract_sae_key() {
654        let mut authenticator = test_util::get_wpa3_authenticator();
655        let mut dummy_update_sink = vec![
656            SecAssocUpdate::ScheduleSaeTimeout(123),
657            SecAssocUpdate::Key(Key::Pmk(vec![1, 2, 3, 4, 5, 6, 7, 8].into())),
658        ];
659        authenticator.extract_sae_key(&mut dummy_update_sink).expect("Failed to extract key");
660        // ESSSA should register the new PMK and report this.
661        assert_eq!(
662            &dummy_update_sink[0..3],
663            vec![
664                SecAssocUpdate::ScheduleSaeTimeout(123),
665                SecAssocUpdate::Key(Key::Pmk(vec![1, 2, 3, 4, 5, 6, 7, 8].into())),
666                SecAssocUpdate::Status(SecAssocStatus::PmkSaEstablished),
667            ]
668            .as_slice(),
669        );
670
671        // ESSSA should also transmit an EAPOL frame since this is the Authenticator.
672        assert_matches!(&dummy_update_sink[3], &SecAssocUpdate::TxEapolKeyFrame { .. });
673    }
674
675    #[test]
676    fn authenticator_extract_sae_key_no_key() {
677        let mut authenticator = test_util::get_wpa3_authenticator();
678        let mut dummy_update_sink = vec![SecAssocUpdate::ScheduleSaeTimeout(123)];
679        authenticator.extract_sae_key(&mut dummy_update_sink).expect("Failed to extract key");
680        // No PMK means no new update.
681        assert_eq!(dummy_update_sink, vec![SecAssocUpdate::ScheduleSaeTimeout(123)]);
682    }
683
684    #[test]
685    fn supplicant_initiate_owe_and_handle_public_key() {
686        let mut supplicant = test_util::get_owe_supplicant();
687        let mut update_sink = vec![];
688        supplicant.initiate_owe(&mut update_sink).expect("Failed to initiate OWE");
689        assert_eq!(update_sink.len(), 1);
690        let (group_id, key) = assert_matches!(update_sink.remove(0), SecAssocUpdate::TxOwePublicKey { group_id, key } => (group_id, key));
691        assert_eq!(group_id, 19);
692        assert!(!key.is_empty());
693
694        const AP_PUBLIC_KEY: [u8; 32] = [
695            0xa9, 0x8c, 0x47, 0xc5, 0xbd, 0xcf, 0x1d, 0x5e, 0x2c, 0x3c, 0x95, 0x8e, 0x10, 0xf3,
696            0x71, 0x61, 0xc4, 0x61, 0x02, 0x13, 0x22, 0xb2, 0x95, 0xf6, 0xc7, 0x81, 0x1e, 0xf8,
697            0x14, 0xc6, 0x03, 0x17,
698        ];
699        supplicant
700            .on_owe_public_key_rx(&mut update_sink, group_id, AP_PUBLIC_KEY.to_vec())
701            .expect("Failed to handle OWE public key");
702        // After handling the AP's public key, the supplicant should derive the PMK and
703        // notify the ESSSA.
704        assert_eq!(update_sink.len(), 2);
705        let pmk = assert_matches!(update_sink.remove(0), SecAssocUpdate::Key(Key::Pmk(pmk)) => pmk);
706        assert!(!pmk.pmk.is_empty());
707        assert_eq!(update_sink.remove(0), SecAssocUpdate::Status(SecAssocStatus::PmkSaEstablished));
708    }
709
710    #[test_case(
711        vec![
712            0xa9, 0x8c, 0x47, 0xc5, 0xbd, 0xcf, 0x1d, 0x5e, 0x2c, 0x3c, 0x95, 0x8e, 0x10, 0xf3,
713            0x71, 0x61, 0xc4, 0x61, 0x02, 0x13, 0x22, 0xb2, 0x95, 0xf6, 0xc7, 0x81, 0x1e, 0xf8,
714            0x14, 0xc6, 0x03,
715        ];
716        "invalid key size"
717    )]
718    #[test_case(
719        vec![
720            0xa9, 0x8c, 0x47, 0xc5, 0xbd, 0xcf, 0x1d, 0x5e, 0x2c, 0x3c, 0x95, 0x8e, 0x10, 0xf3,
721            0x71, 0x61, 0xc4, 0x61, 0x02, 0x13, 0x22, 0xb2, 0x95, 0xf6, 0xc7, 0x81, 0x1e, 0xf8,
722            0x14, 0xc6, 0x03, 0x16,
723        ];
724        "not a valid point on curve"
725    )]
726    #[fuchsia::test(add_test_attr = false)]
727    fn supplicant_handle_public_key_wrong_key_size(public_key: Vec<u8>) {
728        let mut supplicant = test_util::get_owe_supplicant();
729        let mut update_sink = vec![];
730        supplicant.initiate_owe(&mut update_sink).expect("Failed to initiate OWE");
731        let (group_id, _key) = assert_matches!(update_sink.remove(0), SecAssocUpdate::TxOwePublicKey { group_id, key } => (group_id, key));
732
733        supplicant
734            .on_owe_public_key_rx(&mut update_sink, group_id, public_key)
735            .expect_err("Should fail due to invalid public key");
736    }
737
738    #[test_case(true; "pmksa caching supported")]
739    #[test_case(false; "pmksa caching not supported")]
740    #[fuchsia::test(add_test_attr = false)]
741    fn supplicant_driver_sae_on_pmk_available(pmksa_caching_supported: bool) {
742        let mut supplicant =
743            test_util::get_driver_sae_supplicant_with_pmksa_caching(pmksa_caching_supported);
744        let mut update_sink = vec![];
745        let pmk = vec![0x11; 32];
746        let pmkid = vec![0x22; 16];
747        supplicant
748            .on_pmk_available(&mut update_sink, &pmk, &pmkid)
749            .expect("Failed to process OnPmkAvailable");
750        assert_eq!(update_sink.len(), 2);
751        assert_eq!(
752            update_sink.remove(0),
753            SecAssocUpdate::Key(Key::Pmk(Pmk::new(pmk, Some(pmkid.clone()))))
754        );
755        assert_eq!(update_sink.remove(0), SecAssocUpdate::Status(SecAssocStatus::PmkSaEstablished));
756
757        let anonce = [0xaa; 32];
758        let msg1 = test_util::get_wpa3_4whs_msg1(&anonce[..]);
759        let msg1_frame = eapol::Frame::Key(msg1.keyframe());
760        supplicant
761            .on_eapol_frame(&mut update_sink, msg1_frame)
762            .expect("Failed to process EAPOL Msg 1");
763        let msg2 = test_util::expect_eapol_resp(&update_sink[..]);
764        let raw_key_data = &msg2.keyframe().key_data[..];
765        let elements =
766            key_data::extract_elements(raw_key_data).expect("Failed to extract key data");
767        let rsne = elements
768            .into_iter()
769            .find_map(|e| match e {
770                key_data::Element::Rsne(rsne) => Some(rsne),
771                _ => None,
772            })
773            .expect("RSNE missing in Msg 2");
774        let expected_pmkids = if pmksa_caching_supported {
775            vec![bytes::Bytes::copy_from_slice(&pmkid)]
776        } else {
777            vec![]
778        };
779        assert_eq!(rsne.pmkids, expected_pmkids);
780    }
781}