Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
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
36static const uint8_t constants[16] = "expand 32-byte k";
37
38/* Chacha-20 quarter round function */
39static 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
51static 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
76void 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
82cx_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
106cx_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
122cx_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
159cx_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
175end:
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
185static 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
244int 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:98
unsigned char uint8_t
Definition usbd_conf.h:53