Embedded SDK
Embedded SDK
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)
35 static 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 
65 const cx_aead_info_t cx_chacha20_poly1305_info
66  = {CX_AEAD_CHACHA20_POLY1305, 256, 512, &cx_chachapoly_functions};
67 #endif // HAVE_AEAD
68 
69 void 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 
79 cx_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 
84 cx_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 
110 end:
111  memset(poly1305_key, 0, 64);
112  return error;
113 }
114 
115 cx_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 
125 static 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 
138 cx_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  }
161 end:
162  return error;
163 }
164 
165 cx_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 
193 end:
194  return error;
195 }
196 
197 static 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));
214 end:
215  return error;
216 }
217 
218 cx_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 
233 cx_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 
258 end:
259  return error;
260 }
261 
262 cx_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;
272 end:
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