Expand description

Partial implementation of security for 802.4.15 frames.

For specifications of the procedures and structures, see section 7.4 of the 802.15.4-2011 standard.

Example on how to use frames with security

Note that the example below is very insecure, and should not be used in any production setting

use ieee802154::mac::{
    frame::security::{
        KeyDescriptorLookup,
        DeviceDescriptorLookup,
        DeviceDescriptor,
        SecurityContext,
        AddressingMode,
        U16,
        KeyIdentifier,
        SecurityControl,
        AuxiliarySecurityHeader,
        SecurityLevel,
        KeySource,
    },
    Address,
    ExtendedAddress,
    PanId,
    Header,
    Frame,
    FrameVersion,
    FrameContent,
    FrameType,
    FrameSerDesContext,
    FooterMode,
};
use ccm::aead::generic_array::GenericArray;
use aes::Aes128;
use byte::TryWrite;

/// Static key descriptor lookup struct that always returns
/// the same key, given that the input address is
/// an extended address
struct StaticKeyLookup;

impl KeyDescriptorLookup<U16> for StaticKeyLookup {
    fn lookup_key_descriptor(
        &self,
        _address_mode: AddressingMode,
        _key_identifier: Option<KeyIdentifier>,
        device_address: Option<Address>,
    ) -> Option<(u64, GenericArray<u8, U16>)> {
        let key = GenericArray::default();
        if let Some(Address::Extended(pan_id, ExtendedAddress(u64_id))) = device_address {
            Some((u64_id, key))
        } else {
            None
        }
    }
}

/// A device descriptor lookup that always returns the same
/// device descriptor
struct BasicDevDescriptorLookup<'a> {
    descriptor: &'a mut DeviceDescriptor,
}

impl<'a> BasicDevDescriptorLookup<'a> {
    pub fn new(descriptor: &'a mut DeviceDescriptor) -> Self {
        Self { descriptor }
    }
}

impl<'a> DeviceDescriptorLookup for BasicDevDescriptorLookup<'a> {
    fn lookup_device(
        &mut self,
        _addressing_mode: AddressingMode,
        _address: Address,
    ) -> Option<&mut DeviceDescriptor> {
        Some(self.descriptor)
    }
}

const STATIC_KEY_LOOKUP: StaticKeyLookup = StaticKeyLookup {};
const FRAME_CTR: u32 = 0x00000000;

/// Get a new security context that uses the Aes128 block cipher for CCM operations,
/// and the static key lookup for determining the key to use
fn aes_sec_ctx<'a>(source_euid: u64, frame_counter: u32) -> SecurityContext<Aes128, StaticKeyLookup> {
    SecurityContext::new(source_euid, frame_counter, STATIC_KEY_LOOKUP)
}

fn main() {
    let source_euid = 0x01;
    let source = Some(Address::Extended(PanId(0x111), ExtendedAddress(source_euid)));
    let destination = Some(Address::Extended(PanId(0x111), ExtendedAddress(0x02)));

    let device_desc = &mut DeviceDescriptor {
        frame_counter: FRAME_CTR,
        exempt: false,
    };

    let mut sec_ctx = aes_sec_ctx(source_euid, FRAME_CTR);
    let auxiliary_security_header = Some(AuxiliarySecurityHeader::new(
        SecurityControl::new(SecurityLevel::ENCMIC128),
        Some(KeyIdentifier {
            key_source: Some(KeySource::Long(0xAA)),
            key_index: 48,
        }),
    ));

    let payload = &[0u8, 1u8, 2u8, 3u8, 4u8];
    let frame_to_secure = 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,
            seq: 127,
            destination,
            source,
            auxiliary_security_header,
        },
        content: FrameContent::Data,
        payload,
        footer: [0x00, 0x00],
    };
    let mut buffer = [0u8; 128];
    // Write/"send" a MAC frame. Security is applied if an auxiliary security header
    // is present (which it is, in this case)
    // Note that this does _not_ change the contents of the frame, the secured data
    // is only written to the buffer
    let len = match frame_to_secure.try_write(
        &mut buffer,
        &mut FrameSerDesContext::new(FooterMode::None, Some(&mut sec_ctx)),
    ) {
        Ok(size) => size,
        Err(e) => 0,
    };

    // Verify that encryption succeeded and tag was appended correctly
    assert_eq!(&buffer[..len], &[
        0x9, 0xec, 0x7f, 0x11, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x1, 0x1, 0x0, 0x0,
        0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x0, 0x0, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        0x30, 0xf8, 0x58, 0x90, 0xd2, 0x7f, 0x46, 0xcf, 0x1a, 0x73, 0xd3, 0xad, 0x65, 0xda, 0x6c, 0xd1,
        0x4b, 0x73, 0xef, 0xbe, 0x79, 0x31,
    ]);
    // Read/"receive" a MAC frame. Unsecuring it is attempted if the header
    // has security enabled
    let unsecured_frame = match Frame::try_read_and_unsecure(
        &mut buffer[..len],
        &mut FrameSerDesContext::new(FooterMode::None, Some(&mut sec_ctx)),
        &mut BasicDevDescriptorLookup::new(device_desc),
    ) {
        Ok((frame, _)) => frame,
        Err(e) => panic!("Could not unsecure frame! {:?}", e),
    };
    assert_eq!(unsecured_frame.payload, &[0u8, 1u8, 2u8, 3u8, 4u8])
}

Modules

Provides a default AEAD, key descriptor lookup, and device descriptor lookups to satisfy the type requirements for (de-)serializing frames without providing any security

Structs

A struct describing the Auxiliary Security Header

A partial device descriptor

A key identifier

A context that used to keep track of cryptographic properties that are necessary for securing/unsecuring frames

The Security Control header

Enums

The addressing mode to use during descriptor lookups

The key identifier mode

A key source

Errors that can occur while performing security operations on frames

The level of security applied to the payload

Traits

Trait which marks a type as being a block cipher.

Encrypt-only functionality for block ciphers.

Perform a lookup of a device descriptor based on the provided address

Used to create a KeyDescriptor from a KeyIdentifier and device address

Instantiate a BlockCipher algorithm.

Type Definitions