ecdh.c
Go to the documentation of this file.
1 /**
2  * @file ecdh.c
3  * @brief ECDH (Elliptic Curve Diffie-Hellman) key exchange
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.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/crypto.h"
36 #include "ecc/ecdh.h"
37 #include "debug.h"
38 
39 //Check crypto library configuration
40 #if (ECDH_SUPPORT == ENABLED)
41 
42 
43 /**
44  * @brief Initialize ECDH context
45  * @param[in] context Pointer to the ECDH context
46  **/
47 
48 void ecdhInit(EcdhContext *context)
49 {
50  //Initialize EC domain parameters
51  ecInitDomainParameters(&context->params);
52 
53  //Initialize private and public keys
54  ecInitPrivateKey(&context->da);
55  ecInitPublicKey(&context->qa);
56  ecInitPublicKey(&context->qb);
57 }
58 
59 
60 /**
61  * @brief Release ECDH context
62  * @param[in] context Pointer to the ECDH context
63  **/
64 
65 void ecdhFree(EcdhContext *context)
66 {
67  //Release EC domain parameters
68  ecFreeDomainParameters(&context->params);
69 
70  //Release private and public keys
71  ecFreePrivateKey(&context->da);
72  ecFreePublicKey(&context->qa);
73  ecFreePublicKey(&context->qb);
74 }
75 
76 
77 /**
78  * @brief ECDH key pair generation
79  * @param[in] context Pointer to the ECDH context
80  * @param[in] prngAlgo PRNG algorithm
81  * @param[in] prngContext Pointer to the PRNG context
82  * @return Error code
83  **/
84 
85 error_t ecdhGenerateKeyPair(EcdhContext *context, const PrngAlgo *prngAlgo,
86  void *prngContext)
87 {
88  error_t error;
89 
90  //Debug message
91  TRACE_DEBUG("Generating ECDH key pair...\r\n");
92 
93  //Weierstrass elliptic curve?
94  if(context->params.type == EC_CURVE_TYPE_SECT_K1 ||
95  context->params.type == EC_CURVE_TYPE_SECT_R1 ||
96  context->params.type == EC_CURVE_TYPE_SECT_R2 ||
97  context->params.type == EC_CURVE_TYPE_SECP_K1 ||
98  context->params.type == EC_CURVE_TYPE_SECP_R1 ||
99  context->params.type == EC_CURVE_TYPE_SECP_R2 ||
101  {
102  //Generate an EC key pair
103  error = ecGenerateKeyPair(prngAlgo, prngContext, &context->params,
104  &context->da, &context->qa);
105  }
106 #if (X25519_SUPPORT == ENABLED)
107  //Curve25519 elliptic curve?
108  else if(context->params.type == EC_CURVE_TYPE_X25519)
109  {
110  uint8_t da[CURVE25519_BYTE_LEN];
111  uint8_t qa[CURVE25519_BYTE_LEN];
112  uint8_t g[CURVE25519_BYTE_LEN];
113 
114  //Generate 32 random bytes
115  error = prngAlgo->read(prngContext, da, CURVE25519_BYTE_LEN);
116 
117  //Check status code
118  if(!error)
119  {
120  //Debug message
121  TRACE_DEBUG(" Private key:\r\n");
123 
124  //Get the u-coordinate of the base point
125  error = mpiExport(&context->params.g.x, g, CURVE25519_BYTE_LEN,
127  }
128 
129  //Check status code
130  if(!error)
131  {
132  //Generate the public value using X25519 function
133  error = x25519(qa, da, g);
134  }
135 
136  //Check status code
137  if(!error)
138  {
139  //Save private key
140  error = mpiImport(&context->da.d, da, CURVE25519_BYTE_LEN,
142  }
143 
144  //Check status code
145  if(!error)
146  {
147  //Debug message
148  TRACE_DEBUG(" Public key:\r\n");
150 
151  //Save public key
152  error = mpiImport(&context->qa.q.x, qa, CURVE25519_BYTE_LEN,
154  }
155  }
156 #endif
157 #if (X448_SUPPORT == ENABLED)
158  //Curve448 elliptic curve?
159  else if(context->params.type == EC_CURVE_TYPE_X448)
160  {
161  uint8_t da[CURVE448_BYTE_LEN];
162  uint8_t qa[CURVE448_BYTE_LEN];
163  uint8_t g[CURVE448_BYTE_LEN];
164 
165  //Generate 56 random bytes
166  error = prngAlgo->read(prngContext, da, CURVE448_BYTE_LEN);
167 
168  //Check status code
169  if(!error)
170  {
171  //Debug message
172  TRACE_DEBUG(" Private key:\r\n");
174 
175  //Get the u-coordinate of the base point
176  error = mpiExport(&context->params.g.x, g, CURVE448_BYTE_LEN,
178  }
179 
180  //Check status code
181  if(!error)
182  {
183  //Generate the public value using X448 function
184  error = x448(qa, da, g);
185  }
186 
187  //Check status code
188  if(!error)
189  {
190  //Save private key
191  error = mpiImport(&context->da.d, da, CURVE448_BYTE_LEN,
193  }
194 
195  //Check status code
196  if(!error)
197  {
198  //Debug message
199  TRACE_DEBUG(" Public key:\r\n");
201 
202  //Save public key
203  error = mpiImport(&context->qa.q.x, qa, CURVE448_BYTE_LEN,
205  }
206  }
207 #endif
208  //Invalid elliptic curve?
209  else
210  {
211  //Report an error
212  error = ERROR_INVALID_TYPE;
213  }
214 
215  //Return status code
216  return error;
217 }
218 
219 
220 /**
221  * @brief Check ECDH public key
222  * @param[in] params EC domain parameters
223  * @param[in] publicKey Public key to be checked
224  * @return Error code
225  **/
226 
228 {
229  bool_t valid;
230 
231  //Initialize flag
232  valid = FALSE;
233 
234  //Weierstrass elliptic curve?
235  if(params->type == EC_CURVE_TYPE_SECT_K1 ||
236  params->type == EC_CURVE_TYPE_SECT_R1 ||
237  params->type == EC_CURVE_TYPE_SECT_R2 ||
238  params->type == EC_CURVE_TYPE_SECP_K1 ||
239  params->type == EC_CURVE_TYPE_SECP_R1 ||
240  params->type == EC_CURVE_TYPE_SECP_R2 ||
242  {
243  //Verify that 0 <= Qx < p
244  if(mpiCompInt(&publicKey->x, 0) >= 0 &&
245  mpiComp(&publicKey->x, &params->p) < 0)
246  {
247  //Verify that 0 <= Qy < p
248  if(mpiCompInt(&publicKey->y, 0) >= 0 &&
249  mpiComp(&publicKey->y, &params->p) < 0)
250  {
251  //Check whether the point is on the curve
252  valid = ecIsPointAffine(params, publicKey);
253  }
254  }
255 
256  //Valid point?
257  if(valid)
258  {
259  //If the cofactor is not 1, the implementation must verify that n.Q
260  //is the point at the infinity
261  if(params->h != 1)
262  {
263  error_t error;
264  EcPoint r;
265 
266  //Initialize flag
267  valid = FALSE;
268  //Initialize EC points
269  ecInit(&r);
270 
271  //Convert the peer's public key to projective representation
272  error = ecProjectify(params, publicKey, publicKey);
273 
274  //Check status code
275  if(!error)
276  {
277  //Compute R = n.Q
278  error = ecMult(params, &r, &params->q, publicKey);
279  }
280 
281  //Check status code
282  if(!error)
283  {
284  //Verify that the result is the point at the infinity
285  if(mpiCompInt(&r.z, 0) == 0)
286  {
287  valid = TRUE;
288  }
289  }
290 
291  //Release EC point
292  ecFree(&r);
293  }
294  }
295  }
296 #if (X25519_SUPPORT == ENABLED)
297  //Curve25519 elliptic curve?
298  else if(params->type == EC_CURVE_TYPE_X25519)
299  {
300  //The public key does not need to be validated
301  valid = TRUE;
302  }
303 #endif
304 #if (X448_SUPPORT == ENABLED)
305  //Curve448 elliptic curve?
306  else if(params->type == EC_CURVE_TYPE_X448)
307  {
308  //The public key does not need to be validated
309  valid = TRUE;
310  }
311 #endif
312  //Invalid elliptic curve?
313  else
314  {
315  //Just for sanity
316  valid = FALSE;
317  }
318 
319  //Return status code
320  if(valid)
321  {
322  return NO_ERROR;
323  }
324  else
325  {
327  }
328 }
329 
330 
331 /**
332  * @brief Compute ECDH shared secret
333  * @param[in] context Pointer to the ECDH context
334  * @param[out] output Buffer where to store the shared secret
335  * @param[in] outputSize Size of the buffer in bytes
336  * @param[out] outputLen Length of the resulting shared secret
337  * @return Error code
338  **/
339 
341  uint8_t *output, size_t outputSize, size_t *outputLen)
342 {
343  error_t error;
344 
345  //Debug message
346  TRACE_DEBUG("Computing Diffie-Hellman shared secret...\r\n");
347 
348  //Weierstrass elliptic curve?
349  if(context->params.type == EC_CURVE_TYPE_SECT_K1 ||
350  context->params.type == EC_CURVE_TYPE_SECT_R1 ||
351  context->params.type == EC_CURVE_TYPE_SECT_R2 ||
352  context->params.type == EC_CURVE_TYPE_SECP_K1 ||
353  context->params.type == EC_CURVE_TYPE_SECP_R1 ||
354  context->params.type == EC_CURVE_TYPE_SECP_R2 ||
356  {
357  size_t k;
358  EcPoint z;
359 
360  //Get the length in octets of the prime modulus
361  k = mpiGetByteLength(&context->params.p);
362 
363  //Make sure that the output buffer is large enough
364  if(outputSize >= k)
365  {
366  //Length of the resulting shared secret
367  *outputLen = k;
368 
369  //Initialize EC points
370  ecInit(&z);
371 
372  //Convert the peer's public key to projective representation
373  error = ecProjectify(&context->params, &context->qb.q, &context->qb.q);
374 
375  //Check status code
376  if(!error)
377  {
378  //Compute Z = da.Qb
379  error = ecMult(&context->params, &z, &context->da.d, &context->qb.q);
380  }
381 
382  //Check status code
383  if(!error)
384  {
385  //Convert Z to affine representation
386  error = ecAffinify(&context->params, &z, &z);
387  }
388 
389  //Check status code
390  if(!error)
391  {
392  //The shared secret is the x-coordinate of Z
393  error = mpiExport(&z.x, output, k, MPI_FORMAT_BIG_ENDIAN);
394  }
395 
396  //Release EC point
397  ecFree(&z);
398  }
399  else
400  {
401  //Report an error
402  error = ERROR_INVALID_LENGTH;
403  }
404  }
405 #if (X25519_SUPPORT == ENABLED)
406  //Curve25519 elliptic curve?
407  else if(context->params.type == EC_CURVE_TYPE_X25519)
408  {
409  uint_t i;
410  uint8_t mask;
411  uint8_t da[CURVE25519_BYTE_LEN];
412  uint8_t qb[CURVE25519_BYTE_LEN];
413 
414  //Make sure that the output buffer is large enough
415  if(outputSize >= CURVE25519_BYTE_LEN)
416  {
417  //Length of the resulting shared secret
418  *outputLen = CURVE25519_BYTE_LEN;
419 
420  //Retrieve private key
421  error = mpiExport(&context->da.d, da, CURVE25519_BYTE_LEN,
423 
424  //Check status code
425  if(!error)
426  {
427  //Get peer's public key
428  error = mpiExport(&context->qb.q.x, qb, CURVE25519_BYTE_LEN,
430  }
431 
432  //Check status code
433  if(!error)
434  {
435  //Generate shared secret K using X25519 function
436  error = x25519(output, da, qb);
437  }
438 
439  //Since Curve25519 has a cofactor of 8, an input point of small order
440  //will eliminate any contribution from the other party's private key
441  if(!error)
442  {
443  //This situation can be detected by checking for the all-zero output
444  for(mask = 0, i = 0; i < CURVE25519_BYTE_LEN; i++)
445  {
446  mask |= output[i];
447  }
448 
449  //Check whether K is the all-zero value and abort if so (refer to
450  //RFC 8422, sections 5.10 and 5.11)
451  if(mask == 0)
452  {
453  error = ERROR_ILLEGAL_PARAMETER;
454  }
455  }
456  }
457  else
458  {
459  //Report an error
460  error = ERROR_INVALID_LENGTH;
461  }
462  }
463 #endif
464 #if (X448_SUPPORT == ENABLED)
465  //Curve448 elliptic curve?
466  else if(context->params.type == EC_CURVE_TYPE_X448)
467  {
468  uint_t i;
469  uint8_t mask;
470  uint8_t da[CURVE448_BYTE_LEN];
471  uint8_t qb[CURVE448_BYTE_LEN];
472 
473  //Make sure that the output buffer is large enough
474  if(outputSize >= CURVE448_BYTE_LEN)
475  {
476  //Length of the resulting shared secret
477  *outputLen = CURVE448_BYTE_LEN;
478 
479  //Retrieve private key
480  error = mpiExport(&context->da.d, da, CURVE448_BYTE_LEN,
482 
483  //Check status code
484  if(!error)
485  {
486  //Get peer's public key
487  error = mpiExport(&context->qb.q.x, qb, CURVE448_BYTE_LEN,
489  }
490 
491  //Check status code
492  if(!error)
493  {
494  //Generate shared secret K using X448 function
495  error = x448(output, da, qb);
496  }
497 
498  //Since Curve448 has a cofactor of 4, an input point of small order
499  //will eliminate any contribution from the other party's private key
500  if(!error)
501  {
502  //This situation can be detected by checking for the all-zero output
503  for(mask = 0, i = 0; i < CURVE448_BYTE_LEN; i++)
504  {
505  mask |= output[i];
506  }
507 
508  //Check whether K is the all-zero value and abort if so (refer to
509  //RFC 8422, sections 5.10 and 5.11)
510  if(mask == 0)
511  {
512  error = ERROR_ILLEGAL_PARAMETER;
513  }
514  }
515  }
516  else
517  {
518  //Report an error
519  error = ERROR_INVALID_LENGTH;
520  }
521  }
522 #endif
523  //Invalid elliptic curve?
524  else
525  {
526  //Report an error
527  error = ERROR_INVALID_TYPE;
528  }
529 
530  //Check status code
531  if(!error)
532  {
533  //Debug message
534  TRACE_DEBUG(" Shared secret (%" PRIuSIZE " bytes):\r\n", *outputLen);
535  TRACE_DEBUG_ARRAY(" ", output, *outputLen);
536  }
537 
538  //Return status code
539  return error;
540 }
541 
542 #endif
__weak_func bool_t ecIsPointAffine(const EcDomainParameters *params, const EcPoint *s)
Check whether the affine point S is on the curve.
Definition: ec.c:565
EcPublicKey qa
One's own EC public key.
Definition: ecdh.h:62
int bool_t
Definition: compiler_port.h:53
Mpi p
Prime.
Definition: ec.h:79
error_t x448(uint8_t *r, const uint8_t *k, const uint8_t *u)
X448 function (scalar multiplication on Curve448)
Definition: x448.c:53
#define PrngAlgo
Definition: crypto.h:938
@ ERROR_ILLEGAL_PARAMETER
Definition: error.h:243
void ecInitDomainParameters(EcDomainParameters *params)
Initialize EC domain parameters.
Definition: ec.c:51
#define TRUE
Definition: os_port.h:50
EcPublicKey qb
Peer's EC public key.
Definition: ecdh.h:63
void ecdhFree(EcdhContext *context)
Release ECDH context.
Definition: ecdh.c:65
#define CURVE448_BYTE_LEN
Definition: curve448.h:39
Mpi d
Private key.
Definition: ec.h:105
EcPoint g
Base point G.
Definition: ec.h:82
ECDH (Elliptic Curve Diffie-Hellman) key exchange.
uint32_t h
Cofactor h.
Definition: ec.h:84
@ EC_CURVE_TYPE_SECP_K1
Definition: ec_curves.h:279
Mpi y
y-coordinate
Definition: ec.h:66
error_t mpiImport(Mpi *r, const uint8_t *data, uint_t length, MpiFormat format)
Octet string to integer conversion.
Definition: mpi.c:624
EC domain parameters.
Definition: ec.h:76
@ EC_CURVE_TYPE_X448
Definition: ec_curves.h:284
void ecFreeDomainParameters(EcDomainParameters *params)
Release EC domain parameters.
Definition: ec.c:72
error_t x25519(uint8_t *r, const uint8_t *k, const uint8_t *u)
X25519 function (scalar multiplication on Curve25519)
Definition: x25519.c:53
uint8_t r
Definition: ndp.h:346
#define FALSE
Definition: os_port.h:46
@ EC_CURVE_TYPE_SECP_R1
Definition: ec_curves.h:280
error_t mpiExport(const Mpi *a, uint8_t *data, uint_t length, MpiFormat format)
Integer to octet string conversion.
Definition: mpi.c:709
@ MPI_FORMAT_LITTLE_ENDIAN
Definition: mpi.h:70
error_t
Error codes.
Definition: error.h:43
#define CURVE25519_BYTE_LEN
Definition: curve25519.h:39
void ecInitPublicKey(EcPublicKey *key)
Initialize an EC public key.
Definition: ec.c:153
__weak_func error_t ecGenerateKeyPair(const PrngAlgo *prngAlgo, void *prngContext, const EcDomainParameters *params, EcPrivateKey *privateKey, EcPublicKey *publicKey)
EC key pair generation.
Definition: ec.c:209
void ecInit(EcPoint *r)
Initialize elliptic curve point.
Definition: ec.c:307
@ EC_CURVE_TYPE_SECT_R1
Definition: ec_curves.h:277
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ EC_CURVE_TYPE_SECT_K1
Definition: ec_curves.h:276
General definitions for cryptographic algorithms.
Mpi x
x-coordinate
Definition: ec.h:65
uint8_t mask
Definition: web_socket.h:319
@ ERROR_INVALID_TYPE
Definition: error.h:115
EC point.
Definition: ec.h:64
void ecFreePrivateKey(EcPrivateKey *key)
Release an EdDSA private key.
Definition: ec.c:192
error_t ecProjectify(const EcDomainParameters *params, EcPoint *r, const EcPoint *s)
Compute projective representation.
Definition: ec.c:495
@ EC_CURVE_TYPE_SECT_R2
Definition: ec_curves.h:278
__weak_func error_t ecAffinify(const EcDomainParameters *params, EcPoint *r, const EcPoint *s)
Recover affine representation.
Definition: ec.c:519
error_t ecdhComputeSharedSecret(EcdhContext *context, uint8_t *output, size_t outputSize, size_t *outputLen)
Compute ECDH shared secret.
Definition: ecdh.c:340
uint8_t z
Definition: dns_common.h:191
#define TRACE_DEBUG(...)
Definition: debug.h:107
void ecdhInit(EcdhContext *context)
Initialize ECDH context.
Definition: ecdh.c:48
EcPrivateKey da
One's own EC private key.
Definition: ecdh.h:61
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:108
@ EC_CURVE_TYPE_X25519
Definition: ec_curves.h:283
void ecInitPrivateKey(EcPrivateKey *key)
Initialize an EC private key.
Definition: ec.c:177
EcPoint q
Public key.
Definition: ec.h:95
@ EC_CURVE_TYPE_SECP_R2
Definition: ec_curves.h:281
@ MPI_FORMAT_BIG_ENDIAN
Definition: mpi.h:71
EcCurveType type
Curve type.
Definition: ec.h:78
int_t mpiComp(const Mpi *a, const Mpi *b)
Compare two multiple precision integers.
Definition: mpi.c:338
__weak_func error_t ecMult(const EcDomainParameters *params, EcPoint *r, const Mpi *d, const EcPoint *s)
Scalar multiplication.
Definition: ec.c:998
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
void ecFree(EcPoint *r)
Release an elliptic curve point.
Definition: ec.c:321
int_t mpiCompInt(const Mpi *a, int_t b)
Compare a multiple precision integer with an integer.
Definition: mpi.c:382
@ EC_CURVE_TYPE_BRAINPOOLP_R1
Definition: ec_curves.h:282
error_t ecdhCheckPublicKey(const EcDomainParameters *params, EcPoint *publicKey)
Check ECDH public key.
Definition: ecdh.c:227
Mpi q
Order of the point G.
Definition: ec.h:83
EcDomainParameters params
EC domain parameters.
Definition: ecdh.h:60
ECDH context.
Definition: ecdh.h:59
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void ecFreePublicKey(EcPublicKey *key)
Release an EC public key.
Definition: ec.c:165
error_t ecdhGenerateKeyPair(EcdhContext *context, const PrngAlgo *prngAlgo, void *prngContext)
ECDH key pair generation.
Definition: ecdh.c:85
uint_t mpiGetByteLength(const Mpi *a)
Get the actual length in bytes.
Definition: mpi.c:195