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-2024 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.4.0
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
141  //overflowing into word 13
142  w[12] = 0;
143  w[13] = 0;
144 
145  //Input words 14 and 15 are taken from an 64-bit nonce, by reading
146  //the 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 an 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
162  {
163  //Invalid nonce length
165  }
166 
167  //The keystream block is empty
168  context->pos = 0;
169 
170  //No error to report
171  return NO_ERROR;
172 }
173 
174 
175 /**
176  * @brief Encrypt/decrypt data with the ChaCha algorithm
177  * @param[in] context Pointer to the ChaCha context
178  * @param[in] input Pointer to the data to encrypt/decrypt (optional)
179  * @param[in] output Pointer to the resulting data (optional)
180  * @param[in] length Number of bytes to be processed
181  **/
182 
183 void chachaCipher(ChachaContext *context, const uint8_t *input,
184  uint8_t *output, size_t length)
185 {
186  uint_t i;
187  uint_t n;
188  uint8_t *k;
189 
190  //Encryption loop
191  while(length > 0)
192  {
193  //Check whether a new keystream block must be generated
194  if(context->pos == 0 || context->pos >= 64)
195  {
196  //ChaCha successively calls the ChaCha block function, with the same key
197  //and nonce, and with successively increasing block counter parameters
198  chachaProcessBlock(context);
199 
200  //Increment block counter
201  context->state[12]++;
202 
203  //Propagate the carry if necessary
204  if(context->state[12] == 0)
205  {
206  context->state[13]++;
207  }
208 
209  //Rewind to the beginning of the keystream block
210  context->pos = 0;
211  }
212 
213  //Compute the number of bytes to encrypt/decrypt at a time
214  n = MIN(length, 64 - context->pos);
215 
216  //Valid output pointer?
217  if(output != NULL)
218  {
219  //Point to the keystream
220  k = (uint8_t *) context->block + context->pos;
221 
222  //Valid input pointer?
223  if(input != NULL)
224  {
225  //XOR the input data with the keystream
226  for(i = 0; i < n; i++)
227  {
228  output[i] = input[i] ^ k[i];
229  }
230 
231  //Advance input pointer
232  input += n;
233  }
234  else
235  {
236  //Output the keystream
237  for(i = 0; i < n; i++)
238  {
239  output[i] = k[i];
240  }
241  }
242 
243  //Advance output pointer
244  output += n;
245  }
246 
247  //Current position in the keystream block
248  context->pos += n;
249  //Remaining bytes to process
250  length -= n;
251  }
252 }
253 
254 
255 /**
256  * @brief Generate a keystream block
257  * @param[in] context Pointer to the ChaCha context
258  **/
259 
261 {
262  uint_t i;
263  uint32_t *w;
264 
265  //Point to the working state
266  w = (uint32_t *) context->block;
267 
268  //Copy the state to the working state
269  for(i = 0; i < 16; i++)
270  {
271  w[i] = context->state[i];
272  }
273 
274  //ChaCha runs 8, 12 or 20 rounds, alternating between column rounds and
275  //diagonal rounds
276  for(i = 0; i < context->nr; i += 2)
277  {
278  //The column rounds apply the quarter-round function to the four
279  //columns, from left to right
280  QUARTER_ROUND(w[0], w[4], w[8], w[12]);
281  QUARTER_ROUND(w[1], w[5], w[9], w[13]);
282  QUARTER_ROUND(w[2], w[6], w[10], w[14]);
283  QUARTER_ROUND(w[3], w[7], w[11], w[15]);
284 
285  //The diagonal rounds apply the quarter-round function to the top-left,
286  //bottom-right diagonal, followed by the pattern shifted one place to
287  //the right, for three more quarter-rounds
288  QUARTER_ROUND(w[0], w[5], w[10], w[15]);
289  QUARTER_ROUND(w[1], w[6], w[11], w[12]);
290  QUARTER_ROUND(w[2], w[7], w[8], w[13]);
291  QUARTER_ROUND(w[3], w[4], w[9], w[14]);
292  }
293 
294  //Add the original input words to the output words
295  for(i = 0; i < 16; i++)
296  {
297  w[i] += context->state[i];
298  }
299 
300  //Serialize the result by sequencing the words one-by-one in little-endian
301  //order
302  for(i = 0; i < 16; i++)
303  {
304  w[i] = htole32(w[i]);
305  }
306 }
307 
308 
309 /**
310  * @brief Release ChaCha context
311  * @param[in] context Pointer to the ChaCha context
312  **/
313 
315 {
316  //Clear ChaCha context
317  osMemset(context, 0, sizeof(ChachaContext));
318 }
319 
320 #endif
void chachaDeinit(ChachaContext *context)
Release ChaCha context.
Definition: chacha.c:314
void chachaProcessBlock(ChachaContext *context)
Generate a keystream block.
Definition: chacha.c:260
void chachaCipher(ChachaContext *context, const uint8_t *input, uint8_t *output, size_t length)
Encrypt/decrypt data with the ChaCha algorithm.
Definition: chacha.c:183
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
#define QUARTER_ROUND(a, b, c, d)
Definition: chacha.c:42
ChaCha encryption algorithm.
unsigned int uint_t
Definition: compiler_port.h:50
#define htole32(value)
Definition: cpu_endian.h:430
#define LOAD32LE(p)
Definition: cpu_endian.h:203
General definitions for cryptographic algorithms.
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemset(p, value, length)
Definition: os_port.h:135
#define MIN(a, b)
Definition: os_port.h:63
ChaCha algorithm context.
Definition: chacha.h:48
uint32_t block[16]
Definition: chacha.h:51
uint32_t state[16]
Definition: chacha.h:50
size_t pos
Definition: chacha.h:52
uint_t nr
Definition: chacha.h:49
uint8_t length
Definition: tcp.h:368