blob: 80223350a71c60acd432fbe366ccd0aba558e38e [file]
// Copyright 2019 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.
use {
super::{AuthFrameRx, AuthFrameTx},
anyhow::{anyhow, bail, Error},
fidl_fuchsia_wlan_ieee80211::StatusCode,
wlan_common::{appendable::Appendable, buffer_reader::BufferReader},
};
/// An anticlogging token sent to a peer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AntiCloggingTokenMsg<'a> {
pub group_id: u16,
pub token: &'a [u8],
}
/// An SAE Commit message received or sent to a peer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommitMsg<'a> {
pub group_id: u16,
pub scalar: &'a [u8],
pub element: &'a [u8],
pub token: Option<&'a [u8]>, // Anticlogging token
}
/// An SAE Confirm message received or sent to a peer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConfirmMsg<'a> {
pub send_confirm: u16,
pub confirm: &'a [u8],
}
#[derive(Debug)]
pub enum ParseSuccess<'a> {
Commit(CommitMsg<'a>),
Confirm(ConfirmMsg<'a>),
AntiCloggingToken(AntiCloggingTokenMsg<'a>),
}
fn get_scalar_and_element_len_bytes(group_id: u16) -> Result<(usize, usize), Error> {
match group_id {
19 => Ok((32, 64)),
_ => bail!("Unsupported SAE group ID: {}", group_id),
}
}
pub fn parse<'a>(frame: &'a AuthFrameRx) -> Result<ParseSuccess<'a>, Error> {
// IEEE 802.11 9.3.3.12 Table 9-36 specifies all SAE auth frame formats.
match (frame.seq, frame.status_code) {
(1, StatusCode::Success) => parse_commit(frame.body).map(ParseSuccess::Commit),
(1, StatusCode::AntiCloggingTokenRequired) => {
parse_token(frame.body).map(ParseSuccess::AntiCloggingToken)
}
(2, StatusCode::Success) => parse_confirm(frame.body).map(ParseSuccess::Confirm),
_ => bail!("Could not parse received SAE frame"),
}
}
fn parse_token(body: &[u8]) -> Result<AntiCloggingTokenMsg, Error> {
let mut reader = BufferReader::new(body);
let group_id = reader.read_value::<u16>().ok_or(anyhow!("Failed to read group ID"))?;
if reader.bytes_remaining() == 0 {
bail!("Commit indicated AntiCloggingTokenRequired, but no token provided");
}
let token = reader.into_remaining();
Ok(AntiCloggingTokenMsg { group_id, token })
}
fn parse_commit(body: &[u8]) -> Result<CommitMsg, Error> {
let mut reader = BufferReader::new(body);
let group_id = reader.read_value::<u16>().ok_or(anyhow!("Failed to read group ID"))?;
let (scalar_size, element_size) = get_scalar_and_element_len_bytes(group_id)?;
let scalar = reader.read_bytes(scalar_size).ok_or(anyhow!("Buffer truncated"))?;
let element = reader.read_bytes(element_size).ok_or(anyhow!("Buffer truncated"))?;
let remaining_bytes = reader.into_remaining();
let token = match remaining_bytes.len() {
0 => None,
_ => Some(remaining_bytes),
};
Ok(CommitMsg { group_id, scalar, element, token })
}
const CONFIRM_BYTES: usize = 32;
fn parse_confirm(body: &[u8]) -> Result<ConfirmMsg, Error> {
let mut reader = BufferReader::new(body);
let send_confirm = reader.read_value::<u16>().ok_or(anyhow!("Failed to read send confirm"))?;
let confirm = reader.read_bytes(CONFIRM_BYTES).ok_or(anyhow!("Buffer truncated"))?;
match reader.bytes_remaining() {
0 => Ok(ConfirmMsg { send_confirm, confirm }),
_ => bail!("Buffer too long"),
}
}
pub fn write_commit(group_id: u16, scalar: &[u8], element: &[u8], token: &[u8]) -> AuthFrameTx {
let mut body = vec![];
body.reserve(2 + scalar.len() + element.len() + token.len());
body.append_value(&group_id);
body.append_bytes(scalar);
body.append_bytes(element);
body.append_bytes(token);
AuthFrameTx { seq: 1, status_code: StatusCode::Success, body }
}
pub fn write_token(group_id: u16, token: &[u8]) -> AuthFrameTx {
let mut body = vec![];
body.reserve(2 + token.len());
body.append_value(&group_id);
body.append_bytes(token);
AuthFrameTx { seq: 1, status_code: StatusCode::AntiCloggingTokenRequired, body }
}
pub fn write_confirm(send_confirm: u16, confirm: &[u8]) -> AuthFrameTx {
let mut body = vec![];
body.reserve(2 + confirm.len());
body.append_value(&send_confirm);
body.append_bytes(confirm);
AuthFrameTx { seq: 2, status_code: StatusCode::Success, body }
}
#[cfg(test)]
mod tests {
use {super::*, wlan_common::assert_variant};
#[rustfmt::skip]
const ECC_COMMIT_BODY: &[u8] = &[
// group id
19, 00,
// scalar [0x1; 32]
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// element [0x2; 64]
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
];
#[rustfmt::skip]
const ECC_CONFIRM_BODY: &[u8] = &[
// send-confirm
0x01, 0x00,
// confirm [0x3; 32]
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
];
#[rustfmt::skip]
const ECC_ACT_REQUIRED_BODY: &[u8] = &[
// group id
19, 00,
// anti-clogging token [0x4; 8]
4,4,4,4,4,4,4,4
];
#[test]
fn test_parse_commit() {
let commit_msg =
AuthFrameRx { seq: 1, status_code: StatusCode::Success, body: ECC_COMMIT_BODY };
let parse_result = parse(&commit_msg);
let commit = assert_variant!(parse_result, Ok(ParseSuccess::Commit(commit)) => commit);
assert_eq!(commit.group_id, 19);
assert_eq!(commit.scalar, &[1u8; 32][..]);
assert_eq!(commit.element, &[2u8; 64][..]);
assert!(commit.token.is_none());
}
#[test]
fn commit_with_token() {
let mut body = ECC_COMMIT_BODY.to_vec();
body.append(&mut vec![0x4; 8]);
let commit_msg = AuthFrameRx { seq: 1, status_code: StatusCode::Success, body: &body[..] };
let parse_result = parse(&commit_msg);
let commit = assert_variant!(parse_result, Ok(ParseSuccess::Commit(commit)) => commit);
assert_eq!(commit.group_id, 19);
assert_eq!(commit.scalar, &[1u8; 32][..]);
assert_eq!(commit.element, &[2u8; 64][..]);
let token = assert_variant!(commit.token, Some(token) => token);
assert_eq!(token, &[0x4; 8]);
}
#[test]
fn unknown_group_id_commit() {
let mut body = ECC_COMMIT_BODY.to_vec();
body[0] = 0xff; // not a real group
let commit_msg = AuthFrameRx { seq: 1, status_code: StatusCode::Success, body: &body[..] };
assert_variant!(parse(&commit_msg), Err(e) => {
assert!(format!("{:?}", e).contains("Unsupported SAE group ID: 255"))
});
}
#[test]
fn truncated_commit() {
let commit_msg =
AuthFrameRx { seq: 1, status_code: StatusCode::Success, body: &ECC_COMMIT_BODY[..20] };
assert_variant!(parse(&commit_msg), Err(e) => {
assert!(format!("{:?}", e).contains("Buffer truncated"))
});
let commit_msg = AuthFrameRx { seq: 1, status_code: StatusCode::Success, body: &[] };
assert_variant!(parse(&commit_msg), Err(e) => {
assert!(format!("{:?}", e).contains("Failed to read group ID"))
});
}
#[test]
fn test_parse_confirm() {
let confirm_msg =
AuthFrameRx { seq: 2, status_code: StatusCode::Success, body: ECC_CONFIRM_BODY };
let parse_result = parse(&confirm_msg);
let confirm = assert_variant!(parse_result, Ok(ParseSuccess::Confirm(confirm)) => confirm);
assert_eq!(confirm.send_confirm, 1);
assert_eq!(confirm.confirm, &[3u8; 32][..]);
}
#[test]
fn truncated_confirm() {
let confirm_msg =
AuthFrameRx { seq: 2, status_code: StatusCode::Success, body: &ECC_CONFIRM_BODY[..20] };
assert_variant!(parse(&confirm_msg), Err(e) => {
assert!(format!("{:?}", e).contains("Buffer truncated"))
});
let confirm_msg = AuthFrameRx { seq: 2, status_code: StatusCode::Success, body: &[] };
assert_variant!(parse(&confirm_msg), Err(e) => {
assert!(format!("{:?}", e).contains("Failed to read send confirm"))
});
}
#[test]
fn padded_confirm() {
let mut body = ECC_CONFIRM_BODY.to_vec();
body.push(0xff);
let confirm_msg = AuthFrameRx { seq: 2, status_code: StatusCode::Success, body: &body[..] };
assert_variant!(parse(&confirm_msg), Err(e) => {
assert!(format!("{:?}", e).contains("Buffer too long"))
});
}
#[test]
fn test_parse_anticlogging_token_required() {
let act_required = AuthFrameRx {
seq: 1,
status_code: StatusCode::AntiCloggingTokenRequired,
body: ECC_ACT_REQUIRED_BODY,
};
let parse_result = parse(&act_required);
let act = assert_variant!(parse_result, Ok(ParseSuccess::AntiCloggingToken(act)) => act);
assert_eq!(act.group_id, 19);
assert_eq!(act.token, &[0x4; 8][..]);
}
#[test]
fn truncated_anticlogging_token() {
let act_required = AuthFrameRx {
seq: 1,
status_code: StatusCode::AntiCloggingTokenRequired,
body: &[19, 00],
};
assert_variant!(parse(&act_required), Err(e) => {
assert!(format!("{:?}", e).contains("no token provided"))
});
let act_required =
AuthFrameRx { seq: 1, status_code: StatusCode::AntiCloggingTokenRequired, body: &[19] };
assert_variant!(parse(&act_required), Err(e) => {
assert!(format!("{:?}", e).contains("Failed to read group ID"))
});
let act_required =
AuthFrameRx { seq: 1, status_code: StatusCode::AntiCloggingTokenRequired, body: &[] };
assert_variant!(parse(&act_required), Err(e) => {
assert!(format!("{:?}", e).contains("Failed to read group ID"))
});
}
#[test]
fn test_write_commit() {
let auth_frame = write_commit(19, &[1u8; 32], &[2u8; 64], &[]);
assert_eq!(auth_frame.seq, 1);
assert_eq!(auth_frame.status_code, StatusCode::Success);
assert_eq!(&auth_frame.body[..], ECC_COMMIT_BODY);
}
#[test]
fn test_write_commit_with_token() {
let auth_frame = write_commit(19, &[1u8; 32], &[2u8; 64], &[4u8; 8]);
assert_eq!(auth_frame.seq, 1);
assert_eq!(auth_frame.status_code, StatusCode::Success);
let mut expected_body = ECC_COMMIT_BODY.to_vec();
expected_body.append(&mut vec![4u8; 8]);
assert_eq!(&auth_frame.body[..], &expected_body[..]);
}
#[test]
fn test_write_confirm() {
let auth_frame = write_confirm(1, &[3u8; 32]);
assert_eq!(auth_frame.seq, 2);
assert_eq!(auth_frame.status_code, StatusCode::Success);
assert_eq!(&auth_frame.body[..], ECC_CONFIRM_BODY);
}
#[test]
fn test_write_anticlogging_token() {
let auth_frame = write_token(19, &[4u8; 8]);
assert_eq!(auth_frame.seq, 1);
assert_eq!(auth_frame.status_code, StatusCode::AntiCloggingTokenRequired);
assert_eq!(&auth_frame.body[..], ECC_ACT_REQUIRED_BODY);
}
}