1 /*
2 * Copyright (c) 2021 - 2023, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 #include "nrf_802154_encrypt.h"
36
37 #include <assert.h>
38 #include <string.h>
39
40 #include "nrf_802154_aes_ccm.h"
41 #include "nrf_802154_const.h"
42 #include "nrf_802154_pib.h"
43 #include "nrf_802154_types_internal.h"
44 #include "mac_features/nrf_802154_frame_parser.h"
45 #include "mac_features/nrf_802154_security_pib.h"
46
47 /**
48 * @brief Copies memory in reversed byte order.
49 *
50 * @note Source and destination buffers must not be NULL.
51 *
52 * @param[out] p_dst Pointer to the destination buffer.
53 * @param[in] p_src Pointer to the source buffer.
54 * @param[in] n The number of bytes to copy.
55 */
memcpy_rev(void * p_dst,const void * p_src,size_t n)56 static inline void memcpy_rev(void * p_dst, const void * p_src, size_t n)
57 {
58 for (size_t i = 0; i < n; i++)
59 {
60 *((uint8_t *)p_dst + i) = *((uint8_t *)p_src + n - 1 - i);
61 }
62 }
63
frame_version_is_2015_or_above(const nrf_802154_frame_parser_data_t * p_frame_data)64 static bool frame_version_is_2015_or_above(const nrf_802154_frame_parser_data_t * p_frame_data)
65 {
66 switch (nrf_802154_frame_parser_frame_version_get(p_frame_data))
67 {
68 case FRAME_VERSION_0:
69 case FRAME_VERSION_1:
70 return false;
71
72 case FRAME_VERSION_2:
73 return true;
74
75 default:
76 return true;
77 }
78 }
79
mic_level_from_security_level_get(uint8_t security_level)80 static uint8_t mic_level_from_security_level_get(uint8_t security_level)
81 {
82 // According to the specification, two least significant bits of the security level
83 // indicate provided level of data authenticity
84 return security_level & SECURITY_LEVEL_MIC_LEVEL_MASK;
85 }
86
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)87 static bool a_data_and_m_data_prepare(
88 const nrf_802154_frame_parser_data_t * p_frame_data,
89 nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
90 {
91 bool result = true;
92
93 uint8_t open_payload_len = 0;
94
95 if ((nrf_802154_frame_parser_frame_type_get(p_frame_data) == FRAME_TYPE_COMMAND) &&
96 (!frame_version_is_2015_or_above(p_frame_data)))
97 {
98 open_payload_len = MAC_CMD_COMMAND_ID_SIZE;
99 }
100
101 // It is assumed that the provided frame has a placeholder of appropriate length for MIC
102 // at the end. The algorithm inputs should only contain MAC payload, so the MIC placeholder
103 // of the below length should be removed
104 uint8_t mic_len = nrf_802154_frame_parser_mic_size_get(p_frame_data);
105 uint8_t * p_mac_hdr = (uint8_t *)nrf_802154_frame_parser_psdu_get(p_frame_data);
106 uint8_t mac_hdr_len =
107 nrf_802154_frame_parser_mac_header_length_get(p_frame_data) + open_payload_len;
108 uint8_t * p_mac_payload =
109 (uint8_t *)(nrf_802154_frame_parser_mac_payload_get(p_frame_data) + open_payload_len);
110 uint8_t mac_payload_len =
111 nrf_802154_frame_parser_mac_payload_length_get(p_frame_data) - mic_len - open_payload_len;
112
113 switch (nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_frame_data))
114 {
115 case SECURITY_LEVEL_NONE:
116 // No data authenticity nor data confidentiality
117 p_aes_ccm_data->auth_data = NULL;
118 p_aes_ccm_data->auth_data_len = 0;
119 p_aes_ccm_data->plain_text_data = NULL;
120 p_aes_ccm_data->plain_text_data_len = 0;
121 break;
122
123 case SECURITY_LEVEL_MIC_32:
124 case SECURITY_LEVEL_MIC_64:
125 case SECURITY_LEVEL_MIC_128:
126 // Data authenticity without data confidentiality
127 p_aes_ccm_data->auth_data = p_mac_hdr;
128 p_aes_ccm_data->auth_data_len = mac_hdr_len + mac_payload_len;
129 p_aes_ccm_data->plain_text_data = NULL;
130 p_aes_ccm_data->plain_text_data_len = 0;
131 break;
132
133 case SECURITY_LEVEL_ENC_MIC_32:
134 case SECURITY_LEVEL_ENC_MIC_64:
135 case SECURITY_LEVEL_ENC_MIC_128:
136 // Data authenticity and data confidentiality
137 p_aes_ccm_data->auth_data = p_mac_hdr;
138 p_aes_ccm_data->auth_data_len = mac_hdr_len;
139 p_aes_ccm_data->plain_text_data = p_mac_payload;
140 p_aes_ccm_data->plain_text_data_len = mac_payload_len;
141 break;
142
143 default:
144 result = false;
145 break;
146 }
147
148 return result;
149 }
150
151 /**
152 * @brief Retrieves key to be used for the AES CCM transformation.
153 *
154 * @param[in] p_frame_data Pointer to the frame parser data.
155 * @param[out] p_aes_ccm_data Pointer to AES CCM transformation data to be filled.
156 */
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)157 bool aes_ccm_data_key_prepare(const nrf_802154_frame_parser_data_t * p_frame_data,
158 nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
159 {
160 nrf_802154_key_id_t key_id =
161 {
162 .mode = nrf_802154_frame_parser_sec_ctrl_key_id_mode_get(p_frame_data),
163 .p_key_id = (uint8_t *)nrf_802154_frame_parser_key_id_get(p_frame_data),
164 };
165
166 return NRF_802154_SECURITY_ERROR_NONE ==
167 nrf_802154_security_pib_key_use(&key_id, p_aes_ccm_data->key);
168 }
169
170 /**
171 * @brief Generates a CCM nonce.
172 *
173 * @param[in] p_frame_data Pointer to the frame parser data.
174 * @param[out] p_nonce Pointer to the buffer to be filled with generated nonce.
175 *
176 * @retval true Nonce was generated successfully.
177 * @retval false Nonce could not be generated.
178 */
aes_ccm_nonce_generate(const nrf_802154_frame_parser_data_t * p_frame_data,uint8_t * p_nonce)179 bool aes_ccm_nonce_generate(const nrf_802154_frame_parser_data_t * p_frame_data,
180 uint8_t * p_nonce)
181 {
182 if ((p_frame_data == NULL) || (p_nonce == NULL))
183 {
184 return false;
185 }
186
187 const uint8_t * p_src_addr = nrf_802154_pib_extended_address_get();
188 uint8_t offset = 0;
189
190 memcpy_rev(p_nonce, p_src_addr, EXTENDED_ADDRESS_SIZE);
191 offset += EXTENDED_ADDRESS_SIZE;
192
193 // Byte order for Frame Counter gets reversed as defined
194 // in 802.15.4-2015 Std Chapters 9.3.1 and Annex B.2
195 memcpy_rev((p_nonce + offset),
196 nrf_802154_frame_parser_frame_counter_get(p_frame_data),
197 FRAME_COUNTER_SIZE);
198 offset += FRAME_COUNTER_SIZE;
199
200 p_nonce[offset] = nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_frame_data);
201
202 return true;
203 }
204
205 /**
206 * @brief Prepares _a_ data and _m_ data strings as defined in IEEE 802.15.4-2015 9.3.4.2.
207 *
208 * @param[in] p_frame_data Pointer to the frame parser data.
209 * @param[in] p_aux_sec_hdr Pointer to Auxiliary Security Header data.
210 * @param[out] p_aes_ccm_data Pointer to AES CCM transformation data to be filled.
211 *
212 * @retval true Data was prepared successfully.
213 * @retval false Data could not be prepared.
214 */
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)215 bool aes_ccm_data_a_data_and_m_data_prepare(
216 const nrf_802154_frame_parser_data_t * p_frame_data,
217 nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
218 {
219 bool result;
220
221 switch (nrf_802154_frame_parser_frame_type_get(p_frame_data))
222 {
223 case FRAME_TYPE_ACK:
224 // Fallthrough
225 case FRAME_TYPE_DATA:
226 // Fallthrough
227 case FRAME_TYPE_COMMAND:
228 result = a_data_and_m_data_prepare(p_frame_data, p_aes_ccm_data);
229 break;
230
231 case FRAME_TYPE_BEACON:
232 // Fallthrough
233 case FRAME_TYPE_EXTENDED:
234 // Fallthrough
235 case FRAME_TYPE_FRAGMENT:
236 // Fallthrough
237 case FRAME_TYPE_MULTIPURPOSE:
238 // This frame type is currently unsupported.
239 result = false;
240 break;
241
242 default:
243 // No more frame types exist.
244 result = false;
245 assert(false);
246 break;
247 }
248
249 return result;
250 }
251
252 /**
253 * @brief Prepares data for AES CCM transformation.
254 *
255 * @param[in] p_frame_data Pointer to the frame parser data.
256 * @param[out] p_aes_ccm_data Pointer to AES CCM transformation data to be filled.
257 *
258 * @retval true AES CCM transformation data was prepared successfully.
259 * @retval false AES CCM transformation could not be prepared.
260 */
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)261 static bool aes_ccm_data_content_prepare(const nrf_802154_frame_parser_data_t * p_frame_data,
262 nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
263 {
264 bool retval = false;
265
266 do
267 {
268 if (!aes_ccm_data_key_prepare(p_frame_data, p_aes_ccm_data))
269 {
270 // Return immediately if specified key could not be found
271 break;
272 }
273
274 if (!aes_ccm_nonce_generate(p_frame_data, p_aes_ccm_data->nonce))
275 {
276 // Return immediately if nonce could not be generated
277 break;
278 }
279
280 // Fill _a_ data (authenticity) and _m_ data (confidentiality)
281 if (!aes_ccm_data_a_data_and_m_data_prepare(p_frame_data, p_aes_ccm_data))
282 {
283 // Return immediately if transformation data could not be generated
284 break;
285 }
286
287 // Fill in the remaining data
288 p_aes_ccm_data->mic_level = mic_level_from_security_level_get(
289 nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_frame_data));
290 p_aes_ccm_data->raw_frame = (uint8_t *)p_frame_data->p_frame;
291
292 retval = true;
293 }
294 while (0);
295
296 return retval;
297 }
298
nrf_802154_encrypt_ack_prepare(const nrf_802154_frame_parser_data_t * p_ack_data)299 bool nrf_802154_encrypt_ack_prepare(const nrf_802154_frame_parser_data_t * p_ack_data)
300 {
301 nrf_802154_aes_ccm_data_t aes_ccm_data;
302 bool success = false;
303
304 if (!nrf_802154_frame_parser_security_enabled_bit_is_set(p_ack_data) ||
305 (nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(p_ack_data) == SECURITY_LEVEL_NONE))
306 {
307 success = true;
308 }
309 else if (aes_ccm_data_content_prepare(p_ack_data, &aes_ccm_data))
310 {
311 // Algorithm's inputs prepared. Schedule transformation
312 success = nrf_802154_aes_ccm_transform_prepare(&aes_ccm_data);
313 }
314 else
315 {
316 // Intentionally empty
317 }
318
319 return success;
320 }
321
nrf_802154_encrypt_ack_reset(void)322 void nrf_802154_encrypt_ack_reset(void)
323 {
324 nrf_802154_aes_ccm_transform_reset();
325 }
326
nrf_802154_encrypt_tx_setup(uint8_t * p_frame,nrf_802154_transmit_params_t * p_params,nrf_802154_transmit_failed_notification_t notify_function)327 bool nrf_802154_encrypt_tx_setup(
328 uint8_t * p_frame,
329 nrf_802154_transmit_params_t * p_params,
330 nrf_802154_transmit_failed_notification_t notify_function)
331 {
332 nrf_802154_aes_ccm_transform_reset();
333
334 if (p_params->frame_props.is_secured)
335 {
336 // The frame is already secured. Pass.
337 return true;
338 }
339
340 nrf_802154_frame_parser_data_t frame_data;
341 nrf_802154_aes_ccm_data_t aes_ccm_data;
342 bool success = false;
343
344 success = nrf_802154_frame_parser_data_init(p_frame,
345 p_frame[PHR_OFFSET] + PHR_SIZE,
346 PARSE_LEVEL_FULL,
347 &frame_data);
348 assert(success);
349 (void)success;
350
351 if (!nrf_802154_frame_parser_security_enabled_bit_is_set(&frame_data) ||
352 (nrf_802154_frame_parser_sec_ctrl_sec_lvl_get(&frame_data) == SECURITY_LEVEL_NONE))
353 {
354 success = true;
355 }
356 else if (aes_ccm_data_content_prepare(&frame_data, &aes_ccm_data))
357 {
358 // Algorithm's inputs prepared. Schedule transformation
359 success = nrf_802154_aes_ccm_transform_prepare(&aes_ccm_data);
360 }
361 else
362 {
363 success = false;
364 }
365
366 if (!success)
367 {
368 nrf_802154_transmit_done_metadata_t metadata = {};
369
370 metadata.frame_props = p_params->frame_props;
371 notify_function(p_frame, NRF_802154_TX_ERROR_KEY_ID_INVALID, &metadata);
372 }
373
374 return success;
375 }
376
nrf_802154_encrypt_tx_started_hook(uint8_t * p_frame)377 bool nrf_802154_encrypt_tx_started_hook(uint8_t * p_frame)
378 {
379 // The provided pointer is the original buffer. It doesn't need to be changed,
380 // because the AES-CCM* module is aware of two separate buffers (original vs work buffer)
381 nrf_802154_aes_ccm_transform_start(p_frame);
382
383 return true;
384 }
385
nrf_802154_encrypt_tx_ack_started_hook(uint8_t * p_ack)386 void nrf_802154_encrypt_tx_ack_started_hook(uint8_t * p_ack)
387 {
388 nrf_802154_aes_ccm_transform_start(p_ack);
389 }
390
nrf_802154_encrypt_tx_failed_hook(uint8_t * p_frame,nrf_802154_tx_error_t error)391 bool nrf_802154_encrypt_tx_failed_hook(uint8_t * p_frame, nrf_802154_tx_error_t error)
392 {
393 (void)error;
394
395 nrf_802154_aes_ccm_transform_abort(p_frame);
396
397 return true;
398 }
399
nrf_802154_encrypt_tx_ack_failed_hook(uint8_t * p_ack,nrf_802154_tx_error_t error)400 void nrf_802154_encrypt_tx_ack_failed_hook(uint8_t * p_ack, nrf_802154_tx_error_t error)
401 {
402 (void)error;
403
404 nrf_802154_aes_ccm_transform_abort(p_ack);
405 }
406