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}