1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #15150 from fabian18/ieee802154_security

Initial implementation of IEEE 802.15.4 security
This commit is contained in:
benpicco 2020-12-09 14:14:27 +01:00 committed by GitHub
commit 1477a340fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1543 additions and 35 deletions

View File

@ -28,14 +28,91 @@
#include "luid.h"
#include "byteorder.h"
#include "net/ieee802154.h"
#if IS_USED(IEEE802154_SECURITY)
#include "net/ieee802154_security.h"
#endif
#include "net/gnrc.h"
#include "at86rf2xx_registers.h"
#include "at86rf2xx_internal.h"
#include "at86rf2xx_netdev.h"
#if IS_USED(MODULE_AT86RF2XX_AES_SPI)
#include "at86rf2xx_aes.h"
#endif
#define ENABLE_DEBUG 0
#include "debug.h"
#if IS_USED(MODULE_AT86RF2XX_AES_SPI) && \
IS_USED(MODULE_IEEE802154_SECURITY)
/**
* @brief Pass the 802.15.4 encryption key to the transceiver hardware
*
* @param[in] dev Abstract security device descriptor
* @param[in] key Encryption key to be used
* @param[in] key_size Size of the encryption key in bytes
*/
static void _at86rf2xx_set_key(ieee802154_sec_dev_t *dev,
const uint8_t *key, uint8_t key_size)
{
(void)key_size;
at86rf2xx_aes_key_write_encrypt((at86rf2xx_t *)dev->ctx, key);
}
/**
* @brief Compute CBC-MAC from IEEE 802.15.4 security context
*
* @param[in] dev Abstract security device descriptor
* @param[out] cipher Buffer to store cipher blocks
* @param[in] iv Initial vector
* @param[in] plain Input data blocks
* @param[in] nblocks Number of blocks
*/
static void _at86rf2xx_cbc(const ieee802154_sec_dev_t *dev,
uint8_t *cipher,
uint8_t *iv,
const uint8_t *plain,
uint8_t nblocks)
{
at86rf2xx_aes_cbc_encrypt((at86rf2xx_t *)dev->ctx,
(aes_block_t *)cipher,
NULL,
iv,
(aes_block_t *)plain,
nblocks);
}
/**
* @brief Perform ECB encryption
*
* @param[in] dev Abstract security device descriptor
* @param[out] cipher Output cipher blocks
* @param[in] plain Plain blocks
* @param[in] nblocks Number of blocks
*/
static void _at86rf2xx_ecb(const ieee802154_sec_dev_t *dev,
uint8_t *cipher,
const uint8_t *plain,
uint8_t nblocks)
{
at86rf2xx_aes_ecb_encrypt((at86rf2xx_t *)dev->ctx,
(aes_block_t *)cipher,
NULL,
(aes_block_t *)plain,
nblocks);
}
/**
* @brief Struct that contains IEEE 802.15.4 security operations
* which are implemented, using the transceiver´s hardware
* crypto capabilities
*/
static const ieee802154_radio_cipher_ops_t _at86rf2xx_cipher_ops = {
.set_key = _at86rf2xx_set_key,
.ecb = _at86rf2xx_ecb,
.cbc = _at86rf2xx_cbc
};
#endif /* IS_USED(MODULE_AT86RF2XX_COMMON_AES_SPI) && \
IS_USED(MODULE_IEEE802154_SECURITY) */
void at86rf2xx_setup(at86rf2xx_t *dev, const at86rf2xx_params_t *params, uint8_t index)
{
@ -152,6 +229,16 @@ void at86rf2xx_reset(at86rf2xx_t *dev)
/* clear interrupt flags */
at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS);
#if IS_USED(MODULE_IEEE802154_SECURITY) && \
IS_USED(MODULE_AT86RF2XX_AES_SPI)
dev->netdev.sec_ctx.dev.cipher_ops = &_at86rf2xx_cipher_ops;
dev->netdev.sec_ctx.dev.ctx = dev;
/* All configurations of the security module, the SRAM content,
and keys are reset during DEEP_SLEEP or RESET state. */
at86rf2xx_aes_key_write_encrypt(dev,
dev->netdev.sec_ctx.cipher.context.context);
#endif
/* State to return after receiving or transmitting */
dev->idle_state = AT86RF2XX_PHY_STATE_RX;
/* go into RX state */

View File

@ -39,6 +39,9 @@
#include "at86rf2xx_netdev.h"
#include "at86rf2xx_internal.h"
#include "at86rf2xx_registers.h"
#if IS_USED(MODULE_AT86RF2XX_AES_SPI)
#include "at86rf2xx_aes.h"
#endif
#define ENABLE_DEBUG 0
#include "debug.h"
@ -665,6 +668,22 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
#if IS_USED(MODULE_AT86RF2XX_COMMON_AES_SPI) && \
IS_USED(MODULE_IEEE802154_SECURITY)
case NETOPT_ENCRYPTION_KEY:
assert(len >= IEEE802154_SEC_KEY_LENGTH);
at86rf2xx_aes_key_write_encrypt(dev, val);
if (memcmp(dev->netdev.sec_ctx.cipher.context.context, val, len)) {
/* If the key changes, the frame conter can be reset to 0*/
dev->netdev.sec_ctx.frame_counter = 0;
}
memcpy(dev->netdev.sec_ctx.cipher.context.context, val,
IEEE802154_SEC_KEY_LENGTH);
res = IEEE802154_SEC_KEY_LENGTH;
break;
#endif /* IS_USED(MODULE_AT86RF2XX_AES_SPI) && \
IS_USED(MODULE_IEEE802154_SECURITY) */
default:
break;
}

View File

@ -22,6 +22,9 @@
#include "net/eui_provider.h"
#include "net/ieee802154.h"
#if IS_USED(MODULE_IEEE802154_SECURITY)
#include "net/ieee802154_security.h"
#endif
#include "net/gnrc/nettype.h"
#include "net/netopt.h"
#include "net/netdev.h"
@ -116,6 +119,9 @@ typedef struct {
uint8_t page; /**< channel page */
uint16_t flags; /**< flags as defined above */
int16_t txpower; /**< tx power in dBm */
#if IS_USED(MODULE_IEEE802154_SECURITY) || defined (Doxygen)
ieee802154_sec_context_t sec_ctx; /**< security context */
#endif
/** @} */
} netdev_ieee802154_t;

View File

@ -45,6 +45,12 @@ void netdev_ieee802154_reset(netdev_ieee802154_t *dev)
/* Initialize PAN ID and call netdev::set to propagate it */
dev->pan = CONFIG_IEEE802154_DEFAULT_PANID;
dev->netdev.driver->set(&dev->netdev, NETOPT_NID, &dev->pan, sizeof(dev->pan));
#if IS_USED(MODULE_IEEE802154_SECURITY)
ieee802154_sec_init(&dev->sec_ctx);
const netopt_enable_t e = NETOPT_ENABLE;
netdev_ieee802154_set(dev, NETOPT_ENCRYPTION, &e, sizeof(e));
#endif
}
static inline uint16_t _get_ieee802154_pdu(netdev_ieee802154_t *dev)
@ -115,6 +121,18 @@ int netdev_ieee802154_get(netdev_ieee802154_t *dev, netopt_t opt, void *value,
*((uint16_t *)value) = (uint16_t)dev->chan;
res = sizeof(dev->chan);
break;
#if IS_USED(MODULE_IEEE802154_SECURITY)
case NETOPT_ENCRYPTION:
assert(max_len == sizeof(netopt_enable_t));
if (dev->flags & NETDEV_IEEE802154_SECURITY_EN) {
*((netopt_enable_t *)value) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)value) = NETOPT_DISABLE;
}
res = sizeof(netopt_enable_t);
break;
#endif /* IS_USED(MODULE_IEEE802154_SECURITY) */
case NETOPT_ACK_REQ:
assert(max_len == sizeof(netopt_enable_t));
if (dev->flags & NETDEV_IEEE802154_ACK_REQ) {
@ -159,6 +177,9 @@ int netdev_ieee802154_get(netdev_ieee802154_t *dev, netopt_t opt, void *value,
*((uint16_t *)value) = (_get_ieee802154_pdu(dev)
- IEEE802154_MAX_HDR_LEN)
#if IS_USED(MODULE_IEEE802154_SECURITY)
-IEEE802154_MAX_AUX_HDR_LEN
#endif /* IS_USED(MODULE_IEEE802154_SECURITY) */
- IEEE802154_FCS_LEN;
res = sizeof(uint16_t);
break;
@ -219,6 +240,28 @@ int netdev_ieee802154_set(netdev_ieee802154_t *dev, netopt_t opt, const void *va
dev->pan = *((uint16_t *)value);
res = sizeof(dev->pan);
break;
#if IS_USED(MODULE_IEEE802154_SECURITY)
case NETOPT_ENCRYPTION:
assert(len == sizeof(netopt_enable_t));
if ((*(bool *)value)) {
dev->flags |= NETDEV_IEEE802154_SECURITY_EN;
}
else {
dev->flags &= ~NETDEV_IEEE802154_SECURITY_EN;
}
res = sizeof(netopt_enable_t);
break;
case NETOPT_ENCRYPTION_KEY:
assert(len >= IEEE802154_SEC_KEY_LENGTH);
if (memcmp(dev->sec_ctx.cipher.context.context, value, len)) {
/* If the key changes, the frame conter can be reset to 0*/
dev->sec_ctx.frame_counter = 0;
}
memcpy(dev->sec_ctx.cipher.context.context, value,
IEEE802154_SEC_KEY_LENGTH);
res = IEEE802154_SEC_KEY_LENGTH;
break;
#endif /* IS_USED(MODULE_IEEE802154_SECURITY) */
case NETOPT_ACK_REQ:
if ((*(bool *)value)) {
dev->flags |= NETDEV_IEEE802154_ACK_REQ;
@ -283,5 +326,4 @@ int netdev_ieee802154_dst_filter(netdev_ieee802154_t *dev, const uint8_t *mhr)
return 1;
}
/** @} */

View File

@ -64,6 +64,7 @@ PSEUDOMODULES += gnrc_txtsnd
PSEUDOMODULES += heap_cmd
PSEUDOMODULES += i2c_scan
PSEUDOMODULES += ieee802154_radio_hal
PSEUDOMODULES += ieee802154_security
PSEUDOMODULES += ieee802154_submac
PSEUDOMODULES += ina3221_alerts
PSEUDOMODULES += l2filter_blacklist

View File

@ -50,6 +50,12 @@ ifneq (,$(filter sys_bus_%,$(USEMODULE)))
USEMODULE += core_msg_bus
endif
ifneq (,$(filter ieee802154_security,$(USEMODULE)))
USEMODULE += crypto
USEMODULE += crypto_aes
USEMODULE += cipher_modes
endif
ifneq (,$(filter rtt_cmd,$(USEMODULE)))
FEATURES_REQUIRED += periph_rtt
endif

View File

@ -716,6 +716,64 @@ struct ieee802154_radio_ops {
int (*set_rx_mode)(ieee802154_dev_t *dev, ieee802154_rx_mode_t mode);
};
/**
* @brief Forward declaration of the radio cipher ops structure
*/
typedef struct ieee802154_radio_cipher_ops ieee802154_radio_cipher_ops_t;
/**
* @brief Forward declaration of the IEEE802.15.4 security device descriptor
*/
typedef struct ieee802154_sec_dev ieee802154_sec_dev_t;
/**
* @brief IEEE802.15.4 security device descriptor
*/
struct ieee802154_sec_dev {
/**
* @brief Pointer to the operations of the device
*/
const struct ieee802154_radio_cipher_ops *cipher_ops;
/**
* @brief pointer to the context of the device
*/
void *ctx;
};
struct ieee802154_radio_cipher_ops {
/**
* @brief Function to set the encryption key for the
* next cipher operation
*
* @param[in] dev Security device descriptor
* @param[in] key Key to be used for the next cipher operation
* @param[in] key_size key size in bytes
*/
void (*set_key)(ieee802154_sec_dev_t *dev,
const uint8_t *key, uint8_t key_size);
/**
* @brief Function to perform ECB encryption
*
* @param[in] dev Security device descriptor
* @param[out] cipher Output cipher blocks
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
void (*ecb)(const ieee802154_sec_dev_t *dev, uint8_t *cipher,
const uint8_t *plain, uint8_t nblocks);
/**
* @brief Function to compute CBC-MAC
*
* @param[in] dev Security device descriptor
* @param[in] cipher Output cipher blocks
* @param[in, out] iv in: IV; out: computed MIC
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
void (*cbc)(const ieee802154_sec_dev_t *dev, uint8_t *cipher,
uint8_t *iv, const uint8_t *plain, uint8_t nblocks);
};
/**
* @brief Shortcut to @ref ieee802154_radio_ops::write
*
@ -1155,6 +1213,48 @@ static inline int ieee802154_radio_set_rx_mode(ieee802154_dev_t *dev,
return dev->driver->set_rx_mode(dev, mode);
}
/**
* @brief Shortcut to ieee802154_sec_dev_t::ieee802154_radio_cipher_ops_t::set_key
*
* @param[in] dev IEEE802.15.4 security device descriptor
* @param[in] key Encryption key
* @param[in] key_size Size of the key in bytes
*/
static inline void ieee802154_radio_cipher_set_key(ieee802154_sec_dev_t *dev,
const uint8_t *key, uint8_t key_size)
{
dev->cipher_ops->set_key(dev->ctx, key, key_size);
}
/**
* @brief Shortcut to ieee802154_sec_dev_t::ieee802154_radio_cipher_ops_t::ecb
*
* @param[in] dev IEEE802.15.4 security device descriptor
* @param[out] cipher Output cipher blocks
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
static inline void ieee802154_radio_cipher_ecb(const ieee802154_sec_dev_t *dev, uint8_t *cipher,
const uint8_t *plain, uint8_t nblocks)
{
dev->cipher_ops->ecb(dev->ctx, cipher, plain, nblocks);
}
/**
* @brief Shortcut to ieee802154_sec_dev_t::ieee802154_radio_cipher_ops_t::cbc
*
* @param[in] dev IEEE802.15.4 security device descriptor
* @param[out] cipher Output cipher blocks
* @param[in] iv Initial vector to be XOR´ed to the first plain block
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
static inline void ieee802154_radio_cipher_cbc(const ieee802154_sec_dev_t *dev, uint8_t *cipher,
uint8_t *iv, const uint8_t *plain, uint8_t nblocks)
{
dev->cipher_ops->cbc(dev->ctx, cipher, iv, plain, nblocks);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,489 @@
/*
* Copyright (C) 2020 Otto-von-Gericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup net_ieee802154_security IEEE 802.15.4 security
* @ingroup net
* @brief IEEE 802.15.4 security header
* @{
*
* @file
* @brief IEEE 802.15.4 security interface
*
* Specification: IEEE 802.15.4 - 2015
* https://www.silabs.com/content/usergenerated/asi/cloud/attachments/siliconlabs/en/community/wireless/proprietary/forum/jcr:content/content/primary/qna/802_15_4_promiscuous-tbzR/hivukadin_vukadi-iTXQ/802.15.4-2015.pdf
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*/
#ifndef NET_IEEE802154_SECURITY_H
#define NET_IEEE802154_SECURITY_H
#include <stdint.h>
#include "kernel_defines.h"
#include "ieee802154.h"
#include "crypto/ciphers.h"
#ifdef __cplusplus
extern "C" {
#endif
#if IS_USED(MODULE_IEEE802154_RADIO_HAL)
#include "net/ieee802154/radio.h"
#else
/**
* @brief Forward declaration of an IEEE802.15.4 abstract security device
*/
typedef struct ieee802154_sec_dev ieee802154_sec_dev_t;
/**
* @brief Struct of security operations
*/
typedef struct ieee802154_radio_cipher_ops {
/**
* @brief Function to set the encryption key for the
* next cipher operation
*
* @param[in] dev Will be @ref ieee802154_sec_context_t::ieee802154_sec_dev_t
* @param[in] key Key to be used for the next cipher operation
* @param[in] key_size key size in bytes
*/
void (*set_key)(ieee802154_sec_dev_t *dev,
const uint8_t *key,
uint8_t key_size);
/**
* @brief Function type to compute CBC-MAC
*
* @param[in] dev Will be @ref ieee802154_sec_context_t::ieee802154_sec_dev_t
* @param[in] cipher Output cipher blocks
* @param[in, out] iv in: IV; out: computed MIC
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
void (*cbc)(const ieee802154_sec_dev_t *dev,
uint8_t *cipher,
uint8_t *iv,
const uint8_t *plain,
uint8_t nblocks);
/**
* @brief Function type to perform ECB encryption
*
* @param[in] dev Will be @ref ieee802154_sec_context_t::ieee802154_sec_dev_t
* @param[out] cipher Output cipher blocks
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
void (*ecb)(const ieee802154_sec_dev_t *dev,
uint8_t *cipher,
const uint8_t *plain,
uint8_t nblocks);
} ieee802154_radio_cipher_ops_t;
/**
* @brief IEEE802.15.4 security device descriptor
*/
struct ieee802154_sec_dev {
/**
* @brief Pointer to the operations of the device
*/
const struct ieee802154_radio_cipher_ops *cipher_ops;
/**
* @brief Pointer to the context of the device
*/
void *ctx;
};
#endif
#if !defined(IEEE802154_DEFAULT_KEY) || defined(DOXYGEN)
/**
* @brief AES key that is used in the test vectors from the specification
*
* @note Predefine it yourself,
* if you want another key to be set up on initialization
*/
#define IEEE802154_DEFAULT_KEY { 0xc0, 0xc1, 0xc2, 0xc3, \
0xc4, 0xc5, 0xc6, 0xc7, \
0xc8, 0xc9, 0xca, 0xcb, \
0xcc, 0xcd, 0xce, 0xcf }
#endif
/**
* @brief Length of an AES key in bytes
*/
#define IEEE802154_SEC_KEY_LENGTH (16U)
/**
* @brief Block size of an encryption block
*/
#define IEEE802154_SEC_BLOCK_SIZE (16U)
/**
* @brief Maximum length of the security auxiliary header in bytes
*/
#define IEEE802154_MAX_AUX_HDR_LEN (14U)
/**
* @brief Maximum Size of IEEE 802.15.4 MAC
*/
#define IEEE802154_MAC_SIZE (16U)
/**
* @brief Mask to get security level bits
*/
#define IEEE802154_SCF_SECLEVEL_MASK (0x07)
/**
* @brief Number of shifts to set/get security level bits
*/
#define IEEE802154_SCF_SECLEVEL_SHIFT (0)
/**
* @brief Mask to get key mode bits
*/
#define IEEE802154_SCF_KEYMODE_MASK (0x18)
/**
* @brief Number of shifts to set/get key mode bits
*/
#define IEEE802154_SCF_KEYMODE_SHIFT (3)
/**
* @brief Security levels
*
* <em>IEEE802154_SCF_SECLEVEL_MIC*</em>:
* A message integrity code (MIC), also known as MAC,
* is used to prove authentication. The MIC covers the whole frame
* i.e. header, auxiliary header, and frame payload.
* The MIC is always encrypted, thus it must be decrypted by the receiver,
* to be checked.
*
* <em>IEEE802154_SCF_SECLEVEL_ENC*</em>:
* AES-128 in ECB mode is used to encrypt the payload of a frame to provide
* confidentiality.
*
* <em>IEEE802154_SCF_SECLEVEL_ENC_MIC*</em>:
* A combination of the two modes above is used to ensure
* authentication and confidentiality.
*/
typedef enum {
IEEE802154_SCF_SECLEVEL_NONE = 0x00, /**< no security */
IEEE802154_SCF_SECLEVEL_MIC32 = 0x01, /**< 32 bit MIC */
IEEE802154_SCF_SECLEVEL_MIC64 = 0x02, /**< 64 bit MIC */
IEEE802154_SCF_SECLEVEL_MIC128 = 0x03, /**< 128 bit MIC */
IEEE802154_SCF_SECLEVEL_ENC = 0x04, /**< encryption */
IEEE802154_SCF_SECLEVEL_ENC_MIC32 = 0x05, /**< enc. + 32 bit MIC */
IEEE802154_SCF_SECLEVEL_ENC_MIC64 = 0x06, /**< enc. + 64 bit MIC (mandatory) */
IEEE802154_SCF_SECLEVEL_ENC_MIC128 = 0x07 /**< enc. + 128 bit MIC */
} ieee802154_scf_seclevel_t;
/**
* @brief Key identifier modes
*
* The key identifier field in the auxiliary header
* consists of the key source and the key index fields and is only present
* if the key identifier mode is not IEEE802154_SCF_KEYMODE_IMPLICIT.
* (see 9.4.3 in the spec.)
*
* +----------------+-------------+------------------+------------------------------------+
* | mode | key source | key index | description |
* +----------------+-------------+------------------+------------------------------------+
* | IMPLICIT | 0 bytes | 0 bytes | The key is implicitly |
* | | | | known to the receiver. |
* +----------------+-------------+------------------+------------------------------------+
* | INDEX | 0 bytes | 1 byte | The key can be determined |
* | | | | from the key index. |
* +----------------+-------------+------------------+------------------------------------+
* | SHORT_INDEX | 4 bytes | 1 byte | The key is a group key and can be |
* | | | | determined from the key index and |
* | | | | the source PAN ID and the |
* | | | | short source address |
* | | | | of the originator of the frame. |
* +----------------+-------------+------------------+------------------------------------+
* | HX_INDEX | 8 bytes | 1 byte | The key can be determined |
* | | | | from the key index and |
* | | | | the long address of the originator |
* | | | | of the frame. |
* +----------------+-------------+------------------+------------------------------------+
*/
typedef enum {
IEEE802154_SCF_KEYMODE_IMPLICIT = 0x00, /**< Key is determined implicitly */
IEEE802154_SCF_KEYMODE_INDEX = 0x01, /**< Key is determined from key index */
IEEE802154_SCF_KEYMODE_SHORT_INDEX = 0x02, /**< Key is determined from 4 byte key source and key index */
IEEE802154_SCF_KEYMODE_HW_INDEX = 0x03 /**< Key is determined from 8 byte key source and key index */
} ieee802154_scr_keymode_t;
/**
* @brief IEEE 802.15.4 security error codes
*/
typedef enum {
IEEE802154_SEC_OK, /**< Everything went fine */
IEEE802154_SEC_FRAME_COUNTER_OVERFLOW, /**< The requested operation would let the frame counter overflow */
IEEE802154_SEC_NO_KEY, /**< Could not find the key to perform a requested cipher operation */
IEEE802154_SEC_MAC_CHECK_FAILURE, /**< The computet MAC did not match */
IEEE802154_SEC_UNSUPORTED, /**< Unsupported operation */
} ieee802154_sec_error_t;
/**
* @brief Struct to hold IEEE 802.15.4 security information
*/
typedef struct ieee802154_sec_context {
/**
* @brief Cipher context with AES128 interface and key storage
*/
cipher_t cipher;
/**
* @brief Security level IEEE802154_SCF_SECLEVEL_*
*/
uint8_t security_level;
/**
* @brief Key mode IEEE802154_SCF_KEYMODE_*
*/
uint8_t key_id_mode;
/**
* @brief Key index
*/
uint8_t key_index;
/**
* @brief Key source
*
* Content depends on key_id_mode
*/
uint8_t key_source[IEEE802154_LONG_ADDRESS_LEN];
/**
* @brief Own frame counter
*/
uint32_t frame_counter;
/**
* @brief 802.15.4 security dev
*/
ieee802154_sec_dev_t dev;
} ieee802154_sec_context_t;
/**
* @brief IEEE 802.15.4 auxiliary security header
*/
typedef struct __attribute__((packed)) {
/**
* @brief Security Control field (SCF)
*
* Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
* +--------+--------+--------+--------+--------+--------+--------+--------+
* | security level | key id. mode | fc sup.| ASN | r | r |
* +--------+--------+--------+--------+--------+--------+--------+--------+
*
* security level:
* one of IEEE802154_SCF_SECLEVEL_*
* key identifier mode:
* one of IEEE802154_SCF_KEY_*
* frame counter suppression:
* basically always zero because we do not support TSCH right now
* ASN:
* basically always zero because we do not support TSCG right now
*/
uint8_t scf;
/**
* @brief frame counter
*/
uint32_t fc;
/**
* @brief key identifier (0 - 9 bytes) according to key id. mode
*/
uint8_t key_id[];
} ieee802154_aux_sec_t;
/**
* @brief Content of key_source if key mode is IEEE802154_SCF_KEYMODE_INDEX
*/
typedef struct __attribute__((packed)) {
/**
* @brief Key index of key from originator, defined by key source
*/
uint8_t key_index;
} ieee802154_aux_sec_key_identifier_1_t;
/**
* @brief Content of key_source if key mode is IEEE802154_SCF_KEYMODE_SHORT_INDEX
*/
typedef struct __attribute__((packed)) {
/**
* @brief macPANId concatenated with macShortAddress
*/
uint8_t key_source[4];
/**
* @brief Key index of key from originator, defined by key source
*/
uint8_t key_index;
} ieee802154_aux_sec_key_identifier_5_t;
/**
* @brief Content of key_source if key mode is IEEE802154_SCF_KEYMODE_HW_INDEX
*/
typedef struct __attribute__((packed)) {
/**
* @brief macExtendedAddress
*/
uint8_t key_source[IEEE802154_LONG_ADDRESS_LEN];
/**
* @brief Key index of key from originator, defined by key source
*/
uint8_t key_index;
} ieee802154_aux_sec_key_identifier_9_t;
/**
* @brief Format of 13 byte nonce
*/
typedef struct __attribute__((packed)) {
/**
* @brief Source long address
*/
uint8_t src_addr[IEEE802154_LONG_ADDRESS_LEN];
/**
* @brief Frame counter
*/
uint32_t frame_counter;
/**
* @brief One of IEEE802154_SCF_SECLEVEL_*
*/
uint8_t security_level;
} ieee802154_ccm_nonce_t;
/**
* @brief Format of 16 byte input block of CCM
*/
typedef struct __attribute__((packed)) {
/**
* @brief Flags field
*/
uint8_t flags;
/**
* @brief Nonce (Number that is only used once)
*/
ieee802154_ccm_nonce_t nonce;
/**
* @brief Either the length of the actual message (for CBC-MAC) or
* a block counter (for CTR)
*/
uint16_t counter;
} ieee802154_ccm_block_t;
/**
* @brief Initialize IEEE 802.15.4 security context with default values
*
* @param[out] ctx security context
*/
void ieee802154_sec_init(ieee802154_sec_context_t *ctx);
/**
* @brief Encrypt IEEE 802.15.4 frame according to @p ctx
*
* @param[in] ctx IEEE 802.15.4 security context
* @param[in] header Pointer to frame header
* @param[in, out] header_size in: Header size; out: Size of header and auxiliary header
* @param[in,out] payload in: Plain payload; out: Encrypted payload
* @param[in] payload_size Size of payload
* @param[out] mic Buffer to store computed MIC
* @param[out] mic_size Size of MIC
* @param[in] src_address Source address
*
* @pre @p header should be large enough to also store the auxiliary header
*
* @return 0 Success
* @return negative integer on error
*/
int ieee802154_sec_encrypt_frame(ieee802154_sec_context_t *ctx,
const uint8_t *header, uint8_t *header_size,
uint8_t *payload, uint16_t payload_size,
uint8_t *mic, uint8_t *mic_size,
const uint8_t *src_address);
/**
* @brief Decrypt IEEE 802.15.4 frame according to @p ctx
*
* @param[in] ctx IEEE 802.15.4 security context
* @param[in] frame_size Size of received frame
* @param[in] header Poinzter to header, which is also the frame
* @param[in, out] header_size in: Header size; out: Size of header and auxiliary header
* @param[out] payload Will point to the beginning of the payload
* @param[out] payload_size Pointer to store the payload size
* @param[out] mic Will point to the beginning of the MIC
* @param[out] mic_size Pointer to store the size of the MIC
* @param[in] src_address Pointer to remote long source address
*
* @pre After @p header follows the auxiliary header
*
* @return 0 Success
* @return negative integer on error
*/
int ieee802154_sec_decrypt_frame(ieee802154_sec_context_t *ctx,
uint16_t frame_size,
uint8_t *header, uint8_t *header_size,
uint8_t **payload, uint16_t *payload_size,
uint8_t **mic, uint8_t *mic_size,
const uint8_t *src_address);
/**
* @brief Set the encryption key to be used for the next cipher operation
*
* This function should be the default callback operation to set the encryption key,
* if a radio does not provide special hardware security features.
*
* @param[in] dev Security device
* @param[in] key Key to be use for the next cipher operation
* @param[in] key_size Key size
*/
void ieee802154_sec_set_key(ieee802154_sec_dev_t *dev,
const uint8_t *key, uint8_t key_size);
/**
* @brief Perform ECB block cipher for IEEE802154 security layer
*
* This function should be the default callback operation to perform ECB,
* if a radio does not provide special hardware security features.
*
* @param[in] dev Security device
* @param[out] cipher Output cipher blocks
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
void ieee802154_sec_ecb(const ieee802154_sec_dev_t *dev,
uint8_t *cipher,
const uint8_t *plain,
uint8_t nblocks);
/**
* @brief Perform CBC block cipher for IEEE802154 security layer
* MIC computation
*
* This function should be the default callback operation to perform CBC,
* if a radio does not provide special hardware security features.
*
* @param[in] dev Security device
* @param[out] cipher Output cipher blocks
* @param[in] iv Initial vector
* @param[in] plain Input plain blocks
* @param[in] nblocks Number of blocks
*/
void ieee802154_sec_cbc(const ieee802154_sec_dev_t *dev,
uint8_t *cipher,
uint8_t *iv,
const uint8_t *plain,
uint8_t nblocks);
/**
* @brief Implements @ref ieee802154_sec_set_key,
* @ref ieee802154_sec_ecb,
* @ref ieee802154_sec_cbc
*/
extern const ieee802154_radio_cipher_ops_t ieee802154_radio_cipher_ops;
#ifdef __cplusplus
}
#endif
#endif /* NET_IEEE802154_SECURITY_H */
/** @} */

View File

@ -137,7 +137,7 @@ static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
gnrc_pktsnip_t *ieee802154_hdr, *netif_hdr;
gnrc_netif_hdr_t *hdr;
size_t mhr_len = ieee802154_get_frame_hdr_len(pkt->data);
uint8_t *mhr = pkt->data;
/* nread was checked for <= 0 before so we can safely cast it to
* unsigned */
if ((mhr_len == 0) || ((size_t)nread < mhr_len)) {
@ -145,21 +145,12 @@ static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
gnrc_pktbuf_release(pkt);
return NULL;
}
nread -= mhr_len;
/* mark IEEE 802.15.4 header */
ieee802154_hdr = gnrc_pktbuf_mark(pkt, mhr_len, GNRC_NETTYPE_UNDEF);
if (ieee802154_hdr == NULL) {
DEBUG("_recv_ieee802154: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt);
return NULL;
}
netif_hdr = _make_netif_hdr(ieee802154_hdr->data);
netif_hdr = _make_netif_hdr(mhr);
if (netif_hdr == NULL) {
DEBUG("_recv_ieee802154: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt);
return NULL;
}
hdr = netif_hdr->data;
#ifdef MODULE_L2FILTER
@ -172,7 +163,7 @@ static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
}
#endif
#ifdef MODULE_GNRC_NETIF_DEDUP
if (_already_received(netif, hdr, ieee802154_hdr->data)) {
if (_already_received(netif, hdr, mhr)) {
gnrc_pktbuf_release(pkt);
gnrc_pktbuf_release(netif_hdr);
DEBUG("_recv_ieee802154: packet dropped by deduplication\n");
@ -181,9 +172,30 @@ static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
memcpy(netif->last_pkt.src, gnrc_netif_hdr_get_src_addr(hdr),
hdr->src_l2addr_len);
netif->last_pkt.src_len = hdr->src_l2addr_len;
netif->last_pkt.seq = ieee802154_get_seq(ieee802154_hdr->data);
netif->last_pkt.seq = ieee802154_get_seq(mhr);
#endif /* MODULE_GNRC_NETIF_DEDUP */
#if IS_USED(MODULE_IEEE802154_SECURITY)
{
uint8_t *payload = NULL;
uint16_t payload_size = 0;
uint8_t *mic = NULL;
uint8_t mic_size = 0;
if (mhr[0] & NETDEV_IEEE802154_SECURITY_EN) {
if (ieee802154_sec_decrypt_frame(&((netdev_ieee802154_t *)dev)->sec_ctx,
nread,
mhr, (uint8_t *)&mhr_len,
&payload, &payload_size,
&mic, &mic_size,
gnrc_netif_hdr_get_src_addr(hdr)) != 0) {
DEBUG("_recv_ieee802154: packet dropped by security check\n");
gnrc_pktbuf_release(pkt);
gnrc_pktbuf_release(netif_hdr);
return NULL;
}
}
nread -= mic_size;
}
#endif
hdr->lqi = rx_info.lqi;
hdr->rssi = rx_info.rssi;
gnrc_netif_hdr_set_netif(hdr, netif);
@ -200,11 +212,20 @@ static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
od_hex_dump(pkt->data, nread, OD_WIDTH_DEFAULT);
}
}
/* mark IEEE 802.15.4 header */
ieee802154_hdr = gnrc_pktbuf_mark(pkt, mhr_len, GNRC_NETTYPE_UNDEF);
if (ieee802154_hdr == NULL) {
DEBUG("_recv_ieee802154: no space left in packet buffer\n");
gnrc_pktbuf_release(pkt);
gnrc_pktbuf_release(netif_hdr);
return NULL;
}
nread -= ieee802154_hdr->size;
gnrc_pktbuf_remove_snip(pkt, ieee802154_hdr);
pkt = gnrc_pkt_append(pkt, netif_hdr);
}
DEBUG("_recv_ieee802154: reallocating.\n");
DEBUG("_recv_ieee802154: reallocating MAC payload for upper layer.\n");
gnrc_pktbuf_realloc_data(pkt, nread);
} else if (bytes_expected > 0) {
DEBUG("_recv_ieee802154: received frame is too short\n");
@ -222,7 +243,12 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
const uint8_t *src, *dst = NULL;
int res = 0;
size_t src_len, dst_len;
uint8_t mhr_len;
#if IS_USED(MODULE_IEEE802154_SECURITY)
uint8_t mhr[IEEE802154_MAX_HDR_LEN + IEEE802154_MAX_AUX_HDR_LEN];
#else
uint8_t mhr[IEEE802154_MAX_HDR_LEN];
#endif
uint8_t flags = (uint8_t)(state->flags & NETDEV_IEEE802154_SEND_MASK);
le_uint16_t dev_pan = byteorder_htols(state->pan);
@ -250,29 +276,95 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
dst = gnrc_netif_hdr_get_dst_addr(netif_hdr);
dst_len = netif_hdr->dst_l2addr_len;
}
src_len = netif_hdr->src_l2addr_len;
if (src_len > 0) {
src = gnrc_netif_hdr_get_src_addr(netif_hdr);
if (flags & NETDEV_IEEE802154_SECURITY_EN) {
/* need to include long source address because the recipient
will need it to decrypt the frame */
src_len = IEEE802154_LONG_ADDRESS_LEN;
src = state->long_addr;
}
else {
src_len = netif->l2addr_len;
src = netif->l2addr;
src_len = netif_hdr->src_l2addr_len;
if (src_len > 0) {
src = gnrc_netif_hdr_get_src_addr(netif_hdr);
}
else {
src_len = netif->l2addr_len;
src = netif->l2addr;
}
}
/* fill MAC header, seq should be set by device */
if ((res = ieee802154_set_frame_hdr(mhr, src, src_len,
dst, dst_len, dev_pan,
dev_pan, flags, state->seq++)) == 0) {
DEBUG("_send_ieee802154: Error preperaring frame\n");
gnrc_pktbuf_release(pkt);
return -EINVAL;
}
mhr_len = res;
/* prepare iolist for netdev / mac layer */
iolist_t iolist = {
iolist_t iolist_header = {
.iol_next = (iolist_t *)pkt->next,
.iol_base = mhr,
.iol_len = (size_t)res
.iol_len = mhr_len
};
#if IS_USED(MODULE_IEEE802154_SECURITY)
{
/* write protect `pkt` to set `pkt->next` */
gnrc_pktsnip_t *tmp = gnrc_pktbuf_start_write(pkt);
if (!tmp) {
DEBUG("_send_ieee802154: no write access to pkt");
gnrc_pktbuf_release(pkt);
return -ENOMEM;
}
pkt = tmp;
tmp = gnrc_pktbuf_start_write(pkt->next);
if (!tmp) {
DEBUG("_send_ieee802154: no write access to pkt->next");
gnrc_pktbuf_release(pkt);
return -ENOMEM;
}
pkt->next = tmp;
/* merge snippets to store the L2 payload uniformly in one buffer */
res = gnrc_pktbuf_merge(pkt->next);
if (res < 0) {
DEBUG("_send_ieee802154: failed to merge pktbuf\n");
gnrc_pktbuf_release(pkt);
return res;
}
iolist_header.iol_next = (iolist_t *)pkt->next;
uint8_t mic[IEEE802154_MAC_SIZE];
uint8_t mic_size = 0;
if (flags & NETDEV_IEEE802154_SECURITY_EN) {
res = ieee802154_sec_encrypt_frame(&state->sec_ctx,
mhr, &mhr_len,
pkt->next->data, pkt->next->size,
mic, &mic_size,
state->long_addr);
if (res != 0) {
DEBUG("_send_ieee802154: encryption failedf\n");
gnrc_pktbuf_release(pkt);
return res;
}
}
if (mic_size) {
gnrc_pktsnip_t *pktmic = gnrc_pktbuf_add(pkt->next->next,
mic, mic_size,
GNRC_NETTYPE_UNDEF);
if (!pktmic) {
DEBUG("_send_ieee802154: no space left in pktbuf to allocate MIC\n");
gnrc_pktbuf_release(pkt);
return -ENOMEM;
}
pkt->next->next = pktmic;
}
iolist_header.iol_len = mhr_len;
}
#endif
#ifdef MODULE_NETSTATS_L2
if (netif_hdr->flags &
(GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
@ -284,13 +376,13 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
#endif
#ifdef MODULE_GNRC_MAC
if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
res = csma_sender_csma_ca_send(dev, &iolist, &netif->mac.csma_conf);
res = csma_sender_csma_ca_send(dev, &iolist_header, &netif->mac.csma_conf);
}
else {
res = dev->driver->send(dev, &iolist);
res = dev->driver->send(dev, &iolist_header);
}
#else
res = dev->driver->send(dev, &iolist);
res = dev->driver->send(dev, &iolist_header);
#endif
/* release old data */

View File

@ -29,6 +29,7 @@
#include "net/gnrc/gomach/gomach.h"
#endif
#include "net/gnrc.h"
#include "include/init_devs.h"
#include "at86rf215.h"
#include "at86rf215_params.h"
@ -37,7 +38,7 @@
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define AT86RF215_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define AT86RF215_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#ifndef AT86RF215_MAC_PRIO
#define AT86RF215_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -27,6 +27,7 @@
#include "net/gnrc/gomach/gomach.h"
#endif
#include "net/gnrc.h"
#include "include/init_devs.h"
#include "at86rf2xx.h"
#include "at86rf2xx_params.h"
@ -35,7 +36,7 @@
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define AT86RF2XX_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define AT86RF2XX_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#ifndef AT86RF2XX_MAC_PRIO
#define AT86RF2XX_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -23,6 +23,7 @@
#include "board.h"
#include "net/gnrc/netif/ieee802154.h"
#include "net/gnrc.h"
#include "include/init_devs.h"
#include "cc2420.h"
#include "cc2420_params.h"
@ -31,7 +32,7 @@
* @brief MAC layer stack parameters
* @{
*/
#define CC2420_MAC_STACKSIZE (THREAD_STACKSIZE_MAIN)
#define CC2420_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#ifndef CC2420_MAC_PRIO
#define CC2420_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -19,6 +19,7 @@
#include "log.h"
#include "net/gnrc/netif/ieee802154.h"
#include "include/init_devs.h"
#include "cc2538_rf.h"
@ -26,7 +27,7 @@
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define CC2538_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define CC2538_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#ifndef CC2538_MAC_PRIO
#define CC2538_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -24,6 +24,7 @@
#include "board.h"
#include "net/gnrc/netif/ieee802154.h"
#include "net/gnrc.h"
#include "include/init_devs.h"
#include "kw2xrf.h"
#include "kw2xrf_params.h"
@ -32,7 +33,7 @@
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define KW2XRF_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define KW2XRF_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#ifndef KW2XRF_MAC_PRIO
#define KW2XRF_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -22,6 +22,7 @@
#include "board.h"
#include "net/gnrc.h"
#include "net/gnrc/netif/ieee802154.h"
#include "include/init_devs.h"
#ifdef MODULE_GNRC_LWMAC
#include "net/gnrc/lwmac/lwmac.h"
@ -37,7 +38,7 @@
* @{
*/
#ifndef KW41ZRF_NETIF_STACKSIZE
#define KW41ZRF_NETIF_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define KW41ZRF_NETIF_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#endif
#ifndef KW41ZRF_NETIF_PRIO
#define KW41ZRF_NETIF_PRIO (GNRC_NETIF_PRIO)

View File

@ -21,6 +21,7 @@
#include "board.h"
#include "net/gnrc/netif/ieee802154.h"
#include "net/gnrc.h"
#include "include/init_devs.h"
#include "mrf24j40.h"
#include "mrf24j40_params.h"
@ -29,7 +30,7 @@
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define MRF24J40_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define MRF24J40_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#ifndef MRF24J40_MAC_PRIO
#define MRF24J40_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -21,13 +21,14 @@
#include "board.h"
#include "nrf802154.h"
#include "net/gnrc/netif/ieee802154.h"
#include "include/init_devs.h"
/**
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#ifndef NRF802154_MAC_STACKSIZE
#define NRF802154_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#define NRF802154_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT)
#endif
#ifndef NRF802154_MAC_PRIO
#define NRF802154_MAC_PRIO (GNRC_NETIF_PRIO)

View File

@ -21,6 +21,7 @@
#include "socket_zep.h"
#include "socket_zep_params.h"
#include "net/gnrc/netif/ieee802154.h"
#include "include/init_devs.h"
#define ENABLE_DEBUG 0
#include "debug.h"
@ -28,7 +29,7 @@
/**
* @brief Define stack parameters for the MAC layer thread
*/
#define SOCKET_ZEP_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE)
#define SOCKET_ZEP_MAC_STACKSIZE (IEEE802154_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE)
#ifndef SOCKET_ZEP_MAC_PRIO
#define SOCKET_ZEP_MAC_PRIO (GNRC_NETIF_PRIO)
#endif

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_auto_init_gnrc_netif
* @{
*
* @file
* @brief common netif device initialization definitions
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*/
#ifndef INIT_DEVS_H
#define INIT_DEVS_H
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief extra stack size if ieee802154 security is enabled
*
* You may increase this value if you experience a stack overflow
* with IEEE 802.15.4 security enabled.
*/
#define IEEE802154_SECURITY_EXTRA_STACKSIZE (128)
#ifndef IEEE802154_STACKSIZE_DEFAULT
#ifdef MODULE_IEEE802154_SECURITY
#define IEEE802154_STACKSIZE_DEFAULT (THREAD_STACKSIZE_DEFAULT + \
IEEE802154_SECURITY_EXTRA_STACKSIZE)
#else
/**
* @brief stack size of an ieee802154 device
*/
#define IEEE802154_STACKSIZE_DEFAULT (THREAD_STACKSIZE_DEFAULT)
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif /* INIT_DEVS_H */
/** @} */

View File

@ -4,6 +4,10 @@ SRC = \
ieee802154.c \
#
ifneq (,$(filter ieee802154_security,$(USEMODULE)))
SRC += security.c
endif
ifneq (,$(filter ieee802154_submac,$(USEMODULE)))
SRC += submac.c
endif

View File

@ -0,0 +1,522 @@
/*
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
* @}
*/
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include "crypto/ciphers.h"
#include "crypto/modes/ecb.h"
#include "crypto/modes/cbc.h"
#include "net/ieee802154_security.h"
const ieee802154_radio_cipher_ops_t ieee802154_radio_cipher_ops = {
.set_key = ieee802154_sec_set_key,
.ecb = ieee802154_sec_ecb,
.cbc = ieee802154_sec_cbc
};
static inline uint16_t _min(uint16_t a, uint16_t b)
{
return a < b ? a : b;
}
/**
* @brief Flag field of CCM input block
*
* Bit 7 Bit6 Bit 5 - Bit 3 Bit2 - Bit 0
* +--------+-------+-----------------------+-----------------------+
* | 0 (r) | Adata | M | L |
* +--------+-------+-----------------------+-----------------------+
*
* r: value reserved
* Adata: 0 if no MIC is present, 1 else
* M: Number of octets in authentication field (M-2)/2
* L: Number of octets in length field L-1
*
* L will actually always be 2 because the maximul message length is 127
* which is expressed as two bytes.
* Valid values for M are 0 (No MIC), 4 (32 bit MIC), 8 (64 bit MIC)
* and 16 (128 bit MIC)
*/
static inline uint8_t _ccm_flag(uint8_t M, uint8_t L)
{
assert(M == 0 || M == 4 || M == 8 || M == 16);
assert(L == 2);
return (M >= 4 ? ((1 << 6) | (((M) - 2) / 2)) : 0) | ((L) - 1);
}
static inline uint8_t _get_sec_level(uint8_t scf)
{
return (scf & IEEE802154_SCF_SECLEVEL_MASK)
>> IEEE802154_SCF_SECLEVEL_SHIFT;
}
static inline uint8_t _get_key_id_mode(uint8_t scf)
{
return (scf & IEEE802154_SCF_KEYMODE_MASK)
>> IEEE802154_SCF_KEYMODE_SHIFT;
}
static inline uint8_t _mac_size(uint8_t sec_level)
{
switch (sec_level) {
case IEEE802154_SCF_SECLEVEL_MIC32:
case IEEE802154_SCF_SECLEVEL_ENC_MIC32:
return 4;
case IEEE802154_SCF_SECLEVEL_MIC64:
case IEEE802154_SCF_SECLEVEL_ENC_MIC64:
return 8;
case IEEE802154_SCF_SECLEVEL_MIC128:
case IEEE802154_SCF_SECLEVEL_ENC_MIC128:
return 16;
default:
return 0;
}
}
/* frame is secured with signature */
static inline bool _req_mac(uint8_t sec_level)
{
switch (sec_level) {
case IEEE802154_SCF_SECLEVEL_MIC32:
case IEEE802154_SCF_SECLEVEL_MIC64:
case IEEE802154_SCF_SECLEVEL_MIC128:
case IEEE802154_SCF_SECLEVEL_ENC_MIC32:
case IEEE802154_SCF_SECLEVEL_ENC_MIC64:
case IEEE802154_SCF_SECLEVEL_ENC_MIC128:
return true;
default:
return false;
}
}
/* frame is encrypted */
static inline bool _req_encryption(uint8_t sec_level)
{
switch (sec_level) {
case IEEE802154_SCF_SECLEVEL_ENC:
case IEEE802154_SCF_SECLEVEL_ENC_MIC32:
case IEEE802154_SCF_SECLEVEL_ENC_MIC64:
case IEEE802154_SCF_SECLEVEL_ENC_MIC128:
return true;
default:
return false;
}
}
static inline void _memxor(void *dst, const void* src, size_t size)
{
while (size--) {
((uint8_t *)dst)[size] ^= ((uint8_t *)src)[size];
}
}
static inline uint8_t _scf(uint8_t sec_level, uint8_t key_mode)
{
return (sec_level << IEEE802154_SCF_SECLEVEL_SHIFT) |
(key_mode << IEEE802154_SCF_KEYMODE_SHIFT);
}
static inline uint8_t _get_aux_hdr_size(uint8_t security_level,
uint8_t key_mode)
{
if (security_level == IEEE802154_SCF_SECLEVEL_NONE) {
return 0;
}
switch (key_mode) {
case IEEE802154_SCF_KEYMODE_IMPLICIT:
return 5;
case IEEE802154_SCF_KEYMODE_INDEX:
return 6;
case IEEE802154_SCF_KEYMODE_SHORT_INDEX:
return 10;
case IEEE802154_SCF_KEYMODE_HW_INDEX:
return 14;
default:
return 0;
}
}
static uint8_t _set_aux_hdr(const ieee802154_sec_context_t *ctx,
ieee802154_aux_sec_t *ahr)
{
ahr->scf = _scf(ctx->security_level, ctx->key_id_mode);
/* If you look in the specification: Annex C,
integers values are in little endian */
ahr->fc = byteorder_btoll(byteorder_htonl(ctx->frame_counter)).u32;
size_t len = 5;
switch (ctx->key_id_mode) {
case IEEE802154_SCF_KEYMODE_IMPLICIT:
break;
case IEEE802154_SCF_KEYMODE_INDEX:
memcpy(ahr->key_id, &ctx->key_index, 1);
len++;
break;
case IEEE802154_SCF_KEYMODE_SHORT_INDEX:
memcpy(ahr->key_id, ctx->key_source, 4);
memcpy(ahr->key_id + 4, &ctx->key_index, 1);
len += 5;
break;
case IEEE802154_SCF_KEYMODE_HW_INDEX:
memcpy(ahr->key_id, ctx->key_source, 8);
memcpy(ahr->key_id + 4, &ctx->key_index, 1);
len += 9;
break;
default:
break;
}
return len;
}
/**
* @brief Construct the first block A0 for CTR
*/
static inline void _init_ctr_A0(ieee802154_ccm_block_t *A0,
uint32_t frame_counter,
uint8_t security_level,
const uint8_t *src_address)
{
A0->flags = _ccm_flag(0, 2);
A0->nonce.frame_counter = htonl(frame_counter);
A0->nonce.security_level = security_level;
A0->counter = 0;
memcpy(A0->nonce.src_addr, src_address, IEEE802154_LONG_ADDRESS_LEN);
}
/**
* @brief In CTR, the blocks Ai differ in a successive counter
*/
static inline void _advance_ctr_Ai(ieee802154_ccm_block_t *Ai)
{
Ai->counter = htons(ntohs(Ai->counter) + 1);
}
/**
* @brief Construct the first block B0 for CBC-MAC
*/
static inline void _init_cbc_B0(ieee802154_ccm_block_t *B0,
uint32_t frame_counter,
uint8_t security_level,
uint16_t m_len,
uint8_t mic_size,
const uint8_t *src_address)
{
B0->flags = _ccm_flag(mic_size, 2);
B0->nonce.frame_counter = htonl(frame_counter),
B0->nonce.security_level = security_level,
B0->counter = htons(m_len);
memcpy(B0->nonce.src_addr, src_address, IEEE802154_LONG_ADDRESS_LEN);
}
static const uint8_t *_get_encryption_key(const ieee802154_sec_context_t *ctx,
const uint8_t *mhr, uint8_t mhr_len,
const ieee802154_aux_sec_t *ahr)
{
(void)mhr;
(void)mhr_len;
(void)ahr;
/* For simplicity, assume that everyone has the same key */
/* Else you´d have to look up the key based on the destination address */
return ctx->cipher.context.context;
}
static const uint8_t *_get_decryption_key(const ieee802154_sec_context_t *ctx,
const uint8_t *mhr, uint8_t mhr_len,
const ieee802154_aux_sec_t *ahr)
{
(void)mhr;
(void)mhr_len;
(void)ahr;
/* For simplicity, assume that everyone has the same key */
/* Else you´d have to look up the key based on the source address */
return ctx->cipher.context.context;
}
/**
* @brief Perform ECB on one block of data and and add padding if necessary
*/
static uint8_t _ecb(ieee802154_sec_context_t *ctx,
uint8_t *tmp1, uint8_t *tmp2, uint8_t *data,
const uint8_t *Ai, uint16_t size)
{
uint16_t s = _min(IEEE802154_SEC_BLOCK_SIZE, size);
ctx->dev.cipher_ops->ecb(&ctx->dev, tmp2, Ai, 1);
memcpy(tmp1, data, s);
memset(tmp1 + s, 0, IEEE802154_SEC_BLOCK_SIZE - s);
_memxor(tmp1, tmp2, IEEE802154_SEC_BLOCK_SIZE);
memcpy(data, tmp1, s);
return s;
}
/**
* @brief Perform CBC on one block of data and add padding if necessary
*/
static uint8_t _cbc_next(ieee802154_sec_context_t *ctx,
uint8_t *last, uint8_t *tmp,
const uint8_t *next, uint16_t size)
{
uint16_t s = _min(IEEE802154_SEC_BLOCK_SIZE, size);
memcpy(tmp, next, s);
memset(tmp + s, 0, IEEE802154_SEC_BLOCK_SIZE - s);
ctx->dev.cipher_ops->cbc(&ctx->dev, last, last, tmp, 1);
return s;
}
static void _set_key(ieee802154_sec_context_t *ctx, const uint8_t *key)
{
ctx->dev.cipher_ops->set_key(&ctx->dev, key, IEEE802154_SEC_BLOCK_SIZE);
memcpy(ctx->cipher.context.context, key, IEEE802154_SEC_KEY_LENGTH);
}
static void _comp_mic(ieee802154_sec_context_t *ctx,
uint8_t mic[IEEE802154_MAC_SIZE],
ieee802154_ccm_block_t *B0,
const void *a, uint16_t a_len,
const void *m, uint16_t m_len)
{
uint8_t tmp[IEEE802154_SEC_BLOCK_SIZE] = { 0 };
uint16_t off;
memset(mic, 0, IEEE802154_MAC_SIZE);
_cbc_next(ctx, mic, tmp, (uint8_t *)B0, sizeof(*B0));
byteorder_htobebufs(tmp, a_len);
off = _min(sizeof(tmp) - sizeof(uint16_t), a_len);
memcpy(tmp + sizeof(uint16_t), a, off);
_cbc_next(ctx, mic, tmp, tmp, sizeof(uint16_t) + off);
for (;off < a_len;) {
off += _cbc_next(ctx, mic, tmp, &(((uint8_t *)a)[off]), a_len - off);
}
for (off = 0; off < m_len;) {
off += _cbc_next(ctx, mic, tmp, &(((uint8_t *)m)[off]), m_len - off);
}
}
static void _ctr(ieee802154_sec_context_t *ctx,
ieee802154_ccm_block_t *A0,
const void *m, uint16_t m_len)
{
uint8_t tmp1[IEEE802154_SEC_BLOCK_SIZE] = { 0 };
uint8_t tmp2[IEEE802154_SEC_BLOCK_SIZE] = { 0 };
for (uint16_t off = 0; off < m_len;) {
_advance_ctr_Ai(A0);
off += _ecb(ctx, tmp1, tmp2,
&(((uint8_t *)m)[off]), (uint8_t *)A0, m_len - off);
}
}
static void _ctr_mic(ieee802154_sec_context_t *ctx,
ieee802154_ccm_block_t *A0,
void *mic, uint8_t mic_size)
{
uint8_t tmp1[IEEE802154_SEC_BLOCK_SIZE] = { 0 };
uint8_t tmp2[IEEE802154_SEC_BLOCK_SIZE] = { 0 };
_ecb(ctx, tmp1, tmp2, mic, (uint8_t *)A0, mic_size);
}
void ieee802154_sec_init(ieee802154_sec_context_t *ctx)
{
/* device driver can override this */
ctx->dev.cipher_ops = &ieee802154_radio_cipher_ops;
/* device driver can override this */
ctx->dev.ctx = ctx;
/* MIC64 is the only mandatory security mode */
ctx->security_level = IEEE802154_SCF_SECLEVEL_ENC_MIC64;
ctx->key_id_mode = IEEE802154_SCF_KEYMODE_IMPLICIT;
memset(ctx->key_source, 0, sizeof(ctx->key_source));
ctx->key_index = 0;
ctx->frame_counter = 0;
uint8_t key[] = IEEE802154_DEFAULT_KEY;
assert(CIPHER_MAX_CONTEXT_SIZE >= IEEE802154_SEC_KEY_LENGTH);
cipher_init(&ctx->cipher, CIPHER_AES_128, key, IEEE802154_SEC_KEY_LENGTH);
}
int ieee802154_sec_encrypt_frame(ieee802154_sec_context_t *ctx,
const uint8_t *header, uint8_t *header_size,
uint8_t *payload, uint16_t payload_size,
uint8_t *mic, uint8_t *mic_size,
const uint8_t *src_address)
{
/* For non data frames (MAC commands, beacons) a and a_len would be larger.
ACKs are not encrypted. */
assert((*((uint8_t *)header)) & IEEE802154_FCF_TYPE_DATA);
if (ctx->security_level == IEEE802154_SCF_SECLEVEL_NONE) {
*mic_size = 0;
return IEEE802154_SEC_OK;
}
if (ctx->frame_counter == 0xFFFFFFFF) {
/* Letting the frame counter overflow is explicitly prohibited by the specification.
(see 9.4.2) */
return -IEEE802154_SEC_FRAME_COUNTER_OVERFLOW;
}
/* write the auxiliary header */
ieee802154_aux_sec_t *aux = (ieee802154_aux_sec_t *)(header + *header_size);
uint8_t aux_size = _get_aux_hdr_size(ctx->security_level, ctx->key_id_mode);
_set_aux_hdr(ctx, aux);
/* attempt to find the encrypton key */
const uint8_t *key;
if (!(key = _get_encryption_key(ctx, header, *header_size, aux))) {
return -IEEE802154_SEC_NO_KEY;
}
_set_key(ctx, key);
*mic_size = _mac_size(ctx->security_level);
const uint8_t *a = header;
uint8_t *m = payload;
uint16_t a_len = *header_size + aux_size;
uint16_t m_len = payload_size;
ieee802154_ccm_block_t ccm; /* Ai or Bi */
/* compute MIC */
if (_req_mac(ctx->security_level)) {
_init_cbc_B0(&ccm, ctx->frame_counter, ctx->security_level, m_len, *mic_size, src_address);
_comp_mic(ctx, mic, &ccm, a, a_len, m, m_len);
/* encrypt MIC */
_init_ctr_A0(&ccm, ctx->frame_counter, ctx->security_level, src_address);
_ctr_mic(ctx, &ccm, mic, *mic_size);
}
/* encrypt payload */
if (_req_encryption(ctx->security_level)) {
_init_ctr_A0(&ccm, ctx->frame_counter, ctx->security_level, src_address);
_ctr(ctx, &ccm, m, m_len);
}
*header_size += aux_size;
ctx->frame_counter++;
return IEEE802154_SEC_OK;
}
int ieee802154_sec_decrypt_frame(ieee802154_sec_context_t *ctx,
uint16_t frame_size,
uint8_t *header, uint8_t *header_size,
uint8_t **payload, uint16_t *payload_size,
uint8_t **mic, uint8_t *mic_size,
const uint8_t *src_address)
{
/* For non data frames (MAC commands, beacons) a and a_len would be larger.
ACKs are not encrypted. */
assert(*header & IEEE802154_FCF_TYPE_DATA);
/* read the fields of the auxiliary header */
ieee802154_aux_sec_t *aux = (ieee802154_aux_sec_t *)(header + *header_size);
uint8_t security_level = _get_sec_level(aux->scf);
uint8_t key_mode = _get_key_id_mode(aux->scf);
uint8_t aux_size = _get_aux_hdr_size(security_level, key_mode);
uint8_t mac_size = _mac_size(security_level);
/* remember that the frame counter was stored in little endian */
uint32_t frame_counter = byteorder_ntohl(
byteorder_ltobl((le_uint32_t){aux->fc}));
if (security_level == IEEE802154_SCF_SECLEVEL_NONE) {
*payload = header + *header_size;
*payload_size = frame_size - *header_size;
*mic = NULL;
*mic_size = 0;
return IEEE802154_SEC_OK;
}
*payload_size = frame_size - *header_size - aux_size - mac_size;
*payload = header + *header_size + aux_size;
*mic_size = mac_size;
*mic = header + frame_size - mac_size;
/* attempt to find the decryption key */
const uint8_t *key;
if (!(key = _get_decryption_key(ctx, header, *header_size, aux))) {
return -IEEE802154_SEC_NO_KEY;
}
_set_key(ctx, key);
const uint8_t *a = header;
uint8_t *c = *payload;
uint16_t a_len = *header_size + aux_size;
uint16_t c_len = *payload_size;
uint8_t *mac = *mic;
ieee802154_ccm_block_t ccm; /* Ai or Bi */
/* TODO:
A better implementation would check if the received frame counter is
greater then the frame counter that has previously been received from
the other endpoint. This is done to protect against replay attacks.
But we do not store this information because we also do not have
a proper key store, to avoid complexity on embedded devices. */
/* decrypt MIC */
if (mac_size) {
_init_ctr_A0(&ccm, frame_counter, security_level, src_address);
_ctr_mic(ctx, &ccm, mac, mac_size);
}
/* decrypt cipher */
if (_req_encryption(security_level)) {
_init_ctr_A0(&ccm, frame_counter, security_level, src_address);
_ctr(ctx, &ccm, c, c_len);
}
/* check MIC */
if (_req_mac(security_level)) {
uint8_t tmp_mic[IEEE802154_MAC_SIZE];
_init_cbc_B0(&ccm, frame_counter, security_level, c_len, mac_size, src_address);
_comp_mic(ctx, tmp_mic, &ccm, a, a_len, c, c_len);
if (memcmp(tmp_mic, *mic, mac_size)) {
return -IEEE802154_SEC_MAC_CHECK_FAILURE;
}
}
*header_size += aux_size;
return IEEE802154_SEC_OK;
}
void ieee802154_sec_set_key(ieee802154_sec_dev_t *ctx,
const uint8_t *key, uint8_t key_size)
{
/* This is a dummy implementation of the set_key callback
in ieee802154_radio_cipher_ops_t.
The copying of the key is done in the static _set_key() function,
which wraps around the set_key callback and then copies.
For the software encryption / decryption, there is
nothing else to do, hence the NOP. For hardware support,
the key must be transferred to the transceiver. */
(void)ctx;
(void)key;
(void)key_size;
}
void ieee802154_sec_ecb(const ieee802154_sec_dev_t *ctx,
uint8_t *cipher,
const uint8_t *plain,
uint8_t nblocks)
{
cipher_encrypt_ecb(&((ieee802154_sec_context_t *)ctx->ctx)->cipher,
plain,
nblocks * IEEE802154_SEC_BLOCK_SIZE,
cipher);
}
void ieee802154_sec_cbc(const ieee802154_sec_dev_t *ctx,
uint8_t *cipher,
uint8_t *iv,
const uint8_t *plain,
uint8_t nblocks)
{
cipher_encrypt_cbc(&((ieee802154_sec_context_t *)ctx->ctx)->cipher,
iv,
plain,
nblocks * IEEE802154_SEC_BLOCK_SIZE,
cipher);
}

View File

@ -0,0 +1,5 @@
USEMODULE += ieee802154
USEMODULE += ieee802154_security
USEMODULE += gnrc_netif_ieee802154
include ../driver_netdev_common/Makefile.netdev.mk

View File

@ -0,0 +1,21 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-nano \
arduino-uno \
atmega328p \
i-nucleo-lrwan1 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-l552ze-q \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32l0538-disco \
waspmote-pro \
#

View File

@ -0,0 +1,12 @@
Tests for module `ieee802154_security`
======================================
This is just a dummy test application to integrate the
module in the CI process, to check if it compiles.
TODO:
Write a proper test application which runs on `native`
and uses `socket_zep`.
[#15150]: https://github.com/RIOT-OS/RIOT/pull/15150

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for ieee802154_security module
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
* @}
*/
#include "thread.h"
#include "shell.h"
#include "shell_commands.h"
#include "net/gnrc/pktdump.h"
#include "net/gnrc.h"
int main(void)
{
/* enable pktdump output */
gnrc_netreg_entry_t dump = GNRC_NETREG_ENTRY_INIT_PID(
GNRC_NETREG_DEMUX_CTX_ALL, gnrc_pktdump_pid);
gnrc_netreg_register(GNRC_NETTYPE_UNDEF, &dump);
/* start the shell */
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}