Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
cx_chacha_poly.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#if defined(HAVE_CHACHA_POLY)
19#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
20#include "cx_chacha_poly.h"
21#include "cx_utils.h"
22#include "lcx_common.h"
23#include "os_utils.h"
24#include <stddef.h>
25#include <string.h>
26
27#define CHACHAPOLY_STATE_INIT 0
28#define CHACHAPOLY_STATE_AAD 1
29#define CHACHAPOLY_STATE_CIPHERTEXT 2 /* Encrypting or decrypting */
30#define CHACHAPOLY_STATE_FINISHED 3
31#define CHACHAPOLY_N_ROUNDS 20
32#define CHACHAPOLY_TAG_LEN 16
33
34#if defined(HAVE_AEAD)
35static const cx_aead_base_t cx_chachapoly_functions = {
36 (void (*)(void *ctx)) cx_chachapoly_init,
37 (cx_err_t(*)(void *ctx, const uint8_t *key, size_t key_len)) cx_chachapoly_set_key,
38 (cx_err_t(*)(void *ctx, uint32_t mode, const uint8_t *iv, size_t iv_len)) cx_chachapoly_start,
39 (cx_err_t(*)(void *ctx, const uint8_t *aad, size_t aad_len)) cx_chachapoly_update_aad,
40 (cx_err_t(*)(void *ctx, const uint8_t *input, uint8_t *output, size_t len))
41 cx_chachapoly_update,
42 (cx_err_t(*)(void *ctx, uint8_t *tag, size_t tag_len)) cx_chachapoly_finish,
43 (cx_err_t(*)(void *ctx,
44 const uint8_t *input,
45 size_t len,
46 const uint8_t *iv,
47 size_t iv_len,
48 const uint8_t *aad,
49 size_t aad_len,
50 uint8_t *output,
51 uint8_t *tag,
52 size_t tag_len)) cx_chachapoly_encrypt_and_tag,
53 (cx_err_t(*)(void *ctx,
54 const uint8_t *input,
55 size_t len,
56 const uint8_t *iv,
57 size_t iv_len,
58 const uint8_t *aad,
59 size_t aad_len,
60 uint8_t *output,
61 const uint8_t *tag,
62 size_t tag_len)) cx_chachapoly_decrypt_and_auth,
63 (cx_err_t(*)(void *ctx, const uint8_t *tag, size_t tag_len)) cx_chachapoly_check_tag};
64
65const cx_aead_info_t cx_chacha20_poly1305_info
66 = {CX_AEAD_CHACHA20_POLY1305, 256, 512, &cx_chachapoly_functions};
67#endif // HAVE_AEAD
68
69void cx_chachapoly_init(cx_chachapoly_context_t *ctx)
70{
71 cx_chacha_init(&ctx->chacha20_ctx, CHACHAPOLY_N_ROUNDS);
72 cx_poly1305_init(&ctx->poly1305_ctx);
73 ctx->aad_len = 0;
74 ctx->ciphertext_len = 0;
75 ctx->state = CHACHAPOLY_STATE_INIT;
76 ctx->mode = CX_ENCRYPT;
77}
78
79cx_err_t cx_chachapoly_set_key(cx_chachapoly_context_t *ctx, const uint8_t *key, size_t key_len)
80{
81 return cx_chacha_set_key(&ctx->chacha20_ctx, key, key_len);
82}
83
84cx_err_t cx_chachapoly_start(cx_chachapoly_context_t *ctx,
85 uint32_t mode,
86 const uint8_t *iv,
87 size_t iv_len)
88{
89 cx_err_t error;
90 uint8_t poly1305_key[64];
91
92 /* iv = 0 || nonce */
93 CX_CHECK(cx_chacha_start(&ctx->chacha20_ctx, iv, iv_len));
94
95 /* Generate the Poly1305 key by getting the ChaCha20 keystream output with
96 * counter = 0. This is the same as encrypting a buffer of zeroes.
97 * Only the first 256-bits (32 bytes) of the key is used for Poly1305.
98 * The other 256 bits are discarded.
99 */
100 memset(poly1305_key, 0, sizeof(poly1305_key));
101 CX_CHECK(
102 cx_chacha_update(&ctx->chacha20_ctx, poly1305_key, poly1305_key, sizeof(poly1305_key)));
103
104 cx_poly1305_set_key(&ctx->poly1305_ctx, poly1305_key);
105 ctx->aad_len = 0;
106 ctx->ciphertext_len = 0;
107 ctx->state = CHACHAPOLY_STATE_AAD;
108 ctx->mode = mode;
109
110end:
111 memset(poly1305_key, 0, 64);
112 return error;
113}
114
115cx_err_t cx_chachapoly_update_aad(cx_chachapoly_context_t *ctx, const uint8_t *aad, size_t aad_len)
116{
117 if (ctx->state != CHACHAPOLY_STATE_AAD) {
118 return CX_INTERNAL_ERROR;
119 }
120
121 ctx->aad_len += aad_len;
122 return cx_poly1305_update(&ctx->poly1305_ctx, aad, aad_len);
123}
124
125static cx_err_t cx_chachapoly_pad(cx_chachapoly_context_t *ctx, size_t in_to_pad_len)
126{
127 uint32_t pad_len = in_to_pad_len % 16;
128 unsigned char pad_bytes[15];
129
130 if (!pad_len) {
131 return CX_OK;
132 }
133
134 memset(pad_bytes, 0, sizeof(pad_bytes));
135 return cx_poly1305_update(&ctx->poly1305_ctx, pad_bytes, 16 - pad_len);
136}
137
138cx_err_t cx_chachapoly_update(cx_chachapoly_context_t *ctx,
139 const uint8_t *input,
140 uint8_t *output,
141 size_t len)
142{
143 cx_err_t error;
144
145 if ((ctx->state != CHACHAPOLY_STATE_AAD) && (ctx->state != CHACHAPOLY_STATE_CIPHERTEXT)) {
146 return CX_INTERNAL_ERROR;
147 }
148 if (CHACHAPOLY_STATE_AAD == ctx->state) {
149 ctx->state = CHACHAPOLY_STATE_CIPHERTEXT;
150 CX_CHECK(cx_chachapoly_pad(ctx, ctx->aad_len));
151 }
152 ctx->ciphertext_len += len;
153 if (CX_ENCRYPT == ctx->mode) {
154 CX_CHECK(cx_chacha_update(&ctx->chacha20_ctx, input, output, len));
155 CX_CHECK(cx_poly1305_update(&ctx->poly1305_ctx, output, len));
156 }
157 else { /* DECRYPT */
158 CX_CHECK(cx_poly1305_update(&ctx->poly1305_ctx, input, len));
159 CX_CHECK(cx_chacha_update(&ctx->chacha20_ctx, input, output, len));
160 }
161end:
162 return error;
163}
164
165cx_err_t cx_chachapoly_finish(cx_chachapoly_context_t *ctx, uint8_t *tag, size_t tag_len)
166{
167 cx_err_t error;
168 uint8_t len_block[16];
169
170 if (tag_len != CHACHAPOLY_TAG_LEN) {
171 return CX_INVALID_PARAMETER_VALUE;
172 }
173
174 if (CHACHAPOLY_STATE_INIT == ctx->state) {
175 return CX_INTERNAL_ERROR;
176 }
177 if (CHACHAPOLY_STATE_AAD == ctx->state) {
178 CX_CHECK(cx_chachapoly_pad(ctx, ctx->aad_len));
179 }
180 else if (CHACHAPOLY_STATE_CIPHERTEXT == ctx->state) {
181 CX_CHECK(cx_chachapoly_pad(ctx, ctx->ciphertext_len));
182 }
183 ctx->state = CHACHAPOLY_STATE_FINISHED;
184 /* The lengths of the AAD and ciphertext are processed by
185 * Poly1305 as the final 128-bit block, encoded as little-endian integers.
186 */
187 memset(len_block, 0, 16);
188 U4LE_ENCODE(len_block, 0, ctx->aad_len);
189 U4LE_ENCODE(len_block, 8, ctx->ciphertext_len);
190 CX_CHECK(cx_poly1305_update(&ctx->poly1305_ctx, len_block, 16));
191 CX_CHECK(cx_poly1305_finish(&ctx->poly1305_ctx, tag));
192
193end:
194 return error;
195}
196
197static cx_err_t cx_chachapoly_process_and_tag(cx_chachapoly_context_t *ctx,
198 uint32_t mode,
199 const uint8_t *input,
200 size_t len,
201 const uint8_t *iv,
202 size_t iv_len,
203 const uint8_t *aad,
204 size_t aad_len,
205 uint8_t *output,
206 uint8_t *tag,
207 size_t tag_len)
208{
209 cx_err_t error;
210 CX_CHECK(cx_chachapoly_start(ctx, mode, iv, iv_len));
211 CX_CHECK(cx_chachapoly_update_aad(ctx, aad, aad_len));
212 CX_CHECK(cx_chachapoly_update(ctx, input, output, len));
213 CX_CHECK(cx_chachapoly_finish(ctx, tag, tag_len));
214end:
215 return error;
216}
217
218cx_err_t cx_chachapoly_encrypt_and_tag(cx_chachapoly_context_t *ctx,
219 const uint8_t *input,
220 size_t len,
221 const uint8_t *iv,
222 size_t iv_len,
223 const uint8_t *aad,
224 size_t aad_len,
225 uint8_t *output,
226 uint8_t *tag,
227 size_t tag_len)
228{
229 return cx_chachapoly_process_and_tag(
230 ctx, CX_ENCRYPT, input, len, iv, iv_len, aad, aad_len, output, tag, tag_len);
231}
232
233cx_err_t cx_chachapoly_decrypt_and_auth(cx_chachapoly_context_t *ctx,
234 const uint8_t *input,
235 size_t len,
236 const uint8_t *iv,
237 size_t iv_len,
238 const uint8_t *aad,
239 size_t aad_len,
240 uint8_t *output,
241 const uint8_t *tag,
242 size_t tag_len)
243{
244 uint8_t check_tag[CHACHAPOLY_TAG_LEN];
245 uint8_t diff;
246 cx_err_t error;
247
248 memset(check_tag, 0, CHACHAPOLY_TAG_LEN);
249 CX_CHECK(cx_chachapoly_process_and_tag(
250 ctx, CX_DECRYPT, input, len, iv, iv_len, aad, aad_len, output, check_tag, tag_len));
251
252 diff = cx_constant_time_eq(tag, check_tag, CHACHAPOLY_TAG_LEN);
253 error = ((diff == 0) ? CX_OK : CX_INVALID_PARAMETER_VALUE);
254 if (error) {
255 memset(check_tag, 0, CHACHAPOLY_TAG_LEN);
256 }
257
258end:
259 return error;
260}
261
262cx_err_t cx_chachapoly_check_tag(cx_chachapoly_context_t *ctx, const uint8_t *tag, size_t tag_len)
263{
264 cx_err_t error;
265 uint8_t diff;
266 uint8_t check_tag[CHACHAPOLY_TAG_LEN];
267
268 CX_CHECK(cx_chachapoly_finish(ctx, check_tag, tag_len));
269
270 diff = cx_constant_time_eq(tag, check_tag, tag_len);
271 error = diff * CX_INVALID_PARAMETER_VALUE + (1 - diff) * CX_OK;
272end:
273 return error;
274}
275
276#endif // HAVE_CHACHA && HAVE_POLY1305
277#endif // HAVE_CHACHA_POLY
uint8_t cx_constant_time_eq(const uint8_t *buf1, uint8_t *buf2, size_t len)
Definition cx_utils.c:181
Cryptography flags.
#define CX_ENCRYPT
Definition lcx_common.h:126
#define CX_DECRYPT
Definition lcx_common.h:127
unsigned char uint8_t
Definition usbd_conf.h:53