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