Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
cx_ecdsa.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#ifdef HAVE_ECDSA
20
21#include "cx_rng.h"
22#include "cx_rng_rfc6979.h"
23#include "cx_ecfp.h"
24#include "cx_ecdsa.h"
25#include "cx_utils.h"
26#include "cx_ram.h"
27#include "os_math.h"
28
29#include <string.h>
30
31static cx_err_t truernd(uint8_t *rnd, cx_curve_t cv)
32{
33 cx_bn_t r, t, n;
34 uint8_t domain_order[CX_ECDSA_MAX_ORDER_LEN];
35 size_t domain_length;
36 cx_err_t error;
37
38 CX_CHECK(cx_ecdomain_parameters_length(cv, &domain_length));
39 CX_CHECK(cx_ecdomain_parameter(cv, CX_CURVE_PARAM_Order, domain_order, sizeof(domain_order)));
40
41 CX_CHECK(cx_bn_lock(domain_length, 0));
42 CX_CHECK(cx_bn_alloc(&r, domain_length));
43 CX_CHECK(cx_bn_alloc(&t, 2 * domain_length));
44 CX_CHECK(cx_bn_alloc_init(&n, domain_length, domain_order, domain_length));
45 CX_CHECK(cx_bn_rand(t));
46 CX_CHECK(cx_bn_reduce(r, t, n));
47 CX_CHECK(cx_bn_export(r, rnd, domain_length));
48end:
49 cx_bn_unlock();
50 return error;
51}
52
53static cx_err_t rfcrnd(uint8_t *rnd,
54 uint32_t skip,
55 cx_curve_t cv,
56 const cx_ecfp_private_key_t *key,
57 cx_md_t hashID,
58 const uint8_t *hash,
59 size_t hash_len)
60{
61 uint8_t domain_order[CX_ECDSA_MAX_ORDER_LEN];
62 size_t domain_length;
63 cx_err_t error;
64
65 CX_CHECK(cx_ecdomain_parameters_length(cv, &domain_length));
66 CX_CHECK(cx_ecdomain_parameter(cv, CX_CURVE_PARAM_Order, domain_order, sizeof(domain_order)));
67
68 CX_CHECK(cx_rng_rfc6979_init(&G_cx.rfc6979,
69 hashID,
70 key->d,
71 key->d_len,
72 hash,
73 hash_len,
74 domain_order,
75 domain_length /*, NULL, 0*/));
76 skip++;
77 for (uint32_t i = 0; i < skip; i++) {
78 CX_CHECK(cx_rng_rfc6979_next(&G_cx.rfc6979, rnd, domain_length));
79 }
80
81end:
82 return error;
83}
84
85// This function is used to convert the incoming hash into its bn
86// representation, depending on its length compared to the domain's
87// associated lengths.
88// The v parameter must already point to an initialized RAM area, of length
89// domain_length.
90static cx_err_t initialize_hash(const uint8_t *hash,
91 cx_bn_t v,
92 size_t domain_bit_length,
93 size_t hash_length,
94 size_t domain_length)
95{
96 cx_err_t error;
97
98 // If the hash length is greater than the domain length, we only
99 // consider the 'domain bit length' leftmost bits of the hash
100 // for the operation.
101 CX_CHECK(cx_bn_init(v, hash, MIN(hash_length, domain_length)));
102
103 if (domain_bit_length < hash_length * 8) {
104 // We shift the hash if necessary.
105 if (domain_bit_length % 8) {
106 CX_CHECK(cx_bn_shr(v, 8 - (domain_bit_length % 8)));
107 }
108 }
109
110end:
111 return error;
112}
113
114/* ----------------------------------------------------------------------- */
115/* */
116/* ----------------------------------------------------------------------- */
117cx_err_t cx_ecdsa_sign_no_throw(const cx_ecfp_private_key_t *key,
118 uint32_t mode,
119 cx_md_t hashID,
120 const uint8_t *hash,
121 size_t hash_len,
122 uint8_t *sig,
123 size_t *sig_len,
124 uint32_t *info)
125{
126#ifndef HAVE_RNG_RFC6979
127 (void) hashID;
128#endif
129
130#define CX_MAX_TRIES 100
131
132 cx_err_t error;
133 uint32_t volatile out_sig_len = 0;
134 size_t domain_length;
135 size_t domain_bit_length;
136 cx_bn_t r, s;
137 int diff;
138 union {
139 struct {
140 uint32_t _tries;
141 uint8_t _rnd[CX_ECDSA_MAX_ORDER_LEN];
142 cx_ecpoint_t _Q;
143 cx_bn_t _n, _t, _t1, _t2, _v;
144 };
145 uint8_t _rs[CX_ECDSA_MAX_ORDER_LEN * 2];
146 } u;
147#define tries u._tries
148#define rnd u._rnd
149#define Q u._Q
150#define n u._n
151#define t u._t
152#define t1 u._t1
153#define t2 u._t2
154#define v u._v
155#define rs u._rs
156
157 // get dom
158 CX_CHECK(cx_ecdomain_parameters_length(key->curve, &domain_length));
159 CX_CHECK(cx_ecdomain_size(key->curve, &domain_bit_length));
160
161 if (!CX_CURVE_RANGE(key->curve, WEIERSTRASS) || *sig_len < 6 + 2 * (domain_length + 1)
162 || key->d_len != domain_length) {
163 error = CX_INVALID_PARAMETER;
164 goto end;
165 }
166
167 if (info) {
168 *info = 0;
169 }
170
171 out_sig_len = *sig_len;
172
173 // generate random
174 tries = 0;
175RETRY:
176 if (tries == CX_MAX_TRIES) {
177 out_sig_len = 0;
178 error = CX_INTERNAL_ERROR;
179 goto end;
180 }
181
182 switch (mode & CX_MASK_RND) {
183 default:
184 error = CX_INVALID_PARAMETER;
185 goto end;
186
187 case CX_RND_PROVIDED:
188 if (tries) {
189 out_sig_len = 0;
190 error = CX_INTERNAL_ERROR;
191 goto end;
192 }
193 memmove(rnd, sig, domain_length);
194 break;
195
196 case CX_RND_TRNG:
197 CX_CHECK(truernd(rnd, key->curve));
198 break;
199
200#ifdef HAVE_RNG_RFC6979
201 case CX_RND_RFC6979:
202 // If the hash length is greater than the domain length, we only consider
203 // the domain length's leftmost bytes of the hash for the operation.
204 // This optimisation works so long as (hash_len - domain_length) is a
205 // multiple of 8, when hash_len > domain_length. Otherwise, the hash
206 // needs to be shifted by (hash_len - domain_length) bits to fit into
207 // domain_length bytes.
208 CX_CHECK(
209 rfcrnd(rnd, tries, key->curve, key, hashID, hash, MIN(hash_len, domain_length)));
210 break;
211#endif // HAVE_RNG_RFC6979
212 }
213 tries++;
214
215 // compute the sig
216 CX_CHECK(cx_bn_lock(domain_length, 0));
217
218 // --> compute Q = k.G
219 CX_CHECK(cx_ecpoint_alloc(&Q, key->curve));
220 CX_CHECK(cx_ecdomain_generator_bn(key->curve, &Q));
221
222#ifdef HAVE_FIXED_SCALAR_LENGTH
223 // Additive splitting with random projective coordinates
224 // The length of the scalar is fixed to not leak information
225 CX_CHECK(cx_ecpoint_rnd_fixed_scalarmul(&Q, rnd, domain_length));
226#else
227 CX_CHECK(cx_ecpoint_rnd_scalarmul(&Q, rnd, domain_length));
228#endif // HAVE_FIXED_SCALAR_LENGTH
229
230 bool odd;
231 CX_CHECK(cx_bn_is_odd(Q.y, &odd));
232
233 // load order
234 CX_CHECK(cx_bn_alloc(&n, domain_length));
235 CX_CHECK(cx_ecdomain_parameter_bn(key->curve, CX_CURVE_PARAM_Order, n));
236
237 // compute r
238 CX_CHECK(cx_bn_alloc(&r, domain_length));
239 CX_CHECK(cx_ecpoint_export_bn(&Q, &r, NULL));
240 CX_CHECK(cx_ecpoint_destroy(&Q));
241 CX_CHECK(cx_bn_cmp(r, n, &diff));
242 if (diff >= 0) {
243 CX_CHECK_IGNORE_CARRY(cx_bn_sub(r, r, n));
244 if (info) {
245 *info |= CX_ECCINFO_xGTn;
246 }
247 }
248
249 // check r non zero
250 CX_CHECK(cx_bn_cmp_u32(r, 0, &diff));
251 if (diff == 0) {
252 goto RETRY;
253 }
254
255 // some allocs
256 CX_CHECK(cx_bn_alloc(&s, domain_length));
257 CX_CHECK(cx_bn_alloc(&t, domain_length));
258 CX_CHECK(cx_bn_alloc(&v, domain_length));
259 CX_CHECK(cx_bn_alloc(&t1, domain_length));
260 CX_CHECK(cx_bn_alloc(&t2, domain_length));
261
262 // compute s = kinv(h+d.x)
263 //
264 // t random, 0 <= t < n
265 // v = d - t
266 // u = h + (d-t)*x +t*x = h + v*x + t*x
267 // s = k_inv*u
268 //
269
270 CX_CHECK(cx_bn_rng(t, n));
271 CX_CHECK(cx_bn_init(t1, key->d, key->d_len));
272 CX_CHECK(cx_bn_mod_sub(v, t1, t, n)); // v
273 CX_CHECK(cx_bn_mod_mul(t2, v, r, n)); // v.x
274 CX_CHECK(cx_bn_mod_mul(t1, t, r, n)); // t.x
275
276 CX_CHECK(initialize_hash(hash, v, domain_bit_length, hash_len, domain_length));
277 // v = h (or domain bit length's leftmost bits of h)
278 CX_CHECK(cx_bn_mod_add(v, v, t1, n)); // v += t.x
279 CX_CHECK(cx_bn_mod_add(v, v, t2, n)); // v += v.x
280 CX_CHECK(cx_bn_init(t1, rnd, domain_length)); // k
281 CX_CHECK(cx_bn_mod_invert_nprime(t2, t1, n)); // k_inv
282 CX_CHECK(cx_bn_mod_mul(s, v, t2, n)); // s = k_inv*u
283 // check s non zero
284 CX_CHECK(cx_bn_cmp_u32(s, 0, &diff));
285 if (diff == 0) {
286 goto RETRY;
287 }
288
289 // "Sainte Canonisation"
290 if ((mode & CX_NO_CANONICAL) == 0) {
291 // if s > order/2, s = -s = order-s
292 CX_CHECK_IGNORE_CARRY(cx_bn_sub(t1, n, s));
293 CX_CHECK(cx_bn_shr(n, 1));
294 CX_CHECK(cx_bn_cmp(s, n, &diff));
295 if (diff > 0) {
296 CX_CHECK(cx_bn_copy(s, t1));
297 odd = !odd;
298 }
299 }
300
301 // WARN: from here only args and locals out_sig_len, r, s, rs, domain_length are valid
302
303 CX_CHECK(cx_bn_export(r, rs, domain_length));
304 CX_CHECK(cx_bn_export(s, rs + domain_length, domain_length));
305
306 // --> build the signature in TLV: T L T L r T L s
307 // 30 ll 02 ll X1 02 ll Y1
308 // r,s == X2,Y2
309 out_sig_len = cx_ecfp_encode_sig_der(
310 sig, out_sig_len, rs, domain_length, rs + domain_length, domain_length);
311 if (info) {
312 *info |= odd ? CX_ECCINFO_PARITY_ODD : 0;
313 }
314
315end:
316 *sig_len = out_sig_len;
317 cx_bn_unlock();
318 return error;
319
320#undef CX_MAX_TRIES
321#undef tries
322#undef rnd
323#undef Q
324#undef n
325#undef t
326#undef t1
327#undef t2
328#undef v
329#undef rs
330}
331
332/* ----------------------------------------------------------------------- */
333/* */
334/* ----------------------------------------------------------------------- */
335bool cx_ecdsa_verify_no_throw(const cx_ecfp_public_key_t *key,
336 const uint8_t *hash,
337 size_t hash_len,
338 const uint8_t *sig,
339 size_t sig_len)
340{
341 bool verified;
342 size_t domain_length, domain_bit_length;
343 cx_bn_t c, n, r, s, u1, u2, h;
344 cx_ecpoint_t R, Q, G;
345 cx_err_t error;
346 int diff;
347 bool is_infinite;
348
349 verified = false;
350
351 CX_CHECK(cx_ecdomain_parameters_length(key->curve, &domain_length));
352 CX_CHECK(cx_ecdomain_size(key->curve, &domain_bit_length));
353
354 if (!CX_CURVE_RANGE(key->curve, WEIERSTRASS) || key->W_len != 1 + 2 * domain_length) {
355 error = CX_INVALID_PARAMETER;
356 goto end;
357 }
358
359 // verify
360 //. check sig format
361 if (hash_len > (8 + 2 * domain_length)) {
362 error = CX_INVALID_PARAMETER;
363 goto end;
364 }
365
366 const uint8_t *sig_r, *sig_s;
367 size_t sig_rlen, sig_slen;
368
369 // decode
370 if (!cx_ecfp_decode_sig_der(
371 sig, sig_len, domain_length, &sig_r, &sig_rlen, &sig_s, &sig_slen)) {
372 error = CX_INVALID_PARAMETER;
373 goto end;
374 }
375
376 // setup
377 CX_CHECK(cx_bn_lock(domain_length, 0));
378 CX_CHECK(cx_bn_alloc(&n, domain_length));
379 CX_CHECK(cx_ecdomain_parameter_bn(key->curve, CX_CURVE_PARAM_Order, n));
380 CX_CHECK(cx_bn_alloc_init(&r, domain_length, sig_r, sig_rlen));
381 CX_CHECK(cx_bn_alloc_init(&s, domain_length, sig_s, sig_slen));
382 CX_CHECK(cx_bn_alloc(&h, domain_length));
383 CX_CHECK(initialize_hash(hash, h, domain_bit_length, hash_len, domain_length));
384 CX_CHECK(cx_bn_alloc(&u1, domain_length));
385 CX_CHECK(cx_bn_alloc(&u2, domain_length));
386 CX_CHECK(cx_bn_alloc(&c, domain_length));
387
388 // 1 check 0 < r <= N-1 and 0 < s <= N-1
389 CX_CHECK(cx_bn_set_u32(u2, 0));
390 CX_CHECK(cx_bn_cmp(r, u2, &diff));
391 if (diff == 0) {
392 goto end;
393 }
394 CX_CHECK(cx_bn_cmp(r, n, &diff));
395 if (diff >= 0) {
396 goto end;
397 }
398 CX_CHECK(cx_bn_cmp(s, u2, &diff));
399 if (diff == 0) {
400 goto end;
401 }
402 CX_CHECK(cx_bn_cmp(s, n, &diff));
403 if (diff >= 0) {
404 goto end;
405 }
406
407 // 2 verify r/s
408 //.compute c = inv(s) mod N ;
409 CX_CHECK(cx_bn_mod_invert_nprime(c, s, n));
410 //.compute u1 = hash*c mod N *
411 CX_CHECK(cx_bn_mod_mul(u1, h, c, n));
412 //.compute u2 = r*c mod N
413 CX_CHECK(cx_bn_mod_mul(u2, r, c, n));
414 // uG+vQ
415 CX_CHECK(cx_bn_destroy(&h));
416 CX_CHECK(cx_bn_destroy(&c));
417 CX_CHECK(cx_bn_destroy(&s));
418 CX_CHECK(cx_ecpoint_alloc(&G, key->curve));
419 CX_CHECK(cx_ecdomain_generator_bn(key->curve, &G));
420 CX_CHECK(cx_ecpoint_alloc(&Q, key->curve));
421 CX_CHECK(
422 cx_ecpoint_init(&Q, &key->W[1], domain_length, &key->W[1 + domain_length], domain_length));
423
424 CX_CHECK(cx_ecpoint_alloc(&R, key->curve));
425
426 // Double scalar multiplication using Straus-Shamir 's trick
427 CX_CHECK(cx_ecpoint_double_scalarmul_bn(&R, &G, &Q, u1, u2));
428
429 // Check if R is infinite point
430 CX_CHECK(cx_ecpoint_is_at_infinity(&R, &is_infinite));
431 if (is_infinite) {
432 goto end;
433 }
434 //.compute v = x1 mod N
435 CX_CHECK(cx_ecpoint_export_bn(&R, &u1, NULL));
436 CX_CHECK(cx_bn_reduce(u2, u1, n));
437 //.verify value
438 CX_CHECK(cx_bn_cmp(u2, r, &diff));
439 if (diff) {
440 goto end;
441 }
442
443 verified = true;
444
445end:
446 cx_bn_unlock();
447 return (error == CX_OK) && verified;
448}
449
450#endif // HAVE_ECDSA
union cx_u G_cx
Definition cx_ram.c:21
#define CX_MASK_RND
Definition lcx_common.h:161
#define CX_RND_RFC6979
Definition lcx_common.h:164
#define CX_NO_CANONICAL
Definition lcx_common.h:156
#define CX_RND_PROVIDED
Definition lcx_common.h:165
#define CX_RND_TRNG
Definition lcx_common.h:163
#define MIN(x, y)
Definition nbgl_types.h:98
unsigned char uint8_t
Definition usbd_conf.h:53