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 Wep,
212 Wpa(wpa::WpaDescriptor),
213}
214
215impl SecurityDescriptor {
216 pub const OPEN: Self = SecurityDescriptor::Open;
218 pub const WEP: Self = SecurityDescriptor::Wep;
223 pub const WPA1: Self = SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa1 { credentials: () });
227 pub const WPA2_PERSONAL: Self = SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa2 {
232 cipher: None,
233 authentication: wpa::Authentication::Personal(()),
234 });
235 pub const WPA3_PERSONAL: Self = SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa3 {
240 cipher: None,
241 authentication: wpa::Authentication::Personal(()),
242 });
243
244 pub fn bind(
255 self,
256 credentials: Option<BareCredentials>,
257 ) -> Result<SecurityAuthenticator, SecurityError> {
258 match self {
259 SecurityDescriptor::Open if credentials.is_none() => Ok(SecurityAuthenticator::Open),
260 SecurityDescriptor::Wep => match credentials {
261 Some(BareCredentials::WepKey(key)) => {
262 Ok(SecurityAuthenticator::Wep(wep::WepAuthenticator { key }))
263 }
264 _ => Err(SecurityError::Incompatible),
265 },
266 SecurityDescriptor::Wpa(wpa) => match credentials {
267 Some(credentials) => wpa.bind(credentials).map(SecurityAuthenticator::Wpa),
268 _ => Err(SecurityError::Incompatible),
269 },
270 _ => Err(SecurityError::Incompatible),
271 }
272 }
273
274 pub fn is_open(&self) -> bool {
275 matches!(self, SecurityDescriptor::Open)
276 }
277
278 pub fn is_wep(&self) -> bool {
279 matches!(self, SecurityDescriptor::Wep)
280 }
281
282 pub fn is_wpa(&self) -> bool {
283 matches!(self, SecurityDescriptor::Wpa(_))
284 }
285}
286
287impl From<fidl_security::Protocol> for SecurityDescriptor {
288 fn from(protocol: fidl_security::Protocol) -> Self {
289 match protocol {
290 fidl_security::Protocol::Open => SecurityDescriptor::Open,
291 fidl_security::Protocol::Wep => SecurityDescriptor::Wep,
292 fidl_security::Protocol::Wpa1 => {
293 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa1 { credentials: () })
294 }
295 fidl_security::Protocol::Wpa2Personal => {
296 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa2 {
297 authentication: wpa::Authentication::Personal(()),
298 cipher: None,
299 })
300 }
301 fidl_security::Protocol::Wpa2Enterprise => {
302 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa2 {
303 authentication: wpa::Authentication::Enterprise(()),
304 cipher: None,
305 })
306 }
307 fidl_security::Protocol::Wpa3Personal => {
308 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa3 {
309 authentication: wpa::Authentication::Personal(()),
310 cipher: None,
311 })
312 }
313 fidl_security::Protocol::Wpa3Enterprise => {
314 SecurityDescriptor::Wpa(wpa::WpaDescriptor::Wpa3 {
315 authentication: wpa::Authentication::Enterprise(()),
316 cipher: None,
317 })
318 }
319 _ => panic!("unknown FIDL security protocol variant"),
320 }
321 }
322}
323
324impl From<SecurityDescriptor> for fidl_security::Protocol {
325 fn from(descriptor: SecurityDescriptor) -> Self {
326 match descriptor {
327 SecurityDescriptor::Open => fidl_security::Protocol::Open,
328 SecurityDescriptor::Wep => fidl_security::Protocol::Wep,
329 SecurityDescriptor::Wpa(wpa) => match wpa {
330 wpa::WpaDescriptor::Wpa1 { .. } => fidl_security::Protocol::Wpa1,
331 wpa::WpaDescriptor::Wpa2 { authentication, .. } => match authentication {
332 wpa::Authentication::Personal(_) => fidl_security::Protocol::Wpa2Personal,
333 wpa::Authentication::Enterprise(_) => fidl_security::Protocol::Wpa2Enterprise,
334 },
335 wpa::WpaDescriptor::Wpa3 { authentication, .. } => match authentication {
336 wpa::Authentication::Personal(_) => fidl_security::Protocol::Wpa3Personal,
337 wpa::Authentication::Enterprise(_) => fidl_security::Protocol::Wpa3Enterprise,
338 },
339 },
340 }
341 }
342}
343
344impl From<wpa::WpaDescriptor> for SecurityDescriptor {
345 fn from(descriptor: wpa::WpaDescriptor) -> Self {
346 SecurityDescriptor::Wpa(descriptor)
347 }
348}
349
350#[derive(Clone, Debug, Eq, PartialEq)]
353pub enum SecurityAuthenticator {
354 Open,
355 Wep(wep::WepAuthenticator),
356 Wpa(wpa::WpaAuthenticator),
357}
358
359impl SecurityAuthenticator {
360 pub fn into_descriptor(self) -> SecurityDescriptor {
363 match self {
364 SecurityAuthenticator::Open => SecurityDescriptor::Open,
365 SecurityAuthenticator::Wep(_) => SecurityDescriptor::Wep,
366 SecurityAuthenticator::Wpa(authenticator) => {
367 SecurityDescriptor::Wpa(authenticator.into_descriptor())
368 }
369 }
370 }
371
372 pub fn into_wep(self) -> Option<wep::WepAuthenticator> {
373 match self {
374 SecurityAuthenticator::Wep(authenticator) => Some(authenticator),
375 _ => None,
376 }
377 }
378
379 pub fn into_wpa(self) -> Option<wpa::WpaAuthenticator> {
380 match self {
381 SecurityAuthenticator::Wpa(authenticator) => Some(authenticator),
382 _ => None,
383 }
384 }
385
386 pub fn to_credentials(&self) -> Option<BareCredentials> {
391 match self {
392 SecurityAuthenticator::Open => None,
393 SecurityAuthenticator::Wep(wep::WepAuthenticator { ref key }) => {
394 Some(key.clone().into())
395 }
396 SecurityAuthenticator::Wpa(ref wpa) => Some(wpa.to_credentials().into()),
397 }
398 }
399
400 pub fn as_wep(&self) -> Option<&wep::WepAuthenticator> {
401 match self {
402 SecurityAuthenticator::Wep(ref authenticator) => Some(authenticator),
403 _ => None,
404 }
405 }
406
407 pub fn as_wpa(&self) -> Option<&wpa::WpaAuthenticator> {
408 match self {
409 SecurityAuthenticator::Wpa(ref authenticator) => Some(authenticator),
410 _ => None,
411 }
412 }
413
414 pub fn is_open(&self) -> bool {
415 matches!(self, SecurityAuthenticator::Open)
416 }
417
418 pub fn is_wep(&self) -> bool {
419 matches!(self, SecurityAuthenticator::Wep(_))
420 }
421
422 pub fn is_wpa(&self) -> bool {
423 matches!(self, SecurityAuthenticator::Wpa(_))
424 }
425}
426
427impl From<wep::WepAuthenticator> for SecurityAuthenticator {
428 fn from(authenticator: wep::WepAuthenticator) -> Self {
429 SecurityAuthenticator::Wep(authenticator)
430 }
431}
432
433impl From<wpa::WpaAuthenticator> for SecurityAuthenticator {
434 fn from(authenticator: wpa::WpaAuthenticator) -> Self {
435 SecurityAuthenticator::Wpa(authenticator)
436 }
437}
438
439impl From<SecurityAuthenticator> for fidl_security::Authentication {
440 fn from(authenticator: SecurityAuthenticator) -> Self {
441 match authenticator {
442 SecurityAuthenticator::Open => fidl_security::Authentication {
443 protocol: fidl_security::Protocol::Open,
444 credentials: None,
445 },
446 SecurityAuthenticator::Wep(wep) => fidl_security::Authentication {
447 protocol: fidl_security::Protocol::Wep,
448 credentials: Some(Box::new(fidl_security::Credentials::Wep(wep.into()))),
449 },
450 SecurityAuthenticator::Wpa(wpa) => {
451 use wpa::Authentication::{Enterprise, Personal};
452 use wpa::Wpa::{Wpa1, Wpa2, Wpa3};
453
454 let protocol = match (&wpa, wpa.to_credentials()) {
455 (Wpa1 { .. }, _) => fidl_security::Protocol::Wpa1,
456 (Wpa2 { .. }, Personal(_)) => fidl_security::Protocol::Wpa2Personal,
457 (Wpa2 { .. }, Enterprise(_)) => fidl_security::Protocol::Wpa2Enterprise,
458 (Wpa3 { .. }, Personal(_)) => fidl_security::Protocol::Wpa3Personal,
459 (Wpa3 { .. }, Enterprise(_)) => fidl_security::Protocol::Wpa3Enterprise,
460 };
461 fidl_security::Authentication {
462 protocol,
463 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
465 wpa.into_credentials().into(),
466 ))),
467 }
468 }
469 }
470 }
471}
472
473impl TryFrom<fidl_security::Authentication> for SecurityAuthenticator {
482 type Error = SecurityError;
483
484 fn try_from(authentication: fidl_security::Authentication) -> Result<Self, Self::Error> {
485 let fidl_security::Authentication { protocol, credentials } = authentication;
486 match protocol {
487 fidl_security::Protocol::Open => match credentials {
488 None => Ok(SecurityAuthenticator::Open),
489 _ => Err(SecurityError::Incompatible),
490 },
491 fidl_security::Protocol::Wep => credentials
492 .ok_or(SecurityError::Incompatible)? .into_wep()
494 .map(wep::WepAuthenticator::try_from)
495 .transpose()? .map(From::from)
497 .ok_or(SecurityError::Incompatible), fidl_security::Protocol::Wpa1 => credentials
499 .ok_or(SecurityError::Incompatible)? .into_wpa()
501 .map(wpa::Wpa1Credentials::try_from)
502 .transpose()? .map(|credentials| wpa::WpaAuthenticator::Wpa1 { credentials })
504 .map(From::from)
505 .ok_or(SecurityError::Incompatible), fidl_security::Protocol::Wpa2Personal => credentials
507 .ok_or(SecurityError::Incompatible)? .into_wpa()
509 .map(wpa::Wpa2PersonalCredentials::try_from)
510 .transpose()? .map(From::from)
512 .map(|authentication| wpa::WpaAuthenticator::Wpa2 { cipher: None, authentication })
513 .map(From::from)
514 .ok_or(SecurityError::Incompatible), fidl_security::Protocol::Wpa3Personal => credentials
516 .ok_or(SecurityError::Incompatible)? .into_wpa()
518 .map(wpa::Wpa3PersonalCredentials::try_from)
519 .transpose()? .map(From::from)
521 .map(|authentication| wpa::WpaAuthenticator::Wpa3 { cipher: None, authentication })
522 .map(From::from)
523 .ok_or(SecurityError::Incompatible), _ => Err(SecurityError::Incompatible),
529 }
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use fidl_fuchsia_wlan_common_security as fidl_security;
536
537 use test_case::test_case;
538
539 use crate::security::wpa::{self, Authentication, Wpa2PersonalCredentials};
540 use crate::security::{SecurityAuthenticator, SecurityError};
541
542 pub trait AuthenticationTestCase: Sized {
543 fn wpa2_personal_psk() -> Self;
544 fn wpa3_personal_psk() -> Self;
545 fn wpa3_personal_wep_key() -> Self;
546 fn wpa3_personal_no_credentials() -> Self;
547 }
548
549 impl AuthenticationTestCase for fidl_security::Authentication {
550 fn wpa2_personal_psk() -> Self {
551 fidl_security::Authentication {
552 protocol: fidl_security::Protocol::Wpa2Personal,
553 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
554 fidl_security::WpaCredentials::Psk([0u8; 32]),
555 ))),
556 }
557 }
558
559 fn wpa3_personal_psk() -> Self {
561 fidl_security::Authentication {
562 protocol: fidl_security::Protocol::Wpa3Personal,
563 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
564 fidl_security::WpaCredentials::Psk([0u8; 32]),
565 ))),
566 }
567 }
568
569 fn wpa3_personal_wep_key() -> Self {
571 fidl_security::Authentication {
572 protocol: fidl_security::Protocol::Wpa3Personal,
573 credentials: Some(Box::new(fidl_security::Credentials::Wep(
574 fidl_security::WepCredentials { key: vec![0u8; 13] },
575 ))),
576 }
577 }
578
579 fn wpa3_personal_no_credentials() -> Self {
581 fidl_security::Authentication {
582 protocol: fidl_security::Protocol::Wpa3Personal,
583 credentials: None,
584 }
585 }
586 }
587
588 #[test_case(AuthenticationTestCase::wpa2_personal_psk() => matches
592 Ok(SecurityAuthenticator::Wpa(wpa::Wpa::Wpa2 {
593 authentication: Authentication::Personal(Wpa2PersonalCredentials::Psk(_)),
594 ..
595 }))
596 )]
597 #[test_case(AuthenticationTestCase::wpa3_personal_psk() => Err(SecurityError::Incompatible))]
598 #[test_case(AuthenticationTestCase::wpa3_personal_wep_key() => Err(SecurityError::Incompatible))]
599 #[test_case(AuthenticationTestCase::wpa3_personal_no_credentials() => Err(SecurityError::Incompatible))]
600 fn security_authenticator_from_authentication_fidl(
601 authentication: fidl_security::Authentication,
602 ) -> Result<SecurityAuthenticator, SecurityError> {
603 SecurityAuthenticator::try_from(authentication)
604 }
605
606 #[test]
607 fn authentication_fidl_from_security_authenticator() {
608 let authenticator = SecurityAuthenticator::Wpa(wpa::WpaAuthenticator::Wpa3 {
609 authentication: wpa::Authentication::Personal(
610 wpa::Wpa3PersonalCredentials::Passphrase("roflcopter".try_into().unwrap()),
611 ),
612 cipher: None,
613 });
614 let authentication = fidl_security::Authentication::from(authenticator);
615 assert_eq!(
616 authentication,
617 fidl_security::Authentication {
618 protocol: fidl_security::Protocol::Wpa3Personal,
619 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
620 fidl_security::WpaCredentials::Passphrase(b"roflcopter".as_slice().into()),
621 ))),
622 }
623 );
624 }
625}