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::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#[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 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 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 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 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 None,
258 )?;
259
260 Ok(Authenticator { auth_method, esssa, auth_cfg })
261 }
262
263 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 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 pub fn reset(&mut self) {
316 self.esssa.reset_replay_counter();
317 self.esssa.reset_security_associations();
318
319 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 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 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 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 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 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 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 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 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 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}