use crate::mac::beacon::Beacon;
use crate::mac::command::Command;
mod frame_control;
pub mod header;
pub mod security;
use byte::{ctx::Bytes, BytesExt, TryRead, TryWrite, LE};
use ccm::aead::generic_array::typenum::consts::U16;
use cipher::{BlockCipher, BlockEncrypt, NewBlockCipher};
use header::FrameType;
pub use header::Header;
use self::security::{
default::Unimplemented, DeviceDescriptorLookup, KeyDescriptorLookup,
SecurityContext, SecurityError,
};
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Frame<'p> {
pub header: Header,
pub content: FrameContent,
pub payload: &'p [u8],
pub footer: [u8; 2],
}
pub struct FrameSerDesContext<'a, AEADBLKCIPH, KEYDESCLO>
where
AEADBLKCIPH: NewBlockCipher + BlockCipher<BlockSize = U16>,
KEYDESCLO: KeyDescriptorLookup<AEADBLKCIPH::KeySize>,
{
footer_mode: FooterMode,
security_ctx: Option<&'a mut SecurityContext<AEADBLKCIPH, KEYDESCLO>>,
}
impl<'a, AEADBLKCIPH, KEYDESCLO> FrameSerDesContext<'a, AEADBLKCIPH, KEYDESCLO>
where
AEADBLKCIPH: NewBlockCipher + BlockCipher<BlockSize = U16>,
KEYDESCLO: KeyDescriptorLookup<AEADBLKCIPH::KeySize>,
{
pub fn new(
mode: FooterMode,
security_ctx: Option<&'a mut SecurityContext<AEADBLKCIPH, KEYDESCLO>>,
) -> Self {
FrameSerDesContext {
footer_mode: mode,
security_ctx,
}
}
}
impl FrameSerDesContext<'_, Unimplemented, Unimplemented> {
pub fn no_security(mode: FooterMode) -> Self {
FrameSerDesContext {
footer_mode: mode,
security_ctx: None,
}
}
}
impl<AEADBLKCIPH, KEYDESCLO>
TryWrite<&mut FrameSerDesContext<'_, AEADBLKCIPH, KEYDESCLO>> for Frame<'_>
where
AEADBLKCIPH: NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
KEYDESCLO: KeyDescriptorLookup<AEADBLKCIPH::KeySize>,
{
fn try_write(
self,
bytes: &mut [u8],
context: &mut FrameSerDesContext<AEADBLKCIPH, KEYDESCLO>,
) -> byte::Result<usize> {
let mode = &context.footer_mode;
let offset = &mut 0;
bytes.write_with(offset, self.header, &context.security_ctx)?;
bytes.write(offset, self.content)?;
let mut security_enabled = false;
if let Some(ctx) = context.security_ctx.as_mut() {
let write_secured = security::secure_frame(
self,
ctx,
context.footer_mode,
&mut bytes[*offset..],
);
match write_secured {
Ok(len) => {
security_enabled = true;
*offset += len
}
Err(e) => match e {
SecurityError::SecurityNotEnabled => {}
_ => return Err(e)?,
},
}
}
if !security_enabled {
bytes.write(offset, self.payload.as_ref())?;
}
match mode {
FooterMode::None => {}
FooterMode::Explicit => bytes.write(offset, &self.footer[..])?,
}
Ok(*offset)
}
}
impl<'a> Frame<'a> {
pub fn try_read_and_unsecure<AEADBLKCIPH, KEYDESCLO, DEVDESCLO>(
buf: &'a mut [u8],
ctx: &mut FrameSerDesContext<'_, AEADBLKCIPH, KEYDESCLO>,
dev_desc_lo: &mut DEVDESCLO,
) -> Result<(Frame<'a>, usize), SecurityError>
where
AEADBLKCIPH:
NewBlockCipher + BlockCipher<BlockSize = U16> + BlockEncrypt,
KEYDESCLO: KeyDescriptorLookup<AEADBLKCIPH::KeySize>,
DEVDESCLO: DeviceDescriptorLookup,
{
let offset = &mut 0;
let header: Header = buf.read(offset)?;
let content = buf.read_with(offset, &header)?;
let mut tag_size = 0;
if header.has_security() {
if let Some(sec_ctx) = ctx.security_ctx.as_mut() {
tag_size = match security::unsecure_frame(
&header,
&mut buf[*offset..],
sec_ctx,
ctx.footer_mode,
dev_desc_lo,
) {
Ok(size) => size,
Err(e) => match e {
SecurityError::SecurityNotEnabled => 0,
_ => return Err(e),
},
};
} else {
return Err(SecurityError::InvalidSecContext);
}
}
let payload =
buf.read_with(offset, Bytes::Len(buf.len() - *offset - tag_size))?;
let frame = Frame {
header,
content,
payload,
footer: [0, 0],
};
Ok((frame, *offset))
}
}
impl<'a> TryRead<'a, FooterMode> for Frame<'a> {
fn try_read(
bytes: &'a [u8],
mode: FooterMode,
) -> byte::Result<(Self, usize)> {
let offset = &mut 0;
let header: Header = bytes.read(offset)?;
let content = bytes.read_with(offset, &header)?;
if header.has_security() {
return Err(DecodeError::SecurityEnabled)?;
}
let (payload, footer) = match mode {
FooterMode::None => (
bytes.read_with(offset, Bytes::Len(bytes.len() - *offset))?,
0u16,
),
FooterMode::Explicit => (
bytes
.read_with(offset, Bytes::Len(bytes.len() - *offset - 2))?,
bytes.read_with(offset, LE)?,
),
};
let frame = Frame {
header,
content,
payload,
footer: footer.to_le_bytes(),
};
Ok((frame, *offset))
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FooterMode {
None,
Explicit,
}
impl Default for FooterMode {
fn default() -> Self {
Self::None
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FrameContent {
Beacon(Beacon),
Data,
Acknowledgement,
Command(Command),
Multipurpose,
FragOrFragAck,
Extended,
}
impl TryWrite for FrameContent {
fn try_write(self, bytes: &mut [u8], _ctx: ()) -> byte::Result<usize> {
let offset = &mut 0;
match self {
FrameContent::Beacon(beacon) => bytes.write(offset, beacon)?,
FrameContent::Command(command) => bytes.write(offset, command)?,
_ => (),
};
Ok(*offset)
}
}
impl TryRead<'_, &Header> for FrameContent {
fn try_read(bytes: &[u8], header: &Header) -> byte::Result<(Self, usize)> {
let offset = &mut 0;
Ok((
match header.frame_type {
FrameType::Beacon => FrameContent::Beacon(bytes.read(offset)?),
FrameType::Data => FrameContent::Data,
FrameType::Acknowledgement => FrameContent::Acknowledgement,
FrameType::MacCommand => {
FrameContent::Command(bytes.read(offset)?)
}
FrameType::Multipurpose => FrameContent::Multipurpose,
FrameType::FragOrFragAck => FrameContent::FragOrFragAck,
FrameType::Extended => FrameContent::Extended,
},
*offset,
))
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DecodeError {
NotEnoughBytes,
InvalidFrameType(u8),
SecurityEnabled,
InvalidAddressMode(u8),
InvalidFrameVersion(u8),
InvalidSecurityLevel(u8),
InvalidKeyIdentifierMode(u8),
MissingSecurityCtx,
AuxSecHeaderAbsent,
InvalidValue,
}
impl From<DecodeError> for byte::Error {
fn from(e: DecodeError) -> Self {
match e {
DecodeError::NotEnoughBytes => byte::Error::Incomplete,
DecodeError::InvalidFrameType(_) => byte::Error::BadInput {
err: "InvalidFrameType",
},
DecodeError::InvalidAddressMode(_) => byte::Error::BadInput {
err: "InvalidAddressMode",
},
DecodeError::InvalidFrameVersion(_) => byte::Error::BadInput {
err: "InvalidFrameVersion",
},
DecodeError::InvalidValue => byte::Error::BadInput {
err: "InvalidValue",
},
DecodeError::InvalidSecurityLevel(_) => byte::Error::BadInput {
err: "InvalidSecurityLevel",
},
DecodeError::InvalidKeyIdentifierMode(_) => byte::Error::BadInput {
err: "InvalidKeyIdentifierMode",
},
DecodeError::MissingSecurityCtx => byte::Error::BadInput {
err: "MissingSecurityCtx",
},
DecodeError::AuxSecHeaderAbsent => byte::Error::BadInput {
err: "AuxSecHeaderAbsent",
},
DecodeError::SecurityEnabled => byte::Error::BadInput {
err: "SecurityEnabled (use Frame::try_read_and_unsecure)",
},
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EncodeError {
WriteError,
MissingSecurityCtx,
DisallowedPanIdCompress,
UnknownError,
}
impl From<EncodeError> for byte::Error {
fn from(e: EncodeError) -> Self {
match e {
EncodeError::WriteError => {
byte::Error::BadInput { err: "WriteError" }
}
EncodeError::MissingSecurityCtx => byte::Error::BadInput {
err: "MissingSecurityCtx",
},
EncodeError::DisallowedPanIdCompress => byte::Error::BadInput {
err: "DisallowedPanIdCompress",
},
EncodeError::UnknownError => byte::Error::BadInput {
err: "UnknownError",
},
}
}
}
#[cfg(test)]
mod tests {
use crate::mac::beacon;
use crate::mac::command;
use crate::mac::frame::*;
use crate::mac::{
Address, ExtendedAddress, FrameVersion, PanId, ShortAddress,
};
#[test]
fn decode_ver0_pan_id_compression() {
let data = [
0x41, 0x88, 0x91, 0x8f, 0x20, 0xff, 0xff, 0x33, 0x44, 0x00, 0x00,
];
let frame: Frame = data.read_with(&mut 0, FooterMode::None).unwrap();
let hdr = frame.header;
assert_eq!(hdr.frame_type, FrameType::Data);
assert_eq!(hdr.has_security(), false);
assert_eq!(hdr.frame_pending, false);
assert_eq!(hdr.ack_request, false);
assert_eq!(hdr.pan_id_compress, true);
assert_eq!(hdr.version, FrameVersion::Ieee802154_2003);
assert_eq!(
frame.header.destination,
Some(Address::Short(PanId(0x208f), ShortAddress(0xffff)))
);
assert_eq!(
frame.header.source,
Some(Address::Short(PanId(0x208f), ShortAddress(0x4433)))
);
assert_eq!(frame.header.seq, 145);
}
#[test]
fn decode_ver0_pan_id_compression_bad() {
let data = [
0x41, 0x80, 0x91, 0x8f, 0x20, 0xff, 0xff, 0x33, 0x44, 0x00, 0x00,
];
let frame = data.read_with::<Frame>(&mut 0, FooterMode::None);
assert!(frame.is_err());
if let Err(e) = frame {
assert_eq!(e, DecodeError::InvalidAddressMode(0).into())
}
}
#[test]
fn decode_ver0_extended() {
let data = [
0x21, 0xc8, 0x8b, 0xff, 0xff, 0x02, 0x00, 0x23, 0x00, 0x60, 0xe2,
0x16, 0x21, 0x1c, 0x4a, 0xc2, 0xae, 0xaa, 0xbb, 0xcc,
];
let frame: Frame = data.read_with(&mut 0, FooterMode::None).unwrap();
let hdr = frame.header;
assert_eq!(hdr.frame_type, FrameType::Data);
assert_eq!(hdr.has_security(), false);
assert_eq!(hdr.frame_pending, false);
assert_eq!(hdr.ack_request, true);
assert_eq!(hdr.pan_id_compress, false);
assert_eq!(hdr.version, FrameVersion::Ieee802154_2003);
assert_eq!(
frame.header.destination,
Some(Address::Short(PanId(0xffff), ShortAddress(0x0002)))
);
assert_eq!(
frame.header.source,
Some(Address::Extended(
PanId(0x0023),
ExtendedAddress(0xaec24a1c2116e260)
))
);
assert_eq!(frame.header.seq, 139);
}
#[test]
fn encode_ver0_short() {
let frame = Frame {
header: Header {
ie_present: false,
seq_no_suppress: false,
frame_type: FrameType::Data,
frame_pending: false,
ack_request: false,
pan_id_compress: false,
version: FrameVersion::Ieee802154_2003,
destination: Some(Address::Short(
PanId(0x1234),
ShortAddress(0x5678),
)),
source: Some(Address::Short(
PanId(0x4321),
ShortAddress(0x9abc),
)),
seq: 0x01,
auxiliary_security_header: None,
},
content: FrameContent::Data,
payload: &[0xde, 0xf0],
footer: [0x00, 0x00],
};
let mut buf = [0u8; 32];
let mut len = 0usize;
buf.write_with(
&mut len,
frame,
&mut FrameSerDesContext::no_security(FooterMode::None),
)
.unwrap();
assert_eq!(len, 13);
assert_eq!(
buf[..len],
[
0x01, 0x88, 0x01, 0x34, 0x12, 0x78, 0x56, 0x21, 0x43, 0xbc,
0x9a, 0xde, 0xf0
]
);
}
#[test]
fn encode_ver1_extended() {
let frame = Frame {
header: Header {
ie_present: false,
seq_no_suppress: false,
frame_type: FrameType::Beacon,
frame_pending: true,
ack_request: false,
pan_id_compress: false,
version: FrameVersion::Ieee802154_2006,
destination: Some(Address::Extended(
PanId(0x1234),
ExtendedAddress(0x1122334455667788),
)),
source: Some(Address::Short(
PanId(0x4321),
ShortAddress(0x9abc),
)),
seq: 0xff,
auxiliary_security_header: None,
},
content: FrameContent::Beacon(beacon::Beacon {
superframe_spec: beacon::SuperframeSpecification {
beacon_order: beacon::BeaconOrder::OnDemand,
superframe_order: beacon::SuperframeOrder::Inactive,
final_cap_slot: 15,
battery_life_extension: false,
pan_coordinator: false,
association_permit: false,
},
guaranteed_time_slot_info:
beacon::GuaranteedTimeSlotInformation::new(),
pending_address: beacon::PendingAddress::new(),
}),
payload: &[0xde, 0xf0],
footer: [0x00, 0x00],
};
let mut buf = [0u8; 32];
let mut len = 0usize;
buf.write_with(
&mut len,
frame,
&mut FrameSerDesContext::no_security(FooterMode::None),
)
.unwrap();
assert_eq!(len, 23);
assert_eq!(
buf[..len],
[
0x10, 0x9c, 0xff, 0x34, 0x12, 0x88, 0x77, 0x66, 0x55, 0x44,
0x33, 0x22, 0x11, 0x21, 0x43, 0xbc, 0x9a, 0xff, 0x0f, 0x00,
0x00, 0xde, 0xf0
]
);
}
#[test]
fn encode_ver0_pan_compress() {
let frame = Frame {
header: Header {
ie_present: false,
seq_no_suppress: false,
frame_type: FrameType::Acknowledgement,
frame_pending: false,
ack_request: false,
pan_id_compress: true,
version: FrameVersion::Ieee802154_2003,
destination: Some(Address::Extended(
PanId(0x1234),
ExtendedAddress(0x1122334455667788),
)),
source: Some(Address::Short(
PanId(0x1234),
ShortAddress(0x9abc),
)),
seq: 0xff,
auxiliary_security_header: None,
},
content: FrameContent::Acknowledgement,
payload: &[],
footer: [0x00, 0x00],
};
let mut buf = [0u8; 32];
let mut len = 0usize;
buf.write_with(
&mut len,
frame,
&mut FrameSerDesContext::no_security(FooterMode::None),
)
.unwrap();
assert_eq!(len, 15);
assert_eq!(
buf[..len],
[
0x42, 0x8c, 0xff, 0x34, 0x12, 0x88, 0x77, 0x66, 0x55, 0x44,
0x33, 0x22, 0x11, 0xbc, 0x9a
]
);
}
#[test]
fn encode_ver2_none() {
let frame = Frame {
header: Header {
ie_present: false,
seq_no_suppress: false,
frame_type: FrameType::MacCommand,
frame_pending: false,
ack_request: true,
pan_id_compress: false,
version: FrameVersion::Ieee802154,
destination: None,
source: Some(Address::Short(
PanId(0x1234),
ShortAddress(0x9abc),
)),
seq: 0xff,
auxiliary_security_header: None,
},
content: FrameContent::Command(command::Command::DataRequest),
payload: &[],
footer: [0x00, 0x00],
};
let mut buf = [0u8; 32];
let mut len = 0usize;
buf.write_with(
&mut len,
frame,
&mut FrameSerDesContext::no_security(FooterMode::None),
)
.unwrap();
assert_eq!(len, 8);
assert_eq!(
buf[..len],
[0x23, 0xa0, 0xff, 0x34, 0x12, 0xbc, 0x9a, 0x04]
);
}
#[test]
fn empty_addressing_and_panid_compress() {
let mut frame_data = [0u8; 127];
let mut offset = 0;
let mut header = Header {
frame_type: FrameType::Data,
version: FrameVersion::Ieee802154_2006,
frame_pending: false,
ack_request: false,
pan_id_compress: true,
destination: None,
source: Some(Address::Extended(
PanId(0xABCD),
ExtendedAddress(0xFF),
)),
seq: 1,
seq_no_suppress: false,
ie_present: false,
auxiliary_security_header: None,
};
assert!(frame_data
.write_with(
&mut offset,
header,
&Some(&mut SecurityContext::no_security()),
)
.is_err());
header.destination =
Some(Address::Extended(PanId(0xABCD), ExtendedAddress(0xFF)));
header.source = None;
header.source =
Some(Address::Extended(PanId(0xABCD), ExtendedAddress(0xFF)));
offset = 0;
assert!(frame_data
.write_with(
&mut offset,
header,
&Some(&mut SecurityContext::no_security()),
)
.is_ok());
}
}