wlan_rsn/key_data/
kde.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::Element;
6use crate::ProtectionInfo;
7use bitfield::bitfield;
8use nom::bytes::streaming::take;
9use nom::combinator::{eof, map};
10use nom::error::{Error, ErrorKind};
11use nom::number::streaming::{le_u16, le_u8};
12use nom::sequence::terminated;
13use nom::{IResult, Parser};
14use wlan_common::append::{BufferTooSmall, TrackedAppend, VecCursor};
15use wlan_common::ie::{wpa, write_wpa1_ie};
16use wlan_common::organization::Oui;
17
18pub const TYPE: u8 = 0xDD;
19const PADDING_DATA_LEN: u8 = 0;
20const HDR_LEN: usize = 6;
21/// Octets taken by OUI and data type.
22const HDR_OUI_TYPE_LEN: usize = 4;
23
24// IEEE Std 802.11-2016 Table 12.6
25const GTK_DATA_TYPE: u8 = 1;
26const IGTK_DATA_TYPE: u8 = 9;
27
28/// A GTK KDE's fixed length.
29/// Note: The KDE consists of a fixed and variable length (the GTK).
30const GTK_FIXED_LEN: usize = 2;
31
32const IGTK_IPN_LEN: usize = 6;
33const IGTK_FIXED_LEN: usize = 2 + IGTK_IPN_LEN;
34
35// IEEE Std 802.11-2016, 12.7.2, Figure 12-34
36#[derive(Default, Debug, PartialEq)]
37pub struct Header {
38    pub type_: u8,
39    pub len: u8,
40    pub oui: Oui,
41    pub data_type: u8,
42}
43
44impl Header {
45    pub fn new(type_: u8, len: u8, oui: &[u8], data_type: u8) -> Header {
46        let mut oui_buf = [0u8; 3];
47        oui_buf.copy_from_slice(oui);
48        Header { type_, len, data_type, oui: Oui::new(oui_buf), ..Default::default() }
49    }
50
51    pub fn new_dot11(data_type: u8, data_len: usize) -> Header {
52        Header { type_: TYPE, len: (HDR_OUI_TYPE_LEN + data_len) as u8, data_type, oui: Oui::DOT11 }
53    }
54
55    fn data_len(&self) -> usize {
56        if self.len < 4 {
57            0
58        } else {
59            (self.len as usize) - 4
60        }
61    }
62}
63
64// IEEE Std 802.11-2016, 12.7.2, j)
65pub enum GtkInfoTx {
66    _OnlyRx = 0,
67    BothRxTx = 1,
68}
69
70// IEEE Std 802.11-2016, 12.7.2, Figure 12-35
71bitfield! {
72    pub struct GtkInfo(u8);
73    impl Debug;
74    pub key_id, set_key_id: 1, 0;
75    pub tx, set_tx: 2, 2;
76    // Bit 3-7 reserved.
77    pub value, _: 7,0;
78}
79
80impl PartialEq for GtkInfo {
81    fn eq(&self, other: &Self) -> bool {
82        self.0 == other.0
83    }
84}
85
86/// GTK KDE:
87/// IEEE Std 802.11-2016, 12.7.2, Figure 12-35
88#[derive(Debug, PartialEq)]
89pub struct Gtk {
90    pub info: GtkInfo,
91    // 1 byte reserved.
92    pub gtk: Box<[u8]>,
93}
94
95impl Gtk {
96    pub fn new(key_id: u8, tx: GtkInfoTx, gtk: &[u8]) -> Self {
97        let mut gtk_info = GtkInfo(0);
98        gtk_info.set_key_id(key_id);
99        gtk_info.set_tx(tx as u8);
100        Self { info: gtk_info, gtk: gtk.into() }
101    }
102
103    /// Length of the GTK KDE including its fixed fields, not just the GTK.
104    pub fn len(&self) -> usize {
105        GTK_FIXED_LEN + self.gtk.len()
106    }
107}
108
109/// IGTK KDE:
110/// IEEE Std 802.11-2016, 12.7.2, Figure 12-42
111#[derive(Debug, PartialEq)]
112pub struct Igtk {
113    pub id: u16,
114    // IGTK Packet Number
115    pub ipn: [u8; IGTK_IPN_LEN],
116    pub igtk: Vec<u8>,
117}
118
119impl Igtk {
120    pub fn new(id: u16, ipn: &[u8], igtk: &[u8]) -> Self {
121        let mut ipn_buf = [0u8; IGTK_IPN_LEN];
122        ipn_buf.copy_from_slice(ipn);
123        Self { id, ipn: ipn_buf, igtk: igtk.to_vec() }
124    }
125
126    pub fn len(&self) -> usize {
127        IGTK_FIXED_LEN + self.igtk.len()
128    }
129}
130
131pub fn parse(i0: &[u8]) -> IResult<&[u8], Element> {
132    // Check whether parsing is finished.
133    if i0.len() <= 1 {
134        return Ok((&i0[i0.len()..], Element::Padding));
135    }
136
137    // Check whether the remaining data is padding.
138    let data_len = i0[1];
139    if data_len == PADDING_DATA_LEN {
140        return parse_padding(&i0[1..]);
141    }
142
143    // Read the KDE Header first.
144    let (i1, hdr) = parse_header(i0)?;
145    let (i2, bytes) = take(hdr.data_len()).parse(i1)?;
146    match hdr.oui {
147        Oui::DOT11 => match hdr.data_type {
148            GTK_DATA_TYPE => {
149                let (_, gtk) = parse_gtk(hdr.data_len()).parse(bytes)?;
150                Ok((i2, Element::Gtk(hdr, gtk)))
151            }
152            IGTK_DATA_TYPE => {
153                let (_, gtk) = parse_igtk(hdr.data_len()).parse(bytes)?;
154                Ok((i2, Element::Igtk(hdr, gtk)))
155            }
156            _ => Ok((i2, Element::UnsupportedKde(hdr))),
157        },
158        // The WPA1 IE uses the same vendor IE format as a KDE, so we handle it here as a special
159        // case.
160        Oui::MSFT if hdr.data_type == wpa::VENDOR_SPECIFIC_TYPE => {
161            let (_, wpa) = wpa::from_bytes(&bytes[..])?;
162            Ok((i2, Element::LegacyWpa1(wpa)))
163        }
164        _ => Ok((i2, Element::UnsupportedKde(hdr))),
165    }
166}
167
168fn parse_header(input: &[u8]) -> IResult<&[u8], Header> {
169    map((le_u8, le_u8, take(3usize), le_u8), |(header_type, length, oui, data_type)| {
170        Header::new(header_type, length, oui, data_type)
171    })
172    .parse(input)
173}
174
175fn parse_padding(input: &[u8]) -> IResult<&[u8], Element> {
176    if input.iter().all(|&x| x == 0) {
177        Ok((&[], Element::Padding))
178    } else {
179        // Return ErrorKind::Eof to indicate that we expected that the remaining input should have
180        // been all padding bytes.
181        Err(nom::Err::Error(Error::new(input, ErrorKind::Eof)))
182    }
183}
184
185fn parse_gtk<'a>(data_len: usize) -> impl Parser<&'a [u8], Output = Gtk, Error = Error<&'a [u8]>> {
186    map(
187        terminated(
188            (
189                map(le_u8::<&'a [u8], Error<_>>, GtkInfo),
190                /* 1 byte reserved */ take(1usize),
191                take(data_len - 2),
192            ),
193            eof,
194        ),
195        |(info, _reserved, gtk)| Gtk { info, gtk: Vec::from(gtk).into_boxed_slice() },
196    )
197}
198
199fn parse_igtk<'a>(
200    data_len: usize,
201) -> impl Parser<&'a [u8], Output = Igtk, Error = Error<&'a [u8]>> {
202    map(
203        terminated(
204            (le_u16::<&'a [u8], Error<_>>, take(IGTK_IPN_LEN), take(data_len - IGTK_FIXED_LEN)),
205            eof,
206        ),
207        |(id, ipn, igtk)| Igtk::new(id, ipn, igtk),
208    )
209}
210
211/// KDE Writer to assist with writing key data elements.
212pub struct Writer<A> {
213    buf: A,
214}
215
216impl Writer<VecCursor> {
217    pub fn new() -> Self {
218        Self { buf: VecCursor::new() }
219    }
220}
221
222impl<A: TrackedAppend> Writer<A> {
223    #[cfg(test)]
224    pub fn new_with(buf: A) -> Self {
225        Self { buf }
226    }
227
228    pub fn write_protection(&mut self, protection: &ProtectionInfo) -> Result<(), BufferTooSmall> {
229        match protection {
230            ProtectionInfo::Rsne(rsne) => rsne.write_into(&mut self.buf),
231            ProtectionInfo::LegacyWpa(wpa) => write_wpa1_ie(&mut self.buf, wpa),
232        }
233    }
234
235    fn write_kde_hdr(&mut self, hdr: Header) -> Result<(), BufferTooSmall> {
236        if !self.buf.can_append(HDR_LEN) {
237            return Err(BufferTooSmall);
238        }
239        self.buf.append_byte(hdr.type_)?;
240        self.buf.append_byte(hdr.len)?;
241        self.buf.append_bytes(&hdr.oui[..])?;
242        self.buf.append_byte(hdr.data_type)?;
243        Ok(())
244    }
245
246    pub fn write_gtk(&mut self, gtk_kde: &Gtk) -> Result<(), BufferTooSmall> {
247        if !self.buf.can_append(HDR_LEN + gtk_kde.len()) {
248            return Err(BufferTooSmall);
249        }
250        // KDE Header
251        let hdr = Header::new_dot11(GTK_DATA_TYPE, gtk_kde.len());
252        self.write_kde_hdr(hdr)?;
253
254        // GTK KDE
255        self.buf.append_byte(gtk_kde.info.value())?;
256        self.buf.append_byte(0)?;
257        self.buf.append_bytes(&gtk_kde.gtk[..])?;
258        Ok(())
259    }
260
261    pub fn write_igtk(&mut self, igtk_kde: &Igtk) -> Result<(), BufferTooSmall> {
262        if !self.buf.can_append(HDR_LEN + igtk_kde.len()) {
263            return Err(BufferTooSmall);
264        }
265        // KDE Header
266        let hdr = Header::new_dot11(IGTK_DATA_TYPE, igtk_kde.len());
267        self.write_kde_hdr(hdr)?;
268
269        // IGTK KDE
270        self.buf.append_bytes(&igtk_kde.id.to_le_bytes())?;
271        self.buf.append_bytes(&igtk_kde.ipn[..])?;
272        self.buf.append_bytes(&igtk_kde.igtk[..])?;
273        Ok(())
274    }
275
276    pub fn finalize_for_encryption(mut self) -> Result<A, BufferTooSmall> {
277        // Optional padding must be added if the key data will be encrypted.
278        // See IEEE Std 802.11-2016, 12.7.2 j)
279        // Padding is added to extend the key data field to a minimum size of 16 octets or
280        // otherwise be a multiple of 8 octets.
281        let written = self.buf.bytes_appended();
282        let padding_len =
283            if written < 16 { 16 - written } else { ((written + 7) / 8) * 8 - written };
284        if !self.buf.can_append(padding_len) {
285            return Err(BufferTooSmall);
286        }
287
288        if padding_len != 0 {
289            self.buf.append_byte(TYPE)?;
290            self.buf.append_bytes_zeroed(padding_len - 1)?;
291        }
292        Ok(self.buf)
293    }
294
295    pub fn finalize_for_plaintext(self) -> Result<A, BufferTooSmall> {
296        Ok(self.buf)
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303    use crate::key_data::extract_elements;
304    use wlan_common::assert_variant;
305    use wlan_common::test_utils::FixedSizedTestBuffer;
306
307    fn write_and_extract_padding(gtk_len: usize) -> Vec<u8> {
308        let mut w = Writer::new();
309        w.write_gtk(&Gtk::new(2, GtkInfoTx::BothRxTx, &vec![2; gtk_len]))
310            .expect("failure writing GTK KDE");
311        w.finalize_for_encryption()
312            .expect("failure finializing key data")
313            .into_vec()
314            .split_off(HDR_LEN + GTK_FIXED_LEN + gtk_len)
315    }
316
317    #[test]
318    fn test_padding_min_length() {
319        let buf = write_and_extract_padding(0);
320        assert_eq!(buf, vec![TYPE, 0, 0, 0, 0, 0, 0, 0]);
321
322        let buf = write_and_extract_padding(5);
323        assert_eq!(buf, vec![TYPE, 0, 0]);
324
325        let buf = write_and_extract_padding(7);
326        assert_eq!(buf, vec![TYPE]);
327    }
328
329    #[test]
330    fn test_no_padding_expected_for_plaintext() {
331        let mut w = Writer::new();
332        w.write_gtk(&Gtk::new(2, GtkInfoTx::BothRxTx, &[2; 2])).expect("failure writing GTK KDE");
333        let buf = w.finalize_for_plaintext().expect("failure finalizing key data");
334        assert_eq!(
335            &buf[..],
336            &[
337                // GTK KDE:
338                TYPE,
339                8,
340                0x00,
341                0x0F,
342                0xAC,
343                GTK_DATA_TYPE,
344                0b0000_0110,
345                0,
346                2,
347                2
348            ][..]
349        );
350    }
351
352    #[test]
353    fn test_no_padding() {
354        let buf = write_and_extract_padding(8);
355        assert_eq!(buf, vec![]);
356    }
357
358    #[test]
359    fn test_padding_multiple_8() {
360        let buf = write_and_extract_padding(9);
361        assert_eq!(buf, vec![TYPE, 0, 0, 0, 0, 0, 0]);
362
363        let buf = write_and_extract_padding(16);
364        assert_eq!(buf, vec![]);
365    }
366
367    #[test]
368    fn test_write_read_gtk_with_padding() {
369        // Write KDE:
370        let mut w = Writer::new();
371        w.write_gtk(&Gtk::new(2, GtkInfoTx::BothRxTx, &[24; 5])).expect("failure writing GTK KDE");
372        let buf = w.finalize_for_encryption().expect("failure finializing key data");
373        #[rustfmt::skip]
374        assert_eq!(&buf[..], &[
375            // GTK KDE:
376            TYPE, 11, 0x00, 0x0F, 0xAC, GTK_DATA_TYPE, 0b0000_0110, 0, 24, 24, 24, 24, 24,
377            // Padding:
378            TYPE, 0, 0,
379        ][..]);
380
381        // Read KDE:
382        let result = extract_elements(&buf[..]);
383        assert!(result.is_ok(), "Error: {:?}", result);
384        let mut elements = result.unwrap();
385        assert_eq!(elements.len(), 2);
386
387        assert_variant!(elements.remove(0), Element::Gtk(hdr, kde) => {
388            assert_eq!(hdr, Header { type_: 0xDD, len: 11, oui: Oui::DOT11, data_type: 1 });
389            assert_eq!(kde, Gtk { info: GtkInfo(6), gtk: Box::new([24; 5]) });
390        });
391        assert_variant!(elements.remove(0), Element::Padding);
392    }
393
394    #[test]
395    fn test_write_read_igtk() {
396        let igtk = Igtk::new(10, &[11; 6], &[22; 2]);
397        // Write KDE:
398        let mut w = Writer::new();
399        w.write_igtk(&igtk).expect("failure writing IGTK KDE");
400        let buf = w.finalize_for_encryption().expect("failure finializing key data");
401        #[rustfmt::skip]
402        assert_eq!(&buf[..], &[
403            TYPE, 14, 0x00, 0x0F, 0xAC, IGTK_DATA_TYPE, 10, 0, 11,11,11,11,11,11,22,22
404        ][..]);
405
406        // Read KDE:
407        let result = extract_elements(&buf[..]);
408        assert!(result.is_ok(), "Error: {:?}", result);
409        let mut elements = result.unwrap();
410        assert_eq!(elements.len(), 1);
411
412        assert_variant!(elements.remove(0), Element::Igtk(hdr, kde) => {
413            assert_eq!(hdr, Header {
414                type_: 0xDD, len: 14, oui: Oui::DOT11, data_type: IGTK_DATA_TYPE
415            });
416            assert_eq!(kde, igtk);
417        });
418    }
419
420    #[test]
421    fn test_write_gtk_too_small_buffer() {
422        Writer::new_with(FixedSizedTestBuffer::new(10))
423            .write_gtk(&Gtk::new(2, GtkInfoTx::BothRxTx, &[24; 5]))
424            .expect_err("expected failure writing GTK KDE");
425    }
426
427    #[test]
428    fn test_write_gtk_sufficient_fixed_buffer() {
429        Writer::new_with(FixedSizedTestBuffer::new(13))
430            .write_gtk(&Gtk::new(2, GtkInfoTx::BothRxTx, &[24; 5]))
431            .expect("expected success writing GTK KDE");
432    }
433
434    #[test]
435    fn test_create_gtk_element() {
436        let gtk = Gtk::new(1, GtkInfoTx::_OnlyRx, &[24; 16][..]);
437        assert_eq!(gtk.info.key_id(), 1);
438        assert_eq!(gtk.info.tx(), 0);
439        assert_eq!(&gtk.gtk[..], &[24; 16][..]);
440    }
441
442    #[test]
443    fn test_gtk_len() {
444        let gtk_kde = Gtk { info: GtkInfo(0), gtk: Box::new([]) };
445        assert_eq!(gtk_kde.len(), 2);
446
447        let gtk_kde = Gtk { info: GtkInfo(0), gtk: Box::new([0; 16]) };
448        assert_eq!(gtk_kde.len(), 18);
449
450        let gtk_kde = Gtk { info: GtkInfo(0), gtk: Box::new([0; 8]) };
451        assert_eq!(gtk_kde.len(), 10);
452
453        let gtk_kde = Gtk { info: GtkInfo(0), gtk: Box::new([0; 4]) };
454        assert_eq!(gtk_kde.len(), 6);
455
456        let gtk_kde = Gtk { info: GtkInfo(0), gtk: Box::new([0; 32]) };
457        assert_eq!(gtk_kde.len(), 34);
458    }
459}