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