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