Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/libecc/src/ecdh/x25519_448.c
34869 views
1
/*
2
* Copyright (C) 2021 - This file is part of libecc project
3
*
4
* Authors:
5
* Ryad BENADJILA <[email protected]>
6
* Arnaud EBALARD <[email protected]>
7
*
8
* This software is licensed under a dual BSD and GPL v2 license.
9
* See LICENSE file at the root folder of the project.
10
*/
11
#include <libecc/lib_ecc_config.h>
12
#if defined(WITH_X25519) || defined(WITH_X448)
13
14
/*
15
* XXX: X25519 and X448 are incompatible with small stack devices for now ...
16
*/
17
#if defined(USE_SMALL_STACK)
18
#error "Error: X25519 and X448 are incompatible with USE_SMALL_STACK (devices low on memory)"
19
#endif
20
21
#if defined(WITH_X25519) && !defined(WITH_CURVE_WEI25519)
22
#error "Error: X25519 needs curve WEI25519 to be defined! Please define it in libecc config file"
23
#endif
24
#if defined(WITH_X448) && !defined(WITH_CURVE_WEI448)
25
#error "Error: X448 needs curve WEI448 to be defined! Please define it in libecc config file"
26
#endif
27
28
#include <libecc/ecdh/x25519_448.h>
29
30
#include <libecc/curves/curves.h>
31
#include <libecc/sig/ec_key.h>
32
#include <libecc/utils/utils.h>
33
#include <libecc/fp/fp_sqrt.h>
34
35
/* For randomness source */
36
#include <libecc/external_deps/rand.h>
37
38
/* This module mainly implements the X25519 and X448 functions strictly as defined in
39
* RFC7748.
40
*/
41
42
43
/* Scalar clamping/decoding
44
*
45
* NOTE: the scalar encoding is mainly here to ensure that it is of the form
46
* 2^254 plus eight times a value between 0 and 2^251 - 1 inclusive for X25519
47
* (2^447 plus four times a value between 0 and 2^445 - 1 inclusive for X448).
48
*
49
* This ensures "clearing the cofactor" to avoid small subgroup attacks as well
50
* as setting the scalar MSB to avoid timing/SCA attacks on scalar multiplication.
51
* These two desirable properties are part of the rationale behind X25519/X448).
52
*/
53
ATTRIBUTE_WARN_UNUSED_RET static int decode_scalar(u8 *scalar_decoded, const u8 *scalar, u8 len)
54
{
55
int ret;
56
u8 i;
57
58
/* Aliasing is not supported */
59
MUST_HAVE((scalar != scalar_decoded), ret, err);
60
/* Zero length is not accepted */
61
MUST_HAVE((len > 0), ret, err);
62
63
/* Endianness swapping */
64
for(i = 0; i < len; i++){
65
scalar_decoded[len - 1 - i] = scalar[i];
66
}
67
if(len == X25519_SIZE){
68
/* Curve25519 */
69
scalar_decoded[len - 1] &= 248;
70
scalar_decoded[0] &= 127;
71
scalar_decoded[0] |= 64;
72
}
73
else if(len == X448_SIZE){
74
/* Curve448 */
75
scalar_decoded[len - 1] &= 252;
76
scalar_decoded[0] |= 128;
77
}
78
else{
79
/* Error, unknown type */
80
ret = -1;
81
goto err;
82
}
83
84
ret = 0;
85
86
err:
87
return ret;
88
}
89
90
/* U coordinate decoding, mainly endianness swapping */
91
ATTRIBUTE_WARN_UNUSED_RET static int decode_u_coordinate(u8 *u_decoded, const u8 *u, u8 len)
92
{
93
int ret;
94
u8 i;
95
96
/* Aliasing is not supported */
97
MUST_HAVE((u != u_decoded), ret, err);
98
/* Zero length is not accepted */
99
MUST_HAVE((len > 0), ret, err);
100
101
for(i = 0; i < len; i++){
102
u_decoded[len - 1 - i] = u[i];
103
}
104
105
ret = 0;
106
107
err:
108
return ret;
109
}
110
111
/* U coordinate encoding, mainly endianness swapping */
112
ATTRIBUTE_WARN_UNUSED_RET static int encode_u_coordinate(u8 *u_encoded, const u8 *u, u8 len)
113
{
114
return decode_u_coordinate(u_encoded, u, len);
115
}
116
117
118
/* Find V coordinate from U coordinate on Curve25519 or Curve448 */
119
ATTRIBUTE_WARN_UNUSED_RET static int compute_v_from_u(fp_src_t u, fp_t v, ec_montgomery_crv_src_t crv)
120
{
121
int ret;
122
fp tmp;
123
124
tmp.magic = 0;
125
126
ret = aff_pt_montgomery_v_from_u(v, &tmp, u, crv);
127
/* NOTE: this square root is possibly non-existing if the
128
* u coordinate is on the quadratic twist of the curve.
129
* An error is returned in this case.
130
*/
131
132
fp_uninit(&tmp);
133
134
return ret;
135
}
136
137
138
/*
139
* This is the core computation of X25519 and X448.
140
*
141
* NOTE: the user of this primitive should be warned and aware that is is not fully compliant with the
142
* RFC7748 description as u coordinates on the quadratic twist of the curve are rejected as well
143
* as non canonical u.
144
* See the explanations in the implementation of the function for more context and explanations.
145
*/
146
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_core(const u8 *k, const u8 *u, u8 *res, u8 len)
147
{
148
int ret, iszero, loaded, cmp;
149
/* Note: our local variables holding scalar and coordinate have the maximum size
150
* (X448 size if it is defined, X25519 else).
151
*/
152
#if defined(WITH_X448)
153
u8 k_[X448_SIZE], u_[X448_SIZE];
154
#else
155
u8 k_[X25519_SIZE], u_[X25519_SIZE];
156
#endif
157
aff_pt_montgomery _Tmp;
158
prj_pt Q;
159
ec_montgomery_crv montgomery_curve;
160
ec_params shortw_curve_params;
161
ec_shortw_crv_src_t shortw_curve;
162
fp_src_t alpha_montgomery;
163
fp_src_t gamma_montgomery;
164
nn scalar;
165
nn_t v_coord_nn;
166
fp_t u_coord, v_coord;
167
nn_t cofactor;
168
169
_Tmp.magic = montgomery_curve.magic = Q.magic = WORD(0);
170
scalar.magic = WORD(0);
171
172
MUST_HAVE((k != NULL) && (u != NULL) && (res != NULL), ret, err);
173
/* Sanity check on sizes */
174
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
175
MUST_HAVE(((sizeof(k_) >= len) && (sizeof(u_) >= len)), ret, err);
176
177
/* First of all, we clamp and decode the scalar and u */
178
ret = decode_scalar(k_, k, len); EG(ret, err);
179
ret = decode_u_coordinate(u_, u, len); EG(ret, err);
180
181
/* Import curve parameters */
182
loaded = 0;
183
#if defined(WITH_X25519)
184
if(len == X25519_SIZE){
185
ret = import_params(&shortw_curve_params, &wei25519_str_params); EG(ret, err);
186
loaded = 1;
187
}
188
#endif
189
#if defined(WITH_X448)
190
if(len == X448_SIZE){
191
ret = import_params(&shortw_curve_params, &wei448_str_params); EG(ret, err);
192
loaded = 1;
193
}
194
#endif
195
/* Sanity check that we have loaded our curve parameters */
196
MUST_HAVE((loaded == 1), ret, err);
197
198
/* Make things more readable */
199
shortw_curve = &(shortw_curve_params.ec_curve);
200
cofactor = &(shortw_curve_params.ec_gen_cofactor);
201
alpha_montgomery = &(shortw_curve_params.ec_alpha_montgomery);
202
gamma_montgomery = &(shortw_curve_params.ec_gamma_montgomery);
203
/* NOTE: we use the projective point Q Fp values as temporary
204
* space to save some stack here.
205
*/
206
u_coord = &(Q.X);
207
v_coord = &(Q.Y);
208
v_coord_nn = &(v_coord->fp_val);
209
210
/* Get the isogenic Montgomery curve */
211
ret = curve_shortw_to_montgomery(shortw_curve, &montgomery_curve, alpha_montgomery,
212
gamma_montgomery); EG(ret, err);
213
214
/* Import the u coordinate as a big integer and Fp element */
215
ret = nn_init_from_buf(v_coord_nn, u_, len); EG(ret, err);
216
/* Reject non canonical u values.
217
* NOTE: we use v here as a temporary value.
218
*/
219
ret = nn_cmp(v_coord_nn, &(montgomery_curve.A.ctx->p), &cmp); EG(ret, err);
220
MUST_HAVE((cmp < 0), ret, err);
221
/* Now initialize u as Fp element with the reduced value */
222
ret = fp_init(u_coord, montgomery_curve.A.ctx); EG(ret, err);
223
ret = fp_set_nn(u_coord, v_coord_nn); EG(ret, err);
224
225
/* Compute the v coordinate from u */
226
ret = compute_v_from_u(u_coord, v_coord, &montgomery_curve); EG(ret, err);
227
/* NOTE: since we use isogenies of the Curve25519/448, we must stick to points
228
* belonging to this curve. Since not all u coordinates provide a v coordinate
229
* (i.e. a square residue from the curve formula), the computation above can trigger an error.
230
* When this is the case, this means that the u coordinate is on the quadtratic twist of
231
* the Montgomery curve (which is a secure curve by design here). We could perform computations
232
* on an isogenic curve of this twist, however we choose to return an error instead.
233
*
234
* Although this is not compliant with the Curve2551/448 original spirit (that accepts any u
235
* coordinate thanks to the x-coordinate only computations with the Montgomery Ladder),
236
* we emphasize here that in the key exchange defined in RFC7748 all the exchanged points
237
* (i.e. public keys) are derived from base points that are on the curve and not on its twist, meaning
238
* that all the exchanged u coordinates should belong to the curve. Diverging from this behavior would
239
* suggest that an attacker is trying to inject other values, and we are safe to reject them in the
240
* context of Diffie-Hellman based key exchange as defined in RFC7748.
241
*
242
* On the other hand, the drawback of rejecting u coordinates on the quadratic twist is that
243
* using the current X25519/448 primitive in other contexts than RFC7748 Diffie-Hellman could be
244
* limited and non interoperable with other implementations of this primive. Another issue is that
245
* this specific divergence exposes our implementation to be distinguishable from other standard ones
246
* in a "black box" analysis context, which is generally not very desirable even if no real security
247
* issue is induced.
248
*/
249
250
/* Get the affine point in Montgomery */
251
ret = aff_pt_montgomery_init_from_coords(&_Tmp, &montgomery_curve, u_coord, v_coord); EG(ret, err);
252
/* Transfer from Montgomery to short Weierstrass using the isogeny */
253
ret = aff_pt_montgomery_to_prj_pt_shortw(&_Tmp, shortw_curve, &Q); EG(ret, err);
254
255
/*
256
* Reject small order public keys: while this is not a strict requirement of RFC7748, there is no
257
* good reason to accept these weak values!
258
*/
259
ret = check_prj_pt_order(&Q, cofactor, PUBLIC_PT, &cmp); EG(ret, err);
260
MUST_HAVE((!cmp), ret, err);
261
262
/* Import the scalar as big number NN value */
263
ret = nn_init_from_buf(&scalar, k_, len); EG(ret, err);
264
/* Now proceed with the scalar multiplication */
265
#ifdef USE_SIG_BLINDING
266
ret = prj_pt_mul_blind(&Q, &scalar, &Q); EG(ret, err);
267
#else
268
ret = prj_pt_mul(&Q, &scalar, &Q); EG(ret, err);
269
#endif
270
271
/* Transfer back from short Weierstrass to Montgomery using the isogeny */
272
ret = prj_pt_shortw_to_aff_pt_montgomery(&Q, &montgomery_curve, &_Tmp); EG(ret, err);
273
274
/* Reject the all zero output */
275
ret = fp_iszero(&(_Tmp.u), &iszero); EG(ret, err);
276
MUST_HAVE((!iszero), ret, err);
277
278
/* Now export the resulting u coordinate ... */
279
ret = fp_export_to_buf(u_, len, &(_Tmp.u)); EG(ret, err);
280
/* ... and encode it in the output */
281
ret = encode_u_coordinate(res, u_, len);
282
283
err:
284
IGNORE_RET_VAL(local_memset(u_, 0, sizeof(u_)));
285
IGNORE_RET_VAL(local_memset(k_, 0, sizeof(k_)));
286
IGNORE_RET_VAL(local_memset(&shortw_curve_params, 0, sizeof(shortw_curve_params)));
287
288
nn_uninit(&scalar);
289
aff_pt_montgomery_uninit(&_Tmp);
290
prj_pt_uninit(&Q);
291
ec_montgomery_crv_uninit(&montgomery_curve);
292
293
PTR_NULLIFY(shortw_curve);
294
PTR_NULLIFY(alpha_montgomery);
295
PTR_NULLIFY(gamma_montgomery);
296
PTR_NULLIFY(u_coord);
297
PTR_NULLIFY(v_coord);
298
PTR_NULLIFY(v_coord_nn);
299
PTR_NULLIFY(cofactor);
300
301
return ret;
302
}
303
304
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_gen_priv_key(u8 *priv_key, u8 len)
305
{
306
int ret;
307
308
MUST_HAVE((priv_key != NULL), ret, err);
309
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
310
311
/* Generating a private key consists in generating a random byte string */
312
ret = get_random(priv_key, len);
313
314
err:
315
return ret;
316
}
317
318
319
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_init_pub_key(const u8 *priv_key, u8 *pub_key, u8 len)
320
{
321
int ret;
322
323
MUST_HAVE((priv_key != NULL) && (pub_key != NULL), ret, err);
324
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
325
326
/* Computing the public key is x25519(priv_key, 9) or x448(priv_key, 5)
327
*
328
* NOTE: although we could optimize and accelerate the computation of the public
329
* key by skipping the Montgomery to Weierstrass mapping (as the base point on the two
330
* isomorphic curves are known), we rather use the regular x25519_448_core primitive
331
* as it has the advantages of keeping the code clean and simple (and the performance
332
* cost is not so expensive as the core scalar multiplication will take most of the
333
* cycles ...).
334
*
335
*/
336
if(len == X25519_SIZE){
337
u8 u[X25519_SIZE];
338
339
ret = local_memset(u, 0, sizeof(u)); EG(ret, err);
340
/* X25519 uses the base point with x-coordinate = 0x09 */
341
u[0] = 0x09;
342
ret = x25519_448_core(priv_key, u, pub_key, len);
343
}
344
else if(len == X448_SIZE){
345
u8 u[X448_SIZE];
346
347
ret = local_memset(u, 0, sizeof(u)); EG(ret, err);
348
/* X448 uses the base point with x-coordinate = 0x05 */
349
u[0] = 0x05;
350
ret = x25519_448_core(priv_key, u, pub_key, len);
351
}
352
else{
353
ret = -1;
354
}
355
err:
356
return ret;
357
}
358
359
ATTRIBUTE_WARN_UNUSED_RET static int x25519_448_derive_secret(const u8 *priv_key, const u8 *peer_pub_key, u8 *shared_secret, u8 len)
360
{
361
int ret;
362
363
MUST_HAVE((priv_key != NULL) && (peer_pub_key != NULL) && (shared_secret != NULL), ret, err);
364
MUST_HAVE(((len == X25519_SIZE) || (len == X448_SIZE)), ret, err);
365
366
/* Computing the shared secret is x25519(priv_key, peer_pub_key) or x448(priv_key, peer_pub_key) */
367
ret = x25519_448_core(priv_key, peer_pub_key, shared_secret, len);
368
369
err:
370
return ret;
371
}
372
373
374
#if defined(WITH_X25519)
375
/* The X25519 function as specified in RFC7748.
376
*
377
* NOTE: we use isogenies between Montgomery Curve25519 and short Weierstrass
378
* WEI25519 to perform the Elliptic Curves computations.
379
*/
380
int x25519(const u8 k[X25519_SIZE], const u8 u[X25519_SIZE], u8 res[X25519_SIZE])
381
{
382
return x25519_448_core(k, u, res, X25519_SIZE);
383
}
384
385
int x25519_gen_priv_key(u8 priv_key[X25519_SIZE])
386
{
387
return x25519_448_gen_priv_key(priv_key, X25519_SIZE);
388
}
389
390
int x25519_init_pub_key(const u8 priv_key[X25519_SIZE], u8 pub_key[X25519_SIZE])
391
{
392
return x25519_448_init_pub_key(priv_key, pub_key, X25519_SIZE);
393
}
394
395
int x25519_derive_secret(const u8 priv_key[X25519_SIZE], const u8 peer_pub_key[X25519_SIZE], u8 shared_secret[X25519_SIZE])
396
{
397
return x25519_448_derive_secret(priv_key, peer_pub_key, shared_secret, X25519_SIZE);
398
}
399
#endif
400
401
#if defined(WITH_X448)
402
/* The X448 function as specified in RFC7748.
403
*
404
* NOTE: we use isogenies between Montgomery Curve448 and short Weierstrass
405
* WEI448 to perform the Elliptic Curves computations.
406
*/
407
int x448(const u8 k[X448_SIZE], const u8 u[X448_SIZE], u8 res[X448_SIZE])
408
{
409
return x25519_448_core(k, u, res, X448_SIZE);
410
}
411
412
int x448_gen_priv_key(u8 priv_key[X448_SIZE])
413
{
414
return x25519_448_gen_priv_key(priv_key, X448_SIZE);
415
}
416
417
int x448_init_pub_key(const u8 priv_key[X448_SIZE], u8 pub_key[X448_SIZE])
418
{
419
return x25519_448_init_pub_key(priv_key, pub_key, X448_SIZE);
420
}
421
422
int x448_derive_secret(const u8 priv_key[X448_SIZE], const u8 peer_pub_key[X448_SIZE], u8 shared_secret[X448_SIZE])
423
{
424
return x25519_448_derive_secret(priv_key, peer_pub_key, shared_secret, X448_SIZE);
425
}
426
#endif
427
428
#else /* !(defined(WITH_X25519) || defined(WITH_X448)) */
429
430
/*
431
* Dummy definition to avoid the empty translation unit ISO C warning
432
*/
433
typedef int dummy;
434
435
#endif /* defined(WITH_X25519) || defined(WITH_X448) */
436
437