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