wlan_common/ie/rsn/
suite_filter.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::akm::{self, Akm, AKM_EAP};
6use super::cipher::{self, Cipher, CIPHER_BIP_CMAC_128, CIPHER_CCMP_128};
7use crate::ie;
8
9// IEEE 802.11-2016, 9.4.2.25.2
10// If group data cipher suite field is not included in RSNE, CCMP-128 is the default for
11// non-DMG STA.
12pub const DEFAULT_GROUP_DATA_CIPHER: Cipher = CIPHER_CCMP_128;
13
14// IEEE 802.11-2016, 9.4.2.25.2
15// If pairwise cipher suite field is not included in RSNE, CCMP-128 is the default for
16// non-DMG STA.
17pub const DEFAULT_PAIRWISE_CIPHER: [Cipher; 1] = [CIPHER_CCMP_128];
18
19// IEEE 802.11-2016, 9.4.2.25.3
20// If akm suite list field is not included in RSNE, suite selector value 00-0F-AC:1
21// (i.e. akm::EAP) is the default.
22pub const DEFAULT_AKM: [Akm; 1] = [AKM_EAP];
23
24// IEEE 802.11-2016, 9.4.2.25.2
25// If management frame protection is enabled, and group management cipher suite field is
26// not included in RSNE, BIP-CMAC-128 is the default.
27pub const DEFAULT_GROUP_MGMT_CIPHER: Cipher = CIPHER_BIP_CMAC_128;
28
29pub struct SuiteFilter<'a> {
30    known_group_data_ciphers: &'a [u8],
31    known_akms: &'a [u8],
32    known_pairwise_ciphers: &'a [u8],
33    required_group_mgmt_cipher: Option<u8>,
34}
35
36impl<'a> SuiteFilter<'a> {
37    pub fn is_satisfied(&self, rsne: &ie::rsn::rsne::Rsne) -> bool {
38        let group_data_cipher =
39            rsne.group_data_cipher_suite.as_ref().unwrap_or(&DEFAULT_GROUP_DATA_CIPHER);
40        let group_data_satisfied = group_data_cipher.has_known_usage()
41            && self.known_group_data_ciphers.contains(&group_data_cipher.suite_type);
42
43        let akms = if rsne.akm_suites.is_empty() { &DEFAULT_AKM[..] } else { &rsne.akm_suites[..] };
44        let akm_satisfied =
45            akms.iter().any(|a| a.has_known_algorithm() && self.known_akms.contains(&a.suite_type));
46
47        let pairwise_ciphers = if rsne.pairwise_cipher_suites.is_empty() {
48            &DEFAULT_PAIRWISE_CIPHER[..]
49        } else {
50            &rsne.pairwise_cipher_suites[..]
51        };
52        let pairwise_satisfied = pairwise_ciphers
53            .iter()
54            .any(|c| c.has_known_usage() && self.known_pairwise_ciphers.contains(&c.suite_type));
55
56        let group_mgmt_cipher =
57            rsne.group_mgmt_cipher_suite.as_ref().unwrap_or(&DEFAULT_GROUP_MGMT_CIPHER);
58        let group_mgmt_satisfied = self
59            .required_group_mgmt_cipher
60            .map(|s| group_mgmt_cipher.suite_type == s)
61            .unwrap_or(true);
62
63        group_data_satisfied && akm_satisfied && pairwise_satisfied && group_mgmt_satisfied
64    }
65}
66
67/// WFA, WPA1 Spec. 3.1, Chapter 2.1
68pub const WPA1_PERSONAL: SuiteFilter<'_> = SuiteFilter {
69    known_group_data_ciphers: &[cipher::TKIP, cipher::CCMP_128],
70    known_akms: &[akm::PSK],
71    known_pairwise_ciphers: &[cipher::TKIP, cipher::CCMP_128],
72    required_group_mgmt_cipher: None,
73};
74
75pub const WPA2_PERSONAL_TKIP_ONLY: SuiteFilter<'_> = SuiteFilter {
76    known_group_data_ciphers: &[cipher::TKIP],
77    known_akms: &[akm::PSK],
78    known_pairwise_ciphers: &[cipher::TKIP],
79    required_group_mgmt_cipher: None,
80};
81
82/// IEEE 802.11 2004, Chapter 7.3.2.25.1
83///
84/// Ciphers that encompass different configurations used for WPA2 personal (e.g. base
85/// configuration, configuration that supports BSS fast transition, and configuration
86/// that supports management frame protection)
87pub const WPA2_PERSONAL: SuiteFilter<'_> = SuiteFilter {
88    known_group_data_ciphers: &[cipher::CCMP_128, cipher::TKIP],
89    // From observation: In most WPA2 cases, only akm::PSK is included. If FT is enabled,
90    // akm::FT_PSK is also included.
91    // In the case where management frame protection is set to required, akm::PSK is replaced
92    // with akm::PSK_SHA256 (but not when mfp is only set to capable)
93    known_akms: &[akm::PSK, akm::FT_PSK, akm::PSK_SHA256],
94    // In theory, 256 bit cipher suites (which were added in 802.11ac amendment) could also
95    // be included here and elsewhere. In practice, it's unclear how many APs actually support
96    // it. We'll disallow it for now to keep logic simple because otherwise, we also have
97    // to do the check that pairwise and group keys match.
98    //
99    // TODO(https://fxbug.dev/42104677): deploy metric to measure AKM and cipher use in practice.
100    known_pairwise_ciphers: &[cipher::CCMP_128],
101    required_group_mgmt_cipher: None,
102};
103
104/// WFA, WPA3 Spec. 1.0, Chapter 3
105pub const WPA3_PERSONAL: SuiteFilter<'_> = SuiteFilter {
106    known_group_data_ciphers: &[cipher::CCMP_128, cipher::TKIP],
107    // WPA3 spec doesn't mention Fast BSS Transition, thus akm::FT_SAE is likely not supported.
108    // For some reason, An AP we use for testing provide an option to turn on FT for WPA3 network,
109    // but even then it still uses akm::SAE.
110    known_akms: &[akm::SAE],
111    known_pairwise_ciphers: &[cipher::CCMP_128],
112    required_group_mgmt_cipher: None,
113};
114
115/// Ciphers that encompass different configurations used for WPA2 enterprise (e.g. base
116/// configuration, configuration that supports BSS fast transition, and configuration
117/// that supports management frame protection)
118pub const WPA2_ENTERPRISE: SuiteFilter<'_> = SuiteFilter {
119    known_group_data_ciphers: &[cipher::CCMP_128],
120    // From observation: akm::EAP is included for base WPA2 enterprise configuration. If FT
121    // is enabled, akm::FT_EAP is also included.
122    // In the case where management frame protection is set to required, akm::EAP is replaced
123    // with akm::EAP_SHA256 (but not when mfp is only set to capable).
124    known_akms: &[akm::EAP, akm::FT_EAP, akm::EAP_SHA256],
125    known_pairwise_ciphers: &[cipher::CCMP_128],
126    required_group_mgmt_cipher: None,
127};
128
129/// WFA, WPA3 Spec. 1.0, Chapter 3
130pub const WPA3_ENTERPRISE_192_BIT: SuiteFilter<'_> = SuiteFilter {
131    known_group_data_ciphers: &[cipher::GCMP_256],
132    known_akms: &[akm::EAP_SUITEB_SHA384],
133    known_pairwise_ciphers: &[cipher::GCMP_256],
134    required_group_mgmt_cipher: Some(cipher::BIP_GMAC_256),
135};
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use crate::test_utils::fake_frames::{fake_wpa2_rsne, fake_wpa3_enterprise_192_bit_rsne};
141
142    #[test]
143    fn test_suite_filter() {
144        let wpa2_rsne = ie::rsn::rsne::from_bytes(&fake_wpa2_rsne()[..]).unwrap().1;
145        assert!(WPA2_PERSONAL.is_satisfied(&wpa2_rsne));
146        assert!(!WPA3_PERSONAL.is_satisfied(&wpa2_rsne));
147    }
148
149    #[test]
150    fn test_suite_filter_with_required_group_mgmt() {
151        let mut wpa3_ent_rsne =
152            ie::rsn::rsne::from_bytes(&fake_wpa3_enterprise_192_bit_rsne()[..]).unwrap().1;
153        assert!(WPA3_ENTERPRISE_192_BIT.is_satisfied(&wpa3_ent_rsne));
154        wpa3_ent_rsne.group_mgmt_cipher_suite = None;
155        assert!(!WPA3_ENTERPRISE_192_BIT.is_satisfied(&wpa3_ent_rsne));
156    }
157
158    #[test]
159    fn test_suite_filter_empty_rsne() {
160        let rsne = ie::rsn::rsne::Rsne::default();
161        assert!(WPA2_ENTERPRISE.is_satisfied(&rsne));
162        assert!(!WPA2_PERSONAL.is_satisfied(&rsne));
163    }
164}