wlan_common/ie/rsn/
akm.rs

1// Copyright 2018 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::suite_selector;
6use crate::ie::rsn::suite_selector::OUI;
7use crate::organization::Oui;
8use std::fmt;
9
10macro_rules! return_none_if_unknown_algo {
11    ($e:expr) => {
12        if !$e.has_known_algorithm() {
13            return None;
14        }
15    };
16}
17
18// IEEE Std 802.11-2016, 9.4.2.25.3, Table 9-133
19// 0 - Reserved.
20pub const EAP: u8 = 1;
21pub const PSK: u8 = 2;
22pub const FT_EAP: u8 = 3;
23pub const FT_PSK: u8 = 4;
24pub const EAP_SHA256: u8 = 5;
25pub const PSK_SHA256: u8 = 6;
26pub const TDLS: u8 = 7;
27pub const SAE: u8 = 8;
28pub const FT_SAE: u8 = 9;
29pub const AP_PEERKEY: u8 = 10;
30pub const EAP_SUITEB: u8 = 11;
31pub const EAP_SUITEB_SHA384: u8 = 12;
32pub const FT_EAP_SHA384: u8 = 13;
33// 14-17 - Reserved.
34pub const OWE: u8 = 18;
35// 19-255 - Reserved.
36
37// Shorthands for the most commonly constructed akm suites
38pub const AKM_EAP: Akm = Akm::new_dot11(EAP);
39pub const AKM_PSK: Akm = Akm::new_dot11(PSK);
40pub const AKM_FT_PSK: Akm = Akm::new_dot11(FT_PSK);
41pub const AKM_SAE: Akm = Akm::new_dot11(SAE);
42pub const AKM_OWE: Akm = Akm::new_dot11(OWE);
43
44#[derive(PartialOrd, PartialEq, Eq, Clone)]
45pub struct Akm {
46    pub oui: Oui,
47    pub suite_type: u8,
48}
49
50impl Akm {
51    /// Creates a new AKM instance for 802.11 specified AKMs.
52    /// See IEEE Std 802.11-2016, 9.4.2.25.3, Table 9-133.
53    pub const fn new_dot11(suite_type: u8) -> Self {
54        Akm { oui: OUI, suite_type }
55    }
56
57    /// Only AKMs specified in IEEE 802.11-2016, 9.4.2.25.4, Table 9-133 have known algorithms.
58    pub fn has_known_algorithm(&self) -> bool {
59        if self.is_reserved() || self.is_vendor_specific() {
60            // Support MSFT PSK for WPA1
61            self.oui == Oui::MSFT && self.suite_type == PSK
62        } else {
63            self.suite_type != 7 && self.suite_type != 10
64        }
65    }
66
67    pub fn is_vendor_specific(&self) -> bool {
68        // IEEE 802.11-2016, 9.4.2.25.4, Table 9-133
69        !self.oui.eq(&OUI)
70    }
71
72    pub fn is_reserved(&self) -> bool {
73        // IEEE 802.11-2016, 9.4.2.25.4, Table 9-133
74        // The AKM is reserved if it has an IEEE 802.11 OUI and the suite is **not** defined above.
75        !((EAP..=FT_EAP_SHA384).contains(&self.suite_type)
76            || self.suite_type == OWE
77            || self.is_vendor_specific())
78    }
79
80    pub fn mic_bytes(&self) -> Option<u16> {
81        return_none_if_unknown_algo!(self);
82
83        // IEEE 802.11-2024, 12.7.3, Table 12-11
84        // TODO(https://fxbug.dev/464050097): Update this for other suite types + integrity algo.
85        match self.suite_type {
86            1..=11 | 18 => Some(16),
87            12 | 13 => Some(24),
88            _ => None,
89        }
90    }
91
92    pub fn kck_bytes(&self) -> Option<u16> {
93        return_none_if_unknown_algo!(self);
94
95        // IEEE 802.11-2024, 12.7.3, Table 12-11
96        // TODO(https://fxbug.dev/464050097): Update this for other suite types + integrity algo.
97        match self.suite_type {
98            1..=11 | 18 => Some(16),
99            12 | 13 => Some(24),
100            _ => None,
101        }
102    }
103
104    pub fn kek_bytes(&self) -> Option<u16> {
105        return_none_if_unknown_algo!(self);
106
107        // IEEE 802.11-2024, 12.7.3, Table 12-11
108        // TODO(https://fxbug.dev/464050097): Update this for other suite types + integrity algo.
109        match self.suite_type {
110            1..=11 | 18 => Some(16),
111            12 | 13 => Some(32),
112            _ => None,
113        }
114    }
115
116    pub fn pmk_bytes(&self) -> Option<u16> {
117        return_none_if_unknown_algo!(self);
118
119        // IEEE 802.11-2024, 12.7.1.3
120        // TODO(https://fxbug.dev/464050097): Update this for other suite types.
121        match self.suite_type {
122            1..=11 | 13 | 18 => Some(32),
123            12 => Some(48),
124            _ => None,
125        }
126    }
127
128    #[deprecated(note = "use `kck_bytes` instead")]
129    pub fn kck_bits(&self) -> Option<u16> {
130        return_none_if_unknown_algo!(self);
131
132        // IEEE 802.11-2024, 12.7.3, Table 12-11
133        // TODO(https://fxbug.dev/464050097): Update this for other suite types + integrity algo.
134        match self.suite_type {
135            1..=11 | 18 => Some(128),
136            12 | 13 => Some(192),
137            _ => None,
138        }
139    }
140
141    #[deprecated(note = "use `kek_bytes` instead")]
142    pub fn kek_bits(&self) -> Option<u16> {
143        return_none_if_unknown_algo!(self);
144
145        // IEEE 802.11-2024, 12.7.3, Table 12-11
146        // TODO(https://fxbug.dev/464050097): Update this for other suite types + integrity algo.
147        match self.suite_type {
148            1..=11 | 18 => Some(128),
149            12 | 13 => Some(256),
150            _ => None,
151        }
152    }
153
154    #[deprecated(note = "use `pmk_bytes` instead")]
155    pub fn pmk_bits(&self) -> Option<u16> {
156        return_none_if_unknown_algo!(self);
157
158        // IEEE 802.11-2024, 12.7.1.3
159        // TODO(https://fxbug.dev/464050097): Update this for other suite types.
160        match self.suite_type {
161            1..=11 | 13 | 18 => Some(256),
162            12 => Some(384),
163            _ => None,
164        }
165    }
166}
167
168impl suite_selector::Factory for Akm {
169    type Suite = Akm;
170
171    fn new(oui: Oui, suite_type: u8) -> Self::Suite {
172        Akm { oui, suite_type }
173    }
174}
175
176impl fmt::Debug for Akm {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        write!(f, "{:02X}-{:02X}-{:02X}:{}", self.oui[0], self.oui[1], self.oui[2], self.suite_type)
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_new_dot11() {
188        let psk = AKM_PSK;
189        assert!(!psk.is_vendor_specific());
190        assert!(psk.has_known_algorithm());
191        assert!(!psk.is_reserved());
192    }
193
194    #[test]
195    fn test_msft_akm() {
196        let psk = Akm { oui: Oui::MSFT, suite_type: PSK };
197        assert!(psk.is_vendor_specific());
198        assert!(psk.has_known_algorithm());
199        assert!(!psk.is_reserved());
200    }
201}