1 /*
2  * Copyright (c) 2021-2023, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 
8 #include "cc3xx_internal_chacha20.h"
9 
10 #include "cc_pal_types.h"
11 #include "cc_pal_mem.h"
12 #include "cc_pal_abort.h"
13 
14 /** \file cc3xx_internal_chacha20.c
15  *
16  * This file contains the implementation of the internal functions to
17  * perform symmetric encryption and decryption using the Chacha20
18  * algorithm
19  *
20  */
21 
22 /* This function interacts directly with the low level driver module */
chacha_block(ChachaContext_t * ctx,const uint8_t * input,uint8_t * output,size_t size)23 static psa_status_t chacha_block(ChachaContext_t *ctx, const uint8_t *input,
24                                  uint8_t *output, size_t size)
25 {
26     drvError_t drvRc;
27     CCBuffInfo_t inBuffInfo;
28     CCBuffInfo_t outBuffInfo;
29 
30     drvRc = SetDataBuffersInfo(input, size, &inBuffInfo,
31                                output, size, &outBuffInfo);
32     if (drvRc != 0) {
33         CC_PAL_LOG_ERR("Bad I/O buffers");
34         return PSA_ERROR_INVALID_ARGUMENT;
35     }
36 
37     drvRc = ProcessChacha(ctx, &inBuffInfo, &outBuffInfo, size);
38     if (drvRc != 0) {
39         CC_PAL_LOG_ERR("cc3xx_chacha20_update failed: %d", drvRc);
40         return PSA_ERROR_INVALID_ARGUMENT;
41     }
42 
43     return PSA_SUCCESS;
44 }
45 
46 /** \defgroup internal_chacha20 Internal Chacha20 functions
47  *
48  *  Internal functions used by the driver to perform Chacha20 cipher encryption
49  *  and decryption
50  *
51  *  @{
52  */
cc3xx_chacha20_init(ChachaContext_t * ctx)53 void cc3xx_chacha20_init(ChachaContext_t *ctx)
54 {
55     if (NULL == ctx) {
56         CC_PAL_LOG_ERR("ctx cannot be NULL");
57         return;
58     }
59 
60     CC_PalMemSet(ctx, 0, sizeof(ChachaContext_t));
61 
62     ctx->inputDataAddrType  = DLLI_ADDR;
63     ctx->outputDataAddrType = DLLI_ADDR;
64 
65     /* valid range is [0; CHACHA_BLOCK_SIZE_BYTES), while
66      * CHACHA_BLOCK_SIZE_BYTES indicates empty state
67      */
68     ctx->state.keystream_start = CHACHA_BLOCK_SIZE_BYTES;
69 }
70 
cc3xx_chacha20_free(ChachaContext_t * ctx)71 void cc3xx_chacha20_free(ChachaContext_t *ctx)
72 {
73     if (NULL == ctx) {
74         CC_PAL_LOG_ERR("ctx cannot be NULL");
75         return;
76     }
77 
78     CC_PalMemSet(ctx, 0, sizeof(ChachaContext_t));
79 }
80 
cc3xx_chacha20_setkey(ChachaContext_t * ctx,const uint8_t * key,size_t key_size)81 psa_status_t cc3xx_chacha20_setkey(
82         ChachaContext_t *ctx,
83         const uint8_t *key,
84         size_t key_size)
85 {
86     if (ctx == NULL || key == NULL || key_size != CHACHA_256_BIT_KEY_SIZE) {
87         return PSA_ERROR_INVALID_ARGUMENT;
88     }
89 
90     CC_PalMemCopy(ctx->keyBuf, key, CHACHA_256_BIT_KEY_SIZE);
91 
92     return PSA_SUCCESS;
93 }
94 
cc3xx_chacha20_set_nonce(ChachaContext_t * ctx,const uint8_t * nonce,size_t nonce_size)95 psa_status_t cc3xx_chacha20_set_nonce(ChachaContext_t *ctx,
96                                       const uint8_t *nonce,
97                                       size_t nonce_size)
98 {
99     if (ctx == NULL || nonce == NULL || nonce_size != CHACHA_IV_96_SIZE_BYTES) {
100         return PSA_ERROR_INVALID_ARGUMENT;
101     }
102     ctx->nonceSize = NONCE_SIZE_96;
103     CC_PalMemCopy(ctx->nonceBuf, nonce, CHACHA_IV_96_SIZE_BYTES);
104 
105     return PSA_SUCCESS;
106 }
107 
cc3xx_chacha20_set_counter(ChachaContext_t * ctx,uint32_t counter)108 psa_status_t cc3xx_chacha20_set_counter(ChachaContext_t *ctx,
109                                         uint32_t counter)
110 {
111     if (ctx == NULL) {
112         return PSA_ERROR_INVALID_ARGUMENT;
113     }
114     ctx->blockCounterLsb = counter;
115     ctx->blockCounterMsb = 0;
116 
117     /* Calling set_counter has the effect of resetting any
118      * ongoing operation
119      */
120     ctx->state.keystream_start = CHACHA_BLOCK_SIZE_BYTES;
121 
122     return PSA_SUCCESS;
123 }
124 
cc3xx_chacha20_update(ChachaContext_t * ctx,const uint8_t * input,size_t input_len,uint8_t * output,size_t output_size,size_t * output_len)125 psa_status_t cc3xx_chacha20_update(
126         ChachaContext_t *ctx,
127         const uint8_t *input,
128         size_t input_len,
129         uint8_t *output,
130         size_t output_size,
131         size_t *output_len)
132 {
133     psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
134 
135     if ((ctx == NULL) || (input == NULL) || (output == NULL) || (output_len == NULL)) {
136         CC_PAL_LOG_ERR("Null pointer exception");
137         return PSA_ERROR_INVALID_ARGUMENT;
138     }
139     *output_len = 0;
140 
141     if (input_len == 0) {
142         return PSA_SUCCESS;
143     }
144 
145     if (output_size < input_len) {
146         return PSA_ERROR_BUFFER_TOO_SMALL;
147     }
148 
149     /* If there is some outstanding state we need to process that first */
150     uint8_t keystream_size =
151         (CHACHA_BLOCK_SIZE_BYTES - ctx->state.keystream_start) > 0 ?
152         (CHACHA_BLOCK_SIZE_BYTES - ctx->state.keystream_start) : 0;
153     size_t size_to_process = keystream_size < input_len ?
154                              keystream_size : input_len;
155     if (size_to_process) {
156         for (int i=0; i<size_to_process; i++) {
157             output[i] =
158                 ctx->state.keystream[i+ctx->state.keystream_start] ^ input[i];
159         }
160         ctx->state.keystream_start += size_to_process;
161         input_len -= size_to_process;
162         (*output_len) += size_to_process;
163 
164         if (input_len == 0) {
165             return PSA_SUCCESS;
166         }
167     }
168 
169     size_t process_len =
170         (input_len/CHACHA_BLOCK_SIZE_BYTES)*CHACHA_BLOCK_SIZE_BYTES;
171     if (process_len) {
172         status = chacha_block(ctx, &input[size_to_process],
173                               &output[size_to_process], process_len);
174         if (status != PSA_SUCCESS) {
175             *output_len = 0;
176             return status;
177         }
178         (*output_len) += process_len;
179     }
180 
181     /* Store the last part of the data that couldn't be processed as separate
182      * block
183      */
184     if (input_len - process_len) {
185         /* Generate a keystream block to keep as state */
186         uint8_t all_zero_input[CHACHA_BLOCK_SIZE_BYTES] = {0};
187         status = chacha_block(ctx, all_zero_input, ctx->state.keystream,
188                               CHACHA_BLOCK_SIZE_BYTES);
189         if (status != PSA_SUCCESS) {
190             *output_len = 0;
191             return status;
192         }
193 
194         for (int i=0; i<input_len-process_len; i++) {
195             output[i+size_to_process+process_len] =
196                 ctx->state.keystream[i] ^ input[i+size_to_process+process_len];
197         }
198 
199         (*output_len) += (input_len-process_len);
200 
201         /* Update the pointer to the start of the unprocessed keystream */
202         ctx->state.keystream_start = (input_len-process_len);
203     }
204 
205     return status;
206 }
207 
cc3xx_chacha20_finish(ChachaContext_t * ctx,uint8_t * output,size_t output_size,size_t * output_length)208 psa_status_t cc3xx_chacha20_finish(
209         ChachaContext_t *ctx,
210         uint8_t *output,
211         size_t output_size,
212         size_t *output_length)
213 {
214     (void)ctx;
215     (void)output;
216     (void)output_size;
217     if (output_length == NULL) {
218         return PSA_ERROR_INVALID_ARGUMENT;
219     }
220 
221     *output_length = 0;
222 
223     return PSA_SUCCESS;
224 }
225 /** @} */ // end of internal_chacha20
226