blob: 696cd2fae86a335efe9f4cbd7ff59cfe007ae77f [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod kde;
use nom::IResult::{Done, Incomplete};
use nom::{le_u16, le_u8, ErrorKind, IResult, Needed};
use rsne;
use std::mem;
use {Error, Result};
#[derive(Debug)]
pub enum Element {
Gtk(kde::Header, kde::Gtk),
Rsne(rsne::Rsne),
Padding,
UnsupportedKde(kde::Header),
UnsupportedIe(u8, u8),
}
fn peek_u8_at<'a>(input: &'a [u8], index: usize) -> IResult<&'a [u8], u8> {
if input.len() <= index {
Incomplete(Needed::Size(index))
} else {
Done(input, input[index])
}
}
fn parse_ie<'a>(i0: &'a [u8]) -> IResult<&'a [u8], Element> {
let (i1, id) = try_parse!(i0, call!(peek_u8_at, 0));
let (i2, len) = try_parse!(i1, call!(peek_u8_at, 1));
let (out, bytes) = try_parse!(i2, take!(2 + (len as usize)));
match id {
rsne::ID => {
let (_, rsne) = try_parse!(bytes, rsne::from_bytes);
Done(out, Element::Rsne(rsne))
}
_ => Done(out, Element::UnsupportedIe(id, len)),
}
}
fn parse_element<'a>(input: &'a [u8]) -> IResult<&'a [u8], Element> {
let (_, type_) = try_parse!(input, call!(peek_u8_at, 0));
match type_ {
kde::TYPE => kde::parse(input),
_ => parse_ie(input),
}
}
named!(parse_elements<&[u8], Vec<Element>>, many0!(parse_element));
pub fn extract_elements(key_data: &[u8]) -> Result<Vec<Element>> {
// Key Data field must be at least 16 bytes long and its length a multiple of 8.
if key_data.len() % 8 != 0 || key_data.len() < 16 {
Err(Error::InvaidKeyDataLength(key_data.len()))
} else {
parse_elements(key_data)
.to_full_result()
.map_err(|e| Error::InvalidKeyData(e))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_complex_key_data() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
// GTK KDE
0xDD,
14, // Length
0x00, 0x0F, 0xAC, // OUI
1, // Data Type
5, // GTK Info
0, // Reserved
1, 2, 3, 4, 5, 6, 7, 8, // GTK (8 bytes)
// Unsupported IE
99, 6, 1, 2, 3, 4, 5, 6,
// 1st RSN Element
48, 6, // IE Header
1, 1, // Version
1, 2, 3, 4, // Group Data Cipher
// Unsupported KDE (wrong OUI)
0xDD, 14, 0x01, 0x0F, 0xAC,
1, // Data Type
5, 0, 1, 2, 3, 4, 5, 6, 7, 8,
// 2nd RSN Element
48, 6, // IE Header
9, 0, // Version
0x00, 0x0F, 0xAC, 1, // Group Data Cipher
// Unsupported IE
200, 2, 1, 3,
// 8 bytes padding
0xDD, 0, 0, 0,
];
let result = extract_elements(&buf[..]);
assert!(result.is_ok(), "Error: {:?}", result);
let elements = result.unwrap();
assert_eq!(elements.len(), 7);
let mut pos = 0;
for e in elements {
match e {
Element::Gtk(hdr, kde) => {
assert_eq!(pos, 0);
assert_eq!(hdr.type_, 0xDD);
assert_eq!(hdr.len, 14);
assert_eq!(hdr.oui, kde::OUI);
assert_eq!(hdr.data_type, 1);
assert_eq!(kde.info.value(), 5);
assert_eq!(kde.gtk, vec![1, 2, 3, 4, 5, 6, 7, 8]);
}
Element::UnsupportedIe(id, len) => match pos {
1 => {
assert_eq!(id, 99);
assert_eq!(len, 6);
}
5 => {
assert_eq!(id, 200);
assert_eq!(len, 2);
}
_ => assert!(false),
},
Element::Rsne(rsne) => match pos {
2 => {
assert_eq!(rsne.element_id, 48);
assert_eq!(rsne.length, 6);
assert_eq!(rsne.version, 257);
assert!(rsne.group_data_cipher_suite.is_some());
let cipher = rsne.group_data_cipher_suite.unwrap();
assert_eq!(cipher.suite_type, 4);
let oui = vec![1, 2, 3];
assert_eq!(cipher.oui, &oui[..]);
}
4 => {
assert_eq!(rsne.element_id, 48);
assert_eq!(rsne.length, 6);
assert_eq!(rsne.version, 9);
assert!(rsne.group_data_cipher_suite.is_some());
let cipher = rsne.group_data_cipher_suite.unwrap();
assert_eq!(cipher.suite_type, 1);
let oui = vec![0x00, 0x0F, 0xAC];
assert_eq!(cipher.oui, &oui[..]);
}
_ => assert!(false),
},
Element::UnsupportedKde(hdr) => {
assert_eq!(pos, 3);
assert_eq!(hdr.type_, 0xDD);
assert_eq!(hdr.len, 14);
let oui = vec![0x01, 0x0F, 0xAC];
assert_eq!(hdr.oui, &oui[..]);
assert_eq!(hdr.data_type, 1);
}
Element::Padding => assert_eq!(pos, 6),
_ => assert!(false, "Unexpected element found: {:?}", e),
}
pos += 1;
}
}
#[test]
fn test_too_short_key_data() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
10, 5, 1, 2, 3, 4, 5, // Unsupported IE
];
let result = extract_elements(&buf[..]);
assert_eq!(result.is_ok(), false, "Error: {:?}", result);
}
#[test]
fn test_not_multiple_of_8() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
// Unsupported IE
10, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
];
let result = extract_elements(&buf[..]);
assert_eq!(result.is_ok(), false, "Error: {:?}", result);
}
#[test]
fn test_no_padding() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
10, 14, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // Unsupported IE
];
let result = extract_elements(&buf[..]);
assert!(result.is_ok(), "Error: {:?}", result);
let elements = result.unwrap();
assert_eq!(elements.len(), 1);
for e in elements {
match e {
Element::UnsupportedIe(id, len) => {
assert_eq!(id, 10);
assert_eq!(len, 14);
}
_ => assert!(false, "Unexpected element found: {:?}", e),
}
}
}
#[test]
fn test_single_padding_byte() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
10, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Unsupported IE
0xDD, // 1 byte padding
];
let result = extract_elements(&buf[..]);
assert!(result.is_ok(), "Error: {:?}", result);
let elements = result.unwrap();
assert_eq!(elements.len(), 2);
for e in elements {
match e {
Element::UnsupportedIe(id, len) => {
assert_eq!(id, 10);
assert_eq!(len, 13);
}
Element::Padding => (),
_ => assert!(false, "Unexpected element found: {:?}", e),
}
}
}
#[test]
fn test_long_padding() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
20, 6, 1, 2, 3, 4, 5, 6, // Unsupported IE
0xdd, 0, 0, 0, 0, 0, 0, 0, // 8 bytes padding
];
let result = extract_elements(&buf[..]);
assert!(result.is_ok(), "Error: {:?}", result);
let elements = result.unwrap();
assert_eq!(elements.len(), 2);
for e in elements {
match e {
Element::UnsupportedIe(id, len) => {
assert_eq!(id, 20);
assert_eq!(len, 6);
}
Element::Padding => (),
_ => assert!(false, "Unexpected element found: {:?}", e),
}
}
}
#[test]
fn test_gtk() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
// GTK KDE
0xDD,
14, // Length
0x00, 0x0F, 0xAC, // OUI
1, // Data Type
5, // GTK Info
0, // Reserved
1, 2, 3, 4, 5, 6, 7, 8, // GTK (8 bytes)
];
let result = extract_elements(&buf[..]);
assert!(result.is_ok(), "Error: {:?}", result);
let elements = result.unwrap();
assert_eq!(elements.len(), 1);
for e in elements {
match e {
Element::Gtk(hdr, kde) => {
assert_eq!(hdr.type_, 0xDD);
assert_eq!(hdr.len, 14);
assert_eq!(hdr.oui, kde::OUI);
assert_eq!(hdr.data_type, 1);
assert_eq!(kde.info.value(), 5);
assert_eq!(kde.gtk, vec![1, 2, 3, 4, 5, 6, 7, 8]);
}
_ => assert!(false, "Unexpected element found: {:?}", e),
}
}
}
#[test]
fn test_long_gtk() {
#[cfg_attr(rustfmt, rustfmt_skip)]
let buf = [
// GTK KDE
0xDD,
22, // Length
0x00, 0x0F, 0xAC, // OUI
1, // Data Type
200, // GTK Info
0, // Reserved
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // GTK (16 bytes)
];
let result = extract_elements(&buf[..]);
assert!(result.is_ok(), "Error: {:?}", result);
let elements = result.unwrap();
assert_eq!(elements.len(), 1);
for e in elements {
match e {
Element::Gtk(hdr, kde) => {
assert_eq!(hdr.type_, 0xDD);
assert_eq!(hdr.len, 22);
assert_eq!(hdr.oui, kde::OUI);
assert_eq!(hdr.data_type, 1);
assert_eq!(kde.info.value(), 200);
assert_eq!(
kde.gtk,
vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
);
}
_ => assert!(false, "Unexpected element found: {:?}", e),
}
}
}
}