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   bs_trace_warning_line_time("%s ignored\n", __func__);
251 }
252 
253 NHW_SIDEEFFECTS_INTSET_si(CCM, NRF_CCM_regs., CCM_INTEN)
254 NHW_SIDEEFFECTS_INTCLR_si(CCM, NRF_CCM_regs., CCM_INTEN)
255 
NHW_SIDEEFFECTS_EVENTS(CCM)256 NHW_SIDEEFFECTS_EVENTS(CCM)
257 
258 NHW_SIDEEFFECTS_TASKS_si(CCM, KSGEN)
259 NHW_SIDEEFFECTS_TASKS_si(CCM, CRYPT)
260 NHW_SIDEEFFECTS_TASKS_si(CCM, STOP)
261 NHW_SIDEEFFECTS_TASKS_si(CCM, RATEOVERRIDE)
262 
263 #if (NHW_HAS_DPPI)
264 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, KSGEN)
265 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, CRYPT)
266 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, STOP)
267 NHW_SIDEEFFECTS_SUBSCRIBE_si(CCM, RATEOVERRIDE)
268 #endif /* NHW_HAS_DPPI */
269 
270 void nhw_ccm_radio_received_packet(bool crc_error) {
271   if (!decryption_ongoing) {
272     return;
273   }
274   decryption_ongoing = false;
275   nrf_ccm_decrypt_rx(crc_error);
276 }
277