blob: 15add0a1591d629037a2d36a2ec4b2c9dfe59e88 [file] [log] [blame]
/*
* Copyright (c) 2017 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief 802.15.4 6LoWPAN authentication and encryption implementation
*
* All references to the spec refer to IEEE 802.15.4-2020.
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_ieee802154_security, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
#include "ieee802154_frame.h"
#include "ieee802154_security.h"
#include <zephyr/crypto/crypto.h>
#include <zephyr/net/net_core.h>
extern const uint8_t level_2_authtag_len[4];
int ieee802154_security_setup_session(struct ieee802154_security_ctx *sec_ctx, uint8_t level,
uint8_t key_mode, uint8_t *key, uint8_t key_len)
{
uint8_t authtag_len;
int ret;
if (level > IEEE802154_SECURITY_LEVEL_ENC_MIC_128 ||
key_mode > IEEE802154_KEY_ID_MODE_SRC_8_INDEX) {
return -EINVAL;
}
/* TODO: supporting other key modes */
if (level > IEEE802154_SECURITY_LEVEL_NONE &&
(key_len > IEEE802154_KEY_MAX_LEN || !key ||
key_mode != IEEE802154_KEY_ID_MODE_IMPLICIT)) {
return -EINVAL;
}
sec_ctx->level = level;
if (level == IEEE802154_SECURITY_LEVEL_NONE) {
return 0;
}
if (level > IEEE802154_SECURITY_LEVEL_ENC) {
authtag_len = level_2_authtag_len[level - 4];
} else if (level < IEEE802154_SECURITY_LEVEL_ENC) {
authtag_len = level_2_authtag_len[level];
} else {
/* Encryption-only security is no longer supported since IEEE 802.15.4-2020. */
return -EINVAL;
}
sec_ctx->enc.mode_params.ccm_info.tag_len = authtag_len;
sec_ctx->dec.mode_params.ccm_info.tag_len = authtag_len;
memcpy(sec_ctx->key, key, key_len);
sec_ctx->key_len = key_len;
sec_ctx->key_mode = key_mode;
sec_ctx->enc.key.bit_stream = sec_ctx->key;
sec_ctx->enc.keylen = sec_ctx->key_len;
sec_ctx->dec.key.bit_stream = sec_ctx->key;
sec_ctx->dec.keylen = sec_ctx->key_len;
ret = cipher_begin_session(sec_ctx->enc.device, &sec_ctx->enc, CRYPTO_CIPHER_ALGO_AES,
CRYPTO_CIPHER_MODE_CCM, CRYPTO_CIPHER_OP_ENCRYPT);
if (ret) {
NET_ERR("Could not setup encryption context");
return ret;
}
ret = cipher_begin_session(sec_ctx->dec.device, &sec_ctx->dec, CRYPTO_CIPHER_ALGO_AES,
CRYPTO_CIPHER_MODE_CCM, CRYPTO_CIPHER_OP_DECRYPT);
if (ret) {
NET_ERR("Could not setup decryption context");
cipher_free_session(sec_ctx->enc.device, &sec_ctx->enc);
return ret;
}
return 0;
}
void ieee802154_security_teardown_session(struct ieee802154_security_ctx *sec_ctx)
{
if (sec_ctx->level == IEEE802154_SECURITY_LEVEL_NONE) {
return;
}
cipher_free_session(sec_ctx->enc.device, &sec_ctx->enc);
cipher_free_session(sec_ctx->dec.device, &sec_ctx->dec);
sec_ctx->level = IEEE802154_SECURITY_LEVEL_NONE;
}
static void prepare_cipher_aead_pkt(uint8_t *frame, uint8_t level, uint8_t hdr_len,
uint8_t payload_len, uint8_t authtag_len,
struct cipher_aead_pkt *apkt, struct cipher_pkt *pkt)
{
bool is_authenticated;
bool is_encrypted;
__ASSERT_NO_MSG(level != IEEE802154_SECURITY_LEVEL_ENC);
is_encrypted = level > IEEE802154_SECURITY_LEVEL_ENC;
is_authenticated = level != IEEE802154_SECURITY_LEVEL_NONE;
/* See section 9.3.5.3 */
pkt->in_buf = is_encrypted && payload_len ? frame + hdr_len : NULL;
pkt->in_len = is_encrypted ? payload_len : 0;
/* See section 9.3.5.4 */
uint8_t out_buf_offset = is_encrypted ? hdr_len : hdr_len + payload_len;
uint8_t auth_len = is_authenticated ? out_buf_offset : 0;
pkt->out_buf = frame + out_buf_offset;
pkt->out_buf_max = (is_encrypted ? payload_len : 0) + authtag_len;
apkt->ad = is_authenticated ? frame : NULL;
apkt->ad_len = auth_len;
apkt->tag = is_authenticated ? frame + hdr_len + payload_len : NULL;
apkt->pkt = pkt;
}
bool ieee802154_decrypt_auth(struct ieee802154_security_ctx *sec_ctx, uint8_t *frame,
uint8_t hdr_len, uint8_t payload_len, uint8_t authtag_len,
uint8_t *src_ext_addr, uint32_t frame_counter)
{
struct cipher_aead_pkt apkt;
struct cipher_pkt pkt;
uint8_t nonce[13];
uint8_t level;
int ret;
if (!sec_ctx || sec_ctx->level == IEEE802154_SECURITY_LEVEL_NONE) {
return true;
}
level = sec_ctx->level;
/* See section 9.3.3.1 */
memcpy(nonce, src_ext_addr, IEEE802154_EXT_ADDR_LENGTH);
sys_put_be32(frame_counter, &nonce[8]);
nonce[12] = level;
prepare_cipher_aead_pkt(frame, level, hdr_len, payload_len, authtag_len, &apkt, &pkt);
ret = cipher_ccm_op(&sec_ctx->dec, &apkt, nonce);
if (ret) {
NET_ERR("Cannot decrypt/auth (%i): %p %u/%u - fc %u", ret, frame, hdr_len,
payload_len, frame_counter);
return false;
}
return true;
}
bool ieee802154_encrypt_auth(struct ieee802154_security_ctx *sec_ctx, uint8_t *frame,
uint8_t hdr_len, uint8_t payload_len, uint8_t authtag_len,
uint8_t *src_ext_addr)
{
struct cipher_aead_pkt apkt;
struct cipher_pkt pkt;
uint8_t nonce[13];
uint8_t level;
int ret;
if (!sec_ctx || sec_ctx->level == IEEE802154_SECURITY_LEVEL_NONE) {
return true;
}
level = sec_ctx->level;
if (level == IEEE802154_SECURITY_LEVEL_RESERVED) {
NET_DBG("Encryption-only security is deprecated since IEEE 802.15.4-2015.");
return false;
}
if (sec_ctx->frame_counter == 0xffffffff) {
NET_ERR("Max frame counter reached. Update key material to reset the counter.");
return false;
}
/* See section 9.3.3.1 */
memcpy(nonce, src_ext_addr, IEEE802154_EXT_ADDR_LENGTH);
sys_put_be32(sec_ctx->frame_counter, &nonce[8]);
nonce[12] = level;
prepare_cipher_aead_pkt(frame, level, hdr_len, payload_len, authtag_len, &apkt, &pkt);
ret = cipher_ccm_op(&sec_ctx->enc, &apkt, nonce);
if (ret) {
NET_ERR("Cannot encrypt/auth (%i): %p %u/%u - fc %u", ret, frame, hdr_len,
payload_len, sec_ctx->frame_counter);
return false;
}
sec_ctx->frame_counter++;
return true;
}
int ieee802154_security_init(struct ieee802154_security_ctx *sec_ctx)
{
const struct device *dev;
(void)memset(&sec_ctx->enc, 0, sizeof(struct cipher_ctx));
(void)memset(&sec_ctx->dec, 0, sizeof(struct cipher_ctx));
dev = device_get_binding(CONFIG_NET_L2_IEEE802154_SECURITY_CRYPTO_DEV_NAME);
if (!dev) {
return -ENODEV;
}
sec_ctx->enc.flags = crypto_query_hwcaps(dev);
sec_ctx->dec.flags = crypto_query_hwcaps(dev);
sec_ctx->enc.mode_params.ccm_info.nonce_len = 13U;
sec_ctx->dec.mode_params.ccm_info.nonce_len = 13U;
sec_ctx->enc.device = dev;
sec_ctx->dec.device = dev;
return 0;
}