1 /*
2 * Copyright (c) 2023, The TrustedFirmware-M Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "cc3xx_chacha.h"
9
10 #include "cc3xx_dev.h"
11 #include "cc3xx_engine_state.h"
12 #include "cc3xx_stdlib.h"
13
14 #include <string.h>
15 #include <assert.h>
16
17 struct cc3xx_chacha_state_t chacha_state;
18
set_iv(const uint32_t * iv)19 static void set_iv(const uint32_t *iv)
20 {
21 if (chacha_state.iv_is_96_bit) {
22 P_CC3XX->chacha.chacha_block_cnt_msb = iv[0];
23 iv += 1;
24 }
25 P_CC3XX->chacha.chacha_iv[0] = iv[0];
26 P_CC3XX->chacha.chacha_iv[1] = iv[1];
27 }
28
get_iv(uint32_t * iv)29 static void get_iv(uint32_t *iv)
30 {
31 if (chacha_state.iv_is_96_bit) {
32 iv[0] = P_CC3XX->chacha.chacha_block_cnt_msb;
33 iv += 1;
34 }
35 iv[0] = P_CC3XX->chacha.chacha_iv[0];
36 iv[1] = P_CC3XX->chacha.chacha_iv[1];
37 }
38
set_ctr(const uint64_t ctr)39 static void set_ctr(const uint64_t ctr)
40 {
41 if (!chacha_state.iv_is_96_bit) {
42 P_CC3XX->chacha.chacha_block_cnt_msb = ctr >> 32;
43 }
44 P_CC3XX->chacha.chacha_block_cnt_lsb = (uint32_t)ctr;
45 }
46
get_ctr(void)47 static uint64_t get_ctr(void)
48 {
49 uint64_t ctr = 0;
50
51 if (!chacha_state.iv_is_96_bit) {
52 ctr = (uint64_t)P_CC3XX->chacha.chacha_block_cnt_msb << 32;
53 }
54 ctr |= P_CC3XX->chacha.chacha_block_cnt_lsb;
55
56 return ctr;
57 }
58
set_key(const uint32_t * key)59 static inline void set_key(const uint32_t *key)
60 {
61 size_t idx;
62
63 #ifdef CC3XX_CONFIG_DPA_MITIGATIONS_ENABLE
64 (void)idx;
65
66 cc3xx_dpa_hardened_word_copy(P_CC3XX->chacha.chacha_key, key, 7);
67 P_CC3XX->chacha.chacha_key[7] = key[7];
68 #else
69 for (idx = 0; idx < 8; idx++) {
70 P_CC3XX->chacha.chacha_key[idx] = key[idx];
71 }
72 #endif /* CC3XX_CONFIG_DPA_MITIGATIONS_ENABLE */
73 }
74
75 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
reset_current_output_size(void)76 static void reset_current_output_size(void)
77 {
78 dma_state.current_bytes_output = 0;
79 }
80 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
81
chacha_init_from_state(void)82 static void chacha_init_from_state(void)
83 {
84 P_CC3XX->misc.chacha_clk_enable = 0x1U;
85
86 cc3xx_lowlevel_set_engine(CC3XX_ENGINE_CHACHA);
87
88 /* This is the block size */
89 cc3xx_lowlevel_dma_set_buffer_size(CC3XX_CHACHA_BLOCK_SIZE);
90
91 set_key(chacha_state.key);
92
93 P_CC3XX->chacha.chacha_control_reg = 0x0U;
94
95 while(P_CC3XX->chacha.chacha_busy) {}
96
97 if (chacha_state.iv_is_96_bit) {
98 /* 96 bit IVs use a special case */
99 P_CC3XX->chacha.chacha_control_reg |= 0b1 << 10;
100 }
101
102 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
103 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
104 /* Set to output the poly1305 key */
105 P_CC3XX->chacha.chacha_control_reg |= (chacha_state.mode & 0b1) << 2;
106 }
107 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
108 }
109
cc3xx_lowlevel_chacha20_init(cc3xx_chacha_direction_t direction,cc3xx_chacha_mode_t mode,const uint32_t * key,uint64_t initial_counter,const uint32_t * iv,size_t iv_len)110 cc3xx_err_t cc3xx_lowlevel_chacha20_init(
111 cc3xx_chacha_direction_t direction,
112 cc3xx_chacha_mode_t mode,
113 const uint32_t *key,
114 uint64_t initial_counter,
115 const uint32_t *iv, size_t iv_len)
116 {
117 uint32_t idx;
118 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
119 uint32_t poly_key[CC3XX_CHACHA_BLOCK_SIZE / sizeof(uint32_t)] = {0};
120
121 if (mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
122 assert(initial_counter == 0);
123 }
124 #else
125 assert(mode == CC3XX_CHACHA_MODE_CHACHA);
126 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
127
128 cc3xx_lowlevel_chacha20_uninit();
129
130 chacha_state.direction = direction;
131 chacha_state.mode = mode;
132
133 if (iv_len == 12) {
134 chacha_state.iv_is_96_bit = true;
135 }
136
137 #ifdef CC3XX_CONFIG_DPA_MITIGATIONS_ENABLE
138 (void)idx;
139
140 cc3xx_dpa_hardened_word_copy(chacha_state.key, key, 8);
141 #else
142 for (idx = 0; idx < 8; idx++) {
143 chacha_state.key[idx] = key[idx];
144 }
145 #endif /* CC3XX_CONFIG_DPA_MITIGATIONS_ENABLE */
146
147 chacha_init_from_state();
148
149 set_iv(iv);
150 set_ctr(initial_counter);
151
152 /* Init new message from host. This must be the last thing done before data
153 * is input.
154 */
155 P_CC3XX->chacha.chacha_control_reg |= 0b1 << 1;
156
157 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
158 /* Calculate the poly1305 key by inputting an all-zero block. This uses
159 * counter value 0, leaving the encryption to start on counter value 1.
160 */
161 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
162 cc3xx_lowlevel_dma_set_output(poly_key, sizeof(poly_key));
163 cc3xx_lowlevel_dma_buffered_input_data(poly_key, sizeof(poly_key), true);
164 cc3xx_lowlevel_dma_flush_buffer(true);
165
166 /* This is a preparatory operation so no need to count it in the output size */
167 reset_current_output_size();
168
169 cc3xx_lowlevel_poly1305_init(poly_key,
170 poly_key + (POLY1305_KEY_SIZE / sizeof(uint32_t)));
171 }
172 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
173
174 return CC3XX_ERR_SUCCESS;
175 }
176
cc3xx_lowlevel_chacha20_get_state(struct cc3xx_chacha_state_t * state)177 void cc3xx_lowlevel_chacha20_get_state(struct cc3xx_chacha_state_t *state)
178 {
179 memcpy(state, &chacha_state, sizeof(struct cc3xx_chacha_state_t));
180 memcpy(&state->dma_state, &dma_state, sizeof(dma_state));
181
182 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
183 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
184 cc3xx_lowlevel_poly1305_get_state(&state->poly_state);
185 }
186 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
187
188 get_iv(state->iv);
189 state->counter = get_ctr();
190 }
191
cc3xx_lowlevel_chacha20_set_state(const struct cc3xx_chacha_state_t * state)192 cc3xx_err_t cc3xx_lowlevel_chacha20_set_state(const struct cc3xx_chacha_state_t *state)
193 {
194 memcpy(&chacha_state, state, sizeof(struct cc3xx_chacha_state_t));
195 memcpy(&dma_state, &state->dma_state, sizeof(dma_state));
196
197 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
198 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
199 cc3xx_lowlevel_poly1305_set_state(&state->poly_state);
200 }
201 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
202
203 chacha_init_from_state();
204
205 set_iv(state->iv);
206 set_ctr(state->counter);
207
208 /* Init new message from host. This must be the last thing done before data
209 * is input.
210 */
211 P_CC3XX->chacha.chacha_control_reg |= 0b1 << 1;
212 }
213
cc3xx_lowlevel_chacha20_get_current_output_size(void)214 size_t cc3xx_lowlevel_chacha20_get_current_output_size(void)
215 {
216 return dma_state.current_bytes_output;
217 }
218
cc3xx_lowlevel_chacha20_set_output_buffer(uint8_t * out,size_t out_len)219 void cc3xx_lowlevel_chacha20_set_output_buffer(uint8_t *out, size_t out_len)
220 {
221 cc3xx_lowlevel_dma_set_output(out, out_len);
222 }
223
cc3xx_lowlevel_chacha20_update_authed_data(const uint8_t * in,size_t in_len)224 void cc3xx_lowlevel_chacha20_update_authed_data(const uint8_t* in, size_t in_len)
225 {
226 if (in_len == 0) {
227 return;
228 }
229
230 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
231 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
232 chacha_state.authed_len += in_len;
233 cc3xx_lowlevel_poly1305_update(in, in_len);
234 }
235 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
236 }
237
cc3xx_lowlevel_chacha20_update(const uint8_t * in,size_t in_len)238 cc3xx_err_t cc3xx_lowlevel_chacha20_update(const uint8_t* in, size_t in_len)
239 {
240 cc3xx_err_t err;
241 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
242 size_t block_buf_size_before_update = dma_state.block_buf_size_in_use;
243 size_t bytes_outputted_from_dma;
244 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
245
246 if (in_len == 0) {
247 return CC3XX_ERR_SUCCESS;
248 }
249
250 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
251 /* Input into poly1305 before into the DMA, in case we're doing an in-place
252 * operation.
253 */
254 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
255 const uint8_t zero_block[POLY1305_BLOCK_SIZE] = {0};
256 if (chacha_state.crypted_len == 0) {
257 /* Pad poly1305 between AAD and plaintext */
258 cc3xx_lowlevel_poly1305_update(zero_block, POLY1305_BLOCK_SIZE -
259 (chacha_state.authed_len %
260 POLY1305_BLOCK_SIZE));
261 }
262
263 chacha_state.crypted_len += in_len;
264
265 /* If we're decrypting, then input the input data to poly1305. If we're
266 * encrypting then it's trickier as we have to deal with the DMA
267 * buffering, so is done elsewhere.
268 */
269 if (chacha_state.direction == CC3XX_CHACHA_DIRECTION_DECRYPT) {
270 cc3xx_lowlevel_poly1305_update(in, in_len);
271 }
272 }
273 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
274
275 err = cc3xx_lowlevel_dma_buffered_input_data(in, in_len, true);
276 if (err != CC3XX_ERR_SUCCESS) {
277 return err;
278 }
279
280 /* Flush the block straight away if we have enough data */
281 if (dma_state.block_buf_size_in_use == CC3XX_CHACHA_BLOCK_SIZE) {
282 cc3xx_lowlevel_dma_flush_buffer(false);
283 }
284
285 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
286 /* If we're encrypting, we now need to input any blocks that got outputted
287 * in this operation into poly1305.
288 */
289 bytes_outputted_from_dma = (block_buf_size_before_update + in_len)
290 / CC3XX_CHACHA_BLOCK_SIZE * CC3XX_CHACHA_BLOCK_SIZE;
291
292 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305 &&
293 chacha_state.direction == CC3XX_CHACHA_DIRECTION_ENCRYPT &&
294 bytes_outputted_from_dma > 0) {
295 cc3xx_lowlevel_poly1305_update((uint8_t *)(dma_state.output_addr - bytes_outputted_from_dma),
296 bytes_outputted_from_dma);
297 }
298 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
299
300 return err;
301 }
302
303 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
tag_cmp_or_copy(uint32_t * tag,uint32_t * calculated_tag)304 static cc3xx_err_t tag_cmp_or_copy(uint32_t *tag, uint32_t *calculated_tag)
305 {
306 uint32_t idx;
307 uint32_t tag_word_size = POLY1305_TAG_LEN / sizeof(uint32_t);
308 uint8_t permutation_buf[tag_word_size];
309 bool are_different = 0;
310
311 if (chacha_state.direction == CC3XX_CHACHA_DIRECTION_DECRYPT) {
312 cc3xx_random_permutation_generate(permutation_buf, tag_word_size);
313
314 for (idx = 0; idx < tag_word_size; idx++) {
315 are_different |= tag[permutation_buf[idx]] ^ calculated_tag[permutation_buf[idx]];
316 }
317 } else {
318 #ifdef CC3XX_CONFIG_DPA_MITIGATIONS_ENABLE
319 cc3xx_dpa_hardened_word_copy(tag, calculated_tag, tag_word_size);
320 #else
321 memcpy(tag, calculated_tag, POLY1305_TAG_LEN);
322 #endif
323 }
324
325 if (are_different) {
326 return CC3XX_ERR_INVALID_TAG;
327 } else {
328 return CC3XX_ERR_SUCCESS;
329 }
330 }
331 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
332
cc3xx_lowlevel_chacha20_finish(uint32_t * tag,size_t * size)333 cc3xx_err_t cc3xx_lowlevel_chacha20_finish(uint32_t *tag, size_t *size)
334 {
335 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
336 uint64_t len_block[2] = {0};
337 uint32_t calculated_tag[POLY1305_TAG_LEN / sizeof(uint32_t)];
338 size_t pad_len;
339 uint32_t final_data_size = dma_state.block_buf_size_in_use;
340 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
341 cc3xx_err_t err;
342
343 /* Check alignment */
344 assert(((uintptr_t)tag & 0b11) == 0);
345
346 cc3xx_lowlevel_dma_flush_buffer(false);
347
348 if (size != NULL) {
349 *size = cc3xx_lowlevel_chacha20_get_current_output_size();
350 }
351
352 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
353 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
354 /* Finish inputting the last of the flushed blocks
355 */
356 if (chacha_state.direction == CC3XX_CHACHA_DIRECTION_ENCRYPT) {
357 cc3xx_lowlevel_poly1305_update((uint8_t *)(dma_state.output_addr - final_data_size),
358 final_data_size);
359 }
360
361 if (chacha_state.crypted_len == 0) {
362 pad_len = chacha_state.authed_len;
363 } else {
364 pad_len = chacha_state.crypted_len;
365 }
366 pad_len = POLY1305_BLOCK_SIZE - (pad_len % POLY1305_BLOCK_SIZE);
367
368 /* The len block starts zeroed, so use it for padding */
369 cc3xx_lowlevel_poly1305_update((uint8_t *)len_block, pad_len);
370 len_block[0] = chacha_state.authed_len;
371 len_block[1] = chacha_state.crypted_len;
372
373 cc3xx_lowlevel_poly1305_update((uint8_t *)len_block,
374 sizeof(len_block));
375 cc3xx_lowlevel_poly1305_finish(calculated_tag);
376
377 err = tag_cmp_or_copy(tag, calculated_tag);
378 } else
379 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
380 {
381 err = CC3XX_ERR_SUCCESS;
382 }
383
384 cc3xx_lowlevel_chacha20_uninit();
385
386 return err;
387 }
388
cc3xx_lowlevel_chacha20_uninit(void)389 void cc3xx_lowlevel_chacha20_uninit(void)
390 {
391 uint32_t zero_iv[3] = {0};
392 memset(&chacha_state, 0, sizeof(chacha_state));
393
394 #if defined(CC3XX_CONFIG_CHACHA_POLY1305_ENABLE)
395 if (chacha_state.mode == CC3XX_CHACHA_MODE_CHACHA_POLY1305) {
396 cc3xx_lowlevel_poly1305_uninit();
397 }
398 #endif /* CC3XX_CONFIG_CHACHA_POLY1305_ENABLE */
399
400 set_ctr(0);
401 set_iv(zero_iv);
402
403 P_CC3XX->chacha.chacha_control_reg = 0;
404 P_CC3XX->misc.chacha_clk_enable = 0;
405
406 cc3xx_lowlevel_dma_uninit();
407 }
408