1 /*
2  * Copyright (c) 2024, 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_config.h"
36 
37 #if NRF_802154_ENCRYPTION_ENABLED && !NRF_802154_ENCRYPTION_ACCELERATOR_ECB
38 
39 /** Configures if the CCM's OUT.PTR pointer points to the same memory location as PACKETPTR register
40  *  of the RADIO.
41  *
42  *  Note that with this option set, IEEE 802.15.4 Information Elements injection is not supported.
43  */
44 #define CCM_OUTPTR_POINTS_TO_RADIO_PACKETPTR 0
45 
46 #define CCM_ALEN_ATTR_ID                     11 ///< Attribute field that identifies the alen CCM job
47 #define CCM_MLEN_ATTR_ID                     12 ///< Attribute field that identifies the mlen CCM job
48 #define CCM_ADATA_ATTR_ID                    13 ///< Attribute field that identifies the adata CCM job
49 #define CCM_MDATA_ATTR_ID                    14 ///< Attribute field that identifies the mdata CCM job
50 
51 #include "nrf_802154_aes_ccm.h"
52 
53 #include <string.h>
54 
55 #include "hal/nrf_ccm.h"
56 #include "hal/nrf_dppi.h"
57 #include "hal/nrf_ppib.h"
58 #include "helpers/nrf_vdma.h"
59 
60 #include "nrf_802154_assert.h"
61 #include "nrf_802154_const.h"
62 #include "nrf_802154_tx_work_buffer.h"
63 #include "nrf_802154_peripherals.h"
64 #include "nrf_802154_utils_byteorder.h"
65 #include "platform/nrf_802154_irq.h"
66 
67 #if defined(NRF54H_SERIES)
68 #include "hal/nrf_spu.h"
69 #define PPIB_RAD  NRF_PPIB020
70 #define PPIB_CCM  NRF_PPIB030
71 #ifndef NRF_DPPIC030
72 #define DPPIC_CCM ((NRF_DPPIC_Type *)0x53032000)
73 #else
74 #define DPPIC_CCM NRF_DPPIC030
75 #endif
76 #elif defined(NRF54L_SERIES)
77 #define PPIB_RAD  NRF_PPIB10
78 #define PPIB_CCM  NRF_PPIB00
79 #define DPPIC_CCM NRF_DPPIC00
80 #endif
81 
82 typedef struct
83 {
84     struct
85     {
86         uint16_t alen;
87         uint16_t mlen;
88     } in;
89 
90     struct
91     {
92         uint16_t alen;
93         uint16_t mlen;
94     } out;
95 } ccm_params_t;
96 
97 __ALIGN(8) static nrf_vdma_job_t m_in_job_list[5];  ///< CCM DMA input job list array.
98 __ALIGN(8) static nrf_vdma_job_t m_out_job_list[5]; ///< CCM DMA output job list array.
99 
100 static bool             m_setup;                    ///< Was encryption setup
101 static ccm_params_t     m_ccm_params;               ///< CCM peripheral parameters.
102 static bool             m_initialized;              ///< Module init status.
103 static nrf_ccm_config_t m_ccm_config;               ///< CCM configuration used during the next transmission.
104 static uint32_t         m_key[4];                   ///< Key used during the next transmission.
105 static uint32_t         m_nonce[4];                 ///< Nonce used during the next transmission.
106 
ccm_disable(void)107 static void ccm_disable(void)
108 {
109     nrf_802154_irq_disable(nrfx_get_irq_number(NRF_802154_CCM_INSTANCE));
110     nrf_ccm_subscribe_clear(NRF_802154_CCM_INSTANCE, NRF_CCM_TASK_START);
111     nrf_ccm_subscribe_clear(NRF_802154_CCM_INSTANCE, NRF_CCM_TASK_STOP);
112     nrf_ccm_int_disable(NRF_802154_CCM_INSTANCE, NRF_CCM_INT_ERROR_MASK | NRF_CCM_INT_END_MASK);
113     nrf_ccm_disable(NRF_802154_CCM_INSTANCE);
114 }
115 
ccm_irq_handler(void)116 static void ccm_irq_handler(void)
117 {
118     if (nrf_ccm_int_enable_check(NRF_802154_CCM_INSTANCE, NRF_CCM_INT_ERROR_MASK) &&
119         nrf_ccm_event_check(NRF_802154_CCM_INSTANCE, NRF_CCM_EVENT_ERROR))
120     {
121         nrf_ccm_event_clear(NRF_802154_CCM_INSTANCE, NRF_CCM_EVENT_ERROR);
122         NRF_802154_ASSERT(false);
123     }
124 
125     if (nrf_ccm_int_enable_check(NRF_802154_CCM_INSTANCE, NRF_CCM_INT_END_MASK) &&
126         nrf_ccm_event_check(NRF_802154_CCM_INSTANCE, NRF_CCM_EVENT_END))
127     {
128         nrf_ccm_event_clear(NRF_802154_CCM_INSTANCE, NRF_CCM_EVENT_END);
129         nrf_802154_tx_work_buffer_is_secured_set();
130         ccm_disable();
131     }
132 }
133 
u8_string_to_u32x4_big_endian(const uint8_t * p_input,uint8_t input_len,uint32_t * p_output)134 static void u8_string_to_u32x4_big_endian(const uint8_t * p_input,
135                                           uint8_t         input_len,
136                                           uint32_t      * p_output)
137 {
138     const uint8_t unaligned = input_len % sizeof(uint32_t);
139     const uint8_t len       = unaligned > 0 ? unaligned : sizeof(uint32_t);
140     const uint8_t shift     = (sizeof(uint32_t) - len) * 8;
141     uint32_t      data      = big_32_to_host((uint8_t *)p_input);
142 
143     p_input    += len;
144     data      >>= shift;
145     p_output[3] = data;
146 
147     for (int i = 2; i >= 0; i--)
148     {
149         data        = big_32_to_host((uint8_t *)p_input);
150         p_input    += sizeof(uint32_t);
151         p_output[i] = data;
152     }
153 }
154 
ccm_peripheral_configure(void)155 static void ccm_peripheral_configure(void)
156 {
157     nrf_802154_irq_clear_pending(nrfx_get_irq_number(NRF_802154_CCM_INSTANCE));
158     nrf_802154_irq_enable(nrfx_get_irq_number(NRF_802154_CCM_INSTANCE));
159 
160     nrf_ccm_enable(NRF_802154_CCM_INSTANCE);
161     nrf_ccm_configure(NRF_802154_CCM_INSTANCE, &m_ccm_config);
162     nrf_ccm_key_set(NRF_802154_CCM_INSTANCE, m_key);
163     nrf_ccm_nonce_set(NRF_802154_CCM_INSTANCE, m_nonce);
164     nrf_ccm_in_ptr_set(NRF_802154_CCM_INSTANCE, m_in_job_list);
165     nrf_ccm_out_ptr_set(NRF_802154_CCM_INSTANCE, m_out_job_list);
166     nrf_ccm_event_clear(NRF_802154_CCM_INSTANCE, NRF_CCM_EVENT_ERROR);
167     nrf_ccm_event_clear(NRF_802154_CCM_INSTANCE, NRF_CCM_EVENT_END);
168     nrf_ccm_int_enable(NRF_802154_CCM_INSTANCE, NRF_CCM_INT_ERROR_MASK | NRF_CCM_INT_END_MASK);
169     nrf_ccm_adatamask_set(NRF_802154_CCM_INSTANCE, 0xff);
170 }
171 
ppi_configure(void)172 static void ppi_configure(void)
173 {
174     uint32_t dppi_ch_to_enable = (1 << NRF_802154_DPPI_RADIO_DISABLED);
175 
176     nrf_ppib_subscribe_set(PPIB_RAD,
177                            nrf_ppib_send_task_get(NRF_802154_DPPI_RADIO_DISABLED),
178                            NRF_802154_DPPI_RADIO_DISABLED);
179     nrf_ppib_publish_set(PPIB_CCM,
180                          nrf_ppib_receive_event_get(NRF_802154_DPPI_RADIO_DISABLED),
181                          NRF_802154_DPPI_RADIO_DISABLED);
182 
183     nrf_ccm_subscribe_set(NRF_802154_CCM_INSTANCE,
184                           NRF_CCM_TASK_STOP,
185                           NRF_802154_DPPI_RADIO_DISABLED);
186 
187     nrf_dppi_channels_enable(DPPIC_CCM, dppi_ch_to_enable);
188 }
189 
ccm_configure(void)190 static void ccm_configure(void)
191 {
192     if (!m_initialized)
193     {
194         // DMASEC is set to non-secure by default, which prevents the CCM from accessing
195         // secure memory. Change the DMASEC to secure.
196 #if defined(NRF54H_SERIES)
197         nrf_spu_periph_perm_dmasec_set(NRF_SPU030, 10, true);
198 #endif
199         nrf_802154_irq_init(nrfx_get_irq_number(NRF_802154_CCM_INSTANCE),
200                             NRF_802154_ECB_PRIORITY,
201                             ccm_irq_handler);
202         m_initialized = true;
203     }
204 
205     m_setup = true;
206 }
207 
vdma_jobs_fill(const nrf_802154_aes_ccm_data_t * p_aes_ccm_data,uint8_t * p_adata,uint8_t * p_mdata,uint8_t macsize)208 static void vdma_jobs_fill(const nrf_802154_aes_ccm_data_t * p_aes_ccm_data,
209                            uint8_t                         * p_adata,
210                            uint8_t                         * p_mdata,
211                            uint8_t                           macsize)
212 {
213     // CCM peripheral job list integer params pointed by p_job_ptr must have big-endian byte order.
214     host_16_to_little(p_aes_ccm_data->auth_data_len, (uint8_t *)&m_ccm_params.in.alen);
215     host_16_to_little(p_aes_ccm_data->plain_text_data_len, (uint8_t *)&m_ccm_params.in.mlen);
216 
217     nrf_vdma_job_fill(&m_in_job_list[0],
218                       &m_ccm_params.in.alen,
219                       sizeof(m_ccm_params.in.alen),
220                       CCM_ALEN_ATTR_ID);
221     nrf_vdma_job_fill(&m_in_job_list[1],
222                       &m_ccm_params.in.mlen,
223                       sizeof(m_ccm_params.in.mlen),
224                       CCM_MLEN_ATTR_ID);
225     nrf_vdma_job_fill(&m_in_job_list[2],
226                       p_aes_ccm_data->auth_data,
227                       p_aes_ccm_data->auth_data_len,
228                       CCM_ADATA_ATTR_ID);
229     nrf_vdma_job_fill(&m_in_job_list[3],
230                       p_aes_ccm_data->plain_text_data,
231                       p_aes_ccm_data->plain_text_data_len,
232                       CCM_MDATA_ATTR_ID);
233     nrf_vdma_job_terminate(&m_in_job_list[4]);
234 
235     nrf_vdma_job_fill(&m_out_job_list[0],
236                       &m_ccm_params.out.alen,
237                       sizeof(m_ccm_params.out.alen),
238                       CCM_ALEN_ATTR_ID);
239     nrf_vdma_job_fill(&m_out_job_list[1],
240                       &m_ccm_params.out.mlen,
241                       sizeof(m_ccm_params.out.mlen),
242                       CCM_MLEN_ATTR_ID);
243     nrf_vdma_job_fill(&m_out_job_list[2],
244                       p_adata,
245                       p_aes_ccm_data->auth_data_len,
246                       CCM_ADATA_ATTR_ID);
247     nrf_vdma_job_fill(&m_out_job_list[3],
248                       p_mdata,
249                       p_aes_ccm_data->plain_text_data_len + macsize,
250                       CCM_MDATA_ATTR_ID);
251     nrf_vdma_job_terminate(&m_out_job_list[4]);
252 }
253 
ccm_data_fill(const nrf_802154_aes_ccm_data_t * p_aes_ccm_data,uint8_t * p_adata,uint8_t * p_mdata)254 static bool ccm_data_fill(const nrf_802154_aes_ccm_data_t * p_aes_ccm_data,
255                           uint8_t                         * p_adata,
256                           uint8_t                         * p_mdata)
257 {
258     nrf_ccm_maclen_t maclen;
259     uint8_t          macsize;
260 
261     switch (p_aes_ccm_data->mic_level)
262     {
263         case SECURITY_LEVEL_MIC_32:
264         case SECURITY_LEVEL_ENC_MIC_32:
265             maclen  = NRF_CCM_MODE_MACLEN_M4;
266             macsize = 4;
267             break;
268 
269         case SECURITY_LEVEL_MIC_64:
270         case SECURITY_LEVEL_ENC_MIC_64:
271             maclen  = NRF_CCM_MODE_MACLEN_M8;
272             macsize = 8;
273             break;
274 
275         case SECURITY_LEVEL_MIC_128:
276         case SECURITY_LEVEL_ENC_MIC_128:
277             maclen  = NRF_CCM_MODE_MACLEN_M16;
278             macsize = 16;
279             break;
280 
281         default:
282             return false;
283     }
284 
285     m_ccm_config.mode       = NRF_CCM_MODE_ENCRYPTION;
286     m_ccm_config.datarate   = NRF_CCM_DATARATE_250K;
287     m_ccm_config.protocol   = NRF_CCM_MODE_PROTOCOL_IEEE802154;
288     m_ccm_config.mac_length = maclen;
289 
290     u8_string_to_u32x4_big_endian(p_aes_ccm_data->key, sizeof(p_aes_ccm_data->key), m_key);
291     u8_string_to_u32x4_big_endian(p_aes_ccm_data->nonce, sizeof(p_aes_ccm_data->nonce), m_nonce);
292 
293     vdma_jobs_fill(p_aes_ccm_data, p_adata, p_mdata, macsize);
294 
295     return true;
296 }
297 
nrf_802154_aes_ccm_transform_prepare(const nrf_802154_aes_ccm_data_t * p_aes_ccm_data)298 bool nrf_802154_aes_ccm_transform_prepare(const nrf_802154_aes_ccm_data_t * p_aes_ccm_data)
299 {
300     uint8_t * p_work_buffer;
301     uint8_t * p_ciphertext;
302 
303     m_setup = false;
304 
305     // Verify that all necessary data is available
306     if (p_aes_ccm_data->raw_frame == NULL)
307     {
308         return false;
309     }
310 
311     // Verify that the optional data, if exists, is complete
312     if (((p_aes_ccm_data->auth_data_len != 0) && (p_aes_ccm_data->auth_data == NULL)) ||
313         ((p_aes_ccm_data->plain_text_data_len != 0) && (p_aes_ccm_data->plain_text_data == NULL)))
314     {
315         return false;
316     }
317 
318     // Verify that the MIC level is valid
319     if (p_aes_ccm_data->mic_level > SECURITY_LEVEL_MIC_LEVEL_MASK)
320     {
321         return false;
322     }
323 
324     ptrdiff_t offset;
325 
326     if (p_aes_ccm_data->auth_data)
327     {
328         offset = p_aes_ccm_data->auth_data_len + PHR_SIZE;
329     }
330     else
331     {
332         offset = p_aes_ccm_data->raw_frame[PHR_OFFSET] + PHR_SIZE;
333     }
334 
335     NRF_802154_ASSERT((offset >= 0) && (offset <= MAX_PACKET_SIZE + PHR_SIZE));
336 
337     nrf_802154_tx_work_buffer_plain_text_offset_set(offset);
338     p_work_buffer = nrf_802154_tx_work_buffer_enable_for(p_aes_ccm_data->raw_frame);
339     p_ciphertext  = p_work_buffer + offset;
340 
341 #if CCM_OUTPTR_POINTS_TO_RADIO_PACKETPTR
342     memcpy(p_work_buffer, p_aes_ccm_data->raw_frame, PHR_SIZE);
343     memset(&p_work_buffer[PSDU_OFFSET], 0, p_aes_ccm_data->raw_frame[PHR_OFFSET]);
344 #else
345     memcpy(p_work_buffer, p_aes_ccm_data->raw_frame, offset);
346     memset(p_ciphertext, 0, p_aes_ccm_data->raw_frame[PHR_OFFSET] + PHR_SIZE - offset);
347 #endif // CCM_OUTPTR_POINTS_TO_RADIO_PACKETPTR
348 
349     if (ccm_data_fill(p_aes_ccm_data, p_work_buffer + PHR_SIZE, p_ciphertext))
350     {
351         ccm_configure();
352         m_setup = true;
353     }
354 
355     return m_setup;
356 }
357 
nrf_802154_aes_ccm_transform_start(uint8_t * p_frame)358 void nrf_802154_aes_ccm_transform_start(uint8_t * p_frame)
359 {
360     if (m_setup == false)
361     {
362         return;
363     }
364 
365     ccm_peripheral_configure();
366     ppi_configure();
367 
368     nrf_ccm_task_trigger(NRF_802154_CCM_INSTANCE, NRF_CCM_TASK_START);
369 }
370 
nrf_802154_aes_ccm_transform_abort(uint8_t * p_frame)371 void nrf_802154_aes_ccm_transform_abort(uint8_t * p_frame)
372 {
373     ccm_disable();
374     m_setup = false;
375     memset(m_key, 0, sizeof(m_key));
376     memset(m_nonce, 0, sizeof(m_nonce));
377 }
378 
nrf_802154_aes_ccm_transform_reset(void)379 void nrf_802154_aes_ccm_transform_reset(void)
380 {
381     m_setup = false;
382     memset(m_key, 0, sizeof(m_key));
383     memset(m_nonce, 0, sizeof(m_nonce));
384 }
385 
386 #endif // NRF_802154_ENCRYPTION_ENABLED && !NRF_802154_ENCRYPTION_ACCELERATOR_ECB
387