1 /*
2 * Copyright (c) 2017 Oticon A/S
3 * Copyright (c) 2023 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * CCM - AES CCM mode encryption
10 * https://infocenter.nordicsemi.com/topic/ps_nrf52833/ccm.html?cp=5_1_0_5_3
11 * https://infocenter.nordicsemi.com/topic/ps_nrf5340/ccm.html?cp=4_0_0_6_4
12 *
13 * Notes:
14 *
15 * 1. This model does not try to account for the delay the real CCM HW block has
16 * Instead:
17 * * The key stream generation will be instantaneous
18 * (really nothing will be done in this step)
19 * * In transmission mode the packet will be encrypted instantly
20 * after triggering TASKS_CRYPT
21 * * In reception TASKS_CRYPT will not do anything, instead a "cheat" call
22 * from the RADIO at the end of the packet will trigger the whole
23 * packet to be decoded (if the CRC was ok) instantaneously.
24 * If the CRC failed, nothing will be done with the payload, but micerror
25 * will be set
26 * Therefore we ignore the RATE register, RATEOVERRIDE register and TASK
27 *
28 * 2. MAXPACKETSIZE and MODE.LENGTH are ignored. Packets are decrypted correctly
29 * according to their length
30 *
31 * 3. TASKS_STOP is not really supported
32 *
33 * 4. TASK_RATEOVERRIDE and RATEOVERRIDE are not supported (just ignored)
34 */
35
36 #include "NHW_common_types.h"
37 #include "NHW_config.h"
38 #include "NHW_templates.h"
39 #include "NHW_AES_CCM.h"
40 #include <string.h>
41 #include <stdbool.h>
42 #include "nsi_hw_scheduler.h"
43 #include "NHW_peri_types.h"
44 #include "NHW_xPPI.h"
45 #include "irq_ctrl.h"
46 #include "bs_tracing.h"
47 #include "BLECrypt_if.h"
48 #include "nsi_tasks.h"
49
50 NRF_CCM_Type NRF_CCM_regs;
51 #if (NHW_HAS_DPPI)
52 /* Mapping of peripheral instance to DPPI instance */
53 static uint nhw_CCM_dppi_map[NHW_CCM_TOTAL_INST] = NHW_CCM_DPPI_MAP;
54 #endif
55
56 static uint32_t CCM_INTEN; //interrupt enable
57 static bool decryption_ongoing;
58
nhw_aes_ccm_init(void)59 static void nhw_aes_ccm_init(void) {
60 memset(&NRF_CCM_regs, 0, sizeof(NRF_CCM_regs));
61 NRF_CCM_regs.MODE = 0x01;
62 NRF_CCM_regs.HEADERMASK= 0xE3;
63 NRF_CCM_regs.MAXPACKETSIZE = 0xFB;
64 CCM_INTEN = 0;
65 decryption_ongoing = false;
66 }
67
68 NSI_TASK(nhw_aes_ccm_init, HW_INIT, 100);
69
nhw_CCM_eval_interrupt(uint inst)70 static void nhw_CCM_eval_interrupt(uint inst) {
71 static bool ccm_int_line[NHW_CCM_TOTAL_INST]; /* Is the CCM currently driving its interrupt line high */
72 /* Mapping of peripheral instance to {int controller instance, int number} */
73 static struct nhw_irq_mapping nhw_ccm_irq_map[NHW_CCM_TOTAL_INST] = NHW_CCM_INT_MAP;
74 bool new_int_line = false;
75
76 NHW_CHECK_INTERRUPT_si(CCM, ENDKSGEN, CCM_INTEN)
77 NHW_CHECK_INTERRUPT_si(CCM, ENDCRYPT, CCM_INTEN)
78 NHW_CHECK_INTERRUPT_si(CCM, ERROR, CCM_INTEN)
79
80 hw_irq_ctrl_toggle_level_irq_line_if(&ccm_int_line[inst],
81 new_int_line,
82 &nhw_ccm_irq_map[inst]);
83 }
84
NHW_SIGNAL_EVENT_si(CCM,ENDKSGEN)85 static NHW_SIGNAL_EVENT_si(CCM, ENDKSGEN)
86 static NHW_SIGNAL_EVENT_si(CCM, ENDCRYPT)
87 //Unused so far in this model: static NHW_SIGNAL_EVENT_si(CCM, ERROR)
88
89 static void signal_EVENTS_ENDKSGEN(void) {
90 nhw_CCM_signal_EVENTS_ENDKSGEN(0);
91
92 if (NRF_CCM_regs.SHORTS & CCM_SHORTS_ENDKSGEN_CRYPT_Msk) {
93 nhw_CCM_TASK_CRYPT();
94 }
95 }
96
97 #define IV_LEN 8
98 #define NONCE_LEN 13
99
100 // Calculates CCM nonce.
nonce_calc(const uint8_t * iv,uint64_t packet_counter,uint8_t packet_direction,uint8_t * ccm_nonce)101 static void nonce_calc(
102 // Inputs
103 const uint8_t *iv, // Initialization vector (IV_LEN bytes, little-endian)
104 uint64_t packet_counter, // 39-bit packet count (in given direction, excl. retransmissions and empty packets) since start of encryption
105 uint8_t packet_direction, // Direction of packet (1:master to slave, 0: slave to master)
106 // Outputs (the pointers themselves are inputs and must point to large enough areas)
107 uint8_t *ccm_nonce) // Resulting nonce (NONCE_LEN bytes, little-endian)
108 {
109 int i;
110 // Copy 39-bit packet counter into first 5 bytes of nonce and set 40th bit depending on packet
111 // direction
112 for (i = 0; i < NONCE_LEN - IV_LEN - 1; i++)
113 {
114 ccm_nonce[i] = packet_counter & 0xFF;
115 packet_counter >>= 8;
116 }
117 ccm_nonce[i] = (packet_counter & 0x7F) | (packet_direction == 1 /*master to slave*/ ? 0x80 : 0);
118 // Copy initialization vector into remaining 8 bytes of nonce
119 memcpy(&ccm_nonce[NONCE_LEN - IV_LEN], iv, IV_LEN);
120 }
121
nrf_ccm_encrypt_tx(void)122 static void nrf_ccm_encrypt_tx(void) {
123 const uint8_t* cnfptr;
124 const uint8_t* sk;
125 const uint8_t* iv;
126 uint64_t tx_pkt_ctr;
127 uint8_t pkt_direction;
128 const uint8_t* inptr;
129 uint8_t* outptr;
130 int length;
131 uint8_t ccm_nonce[NONCE_LEN];
132 uint8_t aad;
133
134 cnfptr = (const uint8_t*)NRF_CCM_regs.CNFPTR;
135 sk = cnfptr;
136 tx_pkt_ctr = *(uint64_t*)(cnfptr + 16) & 0x7FFFFFFFFFULL;
137 iv = &cnfptr[25];
138
139 inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
140 outptr = (uint8_t*)NRF_CCM_regs.OUTPTR;
141
142 length = inptr[1];
143 if (length > 0) {
144 length +=4;
145 }
146 outptr[0] = inptr[0];
147 outptr[1] = length;
148 pkt_direction = cnfptr[24] & 1;
149 /* Note that outptr[2] is reserved for S1 in the HW (but unused) */
150
151 nonce_calc(iv, tx_pkt_ctr, pkt_direction, ccm_nonce);
152
153 aad = inptr[0] & NRF_CCM_regs.HEADERMASK;
154
155 BLECrypt_if_encrypt_packet(aad, //Additional Authentication data
156 (uint8_t*)&inptr[3], // Packet payload to be encrypted
157 &outptr[3], //encrypted payload (and MIC if generate_mic==1)
158 length, //including MIC length if ( generate_mic == 1 ) ; [ just the length in the output packet header ]
159 true, //we have MIC, or not
160 sk, // Session key (16 bytes, BIG-ENDIAN)
161 ccm_nonce
162 );
163
164 nhw_CCM_signal_EVENTS_ENDCRYPT(0);
165 }
166
nrf_ccm_decrypt_rx(bool crc_error)167 static void nrf_ccm_decrypt_rx(bool crc_error) {
168 const uint8_t* cnfptr;
169 const uint8_t* sk;
170 const uint8_t* iv;
171 uint64_t rx_pkt_ctr;
172 uint8_t pkt_direction;
173 const uint8_t* inptr;
174 uint8_t* outptr;
175 int length;
176 uint8_t mic_error;
177 uint8_t ccm_nonce[NONCE_LEN];
178 uint8_t aad;
179
180 if (crc_error) {
181 NRF_CCM_regs.MICSTATUS = 0;
182 nhw_CCM_signal_EVENTS_ENDCRYPT(0);
183 return;
184 }
185
186 cnfptr = (const uint8_t*)NRF_CCM_regs.CNFPTR;
187 sk = cnfptr;
188 rx_pkt_ctr = *(uint64_t*)(cnfptr + 16) & 0x7FFFFFFFFFULL;
189 iv = &cnfptr[25];
190
191 inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
192 outptr = (uint8_t*)NRF_CCM_regs.OUTPTR;
193
194 length = inptr[1];
195 if (length > 4) {
196 length -=4;
197 }
198 outptr[0] = inptr[0];
199 outptr[1] = length;
200 pkt_direction = cnfptr[24] & 1;
201 /* Note that outptr[2] is reserved for S1 in the HW (but unused) */
202
203 nonce_calc(iv, rx_pkt_ctr, pkt_direction, ccm_nonce);
204
205 aad = inptr[0] & NRF_CCM_regs.HEADERMASK;
206
207 BLECrypt_if_decrypt_packet(aad, //Additional Authentication data
208 (uint8_t*)&inptr[3], //as received from the air (including a MIC if has_mic)
209 &outptr[3], //buffer for decrypted payload
210 inptr[1], //including MIC lenght if (has_mic == 1) ; [ just the length in the packet header ]
211 true, //we have MIC, or not
212 sk, // Session key (16 bytes, BIG-ENDIAN)
213 ccm_nonce,
214 &mic_error
215 );
216
217 NRF_CCM_regs.MICSTATUS = !mic_error;
218
219 nhw_CCM_signal_EVENTS_ENDCRYPT(0);
220 }
221
nhw_CCM_TASK_KSGEN(void)222 void nhw_CCM_TASK_KSGEN(void) {
223 if (NRF_CCM_regs.ENABLE != CCM_ENABLE_ENABLE_Enabled) {
224 return;
225 }
226 /* In this model we cheat and we do it instantly */
227 signal_EVENTS_ENDKSGEN();
228 }
229
nhw_CCM_TASK_CRYPT(void)230 void nhw_CCM_TASK_CRYPT(void) {
231 if (NRF_CCM_regs.ENABLE != CCM_ENABLE_ENABLE_Enabled) {
232 return;
233 }
234
235 if ((NRF_CCM_regs.MODE & CCM_MODE_MODE_Msk) >> CCM_MODE_MODE_Pos
236 == CCM_MODE_MODE_Encryption) {
237 nrf_ccm_encrypt_tx();
238 } else {
239 decryption_ongoing = true;
240 }
241 }
242
nhw_CCM_TASK_STOP(void)243 void nhw_CCM_TASK_STOP(void) {
244 bs_trace_warning_line_time("CCM: TASK_STOP functionality is not implemented\n");
245 decryption_ongoing = false;
246 }
247
nhw_CCM_TASK_RATEOVERRIDE(void)248 void nhw_CCM_TASK_RATEOVERRIDE(void) {
249 /* We ignore the RATEOVERRIDE task */
250 static bool ever_shown;
251 if (!ever_shown) {
252 bs_trace_info_line_time(3, "%s ignored in the model."
253 "En/Decryption will work fine anyhow."
254 "(This note is shown only once)\n",
255 __func__);
256 ever_shown = true;
257 }
258 }
259
260 NHW_SIDEEFFECTS_INTSET_si(CCM, NRF_CCM_regs., CCM_INTEN)
261 NHW_SIDEEFFECTS_INTCLR_si(CCM, NRF_CCM_regs., CCM_INTEN)
262
NHW_SIDEEFFECTS_EVENTS(CCM)263 NHW_SIDEEFFECTS_EVENTS(CCM)
264
265 NHW_SIDEEFFECTS_TASKS_si(CCM, KSGEN)
266 NHW_SIDEEFFECTS_TASKS_si(CCM, CRYPT)
267 NHW_SIDEEFFECTS_TASKS_si(CCM, STOP)
268 NHW_SIDEEFFECTS_TASKS_si(CCM, RATEOVERRIDE)
269
270 #if (NHW_HAS_DPPI)
271 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, KSGEN)
272 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, CRYPT)
273 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, STOP)
274 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, RATEOVERRIDE)
275 #endif /* NHW_HAS_DPPI */
276
277 void nhw_ccm_radio_received_packet(bool crc_error) {
278 if (!decryption_ongoing) {
279 return;
280 }
281 decryption_ongoing = false;
282 nrf_ccm_decrypt_rx(crc_error);
283 }
284