1 /*
2  * Copyright (c) 2017 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * CCM — AES CCM mode encryption
9  * https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52833%2Fccm.html&cp=4_1_0_5_3
10  *
11  * Notes:
12  *
13  * 1. This model does not try to account for the delay the real CCM HW block has
14  *    Instead:
15  *    * The key stream generation will be instantaneous
16  *      (really nothing will be done in this step)
17  *    * In transmission mode the packet will be encrypted instantly
18  *      after triggering TASKS_CRYPT
19  *    * In reception TASKS_CRYPT will not do anything, instead a "cheat" call
20  *      from the RADIO at the end of the packet will trigger the whole
21  *      packet to be decoded (if the CRC was ok) instantaneously.
22  *      If the CRC failed, nothing will be done with the payload, but micerror
23  *      will be set
24  *    Therefore we ignore the RATE register, RATEOVERRIDE register and TASK
25  *
26  * 2. MAXPACKETSIZE and MODE.LENGTH are ignored. Packets are decrypted correctly
27  *    according to their length
28  *
29  * 3. TASKS_STOP is not really supported
30  *
31  * 4. TASK_RATEOVERRIDE and RATEOVERRIDE are not supported
32  */
33 
34 #include "NRF_AES_CCM.h"
35 #include <string.h>
36 #include <stdbool.h>
37 #include "time_machine_if.h"
38 #include "NRF_HW_model_top.h"
39 #include "NRF_PPI.h"
40 #include "irq_ctrl.h"
41 #include "bs_tracing.h"
42 #include "BLECrypt_if.h"
43 
44 NRF_CCM_Type NRF_CCM_regs;
45 static uint32_t CCM_INTEN; //interrupt enable
46 static bool decryption_ongoing;
47 
nrf_aes_ccm_init()48 void nrf_aes_ccm_init(){
49   memset(&NRF_CCM_regs, 0, sizeof(NRF_CCM_regs));
50   NRF_CCM_regs.MODE = 0x01;
51   NRF_CCM_regs.HEADERMASK= 0xE3;
52   NRF_CCM_regs.MAXPACKETSIZE = 0xFB;
53   CCM_INTEN = 0;
54   decryption_ongoing = false;
55 }
56 
nrf_aes_ccm_clean_up()57 void nrf_aes_ccm_clean_up(){
58 
59 }
60 
61 
62 void nrf_ccm_TASK_CRYPT ();
63 static void signal_EVENTS_ENDCRYPT();
64 
65 #define IV_LEN      8
66 #define NONCE_LEN   13
67 
68 // Calculates CCM nonce.
nonce_calc(const uint8_t * iv,uint64_t packet_counter,uint8_t packet_direction,uint8_t * ccm_nonce)69 static void nonce_calc(
70     // Inputs
71     const uint8_t *iv,         // Initialization vector (IV_LEN bytes, little-endian)
72     uint64_t packet_counter,   // 39-bit packet count (in given direction, excl. retransmissions and empty packets) since start of encryption
73     uint8_t packet_direction,  // Direction of packet (1:master to slave, 0: slave to master)
74     // Outputs (the pointers themselves are inputs and must point to large enough areas)
75     uint8_t *ccm_nonce)        // Resulting nonce (NONCE_LEN bytes, little-endian)
76 {
77   int i;
78   // Copy 39-bit packet counter into first 5 bytes of nonce and set 40th bit depending on packet
79   // direction
80   for (i = 0; i < NONCE_LEN - IV_LEN - 1; i++)
81   {
82     ccm_nonce[i] = packet_counter & 0xFF;
83     packet_counter >>= 8;
84   }
85   ccm_nonce[i] = (packet_counter & 0x7F) | (packet_direction == 1 /*master to slave*/ ? 0x80 : 0);
86   // Copy initialization vector into remaining 8 bytes of nonce
87   memcpy(&ccm_nonce[NONCE_LEN - IV_LEN], iv, IV_LEN);
88 }
89 
90 
nrf_ccm_encrypt_tx()91 static void nrf_ccm_encrypt_tx() {
92   const uint8_t* cnfptr;
93   const uint8_t* sk;
94   const uint8_t* iv;
95   uint64_t tx_pkt_ctr;
96   uint8_t pkt_direction;
97   const uint8_t* inptr;
98   uint8_t* outptr;
99   int length;
100   uint8_t ccm_nonce[NONCE_LEN];
101   uint8_t aad;
102 
103   cnfptr = (const uint8_t*)NRF_CCM_regs.CNFPTR;
104   sk = cnfptr;
105   tx_pkt_ctr = *(uint64_t*)(cnfptr + 16) & 0x7FFFFFFFFFULL;
106   iv = &cnfptr[25];
107 
108   inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
109   outptr =  (uint8_t*)NRF_CCM_regs.OUTPTR;
110 
111   length = inptr[1];
112   if (length > 0) {
113     length +=4;
114   }
115   outptr[0] = inptr[0];
116   outptr[1] = length;
117   pkt_direction = cnfptr[24] & 1;
118   /* Note that outptr[2] is reserved for S1 in the HW (but unused) */
119 
120   nonce_calc(iv, tx_pkt_ctr, pkt_direction, ccm_nonce);
121 
122   aad = inptr[0] & NRF_CCM_regs.HEADERMASK;
123 
124   BLECrypt_if_encrypt_packet(aad, //Additional Authentication data
125       (uint8_t*)&inptr[3],      // Packet payload to be encrypted
126       &outptr[3],  //encrypted payload (and MIC if generate_mic==1)
127       length,      //including MIC length if ( generate_mic == 1 ) ; [ just  the length in the output packet header ]
128       true, //we have MIC, or not
129       sk, // Session key (16 bytes, BIG-ENDIAN)
130       ccm_nonce
131   );
132 
133   signal_EVENTS_ENDCRYPT();
134 }
135 
nrf_ccm_decrypt_rx(bool crc_error)136 static void nrf_ccm_decrypt_rx(bool crc_error) {
137   const uint8_t* cnfptr;
138   const uint8_t* sk;
139   const uint8_t* iv;
140   uint64_t rx_pkt_ctr;
141   uint8_t pkt_direction;
142   const uint8_t* inptr;
143   uint8_t* outptr;
144   int length;
145   uint8_t mic_error;
146   uint8_t ccm_nonce[NONCE_LEN];
147   uint8_t aad;
148 
149   if (crc_error) {
150     NRF_CCM_regs.MICSTATUS = 0;
151     signal_EVENTS_ENDCRYPT();
152     return;
153   }
154 
155   cnfptr = (const uint8_t*)NRF_CCM_regs.CNFPTR;
156   sk = cnfptr;
157   rx_pkt_ctr = *(uint64_t*)(cnfptr + 16) & 0x7FFFFFFFFFULL;
158   iv = &cnfptr[25];
159 
160   inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
161   outptr = (uint8_t*)NRF_CCM_regs.OUTPTR;
162 
163   length = inptr[1];
164   if (length > 4) {
165     length -=4;
166   }
167   outptr[0] = inptr[0];
168   outptr[1] = length;
169   pkt_direction = cnfptr[24] & 1;
170   /* Note that outptr[2] is reserved for S1 in the HW (but unused) */
171 
172   nonce_calc(iv, rx_pkt_ctr, pkt_direction, ccm_nonce);
173 
174   aad = inptr[0] & NRF_CCM_regs.HEADERMASK;
175 
176   BLECrypt_if_decrypt_packet(aad, //Additional Authentication data
177       (uint8_t*)&inptr[3], //as received from the air (including a MIC if has_mic)
178       &outptr[3], //buffer for decrypted payload
179       inptr[1],   //including MIC lenght if (has_mic == 1) ; [ just  the length in the packet header ]
180       true, //we have MIC, or not
181       sk, // Session key (16 bytes, BIG-ENDIAN)
182       ccm_nonce,
183       &mic_error
184   );
185 
186   NRF_CCM_regs.MICSTATUS = !mic_error;
187 
188   signal_EVENTS_ENDCRYPT();
189 }
190 
signal_EVENTS_ENDKSGEN()191 static void signal_EVENTS_ENDKSGEN() {
192   NRF_CCM_regs.EVENTS_ENDKSGEN = 1;
193   nrf_ppi_event(CCM_EVENTS_ENDKSGEN);
194 
195   if (CCM_INTEN & CCM_INTENSET_ENDKSGEN_Msk) {
196     hw_irq_ctrl_set_irq(CCM_AAR_IRQn);
197   }
198 
199   if (NRF_CCM_regs.SHORTS & CCM_SHORTS_ENDKSGEN_CRYPT_Msk) {
200     nrf_ccm_TASK_CRYPT();
201   }
202 }
203 
signal_EVENTS_ENDCRYPT()204 static void signal_EVENTS_ENDCRYPT(){
205   NRF_CCM_regs.EVENTS_ENDCRYPT = 1;
206   nrf_ppi_event(CCM_EVENTS_ENDCRYPT);
207 
208   if (CCM_INTEN & CCM_INTENSET_ENDCRYPT_Msk) {
209     hw_irq_ctrl_set_irq(CCM_AAR_IRQn);
210   }
211 }
212 
213 /* static void signal_EVENTS_ERROR(){
214 	NRF_CCM_regs.EVENTS_ERROR = 1;
215 	NRF_PPI_Event(CCM_EVENTS_ERROR);
216 
217 	if (CCM_INTEN & CCM_INTENSET_ERROR_Msk) {
218 		hw_irq_ctrl_set_irq(NRF5_IRQ_CCM_AAR_IRQn);
219 	}
220 } */
221 
nrf_ccm_TASK_KSGEN()222 void nrf_ccm_TASK_KSGEN() {
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 
nrf_ccm_TASK_CRYPT()230 void nrf_ccm_TASK_CRYPT() {
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 
nrf_ccm_TASK_STOP()243 void nrf_ccm_TASK_STOP() {
244   decryption_ongoing = false;
245 }
246 
nrf_ccm_TASK_RATEOVERRIDE()247 void nrf_ccm_TASK_RATEOVERRIDE() {
248   /* We ignore the RATEOVERRIDE task */
249   bs_trace_warning_line_time("%s ignored\n", __func__);
250 }
251 
252 
nrf_ccm_regw_sideeffects_INTENSET()253 void nrf_ccm_regw_sideeffects_INTENSET(){
254   if (NRF_CCM_regs.INTENSET) {
255     CCM_INTEN |= NRF_CCM_regs.INTENSET;
256     NRF_CCM_regs.INTENSET = CCM_INTEN;
257   }
258 }
259 
nrf_ccm_regw_sideeffects_INTENCLR()260 void nrf_ccm_regw_sideeffects_INTENCLR(){
261   if (NRF_CCM_regs.INTENCLR) {
262     CCM_INTEN  &= ~NRF_CCM_regs.INTENCLR;
263     NRF_CCM_regs.INTENSET = CCM_INTEN;
264     NRF_CCM_regs.INTENCLR = 0;
265   }
266 }
267 
nrf_ccm_regw_sideeffects_TASKS_KSGEN()268 void nrf_ccm_regw_sideeffects_TASKS_KSGEN(){
269   if (NRF_CCM_regs.TASKS_KSGEN) {
270     NRF_CCM_regs.TASKS_KSGEN = 0;
271     nrf_ccm_TASK_KSGEN();
272   }
273 }
274 
nrf_ccm_regw_sideeffects_TASKS_CRYPT()275 void nrf_ccm_regw_sideeffects_TASKS_CRYPT(){
276   if (NRF_CCM_regs.TASKS_CRYPT) {
277     NRF_CCM_regs.TASKS_CRYPT = 0;
278     nrf_ccm_TASK_CRYPT();
279   }
280 }
281 
nrf_ccm_regw_sideeffects_TASKS_STOP()282 void nrf_ccm_regw_sideeffects_TASKS_STOP(){
283   if (NRF_CCM_regs.TASKS_STOP) {
284     NRF_CCM_regs.TASKS_STOP = 0;
285     nrf_ccm_TASK_STOP();
286     bs_trace_warning_line_time("CCM: TASK_STOP functionality is not implemented\n");
287   }
288 }
289 
nrf_ccm_radio_received_packet(bool crc_error)290 void nrf_ccm_radio_received_packet(bool crc_error) {
291   if (!decryption_ongoing) {
292     return;
293   }
294   decryption_ongoing = false;
295   nrf_ccm_decrypt_rx(crc_error);
296 }
297