wlan_rsn/key_data/
mod.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
5pub 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            // GTK KDE
85            0xDD,
86            14, // Length
87            0x00, 0x0F, 0xAC, // OUI
88            1, // Data Type
89            5, // GTK Info
90            0, // Reserved
91            1, 2, 3, 4, 5, 6, 7, 8, // GTK (8 bytes)
92            // Unsupported IE
93            99, 6, 1, 2, 3, 4, 5, 6,
94            // 1st RSN Element
95            48, 6, // IE Header
96            1, 1, // Version
97            1, 2, 3, 4, // Group Data Cipher
98            // Unsupported KDE (wrong OUI)
99            0xDD, 14, 0x01, 0x0F, 0xAC,
100            1, // Data Type
101            5, 0, 1, 2, 3, 4, 5, 6, 7, 8,
102            // 2nd RSN Element
103            48, 6, // IE Header
104            9, 0, // Version
105            0x00, 0x0F, 0xAC, 1, // Group Data Cipher
106            // Unsupported IE
107            200, 2, 1, 3,
108            // 4 bytes padding
109            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, // Unsupported IE
180        ];
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, // Unsupported IE
194            0xDD, // 1 byte padding
195        ];
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, // Unsupported IE
212            0xdd, 0, 0, 0, 0, 0, 0, 0, // 8 bytes padding
213        ];
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            // GTK KDE
230            0xDD,
231            14, // Length
232            0x00, 0x0F, 0xAC, // OUI
233            1, // Data Type
234            5, // GTK Info
235            0, // Reserved
236            1, 2, 3, 4, 5, 6, 7, 8, // GTK (8 bytes)
237        ];
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            // GTK KDE
261            0xDD,
262            22, // Length
263            0x00, 0x0F, 0xAC, // OUI
264            1, // Data Type
265            200, // GTK Info
266            0, // Reserved
267            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // GTK (16 bytes)
268        ];
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            // MSFT Vendor IE
295            0xdd, 0x16, 0x00, 0x50, 0xf2,
296            // WPA header
297            0x01, 0x01, 0x00,
298            // Multicast cipher
299            0x00, 0x50, 0xf2, 0x02,
300            // Unicast cipher list
301            0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
302            // AKM list
303            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}