1pub mod wep;
48pub mod wpa;
49
50use fidl_fuchsia_wlan_common_security as fidl_security;
51use thiserror::Error;
52
53use crate::security::wep::WepKey;
54use crate::security::wpa::credential::{Passphrase, PassphraseError, Psk, PskError};
55
56pub trait CredentialsExt {
58 fn into_wep(self) -> Option<fidl_security::WepCredentials>;
59 fn into_wpa(self) -> Option<fidl_security::WpaCredentials>;
60}
61
62impl CredentialsExt for fidl_security::Credentials {
63 fn into_wep(self) -> Option<fidl_security::WepCredentials> {
64 if let fidl_security::Credentials::Wep(credentials) = self {
65 Some(credentials)
66 } else {
67 None
68 }
69 }
70
71 fn into_wpa(self) -> Option<fidl_security::WpaCredentials> {
72 if let fidl_security::Credentials::Wpa(credentials) = self {
73 Some(credentials)
74 } else {
75 None
76 }
77 }
78}
79
80#[derive(Clone, Copy, Debug, Error, Eq, PartialEq)]
81#[non_exhaustive]
82pub enum SecurityError {
83 #[error(transparent)]
84 Wep(#[from] wep::WepError),
85 #[error(transparent)]
86 Wpa(#[from] wpa::WpaError),
87 #[error("incompatible protocol or features")]
92 Incompatible,
93 #[error("unsupported protocol or features")]
99 Unsupported,
100}
101
102impl From<PassphraseError> for SecurityError {
103 fn from(error: PassphraseError) -> Self {
104 SecurityError::Wpa(error.into())
105 }
106}
107
108impl From<PskError> for SecurityError {
109 fn from(error: PskError) -> Self {
110 SecurityError::Wpa(error.into())
111 }
112}
113
114#[derive(Clone, Debug, Eq, PartialEq)]
128pub enum BareCredentials {
129 WepKey(WepKey),
131 WpaPassphrase(Passphrase),
135 WpaPsk(Psk),
140}
141
142impl From<BareCredentials> for fidl_security::Credentials {
143 fn from(credentials: BareCredentials) -> Self {
144 match credentials {
145 BareCredentials::WepKey(key) => {
146 fidl_security::Credentials::Wep(fidl_security::WepCredentials { key: key.into() })
147 }
148 BareCredentials::WpaPassphrase(passphrase) => fidl_security::Credentials::Wpa(
149 fidl_security::WpaCredentials::Passphrase(passphrase.into()),
150 ),
151 BareCredentials::WpaPsk(psk) => {
152 fidl_security::Credentials::Wpa(fidl_security::WpaCredentials::Psk(psk.into()))
153 }
154 }
155 }
156}
157
158impl TryFrom<fidl_security::Credentials> for BareCredentials {
159 type Error = SecurityError;
160
161 fn try_from(credentials: fidl_security::Credentials) -> Result<Self, Self::Error> {
162 match credentials {
163 fidl_security::Credentials::Wep(fidl_security::WepCredentials { key }) => {
164 WepKey::try_from_literal_bytes(key.as_slice())
165 .map(|key| BareCredentials::WepKey(key))
166 .map_err(From::from)
167 }
168 fidl_security::Credentials::Wpa(credentials) => match credentials {
169 fidl_security::WpaCredentials::Passphrase(passphrase) => {
170 Passphrase::try_from(passphrase)
171 .map(|passphrase| BareCredentials::WpaPassphrase(passphrase))
172 .map_err(From::from)
173 }
174 fidl_security::WpaCredentials::Psk(psk) => Psk::try_from(psk.as_slice())
175 .map(|psk| BareCredentials::WpaPsk(psk))
176 .map_err(From::from),
177 _ => Err(SecurityError::Incompatible),
179 },
180 _ => Err(SecurityError::Incompatible),
182 }
183 }
184}
185
186impl From<Passphrase> for BareCredentials {
188 fn from(passphrase: Passphrase) -> Self {
189 BareCredentials::WpaPassphrase(passphrase)
190 }
191}
192
193impl From<Psk> for BareCredentials {
195 fn from(psk: Psk) -> Self {
196 BareCredentials::WpaPsk(psk)
197 }
198}
199
200impl From<WepKey> for BareCredentials {
202 fn from(key: WepKey) -> Self {
203 BareCredentials::WepKey(key)
204 }
205}
206
207#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
209pub enum SecurityDescriptor {
210 Open,
211 Owe,
212 Wep,
213 Wpa(wpa::WpaDescriptor),
214}
215
216impl SecurityDescriptor {
217 pub const OPEN: Self = SecurityDescriptor::Open;
219 pub const OWE: Self = SecurityDescriptor::Owe;
221 pub const WEP: Self = SecurityDescriptor::Wep;
226 pub const WPA1: Self = SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa1 { credentials: () });
230 pub const WPA2_PERSONAL: Self = SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa2 {
235 cipher: None,
236 authentication: wpa::Authentication::Personal(()),
237 });
238 pub const WPA3_PERSONAL: Self = SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa3 {
243 cipher: None,
244 authentication: wpa::Authentication::Personal(()),
245 });
246
247 pub fn bind(
258 self,
259 credentials: Option<BareCredentials>,
260 ) -> Result<SecurityAuthenticator, SecurityError> {
261 match self {
262 SecurityDescriptor::Open if credentials.is_none() => Ok(SecurityAuthenticator::Open),
263 SecurityDescriptor::Owe if credentials.is_none() => Ok(SecurityAuthenticator::Owe),
264 SecurityDescriptor::Wep => match credentials {
265 Some(BareCredentials::WepKey(key)) => {
266 Ok(SecurityAuthenticator::Wep(wep::WepAuthenticator { key }))
267 }
268 _ => Err(SecurityError::Incompatible),
269 },
270 SecurityDescriptor::Wpa(wpa) => match credentials {
271 Some(credentials) => wpa.bind(credentials).map(SecurityAuthenticator::Wpa),
272 _ => Err(SecurityError::Incompatible),
273 },
274 _ => Err(SecurityError::Incompatible),
275 }
276 }
277
278 pub fn is_open(&self) -> bool {
279 matches!(self, SecurityDescriptor::Open)
280 }
281
282 pub fn is_owe(&self) -> bool {
283 matches!(self, SecurityDescriptor::Owe)
284 }
285
286 pub fn is_wep(&self) -> bool {
287 matches!(self, SecurityDescriptor::Wep)
288 }
289
290 pub fn is_wpa(&self) -> bool {
291 matches!(self, SecurityDescriptor::Wpa(_))
292 }
293}
294
295impl From<fidl_security::Protocol> for SecurityDescriptor {
296 fn from(protocol: fidl_security::Protocol) -> Self {
297 match protocol {
298 fidl_security::Protocol::Open => SecurityDescriptor::Open,
299 fidl_security::Protocol::Owe => SecurityDescriptor::Owe,
300 fidl_security::Protocol::Wep => SecurityDescriptor::Wep,
301 fidl_security::Protocol::Wpa1 => {
302 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa1 { credentials: () })
303 }
304 fidl_security::Protocol::Wpa2Personal => {
305 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa2 {
306 authentication: wpa::Authentication::Personal(()),
307 cipher: None,
308 })
309 }
310 fidl_security::Protocol::Wpa2Enterprise => {
311 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa2 {
312 authentication: wpa::Authentication::Enterprise(()),
313 cipher: None,
314 })
315 }
316 fidl_security::Protocol::Wpa3Personal => {
317 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa3 {
318 authentication: wpa::Authentication::Personal(()),
319 cipher: None,
320 })
321 }
322 fidl_security::Protocol::Wpa3Enterprise => {
323 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa3 {
324 authentication: wpa::Authentication::Enterprise(()),
325 cipher: None,
326 })
327 }
328 _ => panic!("unknown FIDL security protocol variant"),
329 }
330 }
331}
332
333impl From<SecurityDescriptor> for fidl_security::Protocol {
334 fn from(descriptor: SecurityDescriptor) -> Self {
335 match descriptor {
336 SecurityDescriptor::Open => fidl_security::Protocol::Open,
337 SecurityDescriptor::Owe => fidl_security::Protocol::Owe,
338 SecurityDescriptor::Wep => fidl_security::Protocol::Wep,
339 SecurityDescriptor::Wpa(wpa) => match wpa {
340 wpa::WpaDescriptor::Wpa1 { .. } => fidl_security::Protocol::Wpa1,
341 wpa::WpaDescriptor::Wpa2 { authentication, .. } => match authentication {
342 wpa::Authentication::Personal(_) => fidl_security::Protocol::Wpa2Personal,
343 wpa::Authentication::Enterprise(_) => fidl_security::Protocol::Wpa2Enterprise,
344 },
345 wpa::WpaDescriptor::Wpa3 { authentication, .. } => match authentication {
346 wpa::Authentication::Personal(_) => fidl_security::Protocol::Wpa3Personal,
347 wpa::Authentication::Enterprise(_) => fidl_security::Protocol::Wpa3Enterprise,
348 },
349 },
350 }
351 }
352}
353
354impl From<wpa::WpaDescriptor> for SecurityDescriptor {
355 fn from(descriptor: wpa::WpaDescriptor) -> Self {
356 SecurityDescriptor::Wpa(descriptor)
357 }
358}
359
360#[derive(Clone, Debug, Eq, PartialEq)]
363pub enum SecurityAuthenticator {
364 Open,
365 Owe,
366 Wep(wep::WepAuthenticator),
367 Wpa(wpa::WpaAuthenticator),
368}
369
370impl SecurityAuthenticator {
371 pub fn into_descriptor(self) -> SecurityDescriptor {
374 match self {
375 SecurityAuthenticator::Open => SecurityDescriptor::Open,
376 SecurityAuthenticator::Owe => SecurityDescriptor::Owe,
377 SecurityAuthenticator::Wep(_) => SecurityDescriptor::Wep,
378 SecurityAuthenticator::Wpa(authenticator) => {
379 SecurityDescriptor::Wpa(authenticator.into_descriptor())
380 }
381 }
382 }
383
384 pub fn into_wep(self) -> Option<wep::WepAuthenticator> {
385 match self {
386 SecurityAuthenticator::Wep(authenticator) => Some(authenticator),
387 _ => None,
388 }
389 }
390
391 pub fn into_wpa(self) -> Option<wpa::WpaAuthenticator> {
392 match self {
393 SecurityAuthenticator::Wpa(authenticator) => Some(authenticator),
394 _ => None,
395 }
396 }
397
398 pub fn to_credentials(&self) -> Option<BareCredentials> {
403 match self {
404 SecurityAuthenticator::Open | SecurityAuthenticator::Owe => None,
405 SecurityAuthenticator::Wep(wep::WepAuthenticator { key }) => Some(key.clone().into()),
406 SecurityAuthenticator::Wpa(wpa) => Some(wpa.to_credentials().into()),
407 }
408 }
409
410 pub fn as_wep(&self) -> Option<&wep::WepAuthenticator> {
411 match self {
412 SecurityAuthenticator::Wep(authenticator) => Some(authenticator),
413 _ => None,
414 }
415 }
416
417 pub fn as_wpa(&self) -> Option<&wpa::WpaAuthenticator> {
418 match self {
419 SecurityAuthenticator::Wpa(authenticator) => Some(authenticator),
420 _ => None,
421 }
422 }
423
424 pub fn is_open(&self) -> bool {
425 matches!(self, SecurityAuthenticator::Open)
426 }
427
428 pub fn is_owe(&self) -> bool {
429 matches!(self, SecurityAuthenticator::Owe)
430 }
431
432 pub fn is_wep(&self) -> bool {
433 matches!(self, SecurityAuthenticator::Wep(_))
434 }
435
436 pub fn is_wpa(&self) -> bool {
437 matches!(self, SecurityAuthenticator::Wpa(_))
438 }
439}
440
441impl From<wep::WepAuthenticator> for SecurityAuthenticator {
442 fn from(authenticator: wep::WepAuthenticator) -> Self {
443 SecurityAuthenticator::Wep(authenticator)
444 }
445}
446
447impl From<wpa::WpaAuthenticator> for SecurityAuthenticator {
448 fn from(authenticator: wpa::WpaAuthenticator) -> Self {
449 SecurityAuthenticator::Wpa(authenticator)
450 }
451}
452
453impl From<SecurityAuthenticator> for fidl_security::Authentication {
454 fn from(authenticator: SecurityAuthenticator) -> Self {
455 match authenticator {
456 SecurityAuthenticator::Open => fidl_security::Authentication {
457 protocol: fidl_security::Protocol::Open,
458 credentials: None,
459 },
460 SecurityAuthenticator::Owe => fidl_security::Authentication {
461 protocol: fidl_security::Protocol::Owe,
462 credentials: None,
463 },
464 SecurityAuthenticator::Wep(wep) => fidl_security::Authentication {
465 protocol: fidl_security::Protocol::Wep,
466 credentials: Some(Box::new(fidl_security::Credentials::Wep(wep.into()))),
467 },
468 SecurityAuthenticator::Wpa(wpa) => {
469 use wpa::Authentication::{Enterprise, Personal};
470 use wpa::Wpa::{Wpa1, Wpa2, Wpa3};
471
472 let protocol = match (&wpa, wpa.to_credentials()) {
473 (Wpa1 { .. }, _) => fidl_security::Protocol::Wpa1,
474 (Wpa2 { .. }, Personal(_)) => fidl_security::Protocol::Wpa2Personal,
475 (Wpa2 { .. }, Enterprise(_)) => fidl_security::Protocol::Wpa2Enterprise,
476 (Wpa3 { .. }, Personal(_)) => fidl_security::Protocol::Wpa3Personal,
477 (Wpa3 { .. }, Enterprise(_)) => fidl_security::Protocol::Wpa3Enterprise,
478 };
479 fidl_security::Authentication {
480 protocol,
481 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
483 wpa.into_credentials().into(),
484 ))),
485 }
486 }
487 }
488 }
489}
490
491impl TryFrom<fidl_security::Authentication> for SecurityAuthenticator {
500 type Error = SecurityError;
501
502 fn try_from(authentication: fidl_security::Authentication) -> Result<Self, Self::Error> {
503 let fidl_security::Authentication { protocol, credentials } = authentication;
504 match protocol {
505 fidl_security::Protocol::Open => match credentials {
506 None => Ok(SecurityAuthenticator::Open),
507 _ => Err(SecurityError::Incompatible),
508 },
509 fidl_security::Protocol::Owe => match credentials {
510 None => Ok(SecurityAuthenticator::Owe),
511 _ => Err(SecurityError::Incompatible),
512 },
513 fidl_security::Protocol::Wep => credentials
514 .ok_or(SecurityError::Incompatible)? .into_wep()
516 .map(wep::WepAuthenticator::try_from)
517 .transpose()? .map(From::from)
519 .ok_or(SecurityError::Incompatible), fidl_security::Protocol::Wpa1 => credentials
521 .ok_or(SecurityError::Incompatible)? .into_wpa()
523 .map(wpa::Wpa1Credentials::try_from)
524 .transpose()? .map(|credentials| wpa::WpaAuthenticator::Wpa1 { credentials })
526 .map(From::from)
527 .ok_or(SecurityError::Incompatible), fidl_security::Protocol::Wpa2Personal => credentials
529 .ok_or(SecurityError::Incompatible)? .into_wpa()
531 .map(wpa::Wpa2PersonalCredentials::try_from)
532 .transpose()? .map(From::from)
534 .map(|authentication| wpa::WpaAuthenticator::Wpa2 { cipher: None, authentication })
535 .map(From::from)
536 .ok_or(SecurityError::Incompatible), fidl_security::Protocol::Wpa3Personal => credentials
538 .ok_or(SecurityError::Incompatible)? .into_wpa()
540 .map(wpa::Wpa3PersonalCredentials::try_from)
541 .transpose()? .map(From::from)
543 .map(|authentication| wpa::WpaAuthenticator::Wpa3 { cipher: None, authentication })
544 .map(From::from)
545 .ok_or(SecurityError::Incompatible), _ => Err(SecurityError::Incompatible),
551 }
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use fidl_fuchsia_wlan_common_security as fidl_security;
558
559 use test_case::test_case;
560
561 use crate::security::wpa::{self, Authentication, Wpa2PersonalCredentials};
562 use crate::security::{SecurityAuthenticator, SecurityError};
563
564 pub trait AuthenticationTestCase: Sized {
565 fn wpa2_personal_psk() -> Self;
566 fn wpa3_personal_psk() -> Self;
567 fn wpa3_personal_wep_key() -> Self;
568 fn wpa3_personal_no_credentials() -> Self;
569 }
570
571 impl AuthenticationTestCase for fidl_security::Authentication {
572 fn wpa2_personal_psk() -> Self {
573 fidl_security::Authentication {
574 protocol: fidl_security::Protocol::Wpa2Personal,
575 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
576 fidl_security::WpaCredentials::Psk([0u8; 32]),
577 ))),
578 }
579 }
580
581 fn wpa3_personal_psk() -> Self {
583 fidl_security::Authentication {
584 protocol: fidl_security::Protocol::Wpa3Personal,
585 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
586 fidl_security::WpaCredentials::Psk([0u8; 32]),
587 ))),
588 }
589 }
590
591 fn wpa3_personal_wep_key() -> Self {
593 fidl_security::Authentication {
594 protocol: fidl_security::Protocol::Wpa3Personal,
595 credentials: Some(Box::new(fidl_security::Credentials::Wep(
596 fidl_security::WepCredentials { key: vec![0u8; 13] },
597 ))),
598 }
599 }
600
601 fn wpa3_personal_no_credentials() -> Self {
603 fidl_security::Authentication {
604 protocol: fidl_security::Protocol::Wpa3Personal,
605 credentials: None,
606 }
607 }
608 }
609
610 #[test_case(AuthenticationTestCase::wpa2_personal_psk() => matches
614 Ok(SecurityAuthenticator::Wpa(wpa::Wpa::Wpa2 {
615 authentication: Authentication::Personal(Wpa2PersonalCredentials::Psk(_)),
616 ..
617 }))
618 )]
619 #[test_case(AuthenticationTestCase::wpa3_personal_psk() => Err(SecurityError::Incompatible))]
620 #[test_case(AuthenticationTestCase::wpa3_personal_wep_key() => Err(SecurityError::Incompatible))]
621 #[test_case(AuthenticationTestCase::wpa3_personal_no_credentials() => Err(SecurityError::Incompatible))]
622 fn security_authenticator_from_authentication_fidl(
623 authentication: fidl_security::Authentication,
624 ) -> Result<SecurityAuthenticator, SecurityError> {
625 SecurityAuthenticator::try_from(authentication)
626 }
627
628 #[test]
629 fn authentication_fidl_from_security_authenticator() {
630 let authenticator = SecurityAuthenticator::Wpa(wpa::WpaAuthenticator::Wpa3 {
631 authentication: wpa::Authentication::Personal(
632 wpa::Wpa3PersonalCredentials::Passphrase("roflcopter".try_into().unwrap()),
633 ),
634 cipher: None,
635 });
636 let authentication = fidl_security::Authentication::from(authenticator);
637 assert_eq!(
638 authentication,
639 fidl_security::Authentication {
640 protocol: fidl_security::Protocol::Wpa3Personal,
641 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
642 fidl_security::WpaCredentials::Passphrase(b"roflcopter".as_slice().into()),
643 ))),
644 }
645 );
646 }
647}