1pub mod fake_wpa_ies;
6
7use super::rsn::{akm, cipher, suite_selector};
8
9use crate::append::{Append, BufferTooSmall};
10use crate::organization::Oui;
11use nom::bytes::streaming::take;
12use nom::combinator::map;
13use nom::multi::length_count;
14use nom::number::streaming::le_u16;
15use nom::{IResult, Parser};
16
17pub const OUI: Oui = Oui::MSFT;
22pub const VENDOR_SPECIFIC_TYPE: u8 = 1;
24pub const WPA_TYPE: u16 = 1;
26#[derive(Debug, PartialOrd, PartialEq, Eq, Clone)]
34pub struct WpaIe {
35 pub multicast_cipher: cipher::Cipher,
36 pub unicast_cipher_list: Vec<cipher::Cipher>,
37 pub akm_list: Vec<akm::Akm>,
38}
39
40impl WpaIe {
41 const FIXED_FIELDS_LENGTH: usize = 10;
42 pub fn len(&self) -> usize {
43 Self::FIXED_FIELDS_LENGTH + self.unicast_cipher_list.len() * 4 + self.akm_list.len() * 4
44 }
45
46 pub fn into_bytes(self) -> Vec<u8> {
47 let mut buf = Vec::new();
48 self.write_into(&mut buf).unwrap();
49 buf
50 }
51
52 pub fn write_into<A: Append>(&self, buf: &mut A) -> Result<(), BufferTooSmall> {
53 if !buf.can_append(self.len()) {
54 return Err(BufferTooSmall);
55 }
56
57 buf.append_value(&WPA_TYPE)?;
58
59 buf.append_bytes(&self.multicast_cipher.oui[..])?;
60 buf.append_value(&self.multicast_cipher.suite_type)?;
61
62 buf.append_value(&(self.unicast_cipher_list.len() as u16))?;
63 for cipher in &self.unicast_cipher_list {
64 buf.append_bytes(&cipher.oui[..])?;
65 buf.append_value(&cipher.suite_type)?;
66 }
67
68 buf.append_value(&(self.akm_list.len() as u16))?;
69 for akm in &self.akm_list {
70 buf.append_bytes(&akm.oui[..])?;
71 buf.append_value(&akm.suite_type)?;
72 }
73
74 Ok(())
75 }
76}
77
78fn read_suite_selector<T>(input: &[u8]) -> IResult<&[u8], T>
79where
80 T: suite_selector::Factory<Suite = T>,
81{
82 let (i1, bytes) = take(4usize).parse(input)?;
83 let oui = Oui::new([bytes[0], bytes[1], bytes[2]]);
84 return Ok((i1, T::new(oui, bytes[3])));
85}
86
87fn parse_akm(input: &[u8]) -> IResult<&[u8], akm::Akm> {
88 read_suite_selector::<akm::Akm>(input)
89}
90
91fn parse_cipher(input: &[u8]) -> IResult<&[u8], cipher::Cipher> {
92 read_suite_selector::<cipher::Cipher>(input)
93}
94
95pub fn from_bytes(input: &[u8]) -> IResult<&[u8], WpaIe> {
97 map(
98 (le_u16, parse_cipher, length_count(le_u16, parse_cipher), length_count(le_u16, parse_akm)),
101 |(_wpa_type, multicast_cipher, unicast_cipher_list, akm_list)| WpaIe {
102 multicast_cipher,
103 unicast_cipher_list,
104 akm_list,
105 },
106 )
107 .parse(input)
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[rustfmt::skip]
115 const DEFAULT_FRAME: [u8; 18] = [
116 0x01, 0x00,
118 0x00, 0x50, 0xf2, 0x02,
120 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
122 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
124 ];
125
126 #[rustfmt::skip]
127 const FRAME_WITH_EXTRA_BYTES: [u8; 20] = [
128 0x01, 0x00,
130 0x00, 0x50, 0xf2, 0x04,
132 0x01, 0x00, 0x00, 0x50, 0xf2, 0x04,
134 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
136 0x0c, 0x00,
138 ];
139
140 #[test]
141 fn test_write_into() {
142 let wpa_frame_bytes = WpaIe {
143 multicast_cipher: cipher::Cipher { oui: OUI, suite_type: cipher::TKIP },
144 unicast_cipher_list: vec![cipher::Cipher { oui: OUI, suite_type: cipher::TKIP }],
145 akm_list: vec![akm::Akm { oui: OUI, suite_type: akm::PSK }],
146 }
147 .into_bytes();
148 assert_eq!(&wpa_frame_bytes[..], &DEFAULT_FRAME[..]);
149 }
150
151 #[test]
152 fn test_write_into_roundtrip() {
153 let wpa_frame = from_bytes(&DEFAULT_FRAME[..]);
154 assert!(wpa_frame.is_ok());
155 let wpa_frame = wpa_frame.unwrap().1;
156 let wpa_frame_bytes = wpa_frame.into_bytes();
157 assert_eq!(&wpa_frame_bytes[..], &DEFAULT_FRAME[..]);
158 }
159
160 #[test]
161 fn test_parse_correct() {
162 let wpa_frame = from_bytes(&DEFAULT_FRAME[..]);
163 assert!(wpa_frame.is_ok());
164 let wpa_frame = wpa_frame.unwrap().1;
165 assert_eq!(
166 wpa_frame.multicast_cipher,
167 cipher::Cipher { oui: OUI, suite_type: cipher::TKIP }
168 );
169 assert_eq!(
170 wpa_frame.unicast_cipher_list,
171 vec![cipher::Cipher { oui: OUI, suite_type: cipher::TKIP }]
172 );
173 assert_eq!(wpa_frame.akm_list, vec![akm::Akm { oui: OUI, suite_type: akm::PSK }]);
174 }
175
176 #[test]
177 fn test_parse_bad_frame() {
178 #[rustfmt::skip]
179 let bad_frame: Vec<u8> = vec![
180 0x01, 0x00,
182 0x00, 0x50, 0xf2, 0x02,
184 0x16, 0x00, 0x00, 0x50, 0xf2, 0x02,
186 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02,
188 ];
189 let wpa_frame = from_bytes(&bad_frame[..]);
190 assert!(!wpa_frame.is_ok());
191 }
192
193 #[test]
194 fn test_truncated_frame() {
195 #[rustfmt::skip]
196 let bad_frame: Vec<u8> = vec![
197 0x01, 0x00,
199 0x00, 0x50
201 ];
202 let wpa_frame = from_bytes(&bad_frame[..]);
203 assert!(!wpa_frame.is_ok());
204 }
205
206 #[test]
207 fn test_parse_with_extra_bytes() {
208 let wpa_frame = from_bytes(&FRAME_WITH_EXTRA_BYTES[..]);
209 assert!(wpa_frame.is_ok());
210 let wpa_frame = wpa_frame.unwrap().1;
211 assert_eq!(
212 wpa_frame.multicast_cipher,
213 cipher::Cipher { oui: OUI, suite_type: cipher::CCMP_128 }
214 );
215 assert_eq!(
216 wpa_frame.unicast_cipher_list,
217 vec![cipher::Cipher { oui: OUI, suite_type: cipher::CCMP_128 }]
218 );
219 assert_eq!(wpa_frame.akm_list, vec![akm::Akm { oui: OUI, suite_type: akm::PSK }]);
220 }
221}