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