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 = 0;
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 memcpy(&rx_pkt_ctr, cnfptr + 16, 5);
189 rx_pkt_ctr &= 0x7FFFFFFFFFULL;
190 iv = &cnfptr[25];
191
192 inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
193 outptr = (uint8_t*)NRF_CCM_regs.OUTPTR;
194
195 length = inptr[1];
196 if (length > 4) {
197 length -=4;
198 }
199 outptr[0] = inptr[0];
200 outptr[1] = length;
201 pkt_direction = cnfptr[24] & 1;
202 /* Note that outptr[2] is reserved for S1 in the HW (but unused) */
203
204 nonce_calc(iv, rx_pkt_ctr, pkt_direction, ccm_nonce);
205
206 aad = inptr[0] & NRF_CCM_regs.HEADERMASK;
207
208 BLECrypt_if_decrypt_packet(aad, //Additional Authentication data
209 (uint8_t*)&inptr[3], //as received from the air (including a MIC if has_mic)
210 &outptr[3], //buffer for decrypted payload
211 inptr[1], //including MIC lenght if (has_mic == 1) ; [ just the length in the packet header ]
212 true, //we have MIC, or not
213 sk, // Session key (16 bytes, BIG-ENDIAN)
214 ccm_nonce,
215 &mic_error
216 );
217
218 NRF_CCM_regs.MICSTATUS = !mic_error;
219
220 nhw_CCM_signal_EVENTS_ENDCRYPT(0);
221 }
222
nhw_CCM_TASK_KSGEN(void)223 void nhw_CCM_TASK_KSGEN(void) {
224 if (NRF_CCM_regs.ENABLE != CCM_ENABLE_ENABLE_Enabled) {
225 return;
226 }
227 /* In this model we cheat and we do it instantly */
228 signal_EVENTS_ENDKSGEN();
229 }
230
nhw_CCM_TASK_CRYPT(void)231 void nhw_CCM_TASK_CRYPT(void) {
232 if (NRF_CCM_regs.ENABLE != CCM_ENABLE_ENABLE_Enabled) {
233 return;
234 }
235
236 if ((NRF_CCM_regs.MODE & CCM_MODE_MODE_Msk) >> CCM_MODE_MODE_Pos
237 == CCM_MODE_MODE_Encryption) {
238 nrf_ccm_encrypt_tx();
239 } else {
240 decryption_ongoing = true;
241 }
242 }
243
nhw_CCM_TASK_STOP(void)244 void nhw_CCM_TASK_STOP(void) {
245 bs_trace_warning_line_time("CCM: TASK_STOP functionality is not implemented\n");
246 decryption_ongoing = false;
247 }
248
nhw_CCM_TASK_RATEOVERRIDE(void)249 void nhw_CCM_TASK_RATEOVERRIDE(void) {
250 /* We ignore the RATEOVERRIDE task */
251 static bool ever_shown;
252 if (!ever_shown) {
253 bs_trace_info_line_time(3, "%s ignored in the model."
254 "En/Decryption will work fine anyhow."
255 "(This note is shown only once)\n",
256 __func__);
257 ever_shown = true;
258 }
259 }
260
261 NHW_SIDEEFFECTS_INTSET_si(CCM, NRF_CCM_regs., CCM_INTEN)
262 NHW_SIDEEFFECTS_INTCLR_si(CCM, NRF_CCM_regs., CCM_INTEN)
263
NHW_SIDEEFFECTS_EVENTS(CCM)264 NHW_SIDEEFFECTS_EVENTS(CCM)
265
266 NHW_SIDEEFFECTS_TASKS_si(CCM, KSGEN)
267 NHW_SIDEEFFECTS_TASKS_si(CCM, CRYPT)
268 NHW_SIDEEFFECTS_TASKS_si(CCM, STOP)
269 NHW_SIDEEFFECTS_TASKS_si(CCM, RATEOVERRIDE)
270
271 #if (NHW_HAS_DPPI)
272 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, KSGEN)
273 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, CRYPT)
274 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, STOP)
275 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, RATEOVERRIDE)
276 #endif /* NHW_HAS_DPPI */
277
278 void nhw_ccm_radio_received_packet(bool crc_error) {
279 if (!decryption_ongoing) {
280 return;
281 }
282 decryption_ongoing = false;
283 nrf_ccm_decrypt_rx(crc_error);
284 }
285