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