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

sys: Add IEEE 802.15.4 security

This commit is contained in:
Fabian Hüßler 2020-10-02 14:47:38 +02:00
parent 0427e28f17
commit dc16c14b3d
6 changed files with 1139 additions and 25 deletions

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

@ -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_btols(byteorder_htons(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

@ -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);
}