Embedded SDK
Embedded SDK
cx_eddsa.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_TWISTED_EDWARDS
20 
21 #include "cx_hash.h"
22 #include "cx_ecfp.h"
23 #include "cx_eddsa.h"
24 #include "cx_selftests.h"
25 #include "cx_utils.h"
26 #include "cx_ram.h"
27 
28 #include <string.h>
29 
30 #define EDDSA_ED448_DOMAIN_LEN (57)
31 
32 static void cx_encode_int(uint8_t *v, int len)
33 {
34  uint8_t t;
35  int i, j;
36  i = 0;
37  j = len - 1;
38  len = len / 2;
39  while (len--) {
40  t = v[i];
41  v[i] = v[j];
42  v[j] = t;
43  i++;
44  j--;
45  }
46 }
47 
48 #define cx_decode_int(v, l) cx_encode_int(v, l)
49 
50 int cx_decode_coord(uint8_t *coord, int len)
51 {
52  int sign;
53  cx_encode_int(coord, len); // little endian
54  sign = coord[0] & 0x80; // x_0
55  coord[0] &= 0x7F; // y-coordinate
56  return sign;
57 }
58 
59 void cx_encode_coord(uint8_t *coord, int len, int sign)
60 {
61  coord[0] |= sign ? 0x80 : 0x00;
62  cx_encode_int(coord, len);
63 }
64 
65 /* ----------------------------------------------------------------------- */
66 /* */
67 /* ----------------------------------------------------------------------- */
68 cx_err_t cx_eddsa_get_public_key_internal(const cx_ecfp_private_key_t *pv_key,
69  cx_md_t hashID,
70  cx_ecfp_public_key_t *pu_key,
71  uint8_t *a,
72  size_t a_len,
73  uint8_t *h,
74  size_t h_len,
75  uint8_t *scal /*tmp*/)
76 {
77  size_t size;
78  cx_err_t error;
79 
80  CX_CHECK(cx_ecdomain_parameters_length(pv_key->curve, &size));
81 
82  if (!CX_CURVE_RANGE(pv_key->curve, TWISTED_EDWARDS)) {
83  return CX_INVALID_PARAMETER;
84  }
85  if (!((pv_key->d_len == size) || (pv_key->d_len == (2 * size)))) {
86  return CX_INVALID_PARAMETER;
87  }
88  if (!(((a == NULL) && (a_len == 0)) || ((a_len) && (a_len >= size)))) {
89  return CX_INVALID_PARAMETER;
90  }
91  if (!(((h == NULL) && (h_len == 0)) || ((h_len) && (h_len >= size)))) {
92  return CX_INVALID_PARAMETER;
93  }
94 
95  switch (hashID) {
96 #if defined(HAVE_SHA512)
97  case CX_SHA512:
98 #endif // HAVE_SHA512
99 
100 #if defined(HAVE_SHA3)
101  case CX_SHAKE256:
102  case CX_KECCAK:
103  case CX_SHA3:
104 #endif // HAVE_SHA3
105 
106 #if defined(HAVE_BLAKE2)
107  case CX_BLAKE2B:
108  if (cx_hash_init_ex(&G_cx.hash_ctx, hashID, size * 2) != CX_OK) {
109  return CX_INVALID_PARAMETER;
110  }
111  break;
112 #endif // HAVE_BLAKE2
113 
114  default:
115  return CX_INVALID_PARAMETER;
116  }
117 
118  if (pv_key->d_len == size) {
119  /* 1. Hash the 32/57-byte private key using SHA-512/shak256-114, storing the digest in
120  * a 32/114 bytes large buffer, denoted h. Only the lower [CME: first] 32/57 bytes are
121  * used for generating the public key.
122  */
123  CX_CHECK(cx_hash_update(&G_cx.hash_ctx, pv_key->d, pv_key->d_len));
124  CX_CHECK(cx_hash_final(&G_cx.hash_ctx, scal));
125  cx_hash_destroy(&G_cx.hash_ctx);
126  if (pv_key->curve == CX_CURVE_Ed25519) {
127  /* 2. Prune the buffer: The lowest 3 bits of the first octet are
128  * cleared, the highest bit of the last octet is cleared, and the
129  * second highest bit of the last octet is set.
130  */
131  scal[0] &= 0xF8;
132  scal[31] = (scal[31] & 0x7F) | 0x40;
133  }
134  else /* CX_CURVE_Ed448 */ {
135  /* 2. Prune the buffer: The two least significant bits of the first
136  * octet are cleared, all eight bits the last octet are cleared, and
137  * the highest bit of the second to last octet is set.
138  */
139  scal[0] &= 0xFC;
140  scal[56] = 0;
141  scal[55] |= 0x80;
142  }
143  }
144  else {
145  memmove(scal, pv_key->d, pv_key->d_len);
146  }
147 
148  /* 3. Interpret the buffer as the little-endian integer, forming a
149  * secret scalar a. Perform a fixed-base scalar multiplication
150  * [a]B.
151  */
152  cx_decode_int(scal, size);
153  if (a) {
154  memmove(a, scal, size);
155  }
156  if (h) {
157  memmove(h, scal + size, size);
158  }
159  if (pu_key) {
160  cx_ecpoint_t W;
161  CX_CHECK(cx_ecpoint_alloc(&W, pv_key->curve));
162  CX_CHECK(cx_ecdomain_generator_bn(pv_key->curve, &W));
163  CX_CHECK(cx_ecpoint_rnd_scalarmul(&W, scal, size));
164  pu_key->curve = pv_key->curve;
165  pu_key->W_len = 1 + 2 * size;
166  pu_key->W[0] = 0x04;
167  CX_CHECK(cx_ecpoint_export(&W, pu_key->W + 1, size, pu_key->W + 1 + size, size));
168  CX_CHECK(cx_ecpoint_destroy(&W));
169  }
170 
171 end:
172  return error;
173 }
174 
175 cx_err_t cx_eddsa_get_public_key_no_throw(const cx_ecfp_private_key_t *pv_key,
176  cx_md_t hashID,
177  cx_ecfp_public_key_t *pu_key,
178  uint8_t *a,
179  size_t a_len,
180  uint8_t *h,
181  size_t h_len)
182 {
183  uint8_t scal[EDDSA_ED448_DOMAIN_LEN * 2];
184  size_t size;
185  cx_err_t error;
186 
187  CX_CHECK(cx_ecdomain_parameters_length(pv_key->curve, &size));
188  CX_CHECK(cx_bn_lock(size, 0));
189  CX_CHECK(cx_eddsa_get_public_key_internal(pv_key, hashID, pu_key, a, a_len, h, h_len, scal));
190 
191 end:
192  cx_bn_unlock();
193  return error;
194 }
195 
196 #ifdef HAVE_EDDSA
197 
198 static uint8_t const C_cx_siged448[] = {'S', 'i', 'g', 'E', 'd', '4', '4', '8'};
199 
200 static cx_err_t cx_eddsa_check_params(cx_curve_t curve, size_t domain_len, cx_md_t hash_id)
201 {
202  if (!CX_CURVE_RANGE(curve, TWISTED_EDWARDS)) {
203  return CX_INVALID_PARAMETER;
204  }
205 
206  // The hash digest must be 2 * domain_len bytes
207  // Typically SHA512 for Ed25519 and SHAKE-256
208  // with 114 bytes for Ed448
209  switch (hash_id) {
210 #if (defined(HAVE_SHA512) || defined(HAVE_SHA3))
211 #if defined(HAVE_SHA512)
212  case CX_SHA512:
213 #endif // HAVE_SHA512
214 
215 #if defined(HAVE_SHA3)
216  case CX_KECCAK:
217  case CX_SHA3:
218 #endif // HAVE_SHA3
219  if (domain_len * 2 != 512 / 8) {
220  return INVALID_PARAMETER;
221  }
222  break;
223 #endif // (defined(HAVE_SHA512) || defined(HAVE_SHA3))
224 
225 #if (defined(HAVE_SHA3) || defined(HAVE_BLAKE2))
226 #if defined(HAVE_SHA3)
227  case CX_SHAKE256:
228 #endif // HAVE_SHA3
229 
230 #if defined(HAVE_BLAKE2)
231  case CX_BLAKE2B:
232 #endif // HAVE_BLAKE2
233  break;
234 #endif // (defined(HAVE_SHA3) || defined(HAVE_BLAKE2))
235 
236  default:
237  return INVALID_PARAMETER;
238  }
239 
240  return CX_OK;
241 }
242 
243 /* ----------------------------------------------------------------------- */
244 /* */
245 /* ----------------------------------------------------------------------- */
246 
247 cx_err_t cx_eddsa_sign_init_first_hash(cx_hash_t *hash_context,
248  const cx_ecfp_private_key_t *private_key,
249  cx_md_t hash_id)
250 {
251  size_t domain_len, hash_len;
252  cx_err_t error;
253  uint8_t prefix[EDDSA_ED448_DOMAIN_LEN];
254  uint8_t private_hash[EDDSA_ED448_DOMAIN_LEN * 2];
255 
256  CX_CHECK(cx_ecdomain_parameters_length(private_key->curve, &domain_len));
257  CX_CHECK(cx_eddsa_check_params(private_key->curve, domain_len, hash_id));
258 
259  if (!((private_key->d_len == domain_len) || (private_key->d_len == 2 * domain_len))) {
260  return CX_INVALID_PARAMETER;
261  }
262 
263  hash_len = 2 * domain_len;
264 
265  // retrieve the prefix from private key hash
266  CX_CHECK(cx_eddsa_get_public_key_internal(
267  private_key, hash_id, NULL, NULL, 0, prefix, sizeof(prefix), private_hash));
268 
269  CX_CHECK(cx_hash_init_ex(hash_context, hash_id, hash_len));
270 
271  // Hash dom4 for Ed448
272  if (private_key->curve == CX_CURVE_Ed448) {
273  CX_CHECK(cx_hash_update(hash_context, C_cx_siged448, sizeof(C_cx_siged448)));
274  private_hash[0] = 0;
275  private_hash[1] = 0;
276  CX_CHECK(cx_hash_update(hash_context, private_hash, 2));
277  }
278  // Hash the prefix
279  CX_CHECK(cx_hash_update(hash_context, prefix, domain_len));
280 
281 end:
282  return error;
283 }
284 
285 cx_err_t cx_eddsa_sign_init_second_hash(cx_hash_t *hash_context,
286  const cx_ecfp_private_key_t *private_key,
287  cx_md_t hash_id,
288  uint8_t *hash,
289  size_t hash_len,
290  uint8_t *sig,
291  size_t sig_len)
292 {
293  size_t domain_len;
294  cx_bn_t bn_h, bn_r, bn_n;
295  cx_ecpoint_t Q;
296  cx_err_t error;
297  uint8_t secret_scalar[EDDSA_ED448_DOMAIN_LEN];
298  uint8_t private_hash[EDDSA_ED448_DOMAIN_LEN * 2];
299  uint32_t sign;
300 
301  CX_CHECK(cx_ecdomain_parameters_length(private_key->curve, &domain_len));
302 
303  if (sig_len < 2 * domain_len) {
304  return CX_INVALID_PARAMETER;
305  }
306 
307  // Retrieve the secret scalar from the private key hash
308  CX_CHECK(cx_eddsa_get_public_key_internal(
309  private_key, hash_id, NULL, secret_scalar, sizeof(secret_scalar), NULL, 0, private_hash));
310  // Encode the digest as little-endian
311  cx_encode_int(hash, hash_len);
312 
313  CX_CHECK(cx_bn_lock(domain_len, 0));
314  CX_CHECK(cx_bn_alloc_init(&bn_h, hash_len, hash, hash_len));
315  CX_CHECK(cx_bn_alloc(&bn_r, domain_len));
316  CX_CHECK(cx_bn_alloc(&bn_n, domain_len));
317  CX_CHECK(cx_ecpoint_alloc(&Q, private_key->curve));
318  CX_CHECK(cx_ecdomain_parameter_bn(private_key->curve, CX_CURVE_PARAM_Order, bn_n));
319  CX_CHECK(cx_bn_reduce(bn_r, bn_h, bn_n));
320  CX_CHECK(cx_bn_destroy(&bn_h));
321 
322  // Compute A = a.B (B group generator)
323  CX_CHECK(cx_ecdomain_generator_bn(private_key->curve, &Q));
324  CX_CHECK(cx_ecpoint_rnd_scalarmul(&Q, secret_scalar, domain_len));
325  CX_CHECK(cx_ecpoint_compress(&Q, sig + domain_len, domain_len, &sign));
326  // Temporary store compressed A into sig + domain_len
327  cx_encode_coord(sig + domain_len, domain_len, sign);
328 
329  // Compute R = r.B
330  // Use secret_scalar to temporary store r = hash mod n
331  CX_CHECK(cx_bn_export(bn_r, secret_scalar, domain_len));
332  CX_CHECK(cx_ecdomain_generator_bn(private_key->curve, &Q));
333  CX_CHECK(cx_ecpoint_rnd_scalarmul(&Q, secret_scalar, domain_len));
334  // Temporary store compressed R into sig
335  CX_CHECK(cx_ecpoint_compress(&Q, sig, domain_len, &sign));
336  cx_encode_coord(sig, domain_len, sign);
337 
338  // Compute H(dom || R || A) and update the message M to the hash context later
339  CX_CHECK(cx_hash_init_ex(hash_context, hash_id, hash_len));
340  // Ed448 dom4
341  if (private_key->curve == CX_CURVE_Ed448) {
342  CX_CHECK(cx_hash_update(hash_context, C_cx_siged448, sizeof(C_cx_siged448)));
343  private_hash[0] = 0;
344  private_hash[1] = 0;
345  CX_CHECK(cx_hash_update(hash_context, private_hash, 2));
346  }
347  CX_CHECK(cx_hash_update(hash_context, sig, domain_len));
348  CX_CHECK(cx_hash_update(hash_context, sig + domain_len, domain_len));
349 
350  // Temporary store r into sig + domain_len
351  memcpy(sig + domain_len, secret_scalar, domain_len);
352 
353 end:
354  cx_bn_unlock();
355  return error;
356 }
357 
358 cx_err_t cx_eddsa_sign_hash(const cx_ecfp_private_key_t *pv_key,
359  cx_md_t hash_id,
360  const uint8_t *hash,
361  size_t hash_len,
362  uint8_t *sig,
363  size_t sig_len)
364 {
365  size_t domain_len;
366  uint8_t secret_scalar[EDDSA_ED448_DOMAIN_LEN];
367  uint8_t hash_in[EDDSA_ED448_DOMAIN_LEN * 2];
368  cx_bn_t bn_h, bn_a, bn_r, bn_s, bn_n;
369  cx_err_t error;
370 
371  CX_CHECK(cx_ecdomain_parameters_length(pv_key->curve, &domain_len));
372 
373  if (sig_len < 2 * domain_len) {
374  return CX_INVALID_PARAMETER;
375  }
376 
377  if (hash_len > EDDSA_ED448_DOMAIN_LEN * 2) {
378  return CX_INVALID_PARAMETER;
379  }
380  memcpy(hash_in, hash, hash_len);
381  // Encode the digest as little-endian
382  cx_encode_int(hash_in, hash_len);
383 
384  CX_CHECK(cx_bn_lock(domain_len, 0));
385  CX_CHECK(cx_bn_alloc(&bn_s, domain_len));
386  CX_CHECK(cx_bn_alloc_init(&bn_h, hash_len, hash_in, hash_len));
387  CX_CHECK(cx_bn_alloc(&bn_n, domain_len));
388  CX_CHECK(cx_bn_alloc(&bn_r, domain_len));
389  CX_CHECK(cx_ecdomain_parameter_bn(pv_key->curve, CX_CURVE_PARAM_Order, bn_n));
390  CX_CHECK(cx_bn_reduce(bn_r, bn_h, bn_n));
391  CX_CHECK(cx_eddsa_get_public_key_internal(
392  pv_key, hash_id, NULL, secret_scalar, sizeof(secret_scalar), NULL, 0, hash_in));
393  CX_CHECK(cx_bn_alloc_init(&bn_a, domain_len, secret_scalar, domain_len));
394  // k * s mod n
395  CX_CHECK(cx_bn_mod_mul(bn_s, bn_r, bn_a, bn_n));
396  // Get previously computed r from sig buffer
397  CX_CHECK(cx_bn_init(bn_r, sig + domain_len, domain_len));
398  // S = r + k *s mod n
399  CX_CHECK(cx_bn_mod_add(bn_s, bn_s, bn_r, bn_n));
400  CX_CHECK(cx_bn_set_u32(bn_r, 0));
401  CX_CHECK(cx_bn_mod_sub(bn_s, bn_s, bn_r, bn_n));
402  CX_CHECK(cx_bn_export(bn_s, sig + domain_len, domain_len));
403  cx_encode_int(sig + domain_len, domain_len);
404 
405 end:
406  cx_bn_unlock();
407  return error;
408 }
409 
410 cx_err_t cx_eddsa_update_hash(cx_hash_t *hash_context, const uint8_t *msg, size_t msg_len)
411 {
412  return cx_hash_update(hash_context, msg, msg_len);
413 }
414 
415 cx_err_t cx_eddsa_final_hash(cx_hash_t *hash_context, uint8_t *hash, size_t hash_len)
416 {
417  if (hash_len != cx_hash_get_size(hash_context)) {
418  return CX_INVALID_PARAMETER;
419  }
420  return cx_hash_final(hash_context, hash);
421 }
422 
423 cx_err_t cx_eddsa_sign_no_throw(const cx_ecfp_private_key_t *pv_key,
424  cx_md_t hashID,
425  const uint8_t *hash,
426  size_t hash_len,
427  uint8_t *sig,
428  size_t sig_len)
429 {
430  uint8_t out_hash[EDDSA_ED448_DOMAIN_LEN * 2] = {0};
431  size_t out_hash_len = 0;
432  cx_err_t error;
433  cx_hash_t *hash_context = &G_cx.hash_ctx;
434 
435  CX_CHECK(cx_eddsa_sign_init_first_hash(hash_context, pv_key, hashID));
436  CX_CHECK(cx_eddsa_update_hash(hash_context, hash, hash_len));
437  out_hash_len = cx_hash_get_size(hash_context);
438  CX_CHECK(cx_eddsa_final_hash(hash_context, out_hash, out_hash_len));
439  explicit_bzero(hash_context, sizeof(cx_hash_t));
440  CX_CHECK(cx_eddsa_sign_init_second_hash(
441  hash_context, pv_key, hashID, out_hash, out_hash_len, sig, sig_len));
442  CX_CHECK(cx_eddsa_update_hash(hash_context, hash, hash_len));
443  CX_CHECK(cx_eddsa_final_hash(hash_context, out_hash, out_hash_len));
444  CX_CHECK(cx_eddsa_sign_hash(pv_key, hashID, out_hash, out_hash_len, sig, sig_len));
445 
446 end:
447  explicit_bzero(hash_context, sizeof(cx_hash_t));
448  return error;
449 }
450 
451 /* ----------------------------------------------------------------------- */
452 /* */
453 /* ----------------------------------------------------------------------- */
454 
455 cx_err_t cx_eddsa_verify_init_hash(cx_hash_t *hash_context,
456  const cx_ecfp_public_key_t *public_key,
457  cx_md_t hash_id,
458  const uint8_t *sig_r,
459  size_t sig_r_len)
460 {
461  size_t domain_len, hash_len;
462  uint8_t scal[EDDSA_ED448_DOMAIN_LEN] = {0};
463  uint32_t sign;
464  cx_ecpoint_t P;
465  cx_err_t error;
466 
467  CX_CHECK(cx_ecdomain_parameters_length(public_key->curve, &domain_len));
468 
469  CX_CHECK(cx_eddsa_check_params(public_key->curve, domain_len, hash_id));
470 
471  if (!((public_key->W_len == 1 + domain_len) || (public_key->W_len == 1 + 2 * domain_len))) {
472  return CX_INVALID_PARAMETER;
473  }
474 
475  if (sig_r_len != domain_len) {
476  return CX_INVALID_PARAMETER;
477  }
478 
479  hash_len = 2 * domain_len;
480 
481  CX_CHECK(cx_bn_lock(domain_len, 0));
482  CX_CHECK(cx_ecpoint_alloc(&P, public_key->curve));
483  CX_CHECK(cx_hash_init_ex(hash_context, hash_id, hash_len));
484  if (CX_CURVE_Ed448 == public_key->curve) {
485  CX_CHECK(cx_hash_update(hash_context, C_cx_siged448, sizeof(C_cx_siged448)));
486  scal[0] = 0;
487  scal[1] = 0;
488  CX_CHECK(cx_hash_update(hash_context, scal, 2));
489  }
490  CX_CHECK(cx_hash_update(hash_context, sig_r, sig_r_len));
491  if (public_key->W[0] == 0x04) {
492  CX_CHECK(cx_ecpoint_init(
493  &P, &public_key->W[1], domain_len, &public_key->W[1 + domain_len], domain_len));
494  CX_CHECK(cx_ecpoint_compress(&P, scal, domain_len, &sign));
495  cx_encode_coord(scal, domain_len, sign);
496  }
497  else {
498  memmove(scal, &public_key->W[1], domain_len);
499  }
500  CX_CHECK(cx_hash_update(hash_context, scal, domain_len));
501 
502 end:
503  cx_bn_unlock();
504  return error;
505 }
506 
507 bool cx_eddsa_verify_hash(const cx_ecfp_public_key_t *public_key,
508  uint8_t *hash,
509  size_t hash_len,
510  const uint8_t *signature,
511  size_t signature_len)
512 {
513  size_t domain_len;
514  uint8_t scal[EDDSA_ED448_DOMAIN_LEN];
515  uint8_t scal_left[EDDSA_ED448_DOMAIN_LEN];
516  int diff;
517  bool are_equal = false;
518  bool verified = false;
519  uint32_t sign;
520  cx_bn_t bn_h, bn_rs, bn_n;
521  cx_bn_t bn_p, bn_y;
522  cx_ecpoint_t P, R, right_point, left_point;
523  cx_err_t error;
524 
525  CX_CHECK(cx_ecdomain_parameters_length(public_key->curve, &domain_len));
526 
527  if (signature_len != 2 * domain_len) {
528  return false;
529  }
530 
531  cx_encode_int(hash, hash_len);
532 
533  CX_CHECK(cx_bn_lock(domain_len, 0));
534  CX_CHECK(cx_bn_alloc(&bn_n, domain_len));
535  CX_CHECK(cx_ecdomain_parameter_bn(public_key->curve, CX_CURVE_PARAM_Order, bn_n));
536  CX_CHECK(cx_bn_alloc(&bn_rs, domain_len));
537  CX_CHECK(cx_bn_alloc_init(&bn_h, hash_len, hash, hash_len));
538  CX_CHECK(cx_bn_reduce(bn_rs, bn_h, bn_n));
539  CX_CHECK(cx_bn_export(bn_rs, scal, domain_len));
540  CX_CHECK(cx_ecpoint_alloc(&P, public_key->curve));
541  CX_CHECK(cx_ecpoint_init(
542  &P, &public_key->W[1], domain_len, &public_key->W[1 + domain_len], domain_len));
543  CX_CHECK(cx_ecpoint_neg(&P));
544 
545  memmove(scal_left, signature + domain_len, domain_len);
546  cx_decode_int(scal_left, domain_len);
547 
548  // The second half of the signature s must be in range 0 <= s < L to prevent
549  // signature malleability.
550  CX_CHECK(cx_bn_alloc_init(&bn_rs, domain_len, scal_left, domain_len));
551  CX_CHECK(cx_bn_cmp(bn_rs, bn_n, &diff));
552  if (diff >= 0) {
553  goto end;
554  }
555 
556  CX_CHECK(cx_ecpoint_alloc(&left_point, public_key->curve));
557  CX_CHECK(cx_ecdomain_generator_bn(public_key->curve, &left_point));
558  CX_CHECK(cx_ecpoint_alloc(&right_point, public_key->curve));
559  CX_CHECK(cx_ecpoint_cmp(&left_point, &P, &are_equal));
560 
561  if (are_equal) {
562  CX_CHECK(cx_ecpoint_scalarmul(&left_point, scal_left, domain_len));
563  CX_CHECK(cx_ecpoint_scalarmul(&P, scal, domain_len));
564  CX_CHECK(cx_ecpoint_add(&right_point, &left_point, &P));
565  }
566  else {
567  CX_CHECK(cx_ecpoint_double_scalarmul(
568  &right_point, &left_point, &P, scal_left, domain_len, scal, domain_len));
569  }
570 
571  CX_CHECK(cx_ecpoint_alloc(&R, public_key->curve));
572  memmove(scal, signature, domain_len);
573  sign = cx_decode_coord(scal, domain_len);
574 
575  CX_CHECK(cx_bn_alloc(&bn_p, domain_len));
576  CX_CHECK(cx_ecdomain_parameter_bn(public_key->curve, CX_CURVE_PARAM_Field, bn_p));
577 
578  // If the y coordinate is >= p then the decoding fails
579  CX_CHECK(cx_bn_alloc(&bn_y, domain_len));
580  CX_CHECK(cx_bn_init(bn_y, scal, domain_len));
581 
582  CX_CHECK(cx_bn_cmp(bn_y, bn_p, &diff));
583  if (diff >= 0) {
584  goto end;
585  }
586 
587  CX_CHECK(cx_ecpoint_decompress(&R, scal, domain_len, sign));
588  CX_CHECK(cx_ecpoint_destroy(&left_point));
589  CX_CHECK(cx_ecpoint_destroy(&P));
590 
591  // Check the signature
592  CX_CHECK(cx_ecpoint_cmp(&R, &right_point, &verified));
593 
594 end:
595  cx_bn_unlock();
596  return error == CX_OK && verified;
597 }
598 
599 bool cx_eddsa_verify_no_throw(const cx_ecfp_public_key_t *pu_key,
600  cx_md_t hashID,
601  const uint8_t *hash,
602  size_t hash_len,
603  const uint8_t *sig,
604  size_t sig_len)
605 {
606  uint8_t out_hash[EDDSA_ED448_DOMAIN_LEN * 2] = {0};
607  size_t out_hash_len = 0;
608  cx_hash_t *hash_context = &G_cx.hash_ctx;
609  bool verified = false;
610  cx_err_t error = CX_INTERNAL_ERROR;
611 
612  CX_CHECK(cx_eddsa_verify_init_hash(hash_context, pu_key, hashID, sig, sig_len / 2));
613  out_hash_len = cx_hash_get_size(hash_context);
614  CX_CHECK(cx_eddsa_update_hash(hash_context, hash, hash_len));
615  CX_CHECK(cx_eddsa_final_hash(hash_context, out_hash, out_hash_len));
616  verified = cx_eddsa_verify_hash(pu_key, out_hash, out_hash_len, sig, sig_len);
617 
618 end:
619  explicit_bzero(&G_cx.hash_ctx, sizeof(G_cx.hash_ctx));
620  return verified;
621 }
622 
623 #endif // HAVE_EDDSA
624 #endif // HAVE_ECC_TWISTED_EDWARDS
union cx_u G_cx
Definition: cx_ram.c:21
unsigned char uint8_t
Definition: usbd_conf.h:53