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 */ 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