1 /*
2 * Copyright (C) 2021 - This file is part of libecc project
3 *
4 * Authors:
5 * Ryad BENADJILA <ryadbenadjila@gmail.com>
6 * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr>
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 */
decode_scalar(u8 * scalar_decoded,const u8 * scalar,u8 len)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 */
decode_u_coordinate(u8 * u_decoded,const u8 * u,u8 len)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 */
encode_u_coordinate(u8 * u_encoded,const u8 * u,u8 len)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 */
compute_v_from_u(fp_src_t u,fp_t v,ec_montgomery_crv_src_t crv)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 */
x25519_448_core(const u8 * k,const u8 * u,u8 * res,u8 len)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
x25519_448_gen_priv_key(u8 * priv_key,u8 len)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
x25519_448_init_pub_key(const u8 * priv_key,u8 * pub_key,u8 len)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
x25519_448_derive_secret(const u8 * priv_key,const u8 * peer_pub_key,u8 * shared_secret,u8 len)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 */
x25519(const u8 k[X25519_SIZE],const u8 u[X25519_SIZE],u8 res[X25519_SIZE])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
x25519_gen_priv_key(u8 priv_key[X25519_SIZE])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
x25519_init_pub_key(const u8 priv_key[X25519_SIZE],u8 pub_key[X25519_SIZE])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
x25519_derive_secret(const u8 priv_key[X25519_SIZE],const u8 peer_pub_key[X25519_SIZE],u8 shared_secret[X25519_SIZE])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 */
x448(const u8 k[X448_SIZE],const u8 u[X448_SIZE],u8 res[X448_SIZE])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
x448_gen_priv_key(u8 priv_key[X448_SIZE])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
x448_init_pub_key(const u8 priv_key[X448_SIZE],u8 pub_key[X448_SIZE])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
x448_derive_secret(const u8 priv_key[X448_SIZE],const u8 peer_pub_key[X448_SIZE],u8 shared_secret[X448_SIZE])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