samd51_crypto_pkc.c
Go to the documentation of this file.
1 /**
2  * @file samd51_crypto_pkc.c
3  * @brief SAMD51 public-key hardware accelerator (PUKCC)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCRYPTO Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
33 
34 //Dependencies
35 #include "sam.h"
36 #include "pukcc/CryptoLib_typedef_pb.h"
37 #include "pukcc/CryptoLib_Headers_pb.h"
38 #include "core/crypto.h"
41 #include "pkc/rsa.h"
42 #include "ecc/ec.h"
43 #include "ecc/ecdsa.h"
44 #include "mpi/mpi.h"
45 #include "debug.h"
46 
47 //Check crypto library configuration
48 #if (SAMD51_CRYPTO_PKC_SUPPORT == ENABLED)
49 
50 //Global variables
51 PPUKCL_PARAM pvPUKCLParam;
52 PUKCL_PARAM PUKCLParam;
53 
54 
55 /**
56  * @brief Initialize PUKCC module
57  **/
58 
60 {
61  //Enable PUKCC clock
62  MCLK_REGS->MCLK_AHBMASK |= MCLK_AHBMASK_PUKCC_Msk;
63 
64  //Clear PUKCLParam structure
65  osMemset(&PUKCLParam, 0, sizeof(PUKCL_PARAM));
67 
68  //Initialize PUKCC
69  vPUKCL_Process(SelfTest, pvPUKCLParam);
70 
71  //Check status code
72  if(PUKCL(u2Status) != PUKCL_OK)
73  return ERROR_FAILURE;
74 
75  //Check version number
76  if(pvPUKCLParam->P.PUKCL_SelfTest.u4Version != PUKCL_VERSION)
77  return ERROR_FAILURE;
78 
79  //The return values from the SelfTest service must be compared against
80  //known values mentioned in the service description
81  if(pvPUKCLParam->P.PUKCL_SelfTest.u4CheckNum1 != 0x6E70DDD2)
82  return ERROR_FAILURE;
83 
84  if(pvPUKCLParam->P.PUKCL_SelfTest.u4CheckNum2 != 0x25C8D64F)
85  return ERROR_FAILURE;
86 
87  //Successful initialization
88  return NO_ERROR;
89 }
90 
91 
92 /**
93  * @brief Import byte array
94  * @param[in,out] dest Pointer to the crypto memory
95  * @param[in] array Pointer to the byte array
96  * @param[in] arrayLen Length of the array to be copied
97  * @param[in] totalLen Desired length of the area, in bytes
98  * @return Pointer to the initialized area
99  **/
100 
101 uint8_t *pukccImportArray(uint8_t **dest, const uint8_t *array,
102  size_t arrayLen, size_t totalLen)
103 {
104  size_t i;
105  uint8_t *p;
106 
107  //Point to the crypto memory
108  p = *dest;
109 
110  //Copy the byte array to the crypto memory
111  for(i = 0; i < arrayLen; i++)
112  {
113  p[i] = array[arrayLen - 1 - i];
114  }
115 
116  //Pad the data with zeroes
117  while(i < totalLen)
118  {
119  p[i++] = 0;
120  }
121 
122  //Advance data pointer
123  *dest = p + i;
124 
125  //Return a pointer to the initialized area
126  return p;
127 }
128 
129 
130 /**
131  * @brief Import multiple-precision integer
132  * @param[in,out] dest Pointer to the crypto memory
133  * @param[in] src Pointer to the multiple-precision integer
134  * @param[in] totalLen Desired length of the area, in bytes
135  * @return Pointer to the initialized area
136  **/
137 
138 uint8_t *pukccImportMpi(uint8_t **dest, const Mpi *src, size_t totalLen)
139 {
140  uint8_t *p;
141 
142  //Point to the crypto memory
143  p = *dest;
144 
145  //Copy the multiple-precision integer to the crypto memory
146  mpiExport(src, p, totalLen, MPI_FORMAT_LITTLE_ENDIAN);
147 
148  //Advance data pointer
149  *dest = p + totalLen;
150 
151  //Return a pointer to the initialized area
152  return p;
153 }
154 
155 
156 /**
157  * @brief Initialize workspace area
158  * @param[in,out] dest Pointer to the crypto memory
159  * @param[in] totalLen Desired length of the area, in bytes
160  * @return Pointer to the initialized area
161  **/
162 
163 uint8_t *pukccWorkspace(uint8_t **dest, size_t totalLen)
164 {
165  size_t i;
166  uint8_t *p;
167 
168  //Point to the crypto memory
169  p = *dest;
170 
171  //Initialize workspace area
172  for(i = 0; i < totalLen; i++)
173  {
174  p[i] = 0;
175  }
176 
177  //Advance data pointer
178  *dest = p + i;
179 
180  //Return a pointer to the initialized area
181  return p;
182 }
183 
184 
185 /**
186  * @brief Multiple precision multiplication
187  * @param[out] r Resulting integer R = A * B
188  * @param[in] a First operand A
189  * @param[in] b Second operand B
190  * @return Error code
191  **/
192 
193 error_t mpiMul(Mpi *r, const Mpi *a, const Mpi *b)
194 {
195  error_t error;
196  size_t m;
197  size_t n;
198  uint8_t *pos;
199  PukccFmultParams params;
200 
201  //Retrieve the length of the input integer, in bytes
202  m = mpiGetByteLength(a);
203  m = (m + 3U) & ~3U;
204 
205  //Retrieve the length of the modulus, in bytes
206  n = mpiGetByteLength(b);
207  n = (n + 3U) & ~3U;
208 
209  //Acquire exclusive access to the PUKCC accelerator
211 
212  //Point to the crypto memory
213  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
214 
215  //Copy input integer X
216  params.x = pukccImportMpi(&pos, a, m);
217  //Copy input integer Y
218  params.y = pukccImportMpi(&pos, b, n);
219 
220  //Unused parameters
221  params.z = 0;
222  params.mod = 0;
223  params.cns = 0;
224 
225  //Initialize output integer R
226  params.r = pukccWorkspace(&pos, m + n);
227 
228  //Set Fmult service parameters
229  PUKCL(u2Option) = SET_MULTIPLIEROPTION(PUKCL_FMULT_ONLY) |
230  SET_CARRYOPTION(CARRY_NONE);
231  PUKCL(Specific).CarryIn = 0;
232  PUKCL(Specific).Gf2n = 0;
233  PUKCL_Fmult(u2ModLength) = 0;
234  PUKCL_Fmult(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
235  PUKCL_Fmult(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
236  PUKCL_Fmult(u2XLength) = m;
237  PUKCL_Fmult(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
238  PUKCL_Fmult(u2YLength) = n;
239  PUKCL_Fmult(nu1YBase) = PUKCC_FAR_TO_NEAR(params.y);
240  PUKCL_Fmult(nu1ZBase) = PUKCC_FAR_TO_NEAR(params.z);
241  PUKCL_Fmult(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
242 
243  //Perform multiplication
244  vPUKCL_Process(Fmult, pvPUKCLParam);
245 
246  //Check status code
247  if(PUKCL(u2Status) == PUKCL_OK)
248  {
249  //If FMult is without reduction, R is filled with the final result
250  error = mpiImport(r, params.r, m + n, MPI_FORMAT_LITTLE_ENDIAN);
251 
252  //Check status code
253  if(!error)
254  {
255  //Set the sign of the result
256  r->sign = (a->sign == b->sign) ? 1 : -1;
257  }
258  }
259  else
260  {
261  //Report an error
262  error = ERROR_FAILURE;
263  }
264 
265  //Release exclusive access to the PUKCC accelerator
267 
268  //Return error code
269  return error;
270 }
271 
272 
273 /**
274  * @brief Modulo operation
275  * @param[out] r Resulting integer R = A mod P
276  * @param[in] a The multiple precision integer to be reduced
277  * @param[in] p The modulus P
278  * @return Error code
279  **/
280 
281 error_t mpiMod2(Mpi *r, const Mpi *a, const Mpi *p)
282 {
283  error_t error;
284  size_t n;
285  size_t modLen;
286  uint8_t *pos;
287  PukccRedModParams params;
288 
289  //Retrieve the length of the input integer, in bytes
290  n = mpiGetByteLength(a);
291 
292  //Retrieve the length of the modulus, in bytes
293  modLen = mpiGetByteLength(p);
294  modLen = (modLen + 3U) & ~3U;
295 
296  //Check the length of the input integer
297  if(n > (2 * modLen + 4))
298  return ERROR_INVALID_LENGTH;
299 
300  //Acquire exclusive access to the PUKCC accelerator
302 
303  //Point to the crypto memory
304  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
305 
306  //Copy modulus
307  params.mod = pukccImportMpi(&pos, p, modLen + 4);
308  //Initialize workspace CNS
309  params.cns = pukccWorkspace(&pos, 68);
310  //Initialize output integer R
311  params.r = pukccWorkspace(&pos, modLen + 4);
312  //Copy input integer X
313  params.x = pukccImportMpi(&pos, a, 2 * modLen + 8);
314 
315  //Set RedMod service parameters
316  PUKCL(u2Option) = PUKCL_REDMOD_REDUCTION | PUKCL_REDMOD_USING_DIVISION;
317  PUKCL(Specific).CarryIn = 0;
318  PUKCL(Specific).Gf2n = 0;
319  PUKCL_RedMod(u2ModLength) = modLen;
320  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
321  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
322  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
323  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
324 
325  //Perform modular reduction setup
326  vPUKCL_Process(RedMod, pvPUKCLParam);
327 
328  //Check status code
329  if(PUKCL(u2Status) == PUKCL_OK)
330  {
331  //If FMult is without reduction, R is filled with the final result
332  error = mpiImport(r, params.r, modLen, MPI_FORMAT_LITTLE_ENDIAN);
333  }
334  else
335  {
336  //Report an error
337  error = ERROR_FAILURE;
338  }
339 
340  //Release exclusive access to the PUKCC accelerator
342 
343  //Return error code
344  return error;
345 }
346 
347 
348 /**
349  * @brief Modular inverse
350  * @param[out] r Resulting integer R = A^-1 mod P
351  * @param[in] a The multiple precision integer A
352  * @param[in] p The modulus P
353  * @return Error code
354  **/
355 
356 error_t mpiInvMod(Mpi *r, const Mpi *a, const Mpi *p)
357 {
358  error_t error;
359  size_t m;
360  size_t n;
361  uint8_t *pos;
362  PukccGcdParams params;
363 
364  //Retrieve the length of the input integer, in bytes
365  m = mpiGetByteLength(a);
366  //Retrieve the length of the modulus, in bytes
367  n = mpiGetByteLength(p);
368 
369  //Compute the length of the areas X, Y, A and Z
370  n = MAX(n, m);
371  n = (n + 7U) & ~3U;
372 
373  //Acquire exclusive access to the PUKCC accelerator
375 
376  //Point to the crypto memory
377  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
378 
379  //Copy input integer
380  params.x = pukccImportMpi(&pos, a, n);
381  //Copy modulus
382  params.y = pukccImportMpi(&pos, p, n);
383  //Initialize output integer A
384  params.a = pukccWorkspace(&pos, n);
385  //Initialize output integer Z
386  params.z = pukccWorkspace(&pos, n + 4);
387  //Initialize workspace >
388  params.w = pukccWorkspace(&pos, 32);
389 
390  //Set GCD service parameters
391  PUKCL(Specific).Gf2n = 0;
392  PUKCL_GCD(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
393  PUKCL_GCD(nu1YBase) = PUKCC_FAR_TO_NEAR(params.y);
394  PUKCL_GCD(nu1ABase) = PUKCC_FAR_TO_NEAR(params.a);
395  PUKCL_GCD(nu1ZBase) = PUKCC_FAR_TO_NEAR(params.z);
396  PUKCL_GCD(nu1WorkSpace) = PUKCC_FAR_TO_NEAR(params.w);
397  PUKCL_GCD(u2Length) = n;
398 
399  //Calculate the modular inverse
400  vPUKCL_Process(GCD, pvPUKCLParam);
401 
402  //Check status code
403  if(PUKCL(u2Status) == PUKCL_OK)
404  {
405  //Copy output integer Z
406  error = mpiImport(r, params.a, n, MPI_FORMAT_LITTLE_ENDIAN);
407  }
408  else
409  {
410  //Report an error
411  error = ERROR_FAILURE;
412  }
413 
414  //Release exclusive access to the PUKCC accelerator
416 
417  //Return error code
418  return error;
419 }
420 
421 
422 /**
423  * @brief Modular exponentiation
424  * @param[out] r Resulting integer R = A ^ E mod P
425  * @param[in] a Pointer to a multiple precision integer
426  * @param[in] e Exponent
427  * @param[in] p Modulus
428  * @return Error code
429  **/
430 
431 error_t mpiExpMod(Mpi *r, const Mpi *a, const Mpi *e, const Mpi *p)
432 {
433  error_t error;
434  size_t n;
435  size_t modLen;
436  size_t expLen;
437  uint8_t *pos;
438  PukccExpModParams params;
439 
440  //Retrieve the length of the input integer, in bytes
441  n = mpiGetByteLength(a);
442 
443  //Retrieve the length of the modulus, in bytes
444  modLen = mpiGetByteLength(p);
445  modLen = (modLen + 3U) & ~3U;
446 
447  //Retrieve the length of the exponent, in bytes
448  expLen = mpiGetByteLength(e);
449  expLen = (expLen + 3U) & ~3U;
450 
451  //Check the length of the input integer
452  if(n > (2 * modLen + 4))
453  return ERROR_INVALID_LENGTH;
454 
455  //Acquire exclusive access to the PUKCC accelerator
457 
458  //Point to the crypto memory
459  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
460 
461  //Copy modulus
462  params.mod = pukccImportMpi(&pos, p, modLen + 4);
463  //Initialize reduction constant
464  params.cns = pukccWorkspace(&pos, modLen + 12);
465  //Initialize workspace R
466  params.r = pukccWorkspace(&pos, 64);
467  //Initialize workspace X
468  params.x = pukccWorkspace(&pos, 2 * modLen + 8);
469 
470  //Set RedMod service parameters
471  PUKCL(u2Option) = PUKCL_REDMOD_SETUP;
472  PUKCL(Specific).CarryIn = 0;
473  PUKCL(Specific).Gf2n = 0;
474  PUKCL_RedMod(u2ModLength) = modLen;
475  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
476  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
477  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
478  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
479 
480  //Perform modular reduction setup
481  vPUKCL_Process(RedMod, pvPUKCLParam);
482 
483  //Check status code
484  if(PUKCL(u2Status) == PUKCL_OK)
485  {
486  //Point to the crypto memory
487  pos = params.r;
488 
489  //Copy input number
490  params.x = pukccImportMpi(&pos, a, 2 * modLen + 8);
491 
492  //Set RedMod service parameters
493  PUKCL(u2Option) = PUKCL_REDMOD_REDUCTION | PUKCL_REDMOD_USING_FASTRED;
494  PUKCL(Specific).CarryIn = 0;
495  PUKCL(Specific).Gf2n = 0;
496  PUKCL_RedMod(u2ModLength) = modLen;
497  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
498  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
499  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.x);
500  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
501 
502  //Perform fast modular reduction
503  vPUKCL_Process(RedMod, pvPUKCLParam);
504  }
505 
506  //Check status code
507  if(PUKCL(u2Status) == PUKCL_OK)
508  {
509  //Set RedMod service parameters
510  PUKCL(u2Option) = PUKCL_REDMOD_NORMALIZE;
511  PUKCL(Specific).CarryIn = 0;
512  PUKCL(Specific).Gf2n = 0;
513  PUKCL_RedMod(u2ModLength) = modLen;
514  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
515  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
516  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.x);
517  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
518 
519  //Normalize the result
520  vPUKCL_Process(RedMod, pvPUKCLParam);
521  }
522 
523  //Check status code
524  if(PUKCL(u2Status) == PUKCL_OK)
525  {
526  //The number to be exponentiated is followed by four 32-bit words
527  //that are used during the computations as a workspace
528  pos = params.x + modLen;
529  pukccWorkspace(&pos, 16);
530 
531  //The exponent must be given with a supplemental word on the LSB
532  //side (low addresses). This word shall be set to zero
533  params.exp = pukccWorkspace(&pos, 4);
534  pukccImportMpi(&pos, e, expLen);
535 
536  //Initialize workspace
537  params.w = pukccWorkspace(&pos, 3 * (modLen + 4) + 8);
538 
539  //Set ExpMod service parameters
540  PUKCL(u2Option) = PUKCL_EXPMOD_REGULARRSA | PUKCL_EXPMOD_WINDOWSIZE_1 |
541  PUKCL_EXPMOD_EXPINPUKCCRAM;
542  PUKCL_ExpMod(u2ModLength) = modLen;
543  PUKCL_ExpMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
544  PUKCL_ExpMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
545  PUKCL_ExpMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
546  PUKCL_ExpMod(nu1PrecompBase) = PUKCC_FAR_TO_NEAR(params.w);
547  PUKCL_ExpMod(u2ExpLength) = expLen;
548  PUKCL_ExpMod(pfu1ExpBase) = params.exp;
549  PUKCL_ExpMod(u1Blinding) = 0;
550 
551  //Perform modular exponentiation
552  vPUKCL_Process(ExpMod, pvPUKCLParam);
553  }
554 
555  //Check status code
556  if(PUKCL(u2Status) == PUKCL_OK)
557  {
558  //Copy resulting integer
559  error = mpiImport(r, params.x, modLen, MPI_FORMAT_LITTLE_ENDIAN);
560  }
561  else
562  {
563  //Report an error
564  error = ERROR_FAILURE;
565  }
566 
567  //Release exclusive access to the PUKCC accelerator
569 
570  //Return error code
571  return error;
572 }
573 
574 
575 /**
576  * @brief Test whether a number is probable prime
577  * @param[in] a Pointer to a multiple precision integer
578  * @return Error code
579  **/
580 
582 {
583  error_t error;
584  uint_t k;
585  size_t n;
586  uint8_t *pos;
587  PukccPrimeGenParams params;
588 
589  //Retrieve the length of the input integer, in bits
590  n = mpiGetBitLength(a);
591 
592  //Prime numbers of a size lower than 96 bits cannot be tested by this
593  //service
594  if(n < 96)
595  return ERROR_INVALID_LENGTH;
596 
597  //The number of repetitions controls the error probability
598  if(n >= 1300)
599  {
600  k = 2;
601  }
602  else if(n >= 850)
603  {
604  k = 3;
605  }
606  else if(n >= 650)
607  {
608  k = 4;
609  }
610  else if(n >= 550)
611  {
612  k = 5;
613  }
614  else if(n >= 450)
615  {
616  k = 6;
617  }
618  else if(n >= 400)
619  {
620  k = 7;
621  }
622  else if(n >= 350)
623  {
624  k = 8;
625  }
626  else if(n >= 300)
627  {
628  k = 9;
629  }
630  else if(n >= 250)
631  {
632  k = 12;
633  }
634  else if(n >= 200)
635  {
636  k = 15;
637  }
638  else if(n >= 150)
639  {
640  k = 18;
641  }
642  else
643  {
644  k = 27;
645  }
646 
647  //Retrieve the length of the input integer, in bytes
648  n = mpiGetByteLength(a);
649  n = (n + 3U) & ~3U;
650 
651  //Acquire exclusive access to the PUKCC accelerator
653 
654  //Point to the crypto memory
655  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
656 
657  //One additional word is used on the LSB side of the NBase parameter. As
658  //a consequence, the parameter nu1NBase must never be at the beginning of
659  //the crypto RAM, but at least at one word from the beginning
660  pukccWorkspace(&pos, 4);
661 
662  //Copy the number to test
663  params.n = pukccImportMpi(&pos, a, n + 4);
664  //Cns is used as a workspace
665  params.cns = pukccWorkspace(&pos, n + 12);
666  //Rnd is used as a workspace
667  params.rnd = pukccWorkspace(&pos, MAX(n + 16, 64));
668  //Precomp is used as a precomputation workspace
669  params.w = pukccWorkspace(&pos, MAX(3 * (n + 4), n + 72) + 8);
670  //Exp is used as a workspace
671  params.exp = pukccWorkspace(&pos, n + 4);
672 
673  //Unused parameter
674  params.r = 0;
675 
676  //Set PrimeGen service parameters
677  PUKCL(u2Option) = PUKCL_PRIMEGEN_TEST | PUKCL_EXPMOD_FASTRSA |
678  PUKCL_EXPMOD_WINDOWSIZE_1;
679  PUKCL_PrimeGen(u2NLength) = n;
680  PUKCL_PrimeGen(nu1NBase) = PUKCC_FAR_TO_NEAR(params.n);
681  PUKCL_PrimeGen(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
682  PUKCL_PrimeGen(nu1RndBase) = PUKCC_FAR_TO_NEAR(params.rnd);
683  PUKCL_PrimeGen(nu1PrecompBase) = PUKCC_FAR_TO_NEAR(params.w);
684  PUKCL_PrimeGen(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
685  PUKCL_PrimeGen(nu1ExpBase) = PUKCC_FAR_TO_NEAR(params.exp);
686  PUKCL_PrimeGen(u1MillerRabinIterations) = k;
687  PUKCL_PrimeGen(u2MaxIncrement) = 1;
688 
689  //Perform probable prime testing
690  vPUKCL_Process(PrimeGen, pvPUKCLParam);
691 
692  //Check status code
693  switch(PUKCL(u2Status))
694  {
695  case PUKCL_NUMBER_IS_PRIME:
696  //The number is probably prime
697  error = NO_ERROR;
698  break;
699  case PUKCL_NUMBER_IS_NOT_PRIME:
700  //The number is not prime
701  error = ERROR_INVALID_VALUE;
702  break;
703  default:
704  //Report an error
705  error = ERROR_FAILURE;
706  break;
707  }
708 
709  //Release exclusive access to the PUKCC accelerator
711 
712  //Return error code
713  return error;
714 }
715 
716 
717 /**
718  * @brief RSA decryption primitive
719  * @param[in] key RSA private key
720  * @param[in] c Ciphertext representative
721  * @param[out] m Message representative
722  * @return Error code
723  **/
724 
725 error_t rsadp(const RsaPrivateKey *key, const Mpi *c, Mpi *m)
726 {
727  error_t error;
728  size_t nLen;
729  size_t dLen;
730  size_t pLen;
731  size_t qLen;
732  size_t dpLen;
733  size_t dqLen;
734  size_t qinvLen;
735 
736  //Retrieve the length of the private key
737  nLen = mpiGetByteLength(&key->n);
738  dLen = mpiGetByteLength(&key->d);
739  pLen = mpiGetByteLength(&key->p);
740  qLen = mpiGetByteLength(&key->q);
741  dpLen = mpiGetByteLength(&key->dp);
742  dqLen = mpiGetByteLength(&key->dq);
743  qinvLen = mpiGetByteLength(&key->qinv);
744 
745  //Sanity check
746  if(nLen == 0)
748 
749  //The ciphertext representative c shall be between 0 and n - 1
750  if(mpiCompInt(c, 0) < 0 || mpiComp(c, &key->n) >= 0)
751  return ERROR_OUT_OF_RANGE;
752 
753  //Use the Chinese remainder algorithm?
754  if(nLen > 0 && pLen > 0 && qLen > 0 && dpLen > 0 && dqLen > 0 && qinvLen > 0)
755  {
756  size_t cLen;
757  size_t modLen;
758  size_t expLen;
759  uint8_t *pos;
760  PukccCrtParams params;
761 
762  //Retrieve the length of the ciphertext, in bytes
763  cLen = mpiGetByteLength(c);
764 
765  //Retrieve the length of the modulus, in bytes
766  modLen = MAX(pLen, qLen);
767  modLen = MAX(modLen, 12);
768  modLen = (modLen + 3U) & ~3U;
769 
770  //Retrieve the length of the reduced exponents, in bytes
771  expLen = MAX(dpLen, dqLen);
772  expLen = (expLen + 3U) & ~3U;
773 
774  //Check the length of the ciphertext
775  if(cLen > (2 * modLen))
776  return ERROR_INVALID_LENGTH;
777 
778  //Acquire exclusive access to the PUKCC accelerator
780 
781  //Point to the crypto memory
782  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
783 
784  //Copy primes
785  params.q = pukccImportMpi(&pos, &key->q, modLen + 4);
786  params.p = pukccImportMpi(&pos, &key->p, modLen + 4);
787 
788  //Copy input number
789  params.x = pukccImportMpi(&pos, c, 2 * modLen + 16);
790 
791  //The reduced exponents must be given with a supplemental word on the
792  //LSB side (low addresses). This word shall be set to zero
793  params.dq = pukccWorkspace(&pos, 4);
794  pukccImportMpi(&pos, &key->dq, expLen);
795  params.dp = pukccWorkspace(&pos, 4);
796  pukccImportMpi(&pos, &key->dp, expLen);
797 
798  //Copy R value
799  params.r = pukccImportMpi(&pos, &key->qinv, modLen + 4);
800  //Initialize workspace
801  pukccWorkspace(&pos, 3 * (modLen + 4) + MAX(64, 1 * (modLen + 4)) + 8);
802 
803  //Set CRT service parameters
804  PUKCL(u2Option) = PUKCL_EXPMOD_REGULARRSA | PUKCL_EXPMOD_WINDOWSIZE_1 |
805  PUKCL_EXPMOD_EXPINPUKCCRAM;
806  PUKCL_CRT(u2ModLength) = modLen;
807  PUKCL_CRT(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.q);
808  PUKCL_CRT(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
809  PUKCL_CRT(nu1PrecompBase) = PUKCC_FAR_TO_NEAR(params.r);
810  PUKCL_CRT(u2ExpLength) = expLen;
811  PUKCL_CRT(pfu1ExpBase) = params.dq;
812  PUKCL_CRT(u1Blinding) = 0;
813 
814  //Perform modular exponentiation (with CRT)
815  vPUKCL_Process(CRT, pvPUKCLParam);
816 
817  //Check status code
818  if(PUKCL(u2Status) == PUKCL_OK)
819  {
820  //Copy resulting integer
821  error = mpiImport(m, params.x, 2 * modLen, MPI_FORMAT_LITTLE_ENDIAN);
822  }
823  else
824  {
825  //Report an error
826  error = ERROR_FAILURE;
827  }
828 
829  //Release exclusive access to the PUKCC accelerator
831  }
832  else if(nLen > 0 && dLen > 0)
833  {
834  //Perform modular exponentiation (without CRT)
835  error = mpiExpMod(m, c, &key->d, &key->n);
836  }
837  else
838  {
839  //Invalid parameters
840  error = ERROR_INVALID_PARAMETER;
841  }
842 
843  //Return status code
844  return error;
845 }
846 
847 
848 /**
849  * @brief Check whether the affine point S is on the curve
850  * @param[in] ecParams EC domain parameters
851  * @param[in] s Affine representation of the point
852  * @return TRUE if the affine point S is on the curve, else FALSE
853  **/
854 
856 {
857  bool_t valid;
858  size_t modLen;
859  uint8_t *pos;
861 
862  //Retrieve the length of the modulus, in bytes
863  modLen = mpiGetByteLength(&ecParams->p);
864  modLen = (modLen + 3U) & ~3U;
865 
866  //Acquire exclusive access to the PUKCC accelerator
868 
869  //Point to the crypto memory
870  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
871 
872  //Copy modulus
873  params.mod = pukccImportMpi(&pos, &ecParams->p, modLen + 4);
874  //Initialize reduction constant
875  params.cns = pukccWorkspace(&pos, modLen + 12);
876  //Initialize workspace R
877  params.r = pukccWorkspace(&pos, 64);
878  //Initialize workspace X
879  params.x = pukccWorkspace(&pos, 2 * modLen + 8);
880 
881  //Set RedMod service parameters
882  PUKCL(u2Option) = PUKCL_REDMOD_SETUP;
883  PUKCL(Specific).CarryIn = 0;
884  PUKCL(Specific).Gf2n = 0;
885  PUKCL_RedMod(u2ModLength) = modLen;
886  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
887  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
888  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
889  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
890 
891  //Perform modular reduction setup
892  vPUKCL_Process(RedMod, pvPUKCLParam);
893 
894  //Check status code
895  if(PUKCL(u2Status) == PUKCL_OK)
896  {
897  //Point to the crypto memory
898  pos = params.r;
899 
900  //Copy point coordinates
901  params.point.x = pukccImportMpi(&pos, &s->x, modLen + 4);
902  params.point.y = pukccImportMpi(&pos, &s->y, modLen + 4);
903  params.point.z = pukccWorkspace(&pos, modLen + 4);
904  params.point.z[0] = 1;
905 
906  //Copy curve parameter a
907  params.a = pukccImportMpi(&pos, &ecParams->a, modLen + 4);
908  //Copy curve parameter b
909  params.b = pukccImportMpi(&pos, &ecParams->b, modLen + 4);
910  //Initialize workspace
911  params.w = pukccWorkspace(&pos, 4 * modLen + 28);
912 
913  //Set ZpEcPointIsOnCurve service parameters
914  PUKCL_ZpEcPointIsOnCurve(u2ModLength) = modLen;
915  PUKCL_ZpEcPointIsOnCurve(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
916  PUKCL_ZpEcPointIsOnCurve(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
917  PUKCL_ZpEcPointIsOnCurve(nu1PointBase) = PUKCC_FAR_TO_NEAR(params.point.x);
918  PUKCL_ZpEcPointIsOnCurve(nu1AParam) = PUKCC_FAR_TO_NEAR(params.a);
919  PUKCL_ZpEcPointIsOnCurve(nu1BParam) = PUKCC_FAR_TO_NEAR(params.b);
920  PUKCL_ZpEcPointIsOnCurve(nu1Workspace) = PUKCC_FAR_TO_NEAR(params.w);
921 
922  //Test whether the point is on the curve
923  vPUKCL_Process(ZpEcPointIsOnCurve, pvPUKCLParam);
924  }
925 
926  //Check status code
927  if(PUKCL(u2Status) == PUKCL_OK)
928  {
929  //The point S is on the elliptic curve
930  valid = TRUE;
931  }
932  else
933  {
934  //The point S is not on the elliptic curve
935  valid = FALSE;
936  }
937 
938  //Release exclusive access to the PUKCC accelerator
940 
941  //Return TRUE if the affine point S is on the curve, else FALSE
942  return valid;
943 }
944 
945 
946 /**
947  * @brief Recover affine representation
948  * @param[in] ecParams EC domain parameters
949  * @param[out] r Affine representation of the point
950  * @param[in] s Projective representation of the point
951  * @return Error code
952  **/
953 
955  const EcPoint *s)
956 {
957  error_t error;
958  size_t modLen;
959  uint8_t *pos;
960  PukccZpEcConvProjToAffineParams params = {0};
961 
962  //Retrieve the length of the modulus, in bytes
963  modLen = mpiGetByteLength(&ecParams->p);
964  modLen = (modLen + 3U) & ~3U;
965 
966  //Acquire exclusive access to the PUKCC accelerator
968 
969  //Point to the crypto memory
970  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
971 
972  //Copy modulus
973  params.mod = pukccImportMpi(&pos, &ecParams->p, modLen + 4);
974  //Initialize reduction constant
975  params.cns = pukccWorkspace(&pos, modLen + 12);
976  //Initialize workspace R
977  params.r = pukccWorkspace(&pos, 64);
978  //Initialize workspace X
979  params.x = pukccWorkspace(&pos, 2 * modLen + 8);
980 
981  //Set RedMod service parameters
982  PUKCL(u2Option) = PUKCL_REDMOD_SETUP;
983  PUKCL(Specific).CarryIn = 0;
984  PUKCL(Specific).Gf2n = 0;
985  PUKCL_RedMod(u2ModLength) = modLen;
986  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
987  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
988  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
989  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
990 
991  //Perform modular reduction setup
992  vPUKCL_Process(RedMod, pvPUKCLParam);
993 
994  //Check status code
995  if(PUKCL(u2Status) == PUKCL_OK)
996  {
997  //Point to the crypto memory
998  pos = params.r;
999 
1000  //Copy point coordinates
1001  params.point.x = pukccImportMpi(&pos, &s->x, modLen + 4);
1002  params.point.y = pukccImportMpi(&pos, &s->y, modLen + 4);
1003  params.point.z = pukccImportMpi(&pos, &s->z, modLen + 4);
1004  //Initialize workspace
1005  params.w = pukccWorkspace(&pos, 4 * modLen + 48);
1006 
1007  //Set ZpEccConvAffineToProjective service parameters
1008  PUKCL_ZpEcConvProjToAffine(u2ModLength) = modLen;
1009  PUKCL_ZpEcConvProjToAffine(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1010  PUKCL_ZpEcConvProjToAffine(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1011  PUKCL_ZpEcConvProjToAffine(nu1PointABase) = PUKCC_FAR_TO_NEAR(params.point.x);
1012  PUKCL_ZpEcConvProjToAffine(nu1Workspace) = PUKCC_FAR_TO_NEAR(params.w);
1013 
1014  //Convert point coordinates from projective to affine representation
1015  vPUKCL_Process(ZpEcConvProjToAffine, pvPUKCLParam);
1016  }
1017 
1018  //Check status code
1019  if(PUKCL(u2Status) == PUKCL_OK)
1020  {
1021  //Copy the x-coordinate of the result
1022  error = mpiImport(&r->x, params.point.x, modLen,
1024 
1025  //Check error code
1026  if(!error)
1027  {
1028  //Copy the y-coordinate of the result
1029  error = mpiImport(&r->y, params.point.y, modLen,
1031  }
1032 
1033  //Check error code
1034  if(!error)
1035  {
1036  //Copy the z-coordinate of the result
1037  error = mpiImport(&r->z, params.point.z, modLen,
1039  }
1040  }
1041  else
1042  {
1043  //Report an error
1044  error = ERROR_FAILURE;
1045  }
1046 
1047  //Release exclusive access to the PUKCC accelerator
1049 
1050  //Return error code
1051  return error;
1052 }
1053 
1054 
1055 /**
1056  * @brief Scalar multiplication
1057  * @param[in] ecParams EC domain parameters
1058  * @param[out] r Resulting point R = d.S
1059  * @param[in] d An integer d such as 0 <= d < p
1060  * @param[in] s EC point
1061  * @return Error code
1062  **/
1063 
1064 error_t ecMult(const EcDomainParameters *ecParams, EcPoint *r, const Mpi *d,
1065  const EcPoint *s)
1066 {
1067  error_t error;
1068  size_t kLen;
1069  size_t modLen;
1070  uint8_t *pos;
1071  PukccZpEccMulParams params = {0};
1072 
1073  //Retrieve the length of the modulus, in bytes
1074  modLen = mpiGetByteLength(&ecParams->p);
1075  modLen = (modLen + 3U) & ~3U;
1076 
1077  //Retrieve the length of the scalar number
1078  kLen = mpiGetByteLength(d);
1079  kLen = (kLen + 3U) & ~3U;
1080 
1081  //Acquire exclusive access to the PUKCC accelerator
1083 
1084  //Point to the crypto memory
1085  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
1086 
1087  //Copy modulus
1088  params.mod = pukccImportMpi(&pos, &ecParams->p, modLen + 4);
1089  //Initialize reduction constant
1090  params.cns = pukccWorkspace(&pos, modLen + 12);
1091  //Initialize workspace R
1092  params.r = pukccWorkspace(&pos, 64);
1093  //Initialize workspace X
1094  params.x = pukccWorkspace(&pos, 2 * modLen + 8);
1095 
1096  //Set RedMod service parameters
1097  PUKCL(u2Option) = PUKCL_REDMOD_SETUP;
1098  PUKCL(Specific).CarryIn = 0;
1099  PUKCL(Specific).Gf2n = 0;
1100  PUKCL_RedMod(u2ModLength) = modLen;
1101  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1102  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1103  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
1104  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
1105 
1106  //Perform modular reduction setup
1107  vPUKCL_Process(RedMod, pvPUKCLParam);
1108 
1109  //Check status code
1110  if(PUKCL(u2Status) == PUKCL_OK)
1111  {
1112  //Point to the crypto memory
1113  pos = params.r;
1114 
1115  //Copy scalar number
1116  params.k = pukccImportMpi(&pos, d, kLen + 4);
1117 
1118  //Copy point coordinates
1119  params.point.x = pukccImportMpi(&pos, &s->x, modLen + 4);
1120  params.point.y = pukccImportMpi(&pos, &s->y, modLen + 4);
1121  params.point.z = pukccWorkspace(&pos, modLen + 4);
1122  params.point.z[0] = 1;
1123 
1124  //Copy curve parameter a
1125  params.a = pukccImportMpi(&pos, &ecParams->a, modLen + 4);
1126  //Initialize workspace
1127  params.w = pukccWorkspace(&pos, 8 * modLen + 44);
1128 
1129  //Set ZpEccMulFast service parameters
1130  PUKCL_ZpEccMul(u2ModLength) = modLen;
1131  PUKCL_ZpEccMul(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1132  PUKCL_ZpEccMul(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1133  PUKCL_ZpEccMul(u2KLength) = kLen;
1134  PUKCL_ZpEccMul(nu1KBase) = PUKCC_FAR_TO_NEAR(params.k);
1135  PUKCL_ZpEccMul(nu1PointBase) = PUKCC_FAR_TO_NEAR(params.point.x);
1136  PUKCL_ZpEccMul(nu1ABase) = PUKCC_FAR_TO_NEAR(params.a);
1137  PUKCL_ZpEccMul(nu1Workspace) = PUKCC_FAR_TO_NEAR(params.w);
1138 
1139  //Perform scalar multiplication over GF(p)
1140  vPUKCL_Process(ZpEccMulFast, pvPUKCLParam);
1141  }
1142 
1143  //Check status code
1144  if(PUKCL(u2Status) == PUKCL_OK)
1145  {
1146  //Copy the x-coordinate of the result
1147  error = mpiImport(&r->x, params.point.x, modLen,
1149 
1150  //Check error code
1151  if(!error)
1152  {
1153  //Copy the y-coordinate of the result
1154  error = mpiImport(&r->y, params.point.y, modLen,
1156  }
1157 
1158  //Check error code
1159  if(!error)
1160  {
1161  //Copy the z-coordinate of the result
1162  error = mpiImport(&r->z, params.point.z, modLen,
1164  }
1165  }
1166  else
1167  {
1168  //Report an error
1169  error = ERROR_FAILURE;
1170  }
1171 
1172  //Release exclusive access to the PUKCC accelerator
1174 
1175  //Return error code
1176  return error;
1177 }
1178 
1179 
1180 /**
1181  * @brief ECDSA signature generation
1182  * @param[in] prngAlgo PRNG algorithm
1183  * @param[in] prngContext Pointer to the PRNG context
1184  * @param[in] ecParams EC domain parameters
1185  * @param[in] privateKey Signer's ECDSA private key
1186  * @param[in] digest Digest of the message to be signed
1187  * @param[in] digestLen Length in octets of the digest
1188  * @param[out] signature (R, S) integer pair
1189  * @return Error code
1190  **/
1191 
1192 error_t ecdsaGenerateSignature(const PrngAlgo *prngAlgo, void *prngContext,
1193  const EcDomainParameters *ecParams, const EcPrivateKey *privateKey,
1194  const uint8_t *digest, size_t digestLen, EcdsaSignature *signature)
1195 {
1196  error_t error;
1197  size_t modLen;
1198  size_t orderLen;
1199  size_t scalarLen;
1200  uint8_t *pos;
1202  Mpi k;
1203 
1204  //Check parameters
1205  if(ecParams == NULL || privateKey == NULL || digest == NULL || signature == NULL)
1206  return ERROR_INVALID_PARAMETER;
1207 
1208  //Retrieve the length of the modulus, in bytes
1209  modLen = mpiGetByteLength(&ecParams->p);
1210  modLen = (modLen + 3U) & ~3U;
1211 
1212  //Retrieve the length of the base point order, in bytes
1213  orderLen = mpiGetByteLength(&ecParams->q);
1214  //Compute the length of the scalar
1215  scalarLen = (orderLen + 3U) & ~3U;
1216  //Keep the leftmost bits of the hash value
1217  digestLen = MIN(digestLen, orderLen);
1218 
1219  //Initialize multiple precision integer
1220  mpiInit(&k);
1221 
1222  //Generate a random number k such as 0 < k < q - 1
1223  error = mpiRandRange(&k, &ecParams->q, prngAlgo, prngContext);
1224 
1225  //Acquire exclusive access to the PUKCC accelerator
1227 
1228  //Check error code
1229  if(!error)
1230  {
1231  //Point to the crypto memory
1232  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
1233 
1234  //Copy modulus
1235  params.mod = pukccImportMpi(&pos, &ecParams->p, modLen + 4);
1236  //Initialize reduction constant
1237  params.cns = pukccWorkspace(&pos, scalarLen + 12);
1238  //Initialize workspace R
1239  params.r = pukccWorkspace(&pos, 64);
1240  //Initialize workspace X
1241  params.x = pukccWorkspace(&pos, 2 * modLen + 8);
1242 
1243  //Set RedMod service parameters
1244  PUKCL(u2Option) = PUKCL_REDMOD_SETUP;
1245  PUKCL(Specific).CarryIn = 0;
1246  PUKCL(Specific).Gf2n = 0;
1247  PUKCL_RedMod(u2ModLength) = modLen;
1248  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1249  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1250  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
1251  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
1252 
1253  //Perform modular reduction setup
1254  vPUKCL_Process(RedMod, pvPUKCLParam);
1255 
1256  //Check status code
1257  if(PUKCL(u2Status) != PUKCL_OK)
1258  error = ERROR_FAILURE;
1259  }
1260 
1261  //Check error code
1262  if(!error)
1263  {
1264  //Point to the crypto memory
1265  pos = params.r;
1266 
1267  //Copy base point coordinates
1268  params.basePoint.x = pukccImportMpi(&pos, &ecParams->g.x, modLen + 4);
1269  params.basePoint.y = pukccImportMpi(&pos, &ecParams->g.y, modLen + 4);
1270  params.basePoint.z = pukccImportMpi(&pos, &ecParams->g.z, modLen + 4);
1271 
1272  //Copy base point order
1273  params.order = pukccImportMpi(&pos, &ecParams->q, scalarLen + 4);
1274  //Copy curve parameter a
1275  params.a = pukccImportMpi(&pos, &ecParams->a, modLen + 4);
1276  //Copy private key
1277  params.privateKey = pukccImportMpi(&pos, &privateKey->d, scalarLen + 4);
1278  //Copy random scalar
1279  params.k = pukccImportMpi(&pos, &k, scalarLen + 4);
1280  //Copy digest
1281  params.h = pukccImportArray(&pos, digest, digestLen, scalarLen + 4);
1282  //Initialize workspace
1283  params.w = pukccWorkspace(&pos, 8 * modLen + 44);
1284 
1285  //Set ZpEcDsaGenerateFast service parameters
1286  PUKCL_ZpEcDsaGenerate(u2ModLength) = modLen;
1287  PUKCL_ZpEcDsaGenerate(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1288  PUKCL_ZpEcDsaGenerate(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1289  PUKCL_ZpEcDsaGenerate(nu1PointABase) = PUKCC_FAR_TO_NEAR(params.basePoint.x);
1290  PUKCL_ZpEcDsaGenerate(nu1OrderPointBase) = PUKCC_FAR_TO_NEAR(params.order);
1291  PUKCL_ZpEcDsaGenerate(nu1ABase) = PUKCC_FAR_TO_NEAR(params.a);
1292  PUKCL_ZpEcDsaGenerate(nu1PrivateKey) = PUKCC_FAR_TO_NEAR(params.privateKey);
1293  PUKCL_ZpEcDsaGenerate(u2ScalarLength) = scalarLen;
1294  PUKCL_ZpEcDsaGenerate(nu1ScalarNumber) = PUKCC_FAR_TO_NEAR(params.k);
1295  PUKCL_ZpEcDsaGenerate(nu1HashBase) = PUKCC_FAR_TO_NEAR(params.h);
1296  PUKCL_ZpEcDsaGenerate(nu1Workspace) = PUKCC_FAR_TO_NEAR(params.w);
1297 
1298  //Perform ECDSA signature generation
1299  vPUKCL_Process(ZpEcDsaGenerateFast, pvPUKCLParam);
1300 
1301  //Check status code
1302  if(PUKCL(u2Status) != PUKCL_OK)
1303  error = ERROR_FAILURE;
1304  }
1305 
1306  //Check error code
1307  if(!error)
1308  {
1309  //Copy the first part of the ECDSA signature
1310  error = mpiImport(&signature->r, params.basePoint.x, scalarLen,
1312  }
1313 
1314  //Check error code
1315  if(!error)
1316  {
1317  //Copy the second part of the ECDSA signature
1318  error = mpiImport(&signature->s, params.basePoint.x + scalarLen + 4,
1319  scalarLen, MPI_FORMAT_LITTLE_ENDIAN);
1320  }
1321 
1322  //Release exclusive access to the PUKCC accelerator
1324 
1325  //Check error code
1326  if(!error)
1327  {
1328  //Dump ECDSA signature
1329  TRACE_DEBUG(" r:\r\n");
1330  TRACE_DEBUG_MPI(" ", &signature->r);
1331  TRACE_DEBUG(" s:\r\n");
1332  TRACE_DEBUG_MPI(" ", &signature->s);
1333  }
1334 
1335  //Release multiple precision integer
1336  mpiFree(&k);
1337 
1338  //Return error code
1339  return error;
1340 }
1341 
1342 
1343 /**
1344  * @brief ECDSA signature verification
1345  * @param[in] ecParams EC domain parameters
1346  * @param[in] publicKey Signer's ECDSA public key
1347  * @param[in] digest Digest of the message whose signature is to be verified
1348  * @param[in] digestLen Length in octets of the digest
1349  * @param[in] signature (R, S) integer pair
1350  * @return Error code
1351  **/
1352 
1354  const EcPublicKey *publicKey, const uint8_t *digest, size_t digestLen,
1355  const EcdsaSignature *signature)
1356 {
1357  error_t error;
1358  size_t modLen;
1359  size_t orderLen;
1360  size_t scalarLen;
1361  uint8_t *pos;
1362  PukccZpEcDsaVerifyParams params;
1363 
1364  //Check parameters
1365  if(ecParams == NULL || publicKey == NULL || digest == NULL || signature == NULL)
1366  return ERROR_INVALID_PARAMETER;
1367 
1368  //The verifier shall check that 0 < r < q
1369  if(mpiCompInt(&signature->r, 0) <= 0 ||
1370  mpiComp(&signature->r, &ecParams->q) >= 0)
1371  {
1372  //If the condition is violated, the signature shall be rejected as invalid
1373  return ERROR_INVALID_SIGNATURE;
1374  }
1375 
1376  //The verifier shall check that 0 < s < q
1377  if(mpiCompInt(&signature->s, 0) <= 0 ||
1378  mpiComp(&signature->s, &ecParams->q) >= 0)
1379  {
1380  //If the condition is violated, the signature shall be rejected as invalid
1381  return ERROR_INVALID_SIGNATURE;
1382  }
1383 
1384  //Retrieve the length of the modulus, in bytes
1385  modLen = mpiGetByteLength(&ecParams->p);
1386  modLen = (modLen + 3U) & ~3U;
1387 
1388  //Retrieve the length of the base point order, in bytes
1389  orderLen = mpiGetByteLength(&ecParams->q);
1390  //Compute the length of the scalar
1391  scalarLen = (orderLen + 3U) & ~3U;
1392  //Keep the leftmost bits of the hash value
1393  digestLen = MIN(digestLen, orderLen);
1394 
1395  //Acquire exclusive access to the PUKCC accelerator
1397 
1398  //Point to the crypto memory
1399  pos = (uint8_t *) PUKCC_CRYPTO_RAM_BASE;
1400 
1401  //Copy modulus
1402  params.mod = pukccImportMpi(&pos, &ecParams->p, modLen + 4);
1403  //Initialize reduction constant
1404  params.cns = pukccWorkspace(&pos, scalarLen + 12);
1405  //Initialize workspace R
1406  params.r = pukccWorkspace(&pos, 64);
1407  //Initialize workspace X
1408  params.x = pukccWorkspace(&pos, 2 * modLen + 8);
1409 
1410  //Set RedMod service parameters
1411  PUKCL(u2Option) = PUKCL_REDMOD_SETUP;
1412  PUKCL(Specific).CarryIn = 0;
1413  PUKCL(Specific).Gf2n = 0;
1414  PUKCL_RedMod(u2ModLength) = modLen;
1415  PUKCL_RedMod(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1416  PUKCL_RedMod(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1417  PUKCL_RedMod(nu1RBase) = PUKCC_FAR_TO_NEAR(params.r);
1418  PUKCL_RedMod(nu1XBase) = PUKCC_FAR_TO_NEAR(params.x);
1419 
1420  //Perform modular reduction setup
1421  vPUKCL_Process(RedMod, pvPUKCLParam);
1422 
1423  //Check status code
1424  if(PUKCL(u2Status) == PUKCL_OK)
1425  {
1426  //Point to the crypto memory
1427  pos = params.r;
1428 
1429  //Copy base point coordinates
1430  params.basePoint.x = pukccImportMpi(&pos, &ecParams->g.x, modLen + 4);
1431  params.basePoint.y = pukccImportMpi(&pos, &ecParams->g.y, modLen + 4);
1432  params.basePoint.z = pukccImportMpi(&pos, &ecParams->g.z, modLen + 4);
1433 
1434  //Copy base point order
1435  params.order = pukccImportMpi(&pos, &ecParams->q, scalarLen + 4);
1436  //Copy curve parameter a
1437  params.a = pukccImportMpi(&pos, &ecParams->a, modLen + 4);
1438 
1439  //Copy public key
1440  params.publicKey.x = pukccImportMpi(&pos, &publicKey->q.x, modLen + 4);
1441  params.publicKey.y = pukccImportMpi(&pos, &publicKey->q.y, modLen + 4);
1442  params.publicKey.z = pukccWorkspace(&pos, modLen + 4);
1443  params.publicKey.z[0] = 1;
1444 
1445  //Copy digest
1446  params.h = pukccImportArray(&pos, digest, digestLen, scalarLen + 4);
1447 
1448  //Copy signature
1449  params.r = pukccImportMpi(&pos, &signature->r, scalarLen + 4);
1450  params.s = pukccImportMpi(&pos, &signature->s, scalarLen + 4);
1451 
1452  //Initialize workspace
1453  params.w = pukccWorkspace(&pos, 8 * modLen + 44);
1454 
1455  //Set ZpEcDsaVerifyFast service parameters
1456  PUKCL(u2Option) = 0;
1457  PUKCL_ZpEcDsaVerify(u2ModLength) = modLen;
1458  PUKCL_ZpEcDsaVerify(nu1ModBase) = PUKCC_FAR_TO_NEAR(params.mod);
1459  PUKCL_ZpEcDsaVerify(nu1CnsBase) = PUKCC_FAR_TO_NEAR(params.cns);
1460  PUKCL_ZpEcDsaVerify(nu1PointABase) = PUKCC_FAR_TO_NEAR(params.basePoint.x);
1461  PUKCL_ZpEcDsaVerify(nu1OrderPointBase) = PUKCC_FAR_TO_NEAR(params.order);
1462  PUKCL_ZpEcDsaVerify(nu1ABase) = PUKCC_FAR_TO_NEAR(params.a);
1463  PUKCL_ZpEcDsaVerify(nu1PointPublicKeyGen) = PUKCC_FAR_TO_NEAR(params.publicKey.x);
1464  PUKCL_ZpEcDsaVerify(u2ScalarLength) = scalarLen;
1465  PUKCL_ZpEcDsaVerify(nu1HashBase) = PUKCC_FAR_TO_NEAR(params.h);
1466  PUKCL_ZpEcDsaVerify(nu1PointSignature) = PUKCC_FAR_TO_NEAR(params.r);
1467  PUKCL_ZpEcDsaVerify(nu1Workspace) = PUKCC_FAR_TO_NEAR(params.w);
1468 
1469  //Perform ECDSA signature verification
1470  vPUKCL_Process(ZpEcDsaVerifyFast, pvPUKCLParam);
1471  }
1472 
1473  //Check status code
1474  if(PUKCL(u2Status) == PUKCL_OK)
1475  {
1476  //The ECDSA signature is valid
1477  error = NO_ERROR;
1478  }
1479  else if(PUKCL(u2Status) == PUKCL_WRONG_SIGNATURE)
1480  {
1481  //The ECDSA signature is not valid
1482  error = ERROR_INVALID_SIGNATURE;
1483  }
1484  else
1485  {
1486  //Report an error
1487  error = ERROR_FAILURE;
1488  }
1489 
1490  //Release exclusive access to the PUKCC accelerator
1492 
1493  //Return status code
1494  return error;
1495 }
1496 
1497 #endif
unsigned int uint_t
Definition: compiler_port.h:50
int bool_t
Definition: compiler_port.h:53
General definitions for cryptographic algorithms.
#define PrngAlgo
Definition: crypto.h:917
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_DEBUG_MPI(p, a)
Definition: debug.h:110
uint8_t n
ECC (Elliptic Curve Cryptography)
ECDSA (Elliptic Curve Digital Signature Algorithm)
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_SIGNATURE
Definition: error.h:226
@ ERROR_OUT_OF_RANGE
Definition: error.h:137
@ ERROR_INVALID_VALUE
Definition: error.h:116
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
error_t mpiRandRange(Mpi *r, const Mpi *p, const PrngAlgo *prngAlgo, void *prngContext)
Generate a random value in the range 1 to p-1.
Definition: mpi.c:564
uint_t mpiGetBitLength(const Mpi *a)
Get the actual length in bits.
Definition: mpi.c:234
int_t mpiCompInt(const Mpi *a, int_t b)
Compare a multiple precision integer with an integer.
Definition: mpi.c:382
int_t mpiComp(const Mpi *a, const Mpi *b)
Compare two multiple precision integers.
Definition: mpi.c:338
void mpiInit(Mpi *r)
Initialize a multiple precision integer.
Definition: mpi.c:48
error_t mpiImport(Mpi *r, const uint8_t *data, uint_t length, MpiFormat format)
Octet string to integer conversion.
Definition: mpi.c:624
uint_t mpiGetByteLength(const Mpi *a)
Get the actual length in bytes.
Definition: mpi.c:195
void mpiFree(Mpi *r)
Release a multiple precision integer.
Definition: mpi.c:64
error_t mpiExport(const Mpi *a, uint8_t *data, uint_t length, MpiFormat format)
Integer to octet string conversion.
Definition: mpi.c:709
MPI (Multiple Precision Integer Arithmetic)
@ MPI_FORMAT_LITTLE_ENDIAN
Definition: mpi.h:70
uint8_t b
Definition: nbns_common.h:104
uint8_t c
Definition: ndp.h:514
uint8_t r
Definition: ndp.h:346
uint8_t s
Definition: ndp.h:345
uint8_t p
Definition: ndp.h:300
uint8_t m
Definition: ndp.h:304
uint8_t a
Definition: ndp.h:411
#define osMemset(p, value, length)
Definition: os_port.h:135
#define MIN(a, b)
Definition: os_port.h:63
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define MAX(a, b)
Definition: os_port.h:67
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
#define PUKCC_CRYPTO_RAM_BASE
#define PUKCC_FAR_TO_NEAR(p)
RSA public-key cryptography standard.
OsMutex samd51CryptoMutex
Definition: samd51_crypto.c:45
SAMD51 hardware cryptographic accelerator.
uint8_t * pukccWorkspace(uint8_t **dest, size_t totalLen)
Initialize workspace area.
error_t ecdsaGenerateSignature(const PrngAlgo *prngAlgo, void *prngContext, const EcDomainParameters *ecParams, const EcPrivateKey *privateKey, const uint8_t *digest, size_t digestLen, EcdsaSignature *signature)
ECDSA signature generation.
error_t ecMult(const EcDomainParameters *ecParams, EcPoint *r, const Mpi *d, const EcPoint *s)
Scalar multiplication.
error_t ecAffinify(const EcDomainParameters *ecParams, EcPoint *r, const EcPoint *s)
Recover affine representation.
error_t mpiInvMod(Mpi *r, const Mpi *a, const Mpi *p)
Modular inverse.
error_t mpiMul(Mpi *r, const Mpi *a, const Mpi *b)
Multiple precision multiplication.
bool_t ecIsPointAffine(const EcDomainParameters *ecParams, const EcPoint *s)
Check whether the affine point S is on the curve.
error_t rsadp(const RsaPrivateKey *key, const Mpi *c, Mpi *m)
RSA decryption primitive.
uint8_t * pukccImportMpi(uint8_t **dest, const Mpi *src, size_t totalLen)
Import multiple-precision integer.
uint8_t * pukccImportArray(uint8_t **dest, const uint8_t *array, size_t arrayLen, size_t totalLen)
Import byte array.
error_t ecdsaVerifySignature(const EcDomainParameters *ecParams, const EcPublicKey *publicKey, const uint8_t *digest, size_t digestLen, const EcdsaSignature *signature)
ECDSA signature verification.
PPUKCL_PARAM pvPUKCLParam
error_t pukccInit(void)
Initialize PUKCC module.
PUKCL_PARAM PUKCLParam
error_t mpiExpMod(Mpi *r, const Mpi *a, const Mpi *e, const Mpi *p)
Modular exponentiation.
error_t mpiMod2(Mpi *r, const Mpi *a, const Mpi *p)
Modulo operation.
error_t mpiCheckProbablePrime(const Mpi *a)
Test whether a number is probable prime.
SAMD51 public-key hardware accelerator (PUKCC)
EC domain parameters.
Definition: ec.h:76
Mpi p
Prime.
Definition: ec.h:79
EcPoint g
Base point G.
Definition: ec.h:82
Mpi q
Order of the point G.
Definition: ec.h:83
Mpi a
Curve parameter a.
Definition: ec.h:80
Mpi b
Curve parameter b.
Definition: ec.h:81
EC point.
Definition: ec.h:64
Mpi y
y-coordinate
Definition: ec.h:66
Mpi x
x-coordinate
Definition: ec.h:65
Mpi z
z-coordinate
Definition: ec.h:67
EC private key.
Definition: ec.h:104
Mpi d
Private key.
Definition: ec.h:105
EC public key.
Definition: ec.h:94
EcPoint q
Public key.
Definition: ec.h:95
ECDSA signature.
Definition: ecdsa.h:49
Arbitrary precision integer.
Definition: mpi.h:80
CRT service parameters.
ExpMod service parameters.
Fmult service parameters.
GCD service parameters.
PrimeGen service parameters.
RedMod service parameters.
ZpEcConvProjToAffine service parameters.
ZpEcDsaGenerate service parameters.
ZpEcDsaVerify service parameters.
ZpEcPointIsOnCurve service parameters.
ZpEccMul service parameters.
RSA private key.
Definition: rsa.h:68
Mpi p
First factor.
Definition: rsa.h:72
Mpi dq
Second factor's CRT exponent.
Definition: rsa.h:75
Mpi q
Second factor.
Definition: rsa.h:73
Mpi d
Private exponent.
Definition: rsa.h:71
Mpi dp
First factor's CRT exponent.
Definition: rsa.h:74
Mpi qinv
CRT coefficient.
Definition: rsa.h:76
Mpi n
Modulus.
Definition: rsa.h:69