1pub mod kde;
6
7use crate::Error;
8use nom::bytes::streaming::take;
9use nom::combinator::complete;
10use nom::error::Error as NomError;
11use nom::multi::many0;
12use nom::{IResult, Needed, Parser};
13use std::num::NonZero;
14use wlan_common::ie::rsn::rsne;
15use wlan_common::ie::{wpa, Id};
16
17#[derive(Debug, PartialEq)]
18pub enum Element {
19 Gtk(kde::Header, kde::Gtk),
20 Igtk(kde::Header, kde::Igtk),
21 Rsne(rsne::Rsne),
22 LegacyWpa1(wpa::WpaIe),
23 Padding,
24 UnsupportedKde(kde::Header),
25 UnsupportedIe(u8, u8),
26}
27
28fn peek_u8_at(index: usize) -> impl FnMut(&[u8]) -> IResult<&[u8], u8> {
29 move |input: &[u8]| {
30 if input.len() <= index {
31 Err(nom::Err::Incomplete(Needed::Size(NonZero::new(index + 1).unwrap())))
32 } else {
33 Ok((input, input[index]))
34 }
35 }
36}
37
38fn parse_ie(i0: &[u8]) -> IResult<&[u8], Element> {
39 let (i1, id) = peek_u8_at(0).parse(i0)?;
40 let (i2, len) = peek_u8_at(1).parse(i1)?;
41 let (out, bytes) = take(2 + (len as usize)).parse(i2)?;
42 match Id(id) {
43 Id::RSNE => {
44 let (_, rsne) = rsne::from_bytes(bytes)?;
45 Ok((out, Element::Rsne(rsne)))
46 }
47 _ => Ok((out, Element::UnsupportedIe(id, len))),
48 }
49}
50
51fn parse_element(input: &[u8]) -> IResult<&[u8], Element> {
52 let (_, type_) = peek_u8_at(0).parse(input)?;
53 match type_ {
54 kde::TYPE => kde::parse(input),
55 _ => parse_ie(input),
56 }
57}
58
59fn parse_elements(input: &[u8]) -> IResult<&[u8], Vec<Element>> {
60 many0(complete(parse_element)).parse(input)
61}
62
63#[allow(clippy::result_large_err, reason = "mass allow for https://fxbug.dev/381896734")]
64pub fn extract_elements(key_data: &[u8]) -> Result<Vec<Element>, Error> {
65 match parse_elements(&key_data[..]) {
66 Ok((_, elements)) => Ok(elements),
67 Err(nom::Err::Error(NomError { code, .. })) => Err(Error::InvalidKeyData(code).into()),
68 Err(nom::Err::Failure(NomError { code, .. })) => Err(Error::InvalidKeyData(code).into()),
69 Err(nom::Err::Incomplete(_)) => Ok(vec![]),
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use wlan_common::assert_variant;
77 use wlan_common::ie::rsn::{akm, cipher};
78 use wlan_common::organization::Oui;
79
80 #[test]
81 fn test_complex_key_data() {
82 #[rustfmt::skip]
83 let buf = [
84 0xDD,
86 14, 0x00, 0x0F, 0xAC, 1, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 99, 6, 1, 2, 3, 4, 5, 6,
94 48, 6, 1, 1, 1, 2, 3, 4, 0xDD, 14, 0x01, 0x0F, 0xAC,
100 1, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8,
102 48, 6, 9, 0, 0x00, 0x0F, 0xAC, 1, 200, 2, 1, 3,
108 0xDD, 0, 0, 0,
110 ];
111 let result = extract_elements(&buf[..]);
112 assert!(result.is_ok(), "Error: {:?}", result);
113
114 let elements = result.unwrap();
115 assert_eq!(elements.len(), 7);
116
117 let mut pos = 0;
118 for e in elements {
119 match e {
120 Element::Gtk(hdr, kde) => {
121 assert_eq!(pos, 0);
122 assert_eq!(hdr.type_, 0xDD);
123 assert_eq!(hdr.len, 14);
124 assert_eq!(hdr.oui, Oui::DOT11);
125 assert_eq!(hdr.data_type, 1);
126 assert_eq!(kde.info.value(), 5);
127 assert_eq!(&kde.gtk[..], &[1, 2, 3, 4, 5, 6, 7, 8][..]);
128 }
129 Element::UnsupportedIe(id, len) => match pos {
130 1 => {
131 assert_eq!(id, 99);
132 assert_eq!(len, 6);
133 }
134 5 => {
135 assert_eq!(id, 200);
136 assert_eq!(len, 2);
137 }
138 other => panic!("unexpected IE position: {}", other),
139 },
140 Element::Rsne(rsne) => match pos {
141 2 => {
142 assert_eq!(rsne.len(), 8);
143 assert_eq!(rsne.version, 257);
144 assert!(rsne.group_data_cipher_suite.is_some());
145 let cipher = rsne.group_data_cipher_suite.unwrap();
146 assert_eq!(cipher.suite_type, 4);
147 let oui = Oui::new([1, 2, 3]);
148 assert_eq!(cipher.oui, oui);
149 }
150 4 => {
151 assert_eq!(rsne.len(), 8);
152 assert_eq!(rsne.version, 9);
153 assert!(rsne.group_data_cipher_suite.is_some());
154 let cipher = rsne.group_data_cipher_suite.unwrap();
155 assert_eq!(cipher.suite_type, 1);
156 assert_eq!(cipher.oui, Oui::DOT11);
157 }
158 other => panic!("unexpected IE position: {}", other),
159 },
160 Element::UnsupportedKde(hdr) => {
161 assert_eq!(pos, 3);
162 assert_eq!(hdr.type_, 0xDD);
163 assert_eq!(hdr.len, 14);
164 let oui = Oui::new([0x01, 0x0F, 0xAC]);
165 assert_eq!(hdr.oui, oui);
166 assert_eq!(hdr.data_type, 1);
167 }
168 Element::Padding => assert_eq!(pos, 6),
169 _ => panic!("Unexpected element in key data"),
170 }
171 pos += 1;
172 }
173 }
174
175 #[test]
176 fn test_no_padding() {
177 #[rustfmt::skip]
178 let buf = [
179 10, 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, ];
181 let result = extract_elements(&buf[..]);
182 assert!(result.is_ok(), "Error: {:?}", result);
183
184 let elements = result.unwrap();
185 assert_eq!(elements.len(), 1);
186 assert_eq!(elements.into_iter().next(), Some(Element::UnsupportedIe(10, 14)));
187 }
188
189 #[test]
190 fn test_single_padding_byte() {
191 #[rustfmt::skip]
192 let buf = [
193 10, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0xDD, ];
196 let result = extract_elements(&buf[..]);
197 assert!(result.is_ok(), "Error: {:?}", result);
198
199 let elements = result.unwrap();
200 assert_eq!(elements.len(), 2);
201
202 for e in elements {
203 assert_variant!(e, Element::UnsupportedIe(10, 13) | Element::Padding);
204 }
205 }
206
207 #[test]
208 fn test_long_padding() {
209 #[rustfmt::skip]
210 let buf = [
211 20, 6, 1, 2, 3, 4, 5, 6, 0xdd, 0, 0, 0, 0, 0, 0, 0, ];
214 let result = extract_elements(&buf[..]);
215 assert!(result.is_ok(), "Error: {:?}", result);
216
217 let elements = result.unwrap();
218 assert_eq!(elements.len(), 2);
219
220 for e in elements {
221 assert_variant!(e, Element::UnsupportedIe(20, 6) | Element::Padding);
222 }
223 }
224
225 #[test]
226 fn test_gtk() {
227 #[rustfmt::skip]
228 let buf = [
229 0xDD,
231 14, 0x00, 0x0F, 0xAC, 1, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, ];
238 let result = extract_elements(&buf[..]);
239 assert!(result.is_ok(), "Error: {:?}", result);
240
241 let elements = result.unwrap();
242 assert_eq!(elements.len(), 1);
243
244 for e in elements {
245 assert_variant!(e, Element::Gtk(hdr, kde) => {
246 assert_eq!(
247 hdr,
248 kde::Header { type_: 0xDD, len: 14, oui: Oui::DOT11, data_type: 1 }
249 );
250 assert_eq!(kde.info.value(), 5);
251 assert_eq!(&kde.gtk[..], &[1, 2, 3, 4, 5, 6, 7, 8][..]);
252 });
253 }
254 }
255
256 #[test]
257 fn test_long_gtk() {
258 #[rustfmt::skip]
259 let buf = [
260 0xDD,
262 22, 0x00, 0x0F, 0xAC, 1, 200, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ];
269 let result = extract_elements(&buf[..]);
270 assert!(result.is_ok(), "Error: {:?}", result);
271
272 let elements = result.unwrap();
273 assert_eq!(elements.len(), 1);
274
275 for e in elements {
276 assert_variant!(e, Element::Gtk(hdr, kde) => {
277 assert_eq!(
278 hdr,
279 kde::Header { type_: 0xDD, len: 22, oui: Oui::DOT11, data_type: 1 }
280 );
281 assert_eq!(kde.info.value(), 200);
282 assert_eq!(
283 &kde.gtk[..],
284 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16][..]
285 );
286 });
287 }
288 }
289
290 #[test]
291 fn test_parse_legacy_wpa() {
292 #[rustfmt::skip]
293 let buf = [
294 0xdd, 0x16, 0x00, 0x50, 0xf2,
296 0x01, 0x01, 0x00,
298 0x00, 0x50, 0xf2, 0x02,
300 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
302 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
304 ];
305 let result = extract_elements(&buf[..]);
306 assert!(result.is_ok(), "Error: {:?}", result);
307
308 let elements = result.unwrap();
309 assert_eq!(elements.len(), 1);
310
311 for e in elements {
312 assert_variant!(e, Element::LegacyWpa1(wpa_ie) => {
313 assert_eq!(
314 wpa_ie.multicast_cipher,
315 cipher::Cipher { oui: Oui::MSFT, suite_type: cipher::TKIP }
316 );
317 assert_eq!(
318 wpa_ie.unicast_cipher_list,
319 vec![cipher::Cipher { oui: Oui::MSFT, suite_type: cipher::TKIP }]
320 );
321 assert_eq!(
322 wpa_ie.akm_list,
323 vec![akm::Akm { oui: Oui::MSFT, suite_type: akm::PSK }]
324 );
325 });
326 }
327 }
328}