Embedded SDK
Embedded SDK
cx_chacha.c
Go to the documentation of this file.
1 
2 /*******************************************************************************
3  * Ledger Nano S - Secure firmware
4  * (c) 2022 Ledger
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  ********************************************************************************/
18 
19 #if defined(HAVE_CHACHA)
20 
21 #include "lcx_chacha.h"
22 #include "cx_ram.h"
23 #include "cx_utils.h"
24 #include "os_math.h"
25 #include "os_utils.h"
26 #include <stddef.h>
27 #include <string.h>
28 
29 #define CHACHA_STATE_ARRAY_SIZE 16
30 #define CHACHA_BLOCK_SIZE 64
31 
32 #define ROTATE(v, n) (cx_rotl(v, n))
33 #define XOR(v, w) ((v) ^ (w))
34 #define PLUS(v, w) ((uint32_t) (v + w))
35 
36 static const uint8_t constants[16] = "expand 32-byte k";
37 
38 /* Chacha-20 quarter round function */
39 static void cx_chacha_quarter_round(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
40 {
41  *a = PLUS(*a, *b);
42  *d = ROTATE(XOR(*d, *a), 16);
43  *c = PLUS(*c, *d);
44  *b = ROTATE(XOR(*b, *c), 12);
45  *a = PLUS(*a, *b);
46  *d = ROTATE(XOR(*d, *a), 8);
47  *c = PLUS(*c, *d);
48  *b = ROTATE(XOR(*b, *c), 7);
49 }
50 
51 static void cx_chacha_process_block(cx_chacha_context_t *ctx)
52 {
53  unsigned int i;
54  uint32_t *x = (uint32_t *) ctx->block;
55  for (i = 0; i < CHACHA_STATE_ARRAY_SIZE; i++) {
56  x[i] = ctx->state[i];
57  }
58  for (i = 0; i < ctx->nrounds; i += 2) {
59  /* Column rounds : apply the quarter-round function to each column */
60  cx_chacha_quarter_round(&x[0], &x[4], &x[8], &x[12]);
61  cx_chacha_quarter_round(&x[1], &x[5], &x[9], &x[13]);
62  cx_chacha_quarter_round(&x[2], &x[6], &x[10], &x[14]);
63  cx_chacha_quarter_round(&x[3], &x[7], &x[11], &x[15]);
64 
65  /* Diagonal rounds : apply the quarter-round function to the 'diagonals' */
66  cx_chacha_quarter_round(&x[0], &x[5], &x[10], &x[15]);
67  cx_chacha_quarter_round(&x[1], &x[6], &x[11], &x[12]);
68  cx_chacha_quarter_round(&x[2], &x[7], &x[8], &x[13]);
69  cx_chacha_quarter_round(&x[3], &x[4], &x[9], &x[14]);
70  }
71  for (i = 0; i < CHACHA_STATE_ARRAY_SIZE; i++) {
72  x[i] = PLUS(x[i], ctx->state[i]);
73  }
74 }
75 
76 void cx_chacha_init(cx_chacha_context_t *ctx, uint32_t nrounds)
77 {
78  memset(ctx, 0, sizeof(cx_chacha_context_t));
79  ctx->nrounds = nrounds;
80 }
81 
82 cx_err_t cx_chacha_set_key(cx_chacha_context_t *ctx, const uint8_t *key, size_t key_len)
83 {
84  if (key_len != 32) {
85  return CX_INVALID_PARAMETER_VALUE;
86  }
87 
88  /* Initialize the state array */
89  ctx->state[4] = U4LE(key, 0);
90  ctx->state[5] = U4LE(key, 4);
91  ctx->state[6] = U4LE(key, 8);
92  ctx->state[7] = U4LE(key, 12);
93  ctx->state[8] = U4LE(key, 16);
94  ctx->state[9] = U4LE(key, 20);
95  ctx->state[10] = U4LE(key, 24);
96  ctx->state[11] = U4LE(key, 28);
97 
98  ctx->state[0] = U4LE(constants, 0);
99  ctx->state[1] = U4LE(constants, 4);
100  ctx->state[2] = U4LE(constants, 8);
101  ctx->state[3] = U4LE(constants, 12);
102 
103  return CX_OK;
104 }
105 
106 cx_err_t cx_chacha_start(cx_chacha_context_t *ctx, const uint8_t *iv, size_t iv_len)
107 {
108  if (iv_len != 16) {
109  return CX_INVALID_PARAMETER_VALUE;
110  }
111  /* Set the counter value */
112  ctx->state[12] = U4BE(iv, 0);
113  /* Set the nonce value */
114  ctx->state[13] = U4LE(iv, 4);
115  ctx->state[14] = U4LE(iv, 8);
116  ctx->state[15] = U4LE(iv, 12);
117  ctx->pos = 0;
118 
119  return CX_OK;
120 }
121 
122 cx_err_t cx_chacha_update(cx_chacha_context_t *ctx,
123  const uint8_t *input,
124  uint8_t *output,
125  size_t len)
126 {
127  unsigned int i, n;
128  uint8_t *key_stream;
129 
130  while (len > 0) {
131  if ((0 == ctx->pos) || (ctx->pos >= 64)) {
132  cx_chacha_process_block(ctx);
133  ctx->state[12]++;
134  if (0 == ctx->state[12]) {
135  ctx->state[13]++;
136  }
137  ctx->pos = 0;
138  }
139  n = MIN(len, CHACHA_BLOCK_SIZE - ctx->pos);
140  key_stream = (uint8_t *) ctx->block + ctx->pos;
141  if (NULL == input) {
142  for (i = 0; i < n; i++) {
143  output[i] = key_stream[i];
144  }
145  }
146  else {
147  for (i = 0; i < n; i++) {
148  output[i] = input[i] ^ key_stream[i];
149  }
150  }
151  input += n;
152  output += n;
153  ctx->pos += n;
154  len -= n;
155  }
156  return CX_OK;
157 }
158 
159 cx_err_t cx_chacha_cipher(uint32_t nrounds,
160  const uint8_t *key,
161  size_t key_len,
162  const uint8_t *iv,
163  size_t iv_len,
164  const uint8_t *input,
165  uint8_t *output,
166  size_t len)
167 {
168  cx_chacha_context_t *ctx = &G_cx.chacha;
169  cx_err_t error;
170  cx_chacha_init(ctx, nrounds);
171  CX_CHECK(cx_chacha_set_key(ctx, key, key_len));
172  CX_CHECK(cx_chacha_start(ctx, iv, iv_len));
173  CX_CHECK(cx_chacha_update(ctx, input, output, len));
174 
175 end:
176  explicit_bzero(ctx, sizeof(cx_chacha_context_t));
177  return error;
178 }
179 #endif // HAVE_CHACHA
180 
181 #ifdef UNIT_TESTING
182 #include <stdarg.h>
183 #include <cmocka.h>
184 
185 static void test_chacha_round(void **state)
186 {
187  (void) state;
188 
189  uint32_t state_array[16] = {0x61707865,
190  0x3320646e,
191  0x79622d32,
192  0x6b206574,
193  0x03020100,
194  0x07060504,
195  0x0b0a0908,
196  0x0f0e0d0c,
197  0x13121110,
198  0x17161514,
199  0x1b1a1918,
200  0x1f1e1d1c,
201  0x00000001,
202  0x09000000,
203  0x4a000000,
204  0x00000000};
205  uint32_t expected_array[16] = {0x837778ab,
206  0xe238d763,
207  0xa67ae21e,
208  0x5950bb2f,
209  0xc4f2d0c7,
210  0xfc62bb2f,
211  0x8fa018fc,
212  0x3f5ec7b7,
213  0x335271c2,
214  0xf29489f3,
215  0xeabda8fc,
216  0x82e46ebd,
217  0xd19c12b4,
218  0xb04e16de,
219  0x9e83d0cb,
220  0x4e3c50a2};
221  for (int i = 0; i < 10; i++) {
222  cx_chacha_quarter_round(
223  &state_array[0], &state_array[4], &state_array[8], &state_array[12]);
224  cx_chacha_quarter_round(
225  &state_array[1], &state_array[5], &state_array[9], &state_array[13]);
226  cx_chacha_quarter_round(
227  &state_array[2], &state_array[6], &state_array[10], &state_array[14]);
228  cx_chacha_quarter_round(
229  &state_array[3], &state_array[7], &state_array[11], &state_array[15]);
230 
231  cx_chacha_quarter_round(
232  &state_array[0], &state_array[5], &state_array[10], &state_array[15]);
233  cx_chacha_quarter_round(
234  &state_array[1], &state_array[6], &state_array[11], &state_array[12]);
235  cx_chacha_quarter_round(
236  &state_array[2], &state_array[7], &state_array[8], &state_array[13]);
237  cx_chacha_quarter_round(
238  &state_array[3], &state_array[4], &state_array[9], &state_array[14]);
239  }
240 
241  assert_memory_equal(state_array, expected_array, sizeof(state_array));
242 }
243 
244 int main(void)
245 {
246  const struct CMUnitTest tests[] = {cmocka_unit_test(test_chacha_round)};
247 
248  return cmocka_run_group_tests(tests, NULL, NULL);
249 }
250 #endif // UNIT_TESTING
union cx_u G_cx
Definition: cx_ram.c:21
Chacha cipher.
#define MIN(x, y)
Definition: nbgl_types.h:79
unsigned char uint8_t
Definition: usbd_conf.h:53