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