406 lines
13 KiB
C
406 lines
13 KiB
C
/*
|
|
* Copyright (c) 2021 - 2023, Nordic Semiconductor ASA
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include "nrf_802154_encrypt.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "nrf_802154_aes_ccm.h"
|
|
#include "nrf_802154_const.h"
|
|
#include "nrf_802154_pib.h"
|
|
#include "nrf_802154_types_internal.h"
|
|
#include "mac_features/nrf_802154_frame_parser.h"
|
|
#include "mac_features/nrf_802154_security_pib.h"
|
|
|
|
/**
|
|
* @brief Copies memory in reversed byte order.
|
|
*
|
|
* @note Source and destination buffers must not be NULL.
|
|
*
|
|
* @param[out] p_dst Pointer to the destination buffer.
|
|
* @param[in] p_src Pointer to the source buffer.
|
|
* @param[in] n The number of bytes to copy.
|
|
*/
|
|
static inline void memcpy_rev(void * p_dst, const void * p_src, size_t n)
|
|
{
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
*((uint8_t *)p_dst + i) = *((uint8_t *)p_src + n - 1 - i);
|
|
}
|
|
}
|
|
|
|
static bool frame_version_is_2015_or_above(const nrf_802154_frame_parser_data_t * p_frame_data)
|
|
{
|
|
switch (nrf_802154_frame_parser_frame_version_get(p_frame_data))
|
|
{
|
|
case FRAME_VERSION_0:
|
|
case FRAME_VERSION_1:
|
|
return false;
|
|
|
|
case FRAME_VERSION_2:
|
|
return true;
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static uint8_t mic_level_from_security_level_get(uint8_t security_level)
|
|
{
|
|
// According to the specification, two least significant bits of the security level
|
|
// indicate provided level of data authenticity
|
|
return security_level & SECURITY_LEVEL_MIC_LEVEL_MASK;
|
|
}
|
|
|
|
static bool a_data_and_m_data_prepare(
|
|
const nrf_802154_frame_parser_data_t * p_frame_data,
|
|
nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
|
|
{
|
|
bool result = true;
|
|
|
|
uint8_t open_payload_len = 0;
|
|
|
|
if ((nrf_802154_frame_parser_frame_type_get(p_frame_data) == FRAME_TYPE_COMMAND) &&
|
|
(!frame_version_is_2015_or_above(p_frame_data)))
|
|
{
|
|
open_payload_len = MAC_CMD_COMMAND_ID_SIZE;
|
|
}
|
|
|
|
// It is assumed that the provided frame has a placeholder of appropriate length for MIC
|
|
// at the end. The algorithm inputs should only contain MAC payload, so the MIC placeholder
|
|
// of the below length should be removed
|
|
uint8_t mic_len = nrf_802154_frame_parser_mic_size_get(p_frame_data);
|
|
uint8_t * p_mac_hdr = (uint8_t *)nrf_802154_frame_parser_psdu_get(p_frame_data);
|
|
uint8_t mac_hdr_len =
|
|
nrf_802154_frame_parser_mac_header_length_get(p_frame_data) + open_payload_len;
|
|
uint8_t * p_mac_payload =
|
|
(uint8_t *)(nrf_802154_frame_parser_mac_payload_get(p_frame_data) + open_payload_len);
|
|
uint8_t mac_payload_len =
|
|
nrf_802154_frame_parser_mac_payload_length_get(p_frame_data) - mic_len - open_payload_len;
|
|
|
|
switch (nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_frame_data))
|
|
{
|
|
case SECURITY_LEVEL_NONE:
|
|
// No data authenticity nor data confidentiality
|
|
p_aes_ccm_data->auth_data = NULL;
|
|
p_aes_ccm_data->auth_data_len = 0;
|
|
p_aes_ccm_data->plain_text_data = NULL;
|
|
p_aes_ccm_data->plain_text_data_len = 0;
|
|
break;
|
|
|
|
case SECURITY_LEVEL_MIC_32:
|
|
case SECURITY_LEVEL_MIC_64:
|
|
case SECURITY_LEVEL_MIC_128:
|
|
// Data authenticity without data confidentiality
|
|
p_aes_ccm_data->auth_data = p_mac_hdr;
|
|
p_aes_ccm_data->auth_data_len = mac_hdr_len + mac_payload_len;
|
|
p_aes_ccm_data->plain_text_data = NULL;
|
|
p_aes_ccm_data->plain_text_data_len = 0;
|
|
break;
|
|
|
|
case SECURITY_LEVEL_ENC_MIC_32:
|
|
case SECURITY_LEVEL_ENC_MIC_64:
|
|
case SECURITY_LEVEL_ENC_MIC_128:
|
|
// Data authenticity and data confidentiality
|
|
p_aes_ccm_data->auth_data = p_mac_hdr;
|
|
p_aes_ccm_data->auth_data_len = mac_hdr_len;
|
|
p_aes_ccm_data->plain_text_data = p_mac_payload;
|
|
p_aes_ccm_data->plain_text_data_len = mac_payload_len;
|
|
break;
|
|
|
|
default:
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves key to be used for the AES CCM transformation.
|
|
*
|
|
* @param[in] p_frame_data Pointer to the frame parser data.
|
|
* @param[out] p_aes_ccm_data Pointer to AES CCM transformation data to be filled.
|
|
*/
|
|
bool aes_ccm_data_key_prepare(const nrf_802154_frame_parser_data_t * p_frame_data,
|
|
nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
|
|
{
|
|
nrf_802154_key_id_t key_id =
|
|
{
|
|
.mode = nrf_802154_frame_parser_sec_ctrl_key_id_mode_get(p_frame_data),
|
|
.p_key_id = (uint8_t *)nrf_802154_frame_parser_key_id_get(p_frame_data),
|
|
};
|
|
|
|
return NRF_802154_SECURITY_ERROR_NONE ==
|
|
nrf_802154_security_pib_key_use(&key_id, p_aes_ccm_data->key);
|
|
}
|
|
|
|
/**
|
|
* @brief Generates a CCM nonce.
|
|
*
|
|
* @param[in] p_frame_data Pointer to the frame parser data.
|
|
* @param[out] p_nonce Pointer to the buffer to be filled with generated nonce.
|
|
*
|
|
* @retval true Nonce was generated successfully.
|
|
* @retval false Nonce could not be generated.
|
|
*/
|
|
bool aes_ccm_nonce_generate(const nrf_802154_frame_parser_data_t * p_frame_data,
|
|
uint8_t * p_nonce)
|
|
{
|
|
if ((p_frame_data == NULL) || (p_nonce == NULL))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const uint8_t * p_src_addr = nrf_802154_pib_extended_address_get();
|
|
uint8_t offset = 0;
|
|
|
|
memcpy_rev(p_nonce, p_src_addr, EXTENDED_ADDRESS_SIZE);
|
|
offset += EXTENDED_ADDRESS_SIZE;
|
|
|
|
// Byte order for Frame Counter gets reversed as defined
|
|
// in 802.15.4-2015 Std Chapters 9.3.1 and Annex B.2
|
|
memcpy_rev((p_nonce + offset),
|
|
nrf_802154_frame_parser_frame_counter_get(p_frame_data),
|
|
FRAME_COUNTER_SIZE);
|
|
offset += FRAME_COUNTER_SIZE;
|
|
|
|
p_nonce[offset] = nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_frame_data);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Prepares _a_ data and _m_ data strings as defined in IEEE 802.15.4-2015 9.3.4.2.
|
|
*
|
|
* @param[in] p_frame_data Pointer to the frame parser data.
|
|
* @param[in] p_aux_sec_hdr Pointer to Auxiliary Security Header data.
|
|
* @param[out] p_aes_ccm_data Pointer to AES CCM transformation data to be filled.
|
|
*
|
|
* @retval true Data was prepared successfully.
|
|
* @retval false Data could not be prepared.
|
|
*/
|
|
bool aes_ccm_data_a_data_and_m_data_prepare(
|
|
const nrf_802154_frame_parser_data_t * p_frame_data,
|
|
nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
|
|
{
|
|
bool result;
|
|
|
|
switch (nrf_802154_frame_parser_frame_type_get(p_frame_data))
|
|
{
|
|
case FRAME_TYPE_ACK:
|
|
// Fallthrough
|
|
case FRAME_TYPE_DATA:
|
|
// Fallthrough
|
|
case FRAME_TYPE_COMMAND:
|
|
result = a_data_and_m_data_prepare(p_frame_data, p_aes_ccm_data);
|
|
break;
|
|
|
|
case FRAME_TYPE_BEACON:
|
|
// Fallthrough
|
|
case FRAME_TYPE_EXTENDED:
|
|
// Fallthrough
|
|
case FRAME_TYPE_FRAGMENT:
|
|
// Fallthrough
|
|
case FRAME_TYPE_MULTIPURPOSE:
|
|
// This frame type is currently unsupported.
|
|
result = false;
|
|
break;
|
|
|
|
default:
|
|
// No more frame types exist.
|
|
result = false;
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Prepares data for AES CCM transformation.
|
|
*
|
|
* @param[in] p_frame_data Pointer to the frame parser data.
|
|
* @param[out] p_aes_ccm_data Pointer to AES CCM transformation data to be filled.
|
|
*
|
|
* @retval true AES CCM transformation data was prepared successfully.
|
|
* @retval false AES CCM transformation could not be prepared.
|
|
*/
|
|
static bool aes_ccm_data_content_prepare(const nrf_802154_frame_parser_data_t * p_frame_data,
|
|
nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
|
|
{
|
|
bool retval = false;
|
|
|
|
do
|
|
{
|
|
if (!aes_ccm_data_key_prepare(p_frame_data, p_aes_ccm_data))
|
|
{
|
|
// Return immediately if specified key could not be found
|
|
break;
|
|
}
|
|
|
|
if (!aes_ccm_nonce_generate(p_frame_data, p_aes_ccm_data->nonce))
|
|
{
|
|
// Return immediately if nonce could not be generated
|
|
break;
|
|
}
|
|
|
|
// Fill _a_ data (authenticity) and _m_ data (confidentiality)
|
|
if (!aes_ccm_data_a_data_and_m_data_prepare(p_frame_data, p_aes_ccm_data))
|
|
{
|
|
// Return immediately if transformation data could not be generated
|
|
break;
|
|
}
|
|
|
|
// Fill in the remaining data
|
|
p_aes_ccm_data->mic_level = mic_level_from_security_level_get(
|
|
nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_frame_data));
|
|
p_aes_ccm_data->raw_frame = (uint8_t *)p_frame_data->p_frame;
|
|
|
|
retval = true;
|
|
}
|
|
while (0);
|
|
|
|
return retval;
|
|
}
|
|
|
|
bool nrf_802154_encrypt_ack_prepare(const nrf_802154_frame_parser_data_t * p_ack_data)
|
|
{
|
|
nrf_802154_aes_ccm_data_t aes_ccm_data;
|
|
bool success = false;
|
|
|
|
if (!nrf_802154_frame_parser_security_enabled_bit_is_set(p_ack_data) ||
|
|
(nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_ack_data) == SECURITY_LEVEL_NONE))
|
|
{
|
|
success = true;
|
|
}
|
|
else if (aes_ccm_data_content_prepare(p_ack_data, &aes_ccm_data))
|
|
{
|
|
// Algorithm's inputs prepared. Schedule transformation
|
|
success = nrf_802154_aes_ccm_transform_prepare(&aes_ccm_data);
|
|
}
|
|
else
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void nrf_802154_encrypt_ack_reset(void)
|
|
{
|
|
nrf_802154_aes_ccm_transform_reset();
|
|
}
|
|
|
|
bool nrf_802154_encrypt_tx_setup(
|
|
uint8_t * p_frame,
|
|
nrf_802154_transmit_params_t * p_params,
|
|
nrf_802154_transmit_failed_notification_t notify_function)
|
|
{
|
|
nrf_802154_aes_ccm_transform_reset();
|
|
|
|
if (p_params->frame_props.is_secured)
|
|
{
|
|
// The frame is already secured. Pass.
|
|
return true;
|
|
}
|
|
|
|
nrf_802154_frame_parser_data_t frame_data;
|
|
nrf_802154_aes_ccm_data_t aes_ccm_data;
|
|
bool success = false;
|
|
|
|
success = nrf_802154_frame_parser_data_init(p_frame,
|
|
p_frame[PHR_OFFSET] + PHR_SIZE,
|
|
PARSE_LEVEL_FULL,
|
|
&frame_data);
|
|
assert(success);
|
|
(void)success;
|
|
|
|
if (!nrf_802154_frame_parser_security_enabled_bit_is_set(&frame_data) ||
|
|
(nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(&frame_data) == SECURITY_LEVEL_NONE))
|
|
{
|
|
success = true;
|
|
}
|
|
else if (aes_ccm_data_content_prepare(&frame_data, &aes_ccm_data))
|
|
{
|
|
// Algorithm's inputs prepared. Schedule transformation
|
|
success = nrf_802154_aes_ccm_transform_prepare(&aes_ccm_data);
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
if (!success)
|
|
{
|
|
nrf_802154_transmit_done_metadata_t metadata = {};
|
|
|
|
metadata.frame_props = p_params->frame_props;
|
|
notify_function(p_frame, NRF_802154_TX_ERROR_KEY_ID_INVALID, &metadata);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool nrf_802154_encrypt_tx_started_hook(uint8_t * p_frame)
|
|
{
|
|
// The provided pointer is the original buffer. It doesn't need to be changed,
|
|
// because the AES-CCM* module is aware of two separate buffers (original vs work buffer)
|
|
nrf_802154_aes_ccm_transform_start(p_frame);
|
|
|
|
return true;
|
|
}
|
|
|
|
void nrf_802154_encrypt_tx_ack_started_hook(uint8_t * p_ack)
|
|
{
|
|
nrf_802154_aes_ccm_transform_start(p_ack);
|
|
}
|
|
|
|
bool nrf_802154_encrypt_tx_failed_hook(uint8_t * p_frame, nrf_802154_tx_error_t error)
|
|
{
|
|
(void)error;
|
|
|
|
nrf_802154_aes_ccm_transform_abort(p_frame);
|
|
|
|
return true;
|
|
}
|
|
|
|
void nrf_802154_encrypt_tx_ack_failed_hook(uint8_t * p_ack, nrf_802154_tx_error_t error)
|
|
{
|
|
(void)error;
|
|
|
|
nrf_802154_aes_ccm_transform_abort(p_ack);
|
|
}
|