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:
parent
0427e28f17
commit
dc16c14b3d
@ -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
|
||||
|
@ -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
|
||||
|
489
sys/include/net/ieee802154_security.h
Normal file
489
sys/include/net/ieee802154_security.h
Normal 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 */
|
||||
/** @} */
|
@ -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 */
|
||||
|
@ -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
|
||||
|
522
sys/net/link_layer/ieee802154/security.c
Normal file
522
sys/net/link_layer/ieee802154/security.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user