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