Embedded SDK
Embedded SDK
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 
31 static 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));
48 end:
49  cx_bn_unlock();
50  return error;
51 }
52 
53 static 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 
81 end:
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.
90 static 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 
110 end:
111  return error;
112 }
113 
114 /* ----------------------------------------------------------------------- */
115 /* */
116 /* ----------------------------------------------------------------------- */
117 cx_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;
175 RETRY:
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 
315 end:
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 /* ----------------------------------------------------------------------- */
335 bool 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 
445 end:
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:79
unsigned char uint8_t
Definition: usbd_conf.h:53