/* * 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 LOG_MODULE_REGISTER(net_ieee802154_security, CONFIG_NET_L2_IEEE802154_LOG_LEVEL); #include "ieee802154_frame.h" #include "ieee802154_security.h" #include #include 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 ll_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 + ll_hdr_len : NULL; pkt->in_len = is_encrypted ? payload_len : 0; /* See section 9.3.5.4 */ uint8_t out_buf_offset = is_encrypted ? ll_hdr_len : ll_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 + ll_hdr_len + payload_len : NULL; apkt->pkt = pkt; } bool ieee802154_decrypt_auth(struct ieee802154_security_ctx *sec_ctx, uint8_t *frame, uint8_t ll_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, ll_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, ll_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 ll_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, ll_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, ll_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; }