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  * http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52840.ps/ccm.html?cp=2_0_0_27#topic
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 
32 #include "NRF_AES_CCM.h"
33 #include <string.h>
34 #include <stdbool.h>
35 #include "time_machine_if.h"
36 #include "NRF_HW_model_top.h"
37 #include "NRF_PPI.h"
38 #include "irq_ctrl.h"
39 #include "irq_sources.h"
40 #include "bs_tracing.h"
41 #include "BLECrypt_if.h"
42 
43 NRF_CCM_Type NRF_CCM_regs;
44 static uint32_t CCM_INTEN; //interrupt enable
45 static bool decryption_ongoing;
46 
nrf_aes_ccm_init()47 void nrf_aes_ccm_init(){
48   memset(&NRF_CCM_regs, 0, sizeof(NRF_CCM_regs));
49   CCM_INTEN = 0;
50   decryption_ongoing = false;
51 }
52 
nrf_aes_ccm_clean_up()53 void nrf_aes_ccm_clean_up(){
54 
55 }
56 
57 
58 void nrf_ccm_TASK_CRYPT ();
59 static void signal_EVENTS_ENDCRYPT();
60 
61 #define IV_LEN      8
62 #define NONCE_LEN   13
63 
64 // Calculates CCM nonce.
nonce_calc(const uint8_t * iv,uint64_t packet_counter,uint8_t packet_direction,uint8_t * ccm_nonce)65 static void nonce_calc(
66     // Inputs
67     const uint8_t *iv,         // Initialization vector (IV_LEN bytes, little-endian)
68     uint64_t packet_counter,   // 39-bit packet count (in given direction, excl. retransmissions and empty packets) since start of encryption
69     uint8_t packet_direction,  // Direction of packet (1:master to slave, 0: slave to master)
70     // Outputs (the pointers themselves are inputs and must point to large enough areas)
71     uint8_t *ccm_nonce)        // Resulting nonce (NONCE_LEN bytes, little-endian)
72 {
73   int i;
74   // Copy 39-bit packet counter into first 5 bytes of nonce and set 40th bit depending on packet
75   // direction
76   for (i = 0; i < NONCE_LEN - IV_LEN - 1; i++)
77   {
78     ccm_nonce[i] = packet_counter & 0xFF;
79     packet_counter >>= 8;
80   }
81   ccm_nonce[i] = (packet_counter & 0x7F) | (packet_direction == 1 /*master to slave*/ ? 0x80 : 0);
82   // Copy initialization vector into remaining 8 bytes of nonce
83   memcpy(&ccm_nonce[NONCE_LEN - IV_LEN], iv, IV_LEN);
84 }
85 
86 
nrf_ccm_encrypt_tx()87 static void nrf_ccm_encrypt_tx() {
88   const uint8_t* cnfptr;
89   const uint8_t* sk;
90   const uint8_t* iv;
91   uint64_t tx_pkt_ctr;
92   uint8_t pkt_direction;
93   const uint8_t* inptr;
94   uint8_t* outptr;
95   int length;
96   uint8_t ccm_nonce[NONCE_LEN];
97 
98   cnfptr = (const uint8_t*)NRF_CCM_regs.CNFPTR;
99   sk = cnfptr;
100   tx_pkt_ctr = *(uint64_t*)(cnfptr + 16) & 0x7FFFFFFFFFULL;
101   iv = &cnfptr[25];
102 
103   inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
104   outptr =  (uint8_t*)NRF_CCM_regs.OUTPTR;
105 
106   length = inptr[1];
107   if (length > 0) {
108     length +=4;
109   }
110   outptr[0] = inptr[0];
111   outptr[1] = length;
112   pkt_direction = cnfptr[24] & 1;
113   /* Note that outptr[2] is reserverd for S1 in the HW (but unused) */
114 
115   nonce_calc(iv, tx_pkt_ctr, pkt_direction, ccm_nonce);
116 
117   BLECrypt_if_encrypt_packet(inptr[0], // First byte of packet header
118       (uint8_t*)&inptr[3],      // Packet payload to be encrypted
119       &outptr[3],  //encrypted payload (and MIC if generate_mic==1)
120       length,      //including MIC length if ( generate_mic == 1 ) ; [ just  the length in the output packet header ]
121       true, //we have MIC, or not
122       sk, // Session key (16 bytes, BIG-ENDIAN)
123       ccm_nonce
124   );
125 
126   signal_EVENTS_ENDCRYPT();
127 }
128 
nrf_ccm_decrypt_rx(bool crc_error)129 static void nrf_ccm_decrypt_rx(bool crc_error) {
130   const uint8_t* cnfptr;
131   const uint8_t* sk;
132   const uint8_t* iv;
133   uint64_t rx_pkt_ctr;
134   uint8_t pkt_direction;
135   const uint8_t* inptr;
136   uint8_t* outptr;
137   int length;
138   uint8_t mic_error;
139   uint8_t ccm_nonce[NONCE_LEN];
140 
141   if (crc_error) {
142     NRF_CCM_regs.MICSTATUS = 0;
143     signal_EVENTS_ENDCRYPT();
144     return;
145   }
146 
147   cnfptr = (const uint8_t*)NRF_CCM_regs.CNFPTR;
148   sk = cnfptr;
149   rx_pkt_ctr = *(uint64_t*)(cnfptr + 16) & 0x7FFFFFFFFFULL;
150   iv = &cnfptr[25];
151 
152   inptr = (const uint8_t*)NRF_CCM_regs.INPTR;
153   outptr = (uint8_t*)NRF_CCM_regs.OUTPTR;
154 
155   length = inptr[1];
156   if (length > 4) {
157     length -=4;
158   }
159   outptr[0] = inptr[0];
160   outptr[1] = length;
161   pkt_direction = cnfptr[24] & 1;
162   /* Note that outptr[2] is reserverd for S1 in the HW (but unused) */
163 
164   nonce_calc(iv, rx_pkt_ctr, pkt_direction, ccm_nonce);
165 
166   BLECrypt_if_decrypt_packet(inptr[0], // First byte of packet header
167       (uint8_t*)&inptr[3], //as received from the air (including a MIC if has_mic)
168       &outptr[3], //buffer for decrypted payload
169       inptr[1],   //including MIC lenght if (has_mic == 1) ; [ just  the length in the packet header ]
170       true, //we have MIC, or not
171       sk, // Session key (16 bytes, BIG-ENDIAN)
172       ccm_nonce,
173       &mic_error
174   );
175 
176   NRF_CCM_regs.MICSTATUS = !mic_error;
177 
178   signal_EVENTS_ENDCRYPT();
179 }
180 
signal_EVENTS_ENDKSGEN()181 static void signal_EVENTS_ENDKSGEN() {
182   NRF_CCM_regs.EVENTS_ENDKSGEN = 1;
183   nrf_ppi_event(CCM_EVENTS_ENDKSGEN);
184 
185   if (CCM_INTEN & CCM_INTENSET_ENDKSGEN_Msk) {
186     hw_irq_ctrl_set_irq(NRF5_IRQ_CCM_AAR_IRQn);
187   }
188 
189   if (NRF_CCM_regs.SHORTS & CCM_SHORTS_ENDKSGEN_CRYPT_Msk) {
190     nrf_ccm_TASK_CRYPT();
191   }
192 }
193 
signal_EVENTS_ENDCRYPT()194 static void signal_EVENTS_ENDCRYPT(){
195   NRF_CCM_regs.EVENTS_ENDCRYPT = 1;
196   nrf_ppi_event(CCM_EVENTS_ENDCRYPT);
197 
198   if (CCM_INTEN & CCM_INTENSET_ENDCRYPT_Msk) {
199     hw_irq_ctrl_set_irq(NRF5_IRQ_CCM_AAR_IRQn);
200   }
201 }
202 
203 /* static void signal_EVENTS_ERROR(){
204 	NRF_CCM_regs.EVENTS_ERROR = 1;
205 	NRF_PPI_Event(CCM_EVENTS_ERROR);
206 
207 	if (CCM_INTEN & CCM_INTENSET_ERROR_Msk) {
208 		hw_irq_ctrl_set_irq(NRF5_IRQ_CCM_AAR_IRQn);
209 	}
210 } */
211 
nrf_ccm_TASK_KSGEN()212 void nrf_ccm_TASK_KSGEN() {
213   if (NRF_CCM_regs.ENABLE != CCM_ENABLE_ENABLE_Enabled) {
214     return;
215   }
216   /* In this model we cheat and we do it instantly */
217   signal_EVENTS_ENDKSGEN();
218 }
219 
nrf_ccm_TASK_CRYPT()220 void nrf_ccm_TASK_CRYPT() {
221   if (NRF_CCM_regs.ENABLE != CCM_ENABLE_ENABLE_Enabled) {
222     return;
223   }
224 
225   if ((NRF_CCM_regs.MODE & CCM_MODE_MODE_Msk) >> CCM_MODE_MODE_Pos
226       == CCM_MODE_MODE_Encryption) {
227     nrf_ccm_encrypt_tx();
228   } else {
229     decryption_ongoing = true;
230   }
231 }
232 
nrf_ccm_TASK_STOP()233 void nrf_ccm_TASK_STOP() {
234   decryption_ongoing = false;
235 }
236 
nrf_ccm_TASK_RATEOVERRIDE()237 void nrf_ccm_TASK_RATEOVERRIDE() {
238   /* We ignore the RATEOVERRIDE task */
239   bs_trace_warning_line_time("%s ignored\n", __func__);
240 }
241 
242 
nrf_ccm_regw_sideeffects_INTENSET()243 void nrf_ccm_regw_sideeffects_INTENSET(){
244   if (NRF_CCM_regs.INTENSET) {
245     CCM_INTEN |= NRF_CCM_regs.INTENSET;
246     NRF_CCM_regs.INTENSET = CCM_INTEN;
247   }
248 }
249 
nrf_ccm_regw_sideeffects_INTENCLR()250 void nrf_ccm_regw_sideeffects_INTENCLR(){
251   if (NRF_CCM_regs.INTENCLR) {
252     CCM_INTEN  &= ~NRF_CCM_regs.INTENCLR;
253     NRF_CCM_regs.INTENSET = CCM_INTEN;
254     NRF_CCM_regs.INTENCLR = 0;
255   }
256 }
257 
nrf_ccm_regw_sideeffects_TASKS_KSGEN()258 void nrf_ccm_regw_sideeffects_TASKS_KSGEN(){
259   if (NRF_CCM_regs.TASKS_KSGEN) {
260     NRF_CCM_regs.TASKS_KSGEN = 0;
261     nrf_ccm_TASK_KSGEN();
262   }
263 }
264 
nrf_ccm_regw_sideeffects_TASKS_STOP()265 void nrf_ccm_regw_sideeffects_TASKS_STOP(){
266   if (NRF_CCM_regs.TASKS_STOP) {
267     NRF_CCM_regs.TASKS_STOP = 0;
268     nrf_ccm_TASK_STOP();
269     bs_trace_warning_line_time("CCM: TASK_STOP functionality is not implemented\n");
270   }
271 }
272 
nrf_ccm_radio_received_packet(bool crc_error)273 void nrf_ccm_radio_received_packet(bool crc_error) {
274   if (!decryption_ongoing) {
275     return;
276   }
277   decryption_ongoing = false;
278   nrf_ccm_decrypt_rx(crc_error);
279 }
280