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