Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
cx_ecfp.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_ECC
20
21#include "lcx_ecfp.h"
22#include "libcxng.h"
23#include "lcx_eddsa.h"
24#include "cx_utils.h"
25#include "cx_ram.h"
26
27#include <string.h>
28
29#if defined(BOLOS_RELEASE) && defined(HAVE_ECC_WITH_NO_RANDOMIZE)
30#error HAVE_ECC_WITH_NO_RANDOMIZE not allowed for release
31#endif // HAVE_ECC_WITH_NO_RANDOMIZE
32
33/* ========================================================================= */
34/* ========================================================================= */
35/* === APIs === */
36/* ========================================================================= */
37/* ========================================================================= */
38
39/* ----------------------------------------------------------------------- */
40/* */
41/* ----------------------------------------------------------------------- */
42cx_err_t cx_ecfp_init_private_key_no_throw(cx_curve_t curve,
43 const uint8_t *raw_key,
44 size_t key_len,
45 cx_ecfp_private_key_t *key)
46{
47 size_t domain_length;
48 cx_err_t error;
49
50 CX_CHECK(cx_ecdomain_parameters_length(curve, &domain_length));
51
52 if (!(((raw_key == NULL) && (key_len == 0)) || ((raw_key) && (key_len == domain_length)))) {
53 error = CX_INVALID_PARAMETER;
54 goto end;
55 }
56
57 if (raw_key) {
58 key->d_len = key_len;
59 memmove(key->d, raw_key, key_len);
60 }
61
62 key->curve = curve;
63
64end:
65 return error;
66}
67
68/* ----------------------------------------------------------------------- */
69/* */
70/* ----------------------------------------------------------------------- */
71cx_err_t cx_ecfp_init_public_key_no_throw(cx_curve_t curve,
72 const uint8_t *rawkey,
73 size_t key_len,
74 cx_ecfp_public_key_t *key)
75{
76 size_t expected_key_len;
77 size_t expected_compressed_key_len;
78 size_t size;
79 cx_err_t error;
80
81 error = cx_ecdomain_parameters_length(curve, &size);
82 if (error) {
83 return error;
84 }
85
86 // SEC: complex assert param dispatched in code....
87
88 memset(key, 0, sizeof(cx_ecfp_public_key_t));
89
90 if (rawkey) {
91 if (key_len) {
92 expected_key_len = 0;
93 expected_compressed_key_len = 0;
94
95// check key length vs curve
96#ifdef HAVE_ECC_WEIERSTRASS
97 if (CX_CURVE_RANGE(curve, WEIERSTRASS)) {
98 expected_key_len = 1 + (size) *2;
99 }
100#endif
101
102#ifdef HAVE_ECC_TWISTED_EDWARDS
103 if (CX_CURVE_RANGE(curve, TWISTED_EDWARDS)) {
104 expected_key_len = 1 + (size) *2;
105 expected_compressed_key_len = 1 + (size);
106 }
107#endif
108
109#ifdef HAVE_ECC_MONTGOMERY
110 if (CX_CURVE_RANGE(curve, MONTGOMERY)) {
111 expected_compressed_key_len = 1 + (size);
112 }
113#endif
114
115 // check key format
116 if ((key_len == expected_key_len) && (rawkey[0] == 0x04)) {
117 goto OK;
118 }
119 if ((key_len == expected_compressed_key_len) && (rawkey[0] == 0x02)) {
120 goto OK;
121 }
122 }
123 return INVALID_PARAMETER;
124 }
125 else {
126 key_len = 0;
127 }
128
129OK:
130 // init key
131 key->curve = curve;
132 key->W_len = key_len;
133 memmove(key->W, rawkey, key_len);
134
135 return CX_OK;
136}
137
138/* ----------------------------------------------------------------------- */
139/* */
140/* ----------------------------------------------------------------------- */
141cx_err_t cx_ecfp_generate_pair_no_throw(cx_curve_t curve,
142 cx_ecfp_public_key_t *public_key,
143 cx_ecfp_private_key_t *private_key,
144 bool keep_private)
145{
146 return cx_ecfp_generate_pair2_no_throw(curve, public_key, private_key, keep_private, CX_SHA512);
147}
148
149cx_err_t cx_ecfp_generate_pair2_no_throw(cx_curve_t curve,
150 cx_ecfp_public_key_t *public_key,
151 cx_ecfp_private_key_t *private_key,
152 bool keep_private,
153 cx_md_t hashID)
154{
155 // domain used
156 size_t size;
157 cx_bn_t r, a, n;
158 cx_ecpoint_t W;
159 cx_err_t error;
160
161#ifndef HAVE_ECC_TWISTED_EDWARDS
162 (void) hashID;
163#endif
164
165 CX_CHECK(cx_ecdomain_parameters_length(curve, &size));
166
167 // SEC: complex assert param dispatched in code....
168
169 CX_CHECK(cx_bn_lock(size, 0));
170 CX_CHECK(cx_bn_alloc(&n, size));
171 CX_CHECK(cx_ecdomain_parameter_bn(curve, CX_CURVE_PARAM_Order, n));
172 CX_CHECK(cx_bn_alloc(&r, size));
173
174 // generate private key
175 if (keep_private) {
176 // keep => check private key
177 if ((private_key->curve != curve) || (private_key->d_len != size)) {
178 error = CX_INVALID_PARAMETER;
179 goto end;
180 }
181 CX_CHECK(cx_bn_init(r, private_key->d, private_key->d_len));
182 }
183 else {
184 CX_CHECK(cx_bn_alloc(&a, 2 * size));
185 CX_CHECK(cx_bn_rand(a));
186 CX_CHECK(cx_bn_reduce(r, a, n));
187 CX_CHECK(cx_bn_destroy(&a));
188 CX_CHECK(cx_bn_export(r, private_key->d, size));
189 private_key->curve = curve;
190 private_key->d_len = size;
191
192#ifdef HAVE_ECC_MONTGOMERY
193 if (CX_CURVE_RANGE(curve, MONTGOMERY)) {
194 if (curve == CX_CURVE_Curve25519) {
195 private_key->d[size - 1] &= 0xF8;
196 private_key->d[0] = (private_key->d[0] & 0x7F) | 0x40;
197 }
198 else /* CX_CURVE_Curve448*/ {
199 private_key->d[size - 1] &= 0xFC;
200 private_key->d[0] |= 0x80;
201 }
202 }
203#endif
204 }
205
206 // generate public key
207#ifdef HAVE_ECC_WEIERSTRASS
208 if (CX_CURVE_RANGE(curve, WEIERSTRASS)) {
209 CX_CHECK(cx_ecpoint_alloc(&W, private_key->curve));
210 CX_CHECK(cx_ecdomain_generator_bn(curve, &W));
211 // 'cx_ecpoint_rnd_fixed_scalarmul' doesn't support BLS12-381 so far
212 // use cx_ecpoint_rnd_scalarmul for now
213 if ((CX_CURVE_BLS12_381_G1 == private_key->curve)
214 || (CX_CURVE_BLS12_377_G1 == private_key->curve)) {
215 CX_CHECK(cx_ecpoint_rnd_scalarmul(&W, private_key->d, private_key->d_len));
216 }
217 else {
218 CX_CHECK(cx_ecpoint_rnd_fixed_scalarmul(&W, private_key->d, private_key->d_len));
219 }
220 public_key->curve = curve;
221 public_key->W_len = 1 + 2 * size;
222 public_key->W[0] = 0x04;
223 CX_CHECK(cx_ecpoint_export(&W, &public_key->W[1], size, &public_key->W[1 + size], size));
224 goto end;
225 }
226#endif
227
228#ifdef HAVE_ECC_TWISTED_EDWARDS
229 if (CX_CURVE_RANGE(curve, TWISTED_EDWARDS)) {
230 uint8_t scal[114];
231
232 if ((curve == CX_CURVE_EdBLS12) || (curve == CX_CURVE_JUBJUB)) {
233 CX_CHECK(cx_ecpoint_alloc(&W, private_key->curve));
234 CX_CHECK(cx_ecdomain_generator_bn(curve, &W));
235 CX_CHECK(cx_ecpoint_rnd_scalarmul(&W, private_key->d, private_key->d_len));
236 public_key->curve = curve;
237 public_key->W_len = 1 + 2 * size;
238 public_key->W[0] = 0x04;
239 CX_CHECK(
240 cx_ecpoint_export(&W, &public_key->W[1], size, &public_key->W[1 + size], size));
241 }
242 else {
243 CX_CHECK(cx_eddsa_get_public_key_internal(
244 private_key, hashID, public_key, NULL, 0, NULL, 0, scal));
245 }
246 goto end;
247 }
248#endif
249
250#ifdef HAVE_ECC_MONTGOMERY
251 if (CX_CURVE_RANGE(curve, MONTGOMERY)) {
252 CX_CHECK(cx_ecpoint_alloc(&W, private_key->curve));
253 CX_CHECK(cx_ecdomain_generator_bn(curve, &W));
254 CX_CHECK(cx_ecpoint_rnd_scalarmul(&W, private_key->d, private_key->d_len));
255 public_key->curve = curve;
256 public_key->W_len = 1 + 2 * size;
257 public_key->W[0] = 0x04;
258 CX_CHECK(cx_ecpoint_export(&W, &public_key->W[1], size, &public_key->W[1 + size], size));
259 goto end;
260 }
261#endif
262
263 error = CX_EC_INVALID_POINT;
264
265end:
266 cx_bn_unlock();
267 return error;
268}
269
270cx_err_t cx_ecfp_add_point_no_throw(cx_curve_t curve,
271 unsigned char *R,
272 const unsigned char *P,
273 const unsigned char *Q)
274{
275 size_t size;
276 cx_ecpoint_t ecR, ecP, ecQ;
277 cx_err_t error;
278
279 CX_CHECK(cx_ecdomain_parameters_length(curve, &size));
280 CX_CHECK(cx_bn_lock(size, 0));
281
282 CX_CHECK(cx_ecpoint_alloc(&ecP, curve));
283 CX_CHECK(cx_ecpoint_alloc(&ecQ, curve));
284 CX_CHECK(cx_ecpoint_alloc(&ecR, curve));
285 CX_CHECK(cx_ecpoint_init(&ecP, P + 1, size, P + 1 + size, size));
286 CX_CHECK(cx_ecpoint_init(&ecQ, Q + 1, size, Q + 1 + size, size));
287 CX_CHECK(cx_ecpoint_add(&ecR, &ecP, &ecQ));
288 R[0] = 0x04;
289 CX_CHECK(cx_ecpoint_export(&ecR, &R[1], size, &R[1 + size], size));
290
291end:
292 cx_bn_unlock();
293 return error;
294}
295
296/* ----------------------------------------------------------------------- */
297/* */
298/* ----------------------------------------------------------------------- */
299cx_err_t cx_ecfp_scalar_mult_no_throw(cx_curve_t curve, uint8_t *P, const uint8_t *k, size_t k_len)
300{
301 size_t size;
302 cx_ecpoint_t ecP;
303 cx_err_t error;
304
305 CX_CHECK(cx_ecdomain_parameters_length(curve, &size));
306 CX_CHECK(cx_bn_lock(size, 0));
307
308 CX_CHECK(cx_ecpoint_alloc(&ecP, curve));
309 CX_CHECK(cx_ecpoint_init(&ecP, P + 1, size, P + 1 + size, size));
310 CX_CHECK(cx_ecpoint_rnd_scalarmul(&ecP, k, k_len));
311 P[0] = 0x04;
312 CX_CHECK(cx_ecpoint_export(&ecP, &P[1], size, &P[1 + size], size));
313
314end:
315 cx_bn_unlock();
316 return error;
317}
318
319#ifdef HAVE_ECC_TWISTED_EDWARDS
320
321cx_err_t cx_edwards_compress_point_no_throw(cx_curve_t curve, uint8_t *P, size_t P_len)
322{
323 cx_ecpoint_t P_ec;
324 cx_err_t error;
325 uint32_t sign;
326 size_t size;
327
328 UNUSED(P_len);
329 CX_CHECK(cx_ecdomain_parameters_length(curve, &size));
330 CX_CHECK(cx_bn_lock(size, 0));
331 CX_CHECK(cx_ecpoint_alloc(&P_ec, curve));
332 CX_CHECK(cx_ecpoint_init(&P_ec, P + 1, size, P + 1 + size, size));
333 CX_CHECK(cx_ecpoint_compress(&P_ec, P + 1, size, &sign));
334 cx_encode_coord(P + 1, size, sign);
335 memmove(P + 1 + size, P + 1, size);
336 P[0] = 0x02;
337
338end:
339 cx_bn_unlock();
340 return error;
341}
342
343cx_err_t cx_edwards_decompress_point_no_throw(cx_curve_t curve, uint8_t *P, size_t P_len)
344{
345 cx_ecpoint_t P_ec;
346 cx_err_t error;
347 uint32_t sign;
348 size_t size;
349
350 UNUSED(P_len);
351 CX_CHECK(cx_ecdomain_parameters_length(curve, &size));
352 CX_CHECK(cx_bn_lock(size, 0));
353 sign = cx_decode_coord(P + 1, size);
354 CX_CHECK(cx_ecpoint_alloc(&P_ec, curve));
355 memmove(P + 1 + size, P + 1, size);
356 CX_CHECK(cx_ecpoint_decompress(&P_ec, P + 1, size, sign));
357 CX_CHECK(cx_ecpoint_export(&P_ec, P + 1, size, P + 1 + size, size));
358 P[0] = 0x04;
359
360end:
361 cx_bn_unlock();
362 return error;
363}
364
365#endif // HAVE_ECC_TWISTED_EDWARDS
366
367static size_t asn1_get_encoded_length_size(size_t len)
368{
369 if (len < 0x80) { // ..
370 return 1;
371 }
372 if (len < 0x100) {
373 // 81 ..
374 return 2;
375 }
376 if (len < 0x10000) {
377 // 82 .. ..
378 return 3;
379 }
380 return 0;
381}
382
383// return the length of an asn1 integer, aka '02' L V
384static size_t asn1_get_encoded_integer_size(uint8_t const *val, size_t len)
385{
386 size_t l0;
387
388 while (len && (*val == 0)) {
389 val++;
390 len--;
391 }
392
393 if (len == 0) {
394 len = 1;
395 }
396 else if (*val & 0x80u) {
397 len++;
398 }
399
400 l0 = asn1_get_encoded_length_size(len);
401 if (l0 == 0) {
402 return 0;
403 }
404 return 1 + l0 + len;
405}
406
407static int asn1_insert_tag(uint8_t **p, const uint8_t *end, unsigned int tag)
408{
409 if ((end - *p) < 1) {
410 return 0;
411 }
412 **p = tag;
413 (*p)++;
414 return 1;
415}
416
417static int asn1_insert_len(uint8_t **p, const uint8_t *end, size_t len)
418{
419 if (len < 0x80) {
420 if ((end - *p) < 1) {
421 return 0;
422 }
423 (*p)[0] = len & 0xFF;
424 (*p) += 1;
425 return 1;
426 }
427
428 if (len < 0x100) {
429 if ((end - *p) < 2) {
430 return 0;
431 }
432 (*p)[0] = 0x81u;
433 (*p)[1] = len & 0xFF;
434 (*p) += 2;
435 return 2;
436 }
437
438 if (len < 0x10000) {
439 if ((end - *p) < 3) {
440 return 0;
441 }
442 (*p)[0] = 0x82u;
443 (*p)[1] = (len >> 8) & 0xFF;
444 (*p)[2] = len & 0xFF;
445 (*p) += 3;
446 return 3;
447 }
448
449 return 0;
450}
451
452static int asn1_insert_integer(uint8_t **p, const uint8_t *end, const uint8_t *val, size_t len)
453{
454 while (len && (*val == 0)) {
455 val++;
456 len--;
457 }
458 if (!asn1_insert_tag(p, end, 0x02)) {
459 return 0;
460 }
461
462 // special case for 0
463 if (len == 0) {
464 if ((end - *p) < 2) {
465 return 0;
466 }
467 (*p)[0] = 0x01u;
468 (*p)[1] = 0x00u;
469 (*p) += 2;
470 return 1;
471 }
472
473 // cont with len != 0, so val != 0
474 if (!asn1_insert_len(p, end, (*val & 0x80) ? len + 1 : len)) {
475 return 0;
476 }
477
478 if (*val & 0x80) {
479 if ((end - *p) < 1) {
480 return 0;
481 }
482 **p = 0;
483 (*p)++;
484 }
485 if ((end - *p) < (int) len) {
486 return 0;
487 }
488 memmove(*p, val, len);
489 (*p) += len;
490 return 1;
491}
492
493/* ----------------------------------------------------------------------- */
494/* */
495/* ----------------------------------------------------------------------- */
496size_t cx_ecfp_encode_sig_der(uint8_t *sig,
497 size_t sig_len,
498 const uint8_t *r,
499 size_t r_len,
500 const uint8_t *s,
501 size_t s_len)
502{
503 size_t l0, len;
504 const uint8_t *sig_end = sig + sig_len;
505
506 len = 0;
507
508 l0 = asn1_get_encoded_integer_size(r, r_len);
509 if (l0 == 0) {
510 return 0;
511 }
512 len += l0;
513
514 l0 = asn1_get_encoded_integer_size(s, s_len);
515 if (l0 == 0) {
516 return 0;
517 }
518 len += l0;
519
520 if (!asn1_insert_tag(&sig, sig_end, 0x30) || !asn1_insert_len(&sig, sig_end, len)
521 || !asn1_insert_integer(&sig, sig_end, r, r_len)
522 || !asn1_insert_integer(&sig, sig_end, s, s_len)) {
523 return 0;
524 }
525 return sig_len - (sig_end - sig);
526}
527
528static int asn1_read_len(const uint8_t **p, const uint8_t *end, size_t *len)
529{
530 /* Adapted from secp256k1 */
531 int lenleft;
532 unsigned int b1;
533 *len = 0;
534
535 if (*p >= end) {
536 return 0;
537 }
538
539 b1 = *((*p)++);
540 if (b1 == 0xff) {
541 /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
542 return 0;
543 }
544 if ((b1 & 0x80u) == 0) {
545 /* X.690-0207 8.1.3.4 short form length octets */
546 *len = b1;
547 return 1;
548 }
549 if (b1 == 0x80) {
550 /* Indefinite length is not allowed in DER. */
551 return 0;
552 }
553 /* X.690-207 8.1.3.5 long form length octets */
554 lenleft = b1 & 0x7Fu;
555 if (lenleft > end - *p) {
556 return 0;
557 }
558 if (**p == 0) {
559 /* Not the shortest possible length encoding. */
560 return 0;
561 }
562 if ((size_t) lenleft > sizeof(size_t)) {
563 /* The resulting length would exceed the range of a size_t, so
564 * certainly longer than the passed array size.
565 */
566 return 0;
567 }
568 while (lenleft > 0) {
569 if ((*len >> ((sizeof(size_t) - 1) * 8)) != 0) {
570 return 0;
571 }
572 *len = (*len << 8u) | **p;
573 if (*len + lenleft > (size_t) (end - *p)) {
574 /* Result exceeds the length of the passed array. */
575 return 0;
576 }
577 (*p)++;
578 lenleft--;
579 }
580 if (*len < 128) {
581 /* Not the shortest possible length encoding. */
582 return 0;
583 }
584 return 1;
585}
586
587static int asn1_read_tag(const uint8_t **p, const uint8_t *end, size_t *len, int tag)
588{
589 if ((end - *p) < 1) {
590 return 0;
591 }
592
593 if (**p != tag) {
594 return 0;
595 }
596
597 (*p)++;
598 return asn1_read_len(p, end, len);
599}
600
601static int asn1_parse_integer(const uint8_t **p,
602 const uint8_t *end,
603 const uint8_t **n,
604 size_t *n_len)
605{
606 size_t len;
607 int ret = 0;
608
609 if (!asn1_read_tag(p, end, &len, 0x02)) { /* INTEGER */
610 goto end;
611 }
612
613 if (len == 0 || len > (size_t) (end - *p)) {
614 goto end;
615 }
616
617 if (((*p)[0] & 0x80u) == 0x80u) {
618 /* Truncated, missing leading 0 (negative number) */
619 goto end;
620 }
621
622 if ((*p)[0] == 0 && len >= 2 && ((*p)[1] & 0x80u) == 0) {
623 /* Zeroes have been prepended to the integer */
624 goto end;
625 }
626
627 while (len > 0 && **p == 0 && *p != end) { /* Skip leading null bytes */
628 (*p)++;
629 len--;
630 }
631
632 *n = *p;
633 *n_len = len;
634
635 *p += len;
636 ret = 1;
637
638end:
639 return ret;
640}
641
642/* ----------------------------------------------------------------------- */
643/* */
644/* ----------------------------------------------------------------------- */
645
646int cx_ecfp_decode_sig_der(const uint8_t *input,
647 size_t input_len,
648 size_t max_size,
649 const uint8_t **r,
650 size_t *r_len,
651 const uint8_t **s,
652 size_t *s_len)
653{
654 size_t len;
655 int ret = 0;
656 const uint8_t *input_end = input + input_len;
657
658 *s = NULL;
659 *r = NULL;
660 *s_len = 0;
661 *r_len = 0;
662
663 const uint8_t *p = (const uint8_t *) input;
664
665 if (!asn1_read_tag(&p, input_end, &len, 0x30)) { /* SEQUENCE */
666 goto end;
667 }
668
669 if (p + len != input_end) {
670 goto end;
671 }
672
673 if (!asn1_parse_integer(&p, input_end, r, r_len)
674 || !asn1_parse_integer(&p, input_end, s, s_len)) {
675 goto end;
676 }
677
678 if (p != input_end) { /* Check if bytes have been appended to the sequence */
679 goto end;
680 }
681
682 if (*r_len > max_size || *s_len > max_size) {
683 return 0;
684 }
685 ret = 1;
686end:
687 return ret;
688}
689
690#endif // HAVE_ECC
Key pair generation based on elliptic curves.
EDDSA (Edwards Curve Digital Signature Algorithm)
Include cryptography files.