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