chacha.c
Go to the documentation of this file.
1 /**
2  * @file chacha.c
3  * @brief ChaCha encryption algorithm
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCRYPTO Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.2
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/crypto.h"
36 #include "cipher/chacha.h"
37 
38 //Check crypto library configuration
39 #if (CHACHA_SUPPORT == ENABLED)
40 
41 //ChaCha quarter-round function
42 #define QUARTER_ROUND(a, b, c, d) \
43 { \
44  a += b; \
45  d ^= a; \
46  d = ROL32(d, 16); \
47  c += d; \
48  b ^= c; \
49  b = ROL32(b, 12); \
50  a += b; \
51  d ^= a; \
52  d = ROL32(d, 8); \
53  c += d; \
54  b ^= c; \
55  b = ROL32(b, 7); \
56 }
57 
58 
59 /**
60  * @brief Initialize ChaCha context using the supplied key and nonce
61  * @param[in] context Pointer to the ChaCha context to initialize
62  * @param[in] nr Number of rounds to be applied (8, 12 or 20)
63  * @param[in] key Pointer to the key
64  * @param[in] keyLen Length of the key, in bytes (16 or 32)
65  * @param[in] nonce Pointer to the nonce
66  * @param[in] nonceLen Length of the nonce, in bytes (8 or 12)
67  * @return Error code
68  **/
69 
70 error_t chachaInit(ChachaContext *context, uint_t nr, const uint8_t *key,
71  size_t keyLen, const uint8_t *nonce, size_t nonceLen)
72 {
73  uint32_t *w;
74 
75  //Check parameters
76  if(context == NULL || key == NULL || nonce == NULL)
78 
79  //The number of rounds must be 8, 12 or 20
80  if(nr != 8 && nr != 12 && nr != 20)
82 
83  //Save the number of rounds to be applied
84  context->nr = nr;
85 
86  //Point to the state
87  w = context->state;
88 
89  //Check the length of the key
90  if(keyLen == 16)
91  {
92  //The first four input words are constants
93  w[0] = 0x61707865;
94  w[1] = 0x3120646E;
95  w[2] = 0x79622D36;
96  w[3] = 0x6B206574;
97 
98  //Input words 4 through 7 are taken from the 128-bit key, by reading
99  //the bytes in little-endian order, in 4-byte chunks
100  w[4] = LOAD32LE(key);
101  w[5] = LOAD32LE(key + 4);
102  w[6] = LOAD32LE(key + 8);
103  w[7] = LOAD32LE(key + 12);
104 
105  //Input words 8 through 11 are taken from the 128-bit key, again by
106  //reading the bytes in little-endian order, in 4-byte chunks
107  w[8] = LOAD32LE(key);
108  w[9] = LOAD32LE(key + 4);
109  w[10] = LOAD32LE(key + 8);
110  w[11] = LOAD32LE(key + 12);
111  }
112  else if(keyLen == 32)
113  {
114  //The first four input words are constants
115  w[0] = 0x61707865;
116  w[1] = 0x3320646E;
117  w[2] = 0x79622D32;
118  w[3] = 0x6B206574;
119 
120  //Input words 4 through 11 are taken from the 256-bit key, by reading
121  //the bytes in little-endian order, in 4-byte chunks
122  w[4] = LOAD32LE(key);
123  w[5] = LOAD32LE(key + 4);
124  w[6] = LOAD32LE(key + 8);
125  w[7] = LOAD32LE(key + 12);
126  w[8] = LOAD32LE(key + 16);
127  w[9] = LOAD32LE(key + 20);
128  w[10] = LOAD32LE(key + 24);
129  w[11] = LOAD32LE(key + 28);
130  }
131  else
132  {
133  //Invalid key length
135  }
136 
137  //Check the length of the nonce
138  if(nonceLen == 8)
139  {
140  //Input words 12 and 13 are a block counter, with word 12 overflowing
141  //into word 13
142  w[12] = 0;
143  w[13] = 0;
144 
145  //Input words 14 and 15 are taken from the 64-bit nonce, by reading the
146  //bytes in little-endian order, in 4-byte chunks
147  w[14] = LOAD32LE(nonce);
148  w[15] = LOAD32LE(nonce + 4);
149  }
150  else if(nonceLen == 12)
151  {
152  //Input word 12 is a block counter
153  w[12] = 0;
154 
155  //Input words 13 to 15 are taken from the 96-bit nonce, by reading
156  //the bytes in little-endian order, in 4-byte chunks
157  w[13] = LOAD32LE(nonce);
158  w[14] = LOAD32LE(nonce + 4);
159  w[15] = LOAD32LE(nonce + 8);
160  }
161  else if(nonceLen == 16)
162  {
163  //Input words 12 to 15 are taken from the 128-bit nonce, by reading
164  //the bytes in little-endian order, in 4-byte chunks
165  w[12] = LOAD32LE(nonce);
166  w[13] = LOAD32LE(nonce + 4);
167  w[14] = LOAD32LE(nonce + 8);
168  w[15] = LOAD32LE(nonce + 12);
169  }
170  else
171  {
172  //Invalid nonce length
174  }
175 
176  //The keystream block is empty
177  context->pos = 0;
178 
179  //Successful initialization
180  return NO_ERROR;
181 }
182 
183 
184 /**
185  * @brief Encrypt/decrypt data with the ChaCha algorithm
186  * @param[in] context Pointer to the ChaCha context
187  * @param[in] input Pointer to the data to encrypt/decrypt (optional)
188  * @param[in] output Pointer to the resulting data (optional)
189  * @param[in] length Number of bytes to be processed
190  **/
191 
192 void chachaCipher(ChachaContext *context, const uint8_t *input,
193  uint8_t *output, size_t length)
194 {
195  uint_t i;
196  uint_t n;
197  uint8_t *k;
198 
199  //Encryption loop
200  while(length > 0)
201  {
202  //Check whether a new keystream block must be generated
203  if(context->pos == 0 || context->pos >= 64)
204  {
205  //ChaCha successively calls the ChaCha block function, with the same key
206  //and nonce, and with successively increasing block counter parameters
207  chachaProcessBlock(context);
208 
209  //Increment block counter
210  context->state[12]++;
211 
212  //Propagate the carry if necessary
213  if(context->state[12] == 0)
214  {
215  context->state[13]++;
216  }
217 
218  //Rewind to the beginning of the keystream block
219  context->pos = 0;
220  }
221 
222  //Compute the number of bytes to encrypt/decrypt at a time
223  n = MIN(length, 64 - context->pos);
224 
225  //Valid output pointer?
226  if(output != NULL)
227  {
228  //Point to the keystream
229  k = context->keystream + context->pos;
230 
231  //Valid input pointer?
232  if(input != NULL)
233  {
234  //XOR the input data with the keystream
235  for(i = 0; i < n; i++)
236  {
237  output[i] = input[i] ^ k[i];
238  }
239 
240  //Advance input pointer
241  input += n;
242  }
243  else
244  {
245  //Output the keystream
246  for(i = 0; i < n; i++)
247  {
248  output[i] = k[i];
249  }
250  }
251 
252  //Advance output pointer
253  output += n;
254  }
255 
256  //Current position in the keystream block
257  context->pos += n;
258  //Remaining bytes to process
259  length -= n;
260  }
261 }
262 
263 
264 /**
265  * @brief Generate a keystream block
266  * @param[in] context Pointer to the ChaCha context
267  **/
268 
270 {
271  uint_t i;
272  uint32_t w[16];
273 
274  //Copy the state to the working state
275  for(i = 0; i < 16; i++)
276  {
277  w[i] = context->state[i];
278  }
279 
280  //ChaCha runs 8, 12 or 20 rounds, alternating between column rounds and
281  //diagonal rounds
282  for(i = 0; i < context->nr; i += 2)
283  {
284  //The column rounds apply the quarter-round function to the four
285  //columns, from left to right
286  QUARTER_ROUND(w[0], w[4], w[8], w[12]);
287  QUARTER_ROUND(w[1], w[5], w[9], w[13]);
288  QUARTER_ROUND(w[2], w[6], w[10], w[14]);
289  QUARTER_ROUND(w[3], w[7], w[11], w[15]);
290 
291  //The diagonal rounds apply the quarter-round function to the top-left,
292  //bottom-right diagonal, followed by the pattern shifted one place to
293  //the right, for three more quarter-rounds
294  QUARTER_ROUND(w[0], w[5], w[10], w[15]);
295  QUARTER_ROUND(w[1], w[6], w[11], w[12]);
296  QUARTER_ROUND(w[2], w[7], w[8], w[13]);
297  QUARTER_ROUND(w[3], w[4], w[9], w[14]);
298  }
299 
300  //Add the original input words to the output words
301  for(i = 0; i < 16; i++)
302  {
303  w[i] += context->state[i];
304  }
305 
306  //Serialize the result by sequencing the words one-by-one in little-endian
307  //order
308  for(i = 0; i < 16; i++)
309  {
310  STORE32LE(w[i], context->keystream + i * 4);
311  }
312 }
313 
314 
315 /**
316  * @brief Release ChaCha context
317  * @param[in] context Pointer to the ChaCha context
318  **/
319 
321 {
322  //Clear ChaCha context
323  osMemset(context, 0, sizeof(ChachaContext));
324 }
325 
326 #endif
#define STORE32LE(a, p)
Definition: cpu_endian.h:279
void chachaDeinit(ChachaContext *context)
Release ChaCha context.
Definition: chacha.c:320
uint8_t keystream[64]
Definition: chacha.h:51
size_t pos
Definition: chacha.h:52
void chachaCipher(ChachaContext *context, const uint8_t *input, uint8_t *output, size_t length)
Encrypt/decrypt data with the ChaCha algorithm.
Definition: chacha.c:192
uint_t nr
Definition: chacha.h:49
ChaCha encryption algorithm.
error_t chachaInit(ChachaContext *context, uint_t nr, const uint8_t *key, size_t keyLen, const uint8_t *nonce, size_t nonceLen)
Initialize ChaCha context using the supplied key and nonce.
Definition: chacha.c:70
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
error_t
Error codes.
Definition: error.h:43
void chachaProcessBlock(ChachaContext *context)
Generate a keystream block.
Definition: chacha.c:269
General definitions for cryptographic algorithms.
uint8_t length
Definition: tcp.h:375
#define MIN(a, b)
Definition: os_port.h:63
ChaCha algorithm context.
Definition: chacha.h:48
uint8_t n
uint32_t state[16]
Definition: chacha.h:50
#define QUARTER_ROUND(a, b, c, d)
Definition: chacha.c:42
#define LOAD32LE(p)
Definition: cpu_endian.h:203
unsigned int uint_t
Definition: compiler_port.h:57
#define osMemset(p, value, length)
Definition: os_port.h:138
uint8_t nonce[]
Definition: ntp_common.h:233
@ NO_ERROR
Success.
Definition: error.h:44